fix: address code review findings for OpenCode integration

- Add _isStopped guard to OpenCode 3s readiness timeout (session.ts)
- Block respawn for opencode sessions on interactive-respawn and
  respawn/enable routes (server.ts)
- Fail fast in direct PTY fallback for OpenCode mode (session.ts)
- Validate configContent as JSON at schema level (schemas.ts)
- Update JSDoc example for createSession options API (tmux-manager.ts)
- Un-hide Context tab for OpenCode sessions (index.html)
- Add OpenCode UI tests (opencode-resize.test.ts)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-02-26 15:58:26 +01:00
co-authored by Claude Opus 4.6
parent 73d9469580
commit a899a8b67a
6 changed files with 367 additions and 5 deletions
+1 -1
View File
@@ -510,7 +510,7 @@
<!-- Modal Tabs -->
<div class="modal-tabs">
<button class="modal-tab-btn active" data-tab="respawn" data-claude-only onclick="app.switchOptionsTab('respawn')">Respawn</button>
<button class="modal-tab-btn" data-tab="context" data-claude-only onclick="app.switchOptionsTab('context')">Context</button>
<button class="modal-tab-btn" data-tab="context" onclick="app.switchOptionsTab('context')">Context</button>
<button class="modal-tab-btn" data-tab="ralph" data-claude-only onclick="app.switchOptionsTab('ralph')">Ralph / Todo</button>
<button class="modal-tab-btn" data-tab="summary" onclick="app.switchOptionsTab('summary')">Summary</button>
</div>
+4 -1
View File
@@ -70,7 +70,10 @@ const OpenCodeConfigSchema = z.object({
autoAllowTools: z.boolean().optional(),
continueSession: z.string().max(100).regex(/^[a-zA-Z0-9_-]+$/).optional(),
forkSession: z.boolean().optional(),
configContent: z.string().max(10000).optional(),
configContent: z.string().max(10000).refine(
(val) => { try { JSON.parse(val); return true; } catch { return false; } },
{ message: 'configContent must be valid JSON' },
).optional(),
}).optional();
export const CreateSessionSchema = z.object({
+11 -2
View File
@@ -2026,10 +2026,14 @@ export class WebServer extends EventEmitter {
return createErrorResponse(ApiErrorCode.SESSION_BUSY, 'Session is busy');
}
// Respawn is not supported for opencode sessions
if (session.mode === 'opencode') {
return createErrorResponse(ApiErrorCode.INVALID_INPUT, 'Respawn is not supported for opencode sessions');
}
try {
// Auto-detect completion phrase from CLAUDE.md BEFORE starting (only if globally enabled and not explicitly disabled by user)
// Ralph tracker is not supported for opencode sessions
if (session.mode !== 'opencode' && this.store.getConfig().ralphEnabled && !session.ralphTracker.autoEnableDisabled) {
if (this.store.getConfig().ralphEnabled && !session.ralphTracker.autoEnableDisabled) {
autoConfigureRalph(session, session.workingDir, () => {});
if (!session.ralphTracker.enabled) {
session.ralphTracker.enable();
@@ -2084,6 +2088,11 @@ export class WebServer extends EventEmitter {
return createErrorResponse(ApiErrorCode.NOT_FOUND, 'Session not found');
}
// Respawn is not supported for opencode sessions
if (session.mode === 'opencode') {
return createErrorResponse(ApiErrorCode.INVALID_INPUT, 'Respawn is not supported for opencode sessions');
}
// Check if session is running (has a PID)
if (!session.pid) {
return createErrorResponse(ApiErrorCode.OPERATION_FAILED, 'Session is not running. Start it first.');