mstro-app 0.5.0 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +129 -190
- package/PRIVACY.md +11 -11
- package/README.md +75 -28
- package/bin/commands/config.js +1 -2
- package/bin/mstro.js +55 -5
- package/bin/postinstall.js +0 -1
- package/dist/server/cli/eta-estimator.d.ts +55 -0
- package/dist/server/cli/eta-estimator.d.ts.map +1 -0
- package/dist/server/cli/eta-estimator.js +222 -0
- package/dist/server/cli/eta-estimator.js.map +1 -0
- package/dist/server/cli/headless/claude-invoker-process.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-process.js +0 -1
- package/dist/server/cli/headless/claude-invoker-process.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stall.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stall.js +0 -1
- package/dist/server/cli/headless/claude-invoker-stall.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stream.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stream.js +0 -1
- package/dist/server/cli/headless/claude-invoker-stream.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker-tools.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-tools.js +0 -1
- package/dist/server/cli/headless/claude-invoker-tools.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker.js +0 -1
- package/dist/server/cli/headless/claude-invoker.js.map +1 -1
- package/dist/server/cli/headless/haiku-assessments.d.ts.map +1 -1
- package/dist/server/cli/headless/haiku-assessments.js +0 -1
- package/dist/server/cli/headless/haiku-assessments.js.map +1 -1
- package/dist/server/cli/headless/headless-logger.d.ts.map +1 -1
- package/dist/server/cli/headless/headless-logger.js +0 -1
- package/dist/server/cli/headless/headless-logger.js.map +1 -1
- package/dist/server/cli/headless/index.d.ts.map +1 -1
- package/dist/server/cli/headless/index.js +0 -1
- package/dist/server/cli/headless/index.js.map +1 -1
- package/dist/server/cli/headless/native-timeout-detector.d.ts.map +1 -1
- package/dist/server/cli/headless/native-timeout-detector.js +0 -1
- package/dist/server/cli/headless/native-timeout-detector.js.map +1 -1
- package/dist/server/cli/headless/output-utils.d.ts.map +1 -1
- package/dist/server/cli/headless/output-utils.js +0 -1
- package/dist/server/cli/headless/output-utils.js.map +1 -1
- package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -1
- package/dist/server/cli/headless/prompt-utils.js +0 -1
- package/dist/server/cli/headless/prompt-utils.js.map +1 -1
- package/dist/server/cli/headless/resilient-runner.d.ts.map +1 -1
- package/dist/server/cli/headless/resilient-runner.js +0 -1
- package/dist/server/cli/headless/resilient-runner.js.map +1 -1
- package/dist/server/cli/headless/retry-strategies.d.ts.map +1 -1
- package/dist/server/cli/headless/retry-strategies.js +0 -1
- package/dist/server/cli/headless/retry-strategies.js.map +1 -1
- package/dist/server/cli/headless/runner.d.ts.map +1 -1
- package/dist/server/cli/headless/runner.js +0 -1
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/stall-assessor.d.ts +50 -0
- package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
- package/dist/server/cli/headless/stall-assessor.js +64 -10
- package/dist/server/cli/headless/stall-assessor.js.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.d.ts +21 -0
- package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.js +19 -13
- package/dist/server/cli/headless/tool-watchdog.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/cli/headless/types.js +0 -1
- package/dist/server/cli/headless/types.js.map +1 -1
- package/dist/server/cli/improvisation-attachments.d.ts.map +1 -1
- package/dist/server/cli/improvisation-attachments.js +0 -1
- package/dist/server/cli/improvisation-attachments.js.map +1 -1
- package/dist/server/cli/improvisation-history-store.d.ts.map +1 -1
- package/dist/server/cli/improvisation-history-store.js +5 -2
- package/dist/server/cli/improvisation-history-store.js.map +1 -1
- package/dist/server/cli/improvisation-movements.d.ts.map +1 -1
- package/dist/server/cli/improvisation-movements.js +0 -1
- package/dist/server/cli/improvisation-movements.js.map +1 -1
- package/dist/server/cli/improvisation-output-queue.d.ts +5 -1
- package/dist/server/cli/improvisation-output-queue.d.ts.map +1 -1
- package/dist/server/cli/improvisation-output-queue.js +30 -8
- package/dist/server/cli/improvisation-output-queue.js.map +1 -1
- package/dist/server/cli/improvisation-retry.d.ts.map +1 -1
- package/dist/server/cli/improvisation-retry.js +0 -1
- package/dist/server/cli/improvisation-retry.js.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +29 -0
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +50 -2
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/cli/improvisation-types.d.ts +2 -0
- package/dist/server/cli/improvisation-types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-types.js +0 -1
- package/dist/server/cli/improvisation-types.js.map +1 -1
- package/dist/server/cli/retry/retry-best-result.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-best-result.js +0 -1
- package/dist/server/cli/retry/retry-best-result.js.map +1 -1
- package/dist/server/cli/retry/retry-context-loss.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-context-loss.js +0 -1
- package/dist/server/cli/retry/retry-context-loss.js.map +1 -1
- package/dist/server/cli/retry/retry-premature-completion.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-premature-completion.js +1 -2
- package/dist/server/cli/retry/retry-premature-completion.js.map +1 -1
- package/dist/server/cli/retry/retry-recovery-strategies.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-recovery-strategies.js +0 -1
- package/dist/server/cli/retry/retry-recovery-strategies.js.map +1 -1
- package/dist/server/cli/retry/retry-resume-strategy.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-resume-strategy.js +0 -1
- package/dist/server/cli/retry/retry-resume-strategy.js.map +1 -1
- package/dist/server/cli/retry/retry-runner-factory.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-runner-factory.js +0 -1
- package/dist/server/cli/retry/retry-runner-factory.js.map +1 -1
- package/dist/server/cli/retry/retry-tool-results.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-tool-results.js +0 -1
- package/dist/server/cli/retry/retry-tool-results.js.map +1 -1
- package/dist/server/cli/retry/retry-types.d.ts.map +1 -1
- package/dist/server/cli/retry/retry-types.js +0 -1
- package/dist/server/cli/retry/retry-types.js.map +1 -1
- package/dist/server/engines/EngineEvent.d.ts +126 -0
- package/dist/server/engines/EngineEvent.d.ts.map +1 -0
- package/dist/server/engines/EngineEvent.js +11 -0
- package/dist/server/engines/EngineEvent.js.map +1 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.d.ts +47 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.d.ts.map +1 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.js +338 -0
- package/dist/server/engines/claude/ClaudeCodeEngine.js.map +1 -0
- package/dist/server/engines/factory.d.ts +21 -0
- package/dist/server/engines/factory.d.ts.map +1 -0
- package/dist/server/engines/factory.js +152 -0
- package/dist/server/engines/factory.js.map +1 -0
- package/dist/server/engines/opencode/OpenCodeEngine.d.ts +148 -0
- package/dist/server/engines/opencode/OpenCodeEngine.d.ts.map +1 -0
- package/dist/server/engines/opencode/OpenCodeEngine.js +630 -0
- package/dist/server/engines/opencode/OpenCodeEngine.js.map +1 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.d.ts +172 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.d.ts.map +1 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.js +390 -0
- package/dist/server/engines/opencode/OpenCodeServerManager.js.map +1 -0
- package/dist/server/engines/opencode/model-catalog.d.ts +94 -0
- package/dist/server/engines/opencode/model-catalog.d.ts.map +1 -0
- package/dist/server/engines/opencode/model-catalog.js +141 -0
- package/dist/server/engines/opencode/model-catalog.js.map +1 -0
- package/dist/server/engines/types.d.ts +146 -0
- package/dist/server/engines/types.d.ts.map +1 -0
- package/dist/server/engines/types.js +4 -0
- package/dist/server/engines/types.js.map +1 -0
- package/dist/server/index.js +1 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/bouncer-cli.js +0 -1
- package/dist/server/mcp/bouncer-cli.js.map +1 -1
- package/dist/server/mcp/bouncer-haiku.d.ts +17 -4
- package/dist/server/mcp/bouncer-haiku.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-haiku.js +8 -125
- package/dist/server/mcp/bouncer-haiku.js.map +1 -1
- package/dist/server/mcp/bouncer-integration.d.ts +45 -0
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +69 -6
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- package/dist/server/mcp/classifier/BouncerClassifier.d.ts +34 -0
- package/dist/server/mcp/classifier/BouncerClassifier.d.ts.map +1 -0
- package/dist/server/mcp/classifier/BouncerClassifier.js +4 -0
- package/dist/server/mcp/classifier/BouncerClassifier.js.map +1 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.d.ts +17 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.d.ts.map +1 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.js +142 -0
- package/dist/server/mcp/classifier/ClaudeBouncerClassifier.js.map +1 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.d.ts +68 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.d.ts.map +1 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.js +182 -0
- package/dist/server/mcp/classifier/OpenCodeBouncerClassifier.js.map +1 -0
- package/dist/server/mcp/classifier/factory.d.ts +70 -0
- package/dist/server/mcp/classifier/factory.d.ts.map +1 -0
- package/dist/server/mcp/classifier/factory.js +155 -0
- package/dist/server/mcp/classifier/factory.js.map +1 -0
- package/dist/server/mcp/security-analysis.d.ts.map +1 -1
- package/dist/server/mcp/security-analysis.js +0 -1
- package/dist/server/mcp/security-analysis.js.map +1 -1
- package/dist/server/mcp/security-audit.d.ts.map +1 -1
- package/dist/server/mcp/security-audit.js +0 -1
- package/dist/server/mcp/security-audit.js.map +1 -1
- package/dist/server/mcp/security-patterns.d.ts.map +1 -1
- package/dist/server/mcp/security-patterns.js +0 -1
- package/dist/server/mcp/security-patterns.js.map +1 -1
- package/dist/server/mcp/server.js +0 -1
- package/dist/server/mcp/server.js.map +1 -1
- package/dist/server/routes/files.d.ts.map +1 -1
- package/dist/server/routes/files.js +0 -1
- package/dist/server/routes/files.js.map +1 -1
- package/dist/server/routes/improvise.d.ts.map +1 -1
- package/dist/server/routes/improvise.js +0 -1
- package/dist/server/routes/improvise.js.map +1 -1
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +0 -1
- package/dist/server/routes/index.js.map +1 -1
- package/dist/server/routes/instances.d.ts.map +1 -1
- package/dist/server/routes/instances.js +0 -1
- package/dist/server/routes/instances.js.map +1 -1
- package/dist/server/routes/notifications.d.ts.map +1 -1
- package/dist/server/routes/notifications.js +0 -1
- package/dist/server/routes/notifications.js.map +1 -1
- package/dist/server/server-setup.d.ts.map +1 -1
- package/dist/server/server-setup.js +0 -1
- package/dist/server/server-setup.js.map +1 -1
- package/dist/server/services/analytics.d.ts.map +1 -1
- package/dist/server/services/analytics.js +0 -1
- package/dist/server/services/analytics.js.map +1 -1
- package/dist/server/services/auth.d.ts.map +1 -1
- package/dist/server/services/auth.js +0 -1
- package/dist/server/services/auth.js.map +1 -1
- package/dist/server/services/client-id.d.ts.map +1 -1
- package/dist/server/services/client-id.js +0 -1
- package/dist/server/services/client-id.js.map +1 -1
- package/dist/server/services/file-explorer-ops.d.ts.map +1 -1
- package/dist/server/services/file-explorer-ops.js +0 -1
- package/dist/server/services/file-explorer-ops.js.map +1 -1
- package/dist/server/services/files.d.ts.map +1 -1
- package/dist/server/services/files.js +0 -1
- package/dist/server/services/files.js.map +1 -1
- package/dist/server/services/instances.d.ts.map +1 -1
- package/dist/server/services/instances.js +0 -1
- package/dist/server/services/instances.js.map +1 -1
- package/dist/server/services/pathUtils.d.ts.map +1 -1
- package/dist/server/services/pathUtils.js +0 -1
- package/dist/server/services/pathUtils.js.map +1 -1
- package/dist/server/services/plan/agent-loader.d.ts.map +1 -1
- package/dist/server/services/plan/agent-loader.js +0 -1
- package/dist/server/services/plan/agent-loader.js.map +1 -1
- package/dist/server/services/plan/agent-resolver.d.ts +26 -0
- package/dist/server/services/plan/agent-resolver.d.ts.map +1 -0
- package/dist/server/services/plan/agent-resolver.js +102 -0
- package/dist/server/services/plan/agent-resolver.js.map +1 -0
- package/dist/server/services/plan/board-config.d.ts.map +1 -1
- package/dist/server/services/plan/board-config.js +0 -1
- package/dist/server/services/plan/board-config.js.map +1 -1
- package/dist/server/services/plan/composer.d.ts.map +1 -1
- package/dist/server/services/plan/composer.js +59 -12
- package/dist/server/services/plan/composer.js.map +1 -1
- package/dist/server/services/plan/config-installer.d.ts.map +1 -1
- package/dist/server/services/plan/config-installer.js +0 -1
- package/dist/server/services/plan/config-installer.js.map +1 -1
- package/dist/server/services/plan/dependency-resolver.d.ts.map +1 -1
- package/dist/server/services/plan/dependency-resolver.js +0 -1
- package/dist/server/services/plan/dependency-resolver.js.map +1 -1
- package/dist/server/services/plan/executor.d.ts.map +1 -1
- package/dist/server/services/plan/executor.js +48 -4
- package/dist/server/services/plan/executor.js.map +1 -1
- package/dist/server/services/plan/front-matter.d.ts.map +1 -1
- package/dist/server/services/plan/front-matter.js +0 -1
- package/dist/server/services/plan/front-matter.js.map +1 -1
- package/dist/server/services/plan/issue-classification.d.ts.map +1 -1
- package/dist/server/services/plan/issue-classification.js +0 -1
- package/dist/server/services/plan/issue-classification.js.map +1 -1
- package/dist/server/services/plan/issue-loader.d.ts.map +1 -1
- package/dist/server/services/plan/issue-loader.js +0 -1
- package/dist/server/services/plan/issue-loader.js.map +1 -1
- package/dist/server/services/plan/issue-prompt-builder.d.ts.map +1 -1
- package/dist/server/services/plan/issue-prompt-builder.js +33 -2
- package/dist/server/services/plan/issue-prompt-builder.js.map +1 -1
- package/dist/server/services/plan/issue-retry.d.ts +3 -1
- package/dist/server/services/plan/issue-retry.d.ts.map +1 -1
- package/dist/server/services/plan/issue-retry.js +2 -1
- package/dist/server/services/plan/issue-retry.js.map +1 -1
- package/dist/server/services/plan/issue-writer.d.ts.map +1 -1
- package/dist/server/services/plan/issue-writer.js +0 -1
- package/dist/server/services/plan/issue-writer.js.map +1 -1
- package/dist/server/services/plan/output-manager.d.ts.map +1 -1
- package/dist/server/services/plan/output-manager.js +0 -1
- package/dist/server/services/plan/output-manager.js.map +1 -1
- package/dist/server/services/plan/parser-core.d.ts.map +1 -1
- package/dist/server/services/plan/parser-core.js +1 -1
- package/dist/server/services/plan/parser-core.js.map +1 -1
- package/dist/server/services/plan/parser-migration.d.ts.map +1 -1
- package/dist/server/services/plan/parser-migration.js +0 -1
- package/dist/server/services/plan/parser-migration.js.map +1 -1
- package/dist/server/services/plan/parser.d.ts.map +1 -1
- package/dist/server/services/plan/parser.js +0 -1
- package/dist/server/services/plan/parser.js.map +1 -1
- package/dist/server/services/plan/progress-log.d.ts.map +1 -1
- package/dist/server/services/plan/progress-log.js +0 -1
- package/dist/server/services/plan/progress-log.js.map +1 -1
- package/dist/server/services/plan/prompt-builder.d.ts.map +1 -1
- package/dist/server/services/plan/prompt-builder.js +0 -1
- package/dist/server/services/plan/prompt-builder.js.map +1 -1
- package/dist/server/services/plan/readiness-planner.d.ts.map +1 -1
- package/dist/server/services/plan/readiness-planner.js +0 -1
- package/dist/server/services/plan/readiness-planner.js.map +1 -1
- package/dist/server/services/plan/review-gate.d.ts.map +1 -1
- package/dist/server/services/plan/review-gate.js +0 -1
- package/dist/server/services/plan/review-gate.js.map +1 -1
- package/dist/server/services/plan/state-reconciler.d.ts.map +1 -1
- package/dist/server/services/plan/state-reconciler.js +0 -1
- package/dist/server/services/plan/state-reconciler.js.map +1 -1
- package/dist/server/services/plan/types.d.ts +1 -0
- package/dist/server/services/plan/types.d.ts.map +1 -1
- package/dist/server/services/plan/types.js +0 -1
- package/dist/server/services/plan/types.js.map +1 -1
- package/dist/server/services/plan/watcher.d.ts.map +1 -1
- package/dist/server/services/plan/watcher.js +0 -1
- package/dist/server/services/plan/watcher.js.map +1 -1
- package/dist/server/services/platform-credentials.d.ts.map +1 -1
- package/dist/server/services/platform-credentials.js +0 -1
- package/dist/server/services/platform-credentials.js.map +1 -1
- package/dist/server/services/platform-token-lifecycle.d.ts +70 -0
- package/dist/server/services/platform-token-lifecycle.d.ts.map +1 -0
- package/dist/server/services/platform-token-lifecycle.js +156 -0
- package/dist/server/services/platform-token-lifecycle.js.map +1 -0
- package/dist/server/services/platform.d.ts +21 -56
- package/dist/server/services/platform.d.ts.map +1 -1
- package/dist/server/services/platform.js +98 -142
- package/dist/server/services/platform.js.map +1 -1
- package/dist/server/services/sentry.d.ts.map +1 -1
- package/dist/server/services/sentry.js +0 -1
- package/dist/server/services/sentry.js.map +1 -1
- package/dist/server/services/settings.d.ts +76 -2
- package/dist/server/services/settings.d.ts.map +1 -1
- package/dist/server/services/settings.js +127 -5
- package/dist/server/services/settings.js.map +1 -1
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-manager.js +0 -1
- package/dist/server/services/terminal/pty-manager.js.map +1 -1
- package/dist/server/services/terminal/pty-utils.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-utils.js +0 -1
- package/dist/server/services/terminal/pty-utils.js.map +1 -1
- package/dist/server/services/websocket/autocomplete.d.ts.map +1 -1
- package/dist/server/services/websocket/autocomplete.js +0 -1
- package/dist/server/services/websocket/autocomplete.js.map +1 -1
- package/dist/server/services/websocket/file-definition-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/file-definition-handlers.js +0 -1
- package/dist/server/services/websocket/file-definition-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-download-handler.d.ts.map +1 -1
- package/dist/server/services/websocket/file-download-handler.js +0 -1
- package/dist/server/services/websocket/file-download-handler.js.map +1 -1
- package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/file-explorer-handlers.js +0 -1
- package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-search-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/file-search-handlers.js +0 -1
- package/dist/server/services/websocket/file-search-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-upload-handler.d.ts +2 -3
- package/dist/server/services/websocket/file-upload-handler.d.ts.map +1 -1
- package/dist/server/services/websocket/file-upload-handler.js +4 -7
- package/dist/server/services/websocket/file-upload-handler.js.map +1 -1
- package/dist/server/services/websocket/file-utils.d.ts.map +1 -1
- package/dist/server/services/websocket/file-utils.js +0 -1
- package/dist/server/services/websocket/file-utils.js.map +1 -1
- package/dist/server/services/websocket/git-branch-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-branch-handlers.js +19 -7
- package/dist/server/services/websocket/git-branch-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-diff-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-diff-handlers.js +0 -1
- package/dist/server/services/websocket/git-diff-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-handlers.js +58 -6
- package/dist/server/services/websocket/git-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-head-watcher.d.ts.map +1 -1
- package/dist/server/services/websocket/git-head-watcher.js +0 -1
- package/dist/server/services/websocket/git-head-watcher.js.map +1 -1
- package/dist/server/services/websocket/git-log-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-log-handlers.js +0 -1
- package/dist/server/services/websocket/git-log-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-pr-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-pr-handlers.js +0 -1
- package/dist/server/services/websocket/git-pr-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-tag-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-tag-handlers.js +0 -1
- package/dist/server/services/websocket/git-tag-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-utils.d.ts +18 -3
- package/dist/server/services/websocket/git-utils.d.ts.map +1 -1
- package/dist/server/services/websocket/git-utils.js +58 -8
- package/dist/server/services/websocket/git-utils.js.map +1 -1
- package/dist/server/services/websocket/git-worktree-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-worktree-handlers.js +230 -14
- package/dist/server/services/websocket/git-worktree-handlers.js.map +1 -1
- package/dist/server/services/websocket/handler-context.d.ts.map +1 -1
- package/dist/server/services/websocket/handler-context.js +0 -1
- package/dist/server/services/websocket/handler-context.js.map +1 -1
- package/dist/server/services/websocket/handler.d.ts +17 -1
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +57 -6
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/index.d.ts.map +1 -1
- package/dist/server/services/websocket/index.js +0 -1
- package/dist/server/services/websocket/index.js.map +1 -1
- package/dist/server/services/websocket/msg-id-tracker.d.ts.map +1 -1
- package/dist/server/services/websocket/msg-id-tracker.js +0 -1
- package/dist/server/services/websocket/msg-id-tracker.js.map +1 -1
- package/dist/server/services/websocket/plan-board-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-board-handlers.js +0 -1
- package/dist/server/services/websocket/plan-board-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-execution-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-execution-handlers.js +6 -2
- package/dist/server/services/websocket/plan-execution-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-handlers.js +0 -1
- package/dist/server/services/websocket/plan-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-helpers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-helpers.js +0 -1
- package/dist/server/services/websocket/plan-helpers.js.map +1 -1
- package/dist/server/services/websocket/plan-issue-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-issue-handlers.js +0 -1
- package/dist/server/services/websocket/plan-issue-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-sprint-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-sprint-handlers.js +0 -1
- package/dist/server/services/websocket/plan-sprint-handlers.js.map +1 -1
- package/dist/server/services/websocket/quality-complexity.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-complexity.js +78 -27
- package/dist/server/services/websocket/quality-complexity.js.map +1 -1
- package/dist/server/services/websocket/quality-eta.d.ts +47 -0
- package/dist/server/services/websocket/quality-eta.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-eta.js +110 -0
- package/dist/server/services/websocket/quality-eta.js.map +1 -0
- package/dist/server/services/websocket/quality-grading.d.ts +69 -0
- package/dist/server/services/websocket/quality-grading.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-grading.js +650 -0
- package/dist/server/services/websocket/quality-grading.js.map +1 -0
- package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-handlers.js +145 -8
- package/dist/server/services/websocket/quality-handlers.js.map +1 -1
- package/dist/server/services/websocket/quality-linting.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-linting.js +0 -1
- package/dist/server/services/websocket/quality-linting.js.map +1 -1
- package/dist/server/services/websocket/quality-operations.d.ts +34 -0
- package/dist/server/services/websocket/quality-operations.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-operations.js +47 -0
- package/dist/server/services/websocket/quality-operations.js.map +1 -0
- package/dist/server/services/websocket/quality-persistence.d.ts +23 -0
- package/dist/server/services/websocket/quality-persistence.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-persistence.js +38 -12
- package/dist/server/services/websocket/quality-persistence.js.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.d.ts +1 -1
- package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.js +105 -57
- package/dist/server/services/websocket/quality-review-agent.js.map +1 -1
- package/dist/server/services/websocket/quality-service.d.ts +12 -2
- package/dist/server/services/websocket/quality-service.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-service.js +387 -72
- package/dist/server/services/websocket/quality-service.js.map +1 -1
- package/dist/server/services/websocket/quality-tools.d.ts +22 -1
- package/dist/server/services/websocket/quality-tools.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-tools.js +55 -3
- package/dist/server/services/websocket/quality-tools.js.map +1 -1
- package/dist/server/services/websocket/quality-types.d.ts +52 -3
- package/dist/server/services/websocket/quality-types.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-types.js +1 -2
- package/dist/server/services/websocket/quality-types.js.map +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts +3 -1
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +57 -10
- package/dist/server/services/websocket/session-handlers.js.map +1 -1
- package/dist/server/services/websocket/session-history.d.ts.map +1 -1
- package/dist/server/services/websocket/session-history.js +3 -1
- package/dist/server/services/websocket/session-history.js.map +1 -1
- package/dist/server/services/websocket/session-initialization.d.ts.map +1 -1
- package/dist/server/services/websocket/session-initialization.js +158 -43
- package/dist/server/services/websocket/session-initialization.js.map +1 -1
- package/dist/server/services/websocket/session-registry.d.ts +25 -0
- package/dist/server/services/websocket/session-registry.d.ts.map +1 -1
- package/dist/server/services/websocket/session-registry.js +19 -1
- package/dist/server/services/websocket/session-registry.js.map +1 -1
- package/dist/server/services/websocket/settings-handlers.d.ts +1 -1
- package/dist/server/services/websocket/settings-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/settings-handlers.js +35 -5
- package/dist/server/services/websocket/settings-handlers.js.map +1 -1
- package/dist/server/services/websocket/skill-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/skill-handlers.js +0 -1
- package/dist/server/services/websocket/skill-handlers.js.map +1 -1
- package/dist/server/services/websocket/skill-watcher.d.ts.map +1 -1
- package/dist/server/services/websocket/skill-watcher.js +0 -1
- package/dist/server/services/websocket/skill-watcher.js.map +1 -1
- package/dist/server/services/websocket/tab-broadcast.d.ts +7 -2
- package/dist/server/services/websocket/tab-broadcast.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-broadcast.js +10 -3
- package/dist/server/services/websocket/tab-broadcast.js.map +1 -1
- package/dist/server/services/websocket/tab-event-buffer.d.ts +97 -8
- package/dist/server/services/websocket/tab-event-buffer.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-event-buffer.js +138 -13
- package/dist/server/services/websocket/tab-event-buffer.js.map +1 -1
- package/dist/server/services/websocket/tab-event-replay.d.ts +29 -13
- package/dist/server/services/websocket/tab-event-replay.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-event-replay.js +55 -3
- package/dist/server/services/websocket/tab-event-replay.js.map +1 -1
- package/dist/server/services/websocket/tab-handlers.d.ts +9 -1
- package/dist/server/services/websocket/tab-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-handlers.js +47 -3
- package/dist/server/services/websocket/tab-handlers.js.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.js +39 -4
- package/dist/server/services/websocket/terminal-handlers.js.map +1 -1
- package/dist/server/services/websocket/types.d.ts +30 -7
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/dist/server/services/websocket/types.js +12 -7
- package/dist/server/services/websocket/types.js.map +1 -1
- package/dist/server/utils/agent-manager.d.ts.map +1 -1
- package/dist/server/utils/agent-manager.js +0 -1
- package/dist/server/utils/agent-manager.js.map +1 -1
- package/dist/server/utils/paths.d.ts.map +1 -1
- package/dist/server/utils/paths.js +0 -1
- package/dist/server/utils/paths.js.map +1 -1
- package/dist/server/utils/port-manager.d.ts.map +1 -1
- package/dist/server/utils/port-manager.js +0 -1
- package/dist/server/utils/port-manager.js.map +1 -1
- package/dist/server/utils/port.d.ts.map +1 -1
- package/dist/server/utils/port.js +0 -1
- package/dist/server/utils/port.js.map +1 -1
- package/package.json +6 -4
- package/server/cli/eta-estimator.ts +249 -0
- package/server/cli/headless/claude-invoker-process.ts +0 -1
- package/server/cli/headless/claude-invoker-stall.ts +0 -1
- package/server/cli/headless/claude-invoker-stream.ts +0 -1
- package/server/cli/headless/claude-invoker-tools.ts +0 -1
- package/server/cli/headless/claude-invoker.ts +0 -1
- package/server/cli/headless/haiku-assessments.ts +0 -1
- package/server/cli/headless/headless-logger.ts +0 -1
- package/server/cli/headless/index.ts +0 -1
- package/server/cli/headless/native-timeout-detector.ts +0 -1
- package/server/cli/headless/output-utils.ts +0 -1
- package/server/cli/headless/prompt-utils.ts +0 -1
- package/server/cli/headless/resilient-runner.ts +0 -1
- package/server/cli/headless/retry-strategies.ts +0 -1
- package/server/cli/headless/runner.ts +0 -1
- package/server/cli/headless/stall-assessor.ts +93 -1
- package/server/cli/headless/tool-watchdog.ts +21 -1
- package/server/cli/headless/types.ts +0 -1
- package/server/cli/improvisation-attachments.ts +0 -1
- package/server/cli/improvisation-history-store.ts +4 -2
- package/server/cli/improvisation-movements.ts +0 -1
- package/server/cli/improvisation-output-queue.ts +29 -8
- package/server/cli/improvisation-retry.ts +0 -1
- package/server/cli/improvisation-session-manager.ts +54 -2
- package/server/cli/improvisation-types.ts +2 -1
- package/server/cli/retry/retry-best-result.ts +0 -1
- package/server/cli/retry/retry-context-loss.ts +0 -1
- package/server/cli/retry/retry-premature-completion.ts +1 -2
- package/server/cli/retry/retry-recovery-strategies.ts +0 -1
- package/server/cli/retry/retry-resume-strategy.ts +0 -1
- package/server/cli/retry/retry-runner-factory.ts +0 -1
- package/server/cli/retry/retry-tool-results.ts +0 -1
- package/server/cli/retry/retry-types.ts +0 -1
- package/server/engines/EngineEvent.ts +156 -0
- package/server/engines/claude/ClaudeCodeEngine.ts +404 -0
- package/server/engines/factory.ts +176 -0
- package/server/engines/opencode/OpenCodeEngine.ts +786 -0
- package/server/engines/opencode/OpenCodeServerManager.ts +577 -0
- package/server/engines/opencode/model-catalog.ts +217 -0
- package/server/engines/types.ts +173 -0
- package/server/index.ts +1 -2
- package/server/mcp/bouncer-cli.ts +0 -1
- package/server/mcp/bouncer-haiku.ts +21 -146
- package/server/mcp/bouncer-integration.ts +107 -6
- package/server/mcp/classifier/BouncerClassifier.ts +40 -0
- package/server/mcp/classifier/ClaudeBouncerClassifier.ts +189 -0
- package/server/mcp/classifier/OpenCodeBouncerClassifier.ts +305 -0
- package/server/mcp/classifier/factory.ts +195 -0
- package/server/mcp/security-analysis.ts +0 -1
- package/server/mcp/security-audit.ts +0 -1
- package/server/mcp/security-patterns.ts +0 -1
- package/server/mcp/server.ts +0 -1
- package/server/routes/files.ts +0 -1
- package/server/routes/improvise.ts +0 -1
- package/server/routes/index.ts +0 -1
- package/server/routes/instances.ts +0 -1
- package/server/routes/notifications.ts +0 -1
- package/server/server-setup.ts +0 -1
- package/server/services/analytics.ts +0 -1
- package/server/services/auth.ts +0 -1
- package/server/services/client-id.ts +0 -1
- package/server/services/file-explorer-ops.ts +0 -1
- package/server/services/files.ts +0 -1
- package/server/services/instances.ts +0 -1
- package/server/services/pathUtils.ts +0 -1
- package/server/services/plan/agent-loader.ts +0 -1
- package/server/services/plan/agent-resolver.ts +115 -0
- package/server/services/plan/agents/code-review.md +43 -11
- package/server/services/plan/board-config.ts +0 -1
- package/server/services/plan/composer.ts +63 -12
- package/server/services/plan/config-installer.ts +0 -1
- package/server/services/plan/dependency-resolver.ts +0 -1
- package/server/services/plan/executor.ts +48 -4
- package/server/services/plan/front-matter.ts +0 -1
- package/server/services/plan/issue-classification.ts +0 -1
- package/server/services/plan/issue-loader.ts +0 -1
- package/server/services/plan/issue-prompt-builder.ts +39 -2
- package/server/services/plan/issue-retry.ts +5 -2
- package/server/services/plan/issue-writer.ts +0 -1
- package/server/services/plan/output-manager.ts +0 -1
- package/server/services/plan/parser-core.ts +1 -1
- package/server/services/plan/parser-migration.ts +0 -1
- package/server/services/plan/parser.ts +0 -1
- package/server/services/plan/progress-log.ts +0 -1
- package/server/services/plan/prompt-builder.ts +0 -1
- package/server/services/plan/readiness-planner.ts +0 -1
- package/server/services/plan/review-gate.ts +0 -1
- package/server/services/plan/state-reconciler.ts +0 -1
- package/server/services/plan/types.ts +4 -1
- package/server/services/plan/watcher.ts +0 -1
- package/server/services/platform-credentials.ts +0 -1
- package/server/services/platform-token-lifecycle.ts +171 -0
- package/server/services/platform.ts +106 -148
- package/server/services/sentry.ts +0 -1
- package/server/services/settings.ts +161 -5
- package/server/services/terminal/pty-manager.ts +0 -1
- package/server/services/terminal/pty-utils.ts +0 -1
- package/server/services/websocket/autocomplete.ts +0 -1
- package/server/services/websocket/file-definition-handlers.ts +0 -1
- package/server/services/websocket/file-download-handler.ts +0 -1
- package/server/services/websocket/file-explorer-handlers.ts +0 -1
- package/server/services/websocket/file-search-handlers.ts +0 -1
- package/server/services/websocket/file-upload-handler.ts +6 -5
- package/server/services/websocket/file-utils.ts +0 -1
- package/server/services/websocket/git-branch-handlers.ts +20 -7
- package/server/services/websocket/git-diff-handlers.ts +0 -1
- package/server/services/websocket/git-handlers.ts +66 -10
- package/server/services/websocket/git-head-watcher.ts +0 -1
- package/server/services/websocket/git-log-handlers.ts +0 -1
- package/server/services/websocket/git-pr-handlers.ts +0 -1
- package/server/services/websocket/git-tag-handlers.ts +0 -1
- package/server/services/websocket/git-utils.ts +69 -9
- package/server/services/websocket/git-worktree-handlers.ts +260 -17
- package/server/services/websocket/handler-context.ts +0 -1
- package/server/services/websocket/handler.ts +62 -6
- package/server/services/websocket/index.ts +0 -1
- package/server/services/websocket/msg-id-tracker.ts +0 -1
- package/server/services/websocket/plan-board-handlers.ts +0 -1
- package/server/services/websocket/plan-execution-handlers.ts +6 -2
- package/server/services/websocket/plan-handlers.ts +0 -1
- package/server/services/websocket/plan-helpers.ts +0 -1
- package/server/services/websocket/plan-issue-handlers.ts +0 -1
- package/server/services/websocket/plan-sprint-handlers.ts +0 -1
- package/server/services/websocket/quality-complexity.ts +80 -27
- package/server/services/websocket/quality-eta.ts +155 -0
- package/server/services/websocket/quality-grading.ts +834 -0
- package/server/services/websocket/quality-handlers.ts +153 -8
- package/server/services/websocket/quality-linting.ts +0 -1
- package/server/services/websocket/quality-operations.ts +72 -0
- package/server/services/websocket/quality-persistence.ts +47 -8
- package/server/services/websocket/quality-review-agent.ts +154 -65
- package/server/services/websocket/quality-service.ts +415 -68
- package/server/services/websocket/quality-tools.ts +62 -3
- package/server/services/websocket/quality-types.ts +61 -4
- package/server/services/websocket/session-handlers.ts +64 -11
- package/server/services/websocket/session-history.ts +3 -1
- package/server/services/websocket/session-initialization.ts +189 -47
- package/server/services/websocket/session-registry.ts +37 -1
- package/server/services/websocket/settings-handlers.ts +41 -5
- package/server/services/websocket/skill-handlers.ts +0 -1
- package/server/services/websocket/skill-watcher.ts +0 -1
- package/server/services/websocket/tab-broadcast.ts +10 -3
- package/server/services/websocket/tab-event-buffer.ts +143 -12
- package/server/services/websocket/tab-event-replay.ts +70 -4
- package/server/services/websocket/tab-handlers.ts +53 -6
- package/server/services/websocket/terminal-handlers.ts +39 -3
- package/server/services/websocket/types.ts +39 -8
- package/server/utils/agent-manager.ts +0 -1
- package/server/utils/paths.ts +0 -1
- package/server/utils/port-manager.ts +0 -1
- package/server/utils/port.ts +0 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
-
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
2
|
|
|
4
3
|
import { spawn } from 'node:child_process';
|
|
5
4
|
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
@@ -104,7 +103,10 @@ async function tryInstallCommands(tool: QualityTool, dirPath: string): Promise<s
|
|
|
104
103
|
let lastStderr = '';
|
|
105
104
|
for (const cmd of commands) {
|
|
106
105
|
const parts = cmd.trim().split(' ');
|
|
107
|
-
|
|
106
|
+
// Force NODE_ENV=development for install: npm/yarn/pnpm silently skip
|
|
107
|
+
// devDependencies under NODE_ENV=production, making `-D` installs no-op
|
|
108
|
+
// (exit 0, nothing installed) so the post-install detection re-fails.
|
|
109
|
+
const result = await runCommand(parts[0], parts.slice(1), dirPath, { NODE_ENV: 'development' });
|
|
108
110
|
if (result.exitCode === 0) return null;
|
|
109
111
|
lastStderr = result.stderr;
|
|
110
112
|
}
|
|
@@ -258,6 +260,57 @@ export function filesByExt(files: SourceFile[], exts: string[]): string[] {
|
|
|
258
260
|
return out;
|
|
259
261
|
}
|
|
260
262
|
|
|
263
|
+
/** Folders that signal "this whole tree is test code" by convention. */
|
|
264
|
+
const TEST_FOLDER_SEGMENTS = ['__tests__', '__mocks__', 'tests', 'test', 'e2e', 'spec'];
|
|
265
|
+
|
|
266
|
+
/** Filename regexes that mark a single file as a test, regardless of folder. */
|
|
267
|
+
const TEST_FILE_PATTERNS: RegExp[] = [
|
|
268
|
+
/\.(test|spec)\.(ts|tsx|js|jsx|mjs|cjs|mts|cts)$/, // JS/TS .test/.spec
|
|
269
|
+
/_test\.(go|py)$/, // Go / Python *_test
|
|
270
|
+
/^test_.+\.py$/, // Python test_*.py
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
function pathHasTestFolder(path: string): boolean {
|
|
274
|
+
for (const segment of TEST_FOLDER_SEGMENTS) {
|
|
275
|
+
if (path.includes(`/${segment}/`) || path.startsWith(`${segment}/`)) return true;
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function fileNameLooksLikeTest(name: string): boolean {
|
|
281
|
+
for (const pattern of TEST_FILE_PATTERNS) {
|
|
282
|
+
if (pattern.test(name)) return true;
|
|
283
|
+
}
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Identify a path as a test/spec file. Test files are exempt from
|
|
289
|
+
* structural-length checks (long-file, long-function) because:
|
|
290
|
+
*
|
|
291
|
+
* - A 600-line test file with 50 small `it()` blocks is easy to read,
|
|
292
|
+
* each block is independent, and "split it" yields zero maintenance
|
|
293
|
+
* benefit while harming discoverability.
|
|
294
|
+
* - A 200-line test function (long Arrange-Act-Assert with helpers
|
|
295
|
+
* inlined) is normal for feature coverage and not a complexity smell.
|
|
296
|
+
*
|
|
297
|
+
* Linters and security/bug findings still apply to test files — only the
|
|
298
|
+
* structural-length heuristics defer. Pattern-matches the conventions used
|
|
299
|
+
* by Code Climate's default-exclude set:
|
|
300
|
+
*
|
|
301
|
+
* - JS/TS: *.test.ts, *.test.tsx, *.spec.js, *.spec.jsx, *.test.mts, ...
|
|
302
|
+
* - Folder: __tests__/, /tests/, /test/, e2e/, spec/, __mocks__/
|
|
303
|
+
* - Python: test_*.py, *_test.py
|
|
304
|
+
* - Go: *_test.go
|
|
305
|
+
* - Rust: files inside `tests/` are integration tests by convention
|
|
306
|
+
*/
|
|
307
|
+
export function isTestFile(relativePath: string): boolean {
|
|
308
|
+
const path = relativePath.replace(/\\/g, '/').toLowerCase();
|
|
309
|
+
if (pathHasTestFolder(path)) return true;
|
|
310
|
+
const name = path.split('/').pop() ?? path;
|
|
311
|
+
return fileNameLooksLikeTest(name);
|
|
312
|
+
}
|
|
313
|
+
|
|
261
314
|
/**
|
|
262
315
|
* Split a file list into chunks so a single command invocation doesn't
|
|
263
316
|
* blow past ARG_MAX. macOS ARG_MAX is ~256KB; 400 paths at ~200 chars each
|
|
@@ -276,13 +329,19 @@ export function chunkFileList(paths: string[], size = 400): string[][] {
|
|
|
276
329
|
// Command Runner
|
|
277
330
|
// ============================================================================
|
|
278
331
|
|
|
279
|
-
export function runCommand(
|
|
332
|
+
export function runCommand(
|
|
333
|
+
cmd: string,
|
|
334
|
+
args: string[],
|
|
335
|
+
cwd: string,
|
|
336
|
+
envOverrides?: Record<string, string>,
|
|
337
|
+
): Promise<{ stdout: string; stderr: string; exitCode: number }> {
|
|
280
338
|
return new Promise((resolve) => {
|
|
281
339
|
const proc = spawn(cmd, args, {
|
|
282
340
|
cwd,
|
|
283
341
|
shell: true,
|
|
284
342
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
285
343
|
timeout: 120000,
|
|
344
|
+
env: envOverrides ? { ...process.env, ...envOverrides } : undefined,
|
|
286
345
|
});
|
|
287
346
|
let stdout = '';
|
|
288
347
|
let stderr = '';
|
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
-
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
2
|
|
|
4
3
|
// ============================================================================
|
|
5
4
|
// Types
|
|
6
5
|
// ============================================================================
|
|
7
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Letter grades produced by the multi-dimensional quality rating.
|
|
9
|
+
*
|
|
10
|
+
* Score → grade mapping (per product spec — note: no `D`; F covers 56-69, F-
|
|
11
|
+
* covers 0-55. `D` is retained ONLY for backward compatibility with reports
|
|
12
|
+
* persisted by older versions of this module — new code never emits it):
|
|
13
|
+
*
|
|
14
|
+
* A+ 97-100 A 93-96 A- 90-92
|
|
15
|
+
* B+ 87-89 B 83-86 B- 80-82
|
|
16
|
+
* C+ 77-79 C 73-76 C- 70-72
|
|
17
|
+
* F+ 65-69 F 56-64 F- 0-55
|
|
18
|
+
*
|
|
19
|
+
* `N/A` means the dimension had no tooling available to evaluate it.
|
|
20
|
+
*/
|
|
21
|
+
export type Grade =
|
|
22
|
+
| 'A+' | 'A' | 'A-'
|
|
23
|
+
| 'B+' | 'B' | 'B-'
|
|
24
|
+
| 'C+' | 'C' | 'C-'
|
|
25
|
+
| 'D' // legacy — only appears on reports persisted before the +/- rollout
|
|
26
|
+
| 'F+' | 'F' | 'F-'
|
|
27
|
+
| 'N/A';
|
|
28
|
+
export type DimensionName = 'security' | 'reliability' | 'maintainability';
|
|
29
|
+
|
|
30
|
+
export interface DimensionScore {
|
|
31
|
+
name: DimensionName;
|
|
32
|
+
score: number;
|
|
33
|
+
grade: Grade;
|
|
34
|
+
rationale: string;
|
|
35
|
+
available: boolean;
|
|
36
|
+
findingCount: number;
|
|
37
|
+
worstSeverity: 'critical' | 'high' | 'medium' | 'low' | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface QualityGate {
|
|
41
|
+
passed: boolean;
|
|
42
|
+
failingConditions: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
8
45
|
export interface QualityTool {
|
|
9
46
|
name: string;
|
|
10
47
|
installed: boolean;
|
|
@@ -15,8 +52,6 @@ export interface QualityTool {
|
|
|
15
52
|
export interface CategoryScore {
|
|
16
53
|
name: string;
|
|
17
54
|
score: number;
|
|
18
|
-
weight: number;
|
|
19
|
-
effectiveWeight: number;
|
|
20
55
|
available: boolean;
|
|
21
56
|
issueCount?: number;
|
|
22
57
|
details?: Record<string, unknown>;
|
|
@@ -62,12 +97,34 @@ export interface QualityResults {
|
|
|
62
97
|
timestamp: string;
|
|
63
98
|
ecosystem: string[];
|
|
64
99
|
scoreBreakdown?: ScoreBreakdown;
|
|
100
|
+
dimensions?: DimensionScore[];
|
|
101
|
+
qualityGate?: QualityGate;
|
|
102
|
+
gradeRationale?: string;
|
|
103
|
+
/** Wall-clock duration of the CLI scan that produced this report. Used to estimate ETA on subsequent scans of the same directory. */
|
|
104
|
+
scanDurationMs?: number;
|
|
105
|
+
/** Wall-clock duration of the AI code-review pass, when one ran. */
|
|
106
|
+
reviewDurationMs?: number;
|
|
65
107
|
}
|
|
66
108
|
|
|
67
109
|
export interface ScanProgress {
|
|
68
110
|
step: string;
|
|
69
111
|
current: number;
|
|
70
112
|
total: number;
|
|
113
|
+
/**
|
|
114
|
+
* Wall-clock estimate of the total scan duration, in milliseconds. Sent on
|
|
115
|
+
* the first progress event of a scan and again on subsequent events so
|
|
116
|
+
* reconnecting clients still get an ETA. The web subtracts elapsed time to
|
|
117
|
+
* render "≈ X remaining".
|
|
118
|
+
*/
|
|
119
|
+
etaMs?: number;
|
|
120
|
+
/**
|
|
121
|
+
* Server-side timestamp (ms since epoch) of when the scan started — paired
|
|
122
|
+
* with `etaMs` so the web can compute elapsed time without trusting its
|
|
123
|
+
* own clock alignment.
|
|
124
|
+
*/
|
|
125
|
+
startedAt?: number;
|
|
126
|
+
/** Optional sub-step detail used by long-running steps (e.g. "tsc --noEmit, 18s elapsed") to keep the UI from looking stuck. */
|
|
127
|
+
detail?: string;
|
|
71
128
|
}
|
|
72
129
|
|
|
73
130
|
export type Ecosystem = 'node' | 'python' | 'rust' | 'go' | 'swift' | 'kotlin' | 'unknown';
|
|
@@ -143,7 +200,7 @@ export const ADDITIONAL_EXCLUDES = new Set([
|
|
|
143
200
|
|
|
144
201
|
export const FILE_LENGTH_THRESHOLD = 300;
|
|
145
202
|
export const FUNCTION_LENGTH_THRESHOLD = 50;
|
|
146
|
-
export const TOTAL_STEPS =
|
|
203
|
+
export const TOTAL_STEPS = 8;
|
|
147
204
|
|
|
148
205
|
export function hasInstalledToolInCategory(
|
|
149
206
|
installedSet: Set<string>,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
-
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
2
|
|
|
4
3
|
import { type FileAttachment, ImprovisationSessionManager } from '../../cli/improvisation-session-manager.js';
|
|
5
4
|
import { getEffortLevel, getModel } from '../settings.js';
|
|
@@ -8,7 +7,42 @@ import { runQualityScan } from './quality-service.js';
|
|
|
8
7
|
import type { SessionRegistry } from './session-registry.js';
|
|
9
8
|
import { resolveSkillPrompt } from './skill-handlers.js';
|
|
10
9
|
import { broadcastTabEvent } from './tab-broadcast.js';
|
|
11
|
-
import type
|
|
10
|
+
import { type EngineId, normalizeEngineId, type WebSocketMessage, type WSContext } from './types.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Apply per-prompt engine/model/effortLevel overrides from the `execute`
|
|
14
|
+
* WebSocket payload onto the session. `session.options` is mutated in place
|
|
15
|
+
* so the next `executePrompt` call (which reads options.model / effortLevel
|
|
16
|
+
* inside the retry loop) picks up the new values without needing a new
|
|
17
|
+
* session. Pass-through for undefined fields — global defaults already live
|
|
18
|
+
* on the session from initialization.
|
|
19
|
+
*/
|
|
20
|
+
function applyExecuteOverrides(
|
|
21
|
+
session: ImprovisationSessionManager,
|
|
22
|
+
data: { engine?: unknown; model?: unknown; effortLevel?: unknown },
|
|
23
|
+
): void {
|
|
24
|
+
const options = (session as unknown as { options: { model?: string; effortLevel?: string } }).options;
|
|
25
|
+
if (typeof data.model === 'string' && data.model.length > 0) {
|
|
26
|
+
options.model = data.model;
|
|
27
|
+
}
|
|
28
|
+
if (typeof data.effortLevel === 'string' && data.effortLevel.length > 0) {
|
|
29
|
+
options.effortLevel = data.effortLevel;
|
|
30
|
+
}
|
|
31
|
+
if (data.engine === 'claude-code' || data.engine === 'opencode') {
|
|
32
|
+
// Record the engine on the session history so `resolveEngineForSession`
|
|
33
|
+
// returns the user-chosen value on subsequent movementStart /
|
|
34
|
+
// movementComplete broadcasts. The actual engine factory swap happens
|
|
35
|
+
// at session-start (later-epic work); per-prompt engine flips flow
|
|
36
|
+
// through here so model/effort stay in sync.
|
|
37
|
+
const history = (session as unknown as { history: { engine: string } }).history;
|
|
38
|
+
history.engine = data.engine;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Resolve the engine for a session, defaulting to 'claude-code' for pre-engine sessions. */
|
|
43
|
+
export function resolveEngineForSession(session: ImprovisationSessionManager | undefined): EngineId {
|
|
44
|
+
return normalizeEngineId(session?.engine);
|
|
45
|
+
}
|
|
12
46
|
|
|
13
47
|
// Re-export from extracted modules for backward compatibility
|
|
14
48
|
export { handleHistoryMessage } from './session-history.js';
|
|
@@ -214,6 +248,7 @@ export function buildOutputHistory(session: ImprovisationSessionManager): Array<
|
|
|
214
248
|
*/
|
|
215
249
|
export function setupSessionListeners(ctx: HandlerContext, session: ImprovisationSessionManager, _ws: WSContext, tabId: string): void {
|
|
216
250
|
session.removeAllListeners();
|
|
251
|
+
const engine = resolveEngineForSession(session);
|
|
217
252
|
|
|
218
253
|
session.on('onHistoryPersisted', () => {
|
|
219
254
|
const registry = ctx.getRegistry('');
|
|
@@ -228,19 +263,19 @@ export function setupSessionListeners(ctx: HandlerContext, session: Improvisatio
|
|
|
228
263
|
broadcastTabEvent(ctx, tabId, 'thinking', { text });
|
|
229
264
|
});
|
|
230
265
|
|
|
231
|
-
session.on('onMovementStart', (sequenceNumber: number, prompt: string, isAutoContinue?: boolean) => {
|
|
232
|
-
broadcastTabEvent(ctx, tabId, 'movementStart', { sequenceNumber, prompt, timestamp: Date.now(), executionStartTimestamp: session.executionStartTimestamp, isAutoContinue });
|
|
233
|
-
ctx.broadcastToAll({ type: 'tabStateChanged', data: { tabId, isExecuting: true, executionStartTimestamp: session.executionStartTimestamp } });
|
|
266
|
+
session.on('onMovementStart', (sequenceNumber: number, prompt: string, isAutoContinue?: boolean, etaProfile?: import('../../cli/eta-estimator.js').EtaProfile | null) => {
|
|
267
|
+
broadcastTabEvent(ctx, tabId, 'movementStart', { sequenceNumber, prompt, timestamp: Date.now(), executionStartTimestamp: session.executionStartTimestamp, isAutoContinue, etaProfile: etaProfile ?? undefined }, engine);
|
|
268
|
+
ctx.broadcastToAll({ type: 'tabStateChanged', engine, data: { tabId, isExecuting: true, executionStartTimestamp: session.executionStartTimestamp } });
|
|
234
269
|
});
|
|
235
270
|
|
|
236
271
|
session.on('onMovementComplete', (movement: Record<string, unknown>) => {
|
|
237
|
-
broadcastTabEvent(ctx, tabId, 'movementComplete', movement);
|
|
272
|
+
broadcastTabEvent(ctx, tabId, 'movementComplete', movement, engine);
|
|
238
273
|
|
|
239
274
|
const registry = ctx.getRegistry('');
|
|
240
275
|
// Use a try/catch since getRegistry may not have been initialized with the right workingDir
|
|
241
276
|
try { registry.markTabUnviewed(tabId); } catch { /* ignore */ }
|
|
242
277
|
|
|
243
|
-
ctx.broadcastToAll({ type: 'tabStateChanged', data: { tabId, isExecuting: false, hasUnviewedCompletion: true } });
|
|
278
|
+
ctx.broadcastToAll({ type: 'tabStateChanged', engine, data: { tabId, isExecuting: false, hasUnviewedCompletion: true } });
|
|
244
279
|
|
|
245
280
|
if (ctx.usageReporter && movement.tokensUsed) {
|
|
246
281
|
ctx.usageReporter({
|
|
@@ -258,12 +293,12 @@ export function setupSessionListeners(ctx: HandlerContext, session: Improvisatio
|
|
|
258
293
|
});
|
|
259
294
|
|
|
260
295
|
session.on('onMovementError', (error: Error) => {
|
|
261
|
-
broadcastTabEvent(ctx, tabId, 'movementError', { message: error.message });
|
|
262
|
-
ctx.broadcastToAll({ type: 'tabStateChanged', data: { tabId, isExecuting: false } });
|
|
296
|
+
broadcastTabEvent(ctx, tabId, 'movementError', { message: error.message }, engine);
|
|
297
|
+
ctx.broadcastToAll({ type: 'tabStateChanged', engine, data: { tabId, isExecuting: false } });
|
|
263
298
|
});
|
|
264
299
|
|
|
265
300
|
session.on('onSessionUpdate', (history: Record<string, unknown>) => {
|
|
266
|
-
broadcastTabEvent(ctx, tabId, 'sessionUpdate', history);
|
|
301
|
+
broadcastTabEvent(ctx, tabId, 'sessionUpdate', history, engine);
|
|
267
302
|
});
|
|
268
303
|
|
|
269
304
|
session.on('onPlanNeedsConfirmation', (plan: Record<string, unknown>) => {
|
|
@@ -337,6 +372,13 @@ function handleExecuteMessage(ctx: HandlerContext, ws: WSContext, msg: WebSocket
|
|
|
337
372
|
console.log(`[session] execute accepted msgId=${msgId} tabId=${tabId} sessionId=${sessionId}`);
|
|
338
373
|
}
|
|
339
374
|
|
|
375
|
+
// Apply per-prompt engine/model/effort overrides from the payload. The
|
|
376
|
+
// web client populates these from the tab's effective EnginePicker state
|
|
377
|
+
// (override > global). Missing fields fall through to whatever the session
|
|
378
|
+
// already has — typically the machine-level defaults from `settings.json`
|
|
379
|
+
// applied at session init.
|
|
380
|
+
applyExecuteOverrides(session, msg.data);
|
|
381
|
+
|
|
340
382
|
const worktreeDir = ctx.gitDirectories.get(tabId);
|
|
341
383
|
const attachments = mergePreUploadedAttachments(ctx, tabId, msg.data.attachments);
|
|
342
384
|
|
|
@@ -347,6 +389,17 @@ function handleExecuteMessage(ctx: HandlerContext, ws: WSContext, msg: WebSocket
|
|
|
347
389
|
const effectiveDir = worktreeDir || session.getSessionInfo().workingDir;
|
|
348
390
|
const resolved = resolveSkillPrompt(rawPrompt, effectiveDir);
|
|
349
391
|
|
|
392
|
+
// Authoritative prompt-input clear for all connected devices. The submitter
|
|
393
|
+
// already cleared locally; this guarantees other devices clear even if the
|
|
394
|
+
// submitter's debounced syncPromptText never fires (e.g. mobile tab
|
|
395
|
+
// suspended after Send). Clients suppress this via locallyEditingTabs if
|
|
396
|
+
// the user is actively typing a new prompt.
|
|
397
|
+
ctx.broadcastToAll({
|
|
398
|
+
type: 'promptTextSync',
|
|
399
|
+
tabId,
|
|
400
|
+
data: { tabId, text: '' },
|
|
401
|
+
});
|
|
402
|
+
|
|
350
403
|
session.executePrompt(
|
|
351
404
|
resolved ? resolved.prompt : rawPrompt,
|
|
352
405
|
attachments,
|
|
@@ -373,7 +426,7 @@ function handleNewSessionMessage(ctx: HandlerContext, ws: WSContext, tabId: stri
|
|
|
373
426
|
if (tabMap) tabMap.set(tabId, newSessionId);
|
|
374
427
|
const registry = ctx.getRegistry('');
|
|
375
428
|
try { registry.updateTabSession(tabId, newSessionId); } catch { /* ignore */ }
|
|
376
|
-
ctx.send(ws, { type: 'newSession', tabId, data: newSession.getSessionInfo() });
|
|
429
|
+
ctx.send(ws, { type: 'newSession', tabId, engine: resolveEngineForSession(newSession), data: newSession.getSessionInfo() });
|
|
377
430
|
}
|
|
378
431
|
|
|
379
432
|
export function handleSessionMessage(ctx: HandlerContext, ws: WSContext, msg: WebSocketMessage, tabId: string, permission?: 'view'): void {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
-
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
2
|
|
|
4
3
|
import { existsSync, readdirSync, readFileSync, unlinkSync } from 'node:fs';
|
|
5
4
|
import { readFile } from 'node:fs/promises';
|
|
@@ -92,6 +91,7 @@ function getSessionById(workingDir: string, sessionId: string): Record<string, u
|
|
|
92
91
|
startedAt: historyData.startedAt,
|
|
93
92
|
lastActivityAt: historyData.lastActivityAt,
|
|
94
93
|
totalTokens: historyData.totalTokens,
|
|
94
|
+
engine: historyData.engine || 'claude-code',
|
|
95
95
|
movementCount: historyData.movements?.length || 0,
|
|
96
96
|
title: firstPrompt.slice(0, 80) + (firstPrompt.length > 80 ? '...' : ''),
|
|
97
97
|
movements: historyData.movements || [],
|
|
@@ -172,11 +172,13 @@ function buildSessionSummary(historyData: Record<string, unknown>): Record<strin
|
|
|
172
172
|
const movementPreviews = (movements || []).slice(0, 3).map((m: Record<string, unknown>) => ({
|
|
173
173
|
userPrompt: (typeof m.userPrompt === 'string' ? m.userPrompt : '').slice(0, 100) || ''
|
|
174
174
|
}));
|
|
175
|
+
const engine = typeof historyData.engine === 'string' && historyData.engine ? historyData.engine : 'claude-code';
|
|
175
176
|
return {
|
|
176
177
|
sessionId: historyData.sessionId,
|
|
177
178
|
startedAt: historyData.startedAt,
|
|
178
179
|
lastActivityAt: historyData.lastActivityAt,
|
|
179
180
|
totalTokens: historyData.totalTokens,
|
|
181
|
+
engine,
|
|
180
182
|
movementCount: movements?.length || 0,
|
|
181
183
|
title: firstPrompt.slice(0, 80) + (firstPrompt.length > 80 ? '...' : ''),
|
|
182
184
|
movements: movementPreviews
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
-
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
2
|
|
|
3
|
+
import type { EtaProfile } from '../../cli/eta-estimator.js';
|
|
4
4
|
import { ImprovisationSessionManager } from '../../cli/improvisation-session-manager.js';
|
|
5
5
|
import { getEffortLevel, getModel } from '../settings.js';
|
|
6
6
|
import type { HandlerContext } from './handler-context.js';
|
|
7
|
-
import { buildOutputHistory, setupSessionListeners } from './session-handlers.js';
|
|
8
|
-
import type { SessionRegistry } from './session-registry.js';
|
|
9
|
-
import { replayTabEventsSince } from './tab-event-replay.js';
|
|
7
|
+
import { buildOutputHistory, resolveEngineForSession, setupSessionListeners } from './session-handlers.js';
|
|
8
|
+
import type { SessionRegistry, TabEngineOverride } from './session-registry.js';
|
|
9
|
+
import { type ReplayResult, replayTabEventsSince } from './tab-event-replay.js';
|
|
10
10
|
import type { WSContext } from './types.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -23,6 +23,138 @@ function extractLastSeenSeq(data: unknown): number | undefined {
|
|
|
23
23
|
return typeof candidate === 'number' && Number.isFinite(candidate) ? candidate : undefined;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* When the session is mid-execution, expose the cached eta profile so the
|
|
28
|
+
* web's ComposingIndicator can render an ETA immediately on reconnect
|
|
29
|
+
* instead of waiting for the next movementStart (which won't fire until
|
|
30
|
+
* the user submits a fresh prompt).
|
|
31
|
+
*/
|
|
32
|
+
function inflightEtaPayload(session: ImprovisationSessionManager): { etaProfile?: EtaProfile } {
|
|
33
|
+
if (session.isExecuting && session.etaProfile) return { etaProfile: session.etaProfile };
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Build the full-snapshot data payload for a `tabInitialized` message.
|
|
39
|
+
*
|
|
40
|
+
* Used in three situations:
|
|
41
|
+
* 1. Cold init (no `lastSeenSeq`) — web has no prior state to merge with.
|
|
42
|
+
* 2. Cold reattach (existing session, no prior seq) — same shape.
|
|
43
|
+
* 3. Replay-gap recovery — `replayTabEventsSince` returned `hadGap`, so
|
|
44
|
+
* the web's incremental state is provably stale; we replace it.
|
|
45
|
+
*
|
|
46
|
+
* `replayGap` flags the recovery case so the web can branch: drop any
|
|
47
|
+
* already-rendered tab output and rebuild from `outputHistory` +
|
|
48
|
+
* `executionEvents` instead of merging on top of stale incremental state.
|
|
49
|
+
* Old web clients that don't know the flag still get the full snapshot and
|
|
50
|
+
* render correctly — `replayGap` is purely additive telemetry.
|
|
51
|
+
*/
|
|
52
|
+
function buildFullSnapshotData(
|
|
53
|
+
session: ImprovisationSessionManager,
|
|
54
|
+
options: {
|
|
55
|
+
worktreePath?: string;
|
|
56
|
+
worktreeBranch?: string;
|
|
57
|
+
engineOverride?: TabEngineOverride;
|
|
58
|
+
replayGap?: boolean;
|
|
59
|
+
extra?: Record<string, unknown>;
|
|
60
|
+
} = {},
|
|
61
|
+
): Record<string, unknown> {
|
|
62
|
+
const isExecuting = session.isExecuting;
|
|
63
|
+
return {
|
|
64
|
+
...session.getSessionInfo(),
|
|
65
|
+
engine: resolveEngineForSession(session),
|
|
66
|
+
outputHistory: buildOutputHistory(session),
|
|
67
|
+
isExecuting,
|
|
68
|
+
...(isExecuting ? { executionEvents: session.getExecutionEventLog() } : {}),
|
|
69
|
+
...(isExecuting && session.executionStartTimestamp
|
|
70
|
+
? { executionStartTimestamp: session.executionStartTimestamp }
|
|
71
|
+
: {}),
|
|
72
|
+
...inflightEtaPayload(session),
|
|
73
|
+
...(options.worktreePath
|
|
74
|
+
? { worktreePath: options.worktreePath, worktreeBranch: options.worktreeBranch }
|
|
75
|
+
: {}),
|
|
76
|
+
...(options.engineOverride ? { engineOverride: options.engineOverride } : {}),
|
|
77
|
+
...(options.replayGap ? { replayGap: true } : {}),
|
|
78
|
+
...(options.extra ?? {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Snapshot vs incremental decision based on the replay outcome.
|
|
84
|
+
*
|
|
85
|
+
* - `incremental`: the web should keep its current state and append the
|
|
86
|
+
* replayed events that already arrived (handled by `replayTabEventsSince`
|
|
87
|
+
* itself when there's no gap). We send `tabInitialized` with
|
|
88
|
+
* `resumedFromSeq: true`.
|
|
89
|
+
* - `snapshot`: the web should discard tab output and rebuild from a full
|
|
90
|
+
* snapshot. Triggered either by `lastSeenSeq === undefined` (cold start)
|
|
91
|
+
* or by `result.hadGap` (replay would silently skip events).
|
|
92
|
+
*/
|
|
93
|
+
function decideRecoveryMode(
|
|
94
|
+
result: ReplayResult,
|
|
95
|
+
lastSeenSeq: number | undefined,
|
|
96
|
+
): 'incremental' | 'snapshot' {
|
|
97
|
+
if (lastSeenSeq === undefined) return 'snapshot';
|
|
98
|
+
if (result.hadGap) return 'snapshot';
|
|
99
|
+
return 'incremental';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Send `tabInitialized` for a resume path (`tryResumeFromDisk` /
|
|
104
|
+
* `resumeHistoricalSession`). Picks the snapshot or incremental envelope
|
|
105
|
+
* shape based on `mode` and threads any extra fields the caller needs to
|
|
106
|
+
* carry (e.g. `resumeFailed`, worktree state, engine override).
|
|
107
|
+
*
|
|
108
|
+
* Extracted to keep the resume call sites flat — without this helper, each
|
|
109
|
+
* caller pushes the function over the project's cognitive-complexity gate.
|
|
110
|
+
*/
|
|
111
|
+
function sendResumedTabInitialized(
|
|
112
|
+
ctx: HandlerContext,
|
|
113
|
+
ws: WSContext,
|
|
114
|
+
tabId: string,
|
|
115
|
+
session: ImprovisationSessionManager,
|
|
116
|
+
mode: 'incremental' | 'snapshot',
|
|
117
|
+
replay: ReplayResult,
|
|
118
|
+
options: {
|
|
119
|
+
worktreePath?: string;
|
|
120
|
+
worktreeBranch?: string;
|
|
121
|
+
engineOverride?: TabEngineOverride;
|
|
122
|
+
extra?: Record<string, unknown>;
|
|
123
|
+
} = {},
|
|
124
|
+
): void {
|
|
125
|
+
const engine = resolveEngineForSession(session);
|
|
126
|
+
if (mode === 'snapshot') {
|
|
127
|
+
ctx.send(ws, {
|
|
128
|
+
type: 'tabInitialized',
|
|
129
|
+
tabId,
|
|
130
|
+
engine,
|
|
131
|
+
data: buildFullSnapshotData(session, {
|
|
132
|
+
worktreePath: options.worktreePath,
|
|
133
|
+
worktreeBranch: options.worktreeBranch,
|
|
134
|
+
engineOverride: options.engineOverride,
|
|
135
|
+
replayGap: replay.hadGap,
|
|
136
|
+
extra: options.extra,
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
ctx.send(ws, {
|
|
142
|
+
type: 'tabInitialized',
|
|
143
|
+
tabId,
|
|
144
|
+
engine,
|
|
145
|
+
data: {
|
|
146
|
+
...session.getSessionInfo(),
|
|
147
|
+
engine,
|
|
148
|
+
resumedFromSeq: true,
|
|
149
|
+
...(options.worktreePath
|
|
150
|
+
? { worktreePath: options.worktreePath, worktreeBranch: options.worktreeBranch }
|
|
151
|
+
: {}),
|
|
152
|
+
...(options.engineOverride ? { engineOverride: options.engineOverride } : {}),
|
|
153
|
+
...(options.extra ?? {}),
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
26
158
|
function tryResumeFromDisk(
|
|
27
159
|
ctx: HandlerContext,
|
|
28
160
|
ws: WSContext,
|
|
@@ -55,16 +187,17 @@ function tryResumeFromDisk(
|
|
|
55
187
|
// BEFORE tabInitialized so they arrive in the right order. Web-side
|
|
56
188
|
// handlers append; `tabInitialized` does NOT reset when `resumedFromSeq`
|
|
57
189
|
// is set, preserving the replayed additions.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
190
|
+
//
|
|
191
|
+
// If `replayTabEventsSince` reports `hadGap`, no events were emitted and
|
|
192
|
+
// we fall back to a full-snapshot `tabInitialized` so the web replaces
|
|
193
|
+
// its (now-known-stale) incremental state instead of merging on top.
|
|
194
|
+
const replay = replayTabEventsSince(ctx, ws, tabId, lastSeenSeq);
|
|
195
|
+
const mode = decideRecoveryMode(replay, lastSeenSeq);
|
|
196
|
+
|
|
197
|
+
sendResumedTabInitialized(ctx, ws, tabId, diskSession, mode, replay, {
|
|
198
|
+
worktreePath,
|
|
199
|
+
worktreeBranch,
|
|
200
|
+
engineOverride: regTab?.engineOverride,
|
|
68
201
|
});
|
|
69
202
|
return true;
|
|
70
203
|
} catch {
|
|
@@ -122,21 +255,32 @@ export async function initializeTab(ctx: HandlerContext, ws: WSContext, tabId: s
|
|
|
122
255
|
|
|
123
256
|
registry.registerTab(tabId, sessionId, tabName || existingTab?.tabName);
|
|
124
257
|
const registeredTab = registry.getTab(tabId);
|
|
125
|
-
|
|
258
|
+
const engine = resolveEngineForSession(session);
|
|
259
|
+
// Mirror terminal-handlers.ts: broadcastToOthers, not broadcastToAll. The
|
|
260
|
+
// requesting client already drove this initTab and will receive
|
|
261
|
+
// `tabInitialized` below — echoing `tabCreated` back risks racing the
|
|
262
|
+
// discovery handler during a flicker and producing a phantom tab.
|
|
263
|
+
ctx.broadcastToOthers(ws, {
|
|
126
264
|
type: 'tabCreated',
|
|
127
|
-
|
|
265
|
+
engine,
|
|
266
|
+
data: { tabId, tabName: registeredTab?.tabName || 'Chat', createdAt: registeredTab?.createdAt, order: registeredTab?.order, engine, sessionInfo: session.getSessionInfo() }
|
|
128
267
|
});
|
|
129
268
|
|
|
130
269
|
// Fresh session (no disk/memory predecessor) has nothing to replay,
|
|
131
270
|
// but we still pass lastSeenSeq through so the web flag is consistent.
|
|
132
|
-
|
|
271
|
+
// hadGap is impossible here (buffer is empty for a brand-new tab), but
|
|
272
|
+
// route through `decideRecoveryMode` for uniformity with the resume paths.
|
|
273
|
+
const replay = replayTabEventsSince(ctx, ws, tabId, lastSeenSeq);
|
|
274
|
+
const mode = decideRecoveryMode(replay, lastSeenSeq);
|
|
133
275
|
|
|
134
276
|
ctx.send(ws, {
|
|
135
277
|
type: 'tabInitialized',
|
|
136
278
|
tabId,
|
|
279
|
+
engine,
|
|
137
280
|
data: {
|
|
138
281
|
...session.getSessionInfo(),
|
|
139
|
-
...(
|
|
282
|
+
...(mode === 'incremental' ? { resumedFromSeq: true } : {}),
|
|
283
|
+
...(replay.hadGap ? { replayGap: true } : {}),
|
|
140
284
|
}
|
|
141
285
|
});
|
|
142
286
|
}
|
|
@@ -193,17 +337,14 @@ export async function resumeHistoricalSession(
|
|
|
193
337
|
|
|
194
338
|
registry.registerTab(tabId, sessionId);
|
|
195
339
|
|
|
196
|
-
replayTabEventsSince(ctx, ws, tabId, lastSeenSeq);
|
|
340
|
+
const replay = replayTabEventsSince(ctx, ws, tabId, lastSeenSeq);
|
|
341
|
+
const mode = decideRecoveryMode(replay, lastSeenSeq);
|
|
197
342
|
|
|
198
|
-
ctx
|
|
199
|
-
|
|
200
|
-
tabId,
|
|
201
|
-
data: {
|
|
202
|
-
...session.getSessionInfo(),
|
|
203
|
-
...(lastSeenSeq === undefined ? { outputHistory: buildOutputHistory(session) } : { resumedFromSeq: true }),
|
|
343
|
+
sendResumedTabInitialized(ctx, ws, tabId, session, mode, replay, {
|
|
344
|
+
extra: {
|
|
204
345
|
resumeFailed: isNewSession,
|
|
205
|
-
originalSessionId: isNewSession ? historicalSessionId : undefined
|
|
206
|
-
}
|
|
346
|
+
originalSessionId: isNewSession ? historicalSessionId : undefined,
|
|
347
|
+
},
|
|
207
348
|
});
|
|
208
349
|
}
|
|
209
350
|
|
|
@@ -232,11 +373,14 @@ function reattachSession(
|
|
|
232
373
|
const worktreePath = ctx.gitDirectories.get(tabId);
|
|
233
374
|
const worktreeBranch = ctx.gitBranches.get(tabId);
|
|
234
375
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
376
|
+
const inflightEta = inflightEtaPayload(session);
|
|
377
|
+
const replay = replayTabEventsSince(ctx, ws, tabId, lastSeenSeq);
|
|
378
|
+
const mode = decideRecoveryMode(replay, lastSeenSeq);
|
|
379
|
+
|
|
380
|
+
// Fast path: the web already has local state (via Zustand) AND the replay
|
|
381
|
+
// covered the gap cleanly — just replay the buffered events and tell the
|
|
382
|
+
// client to skip the destructive reset in its tabInitialized handler.
|
|
383
|
+
if (mode === 'incremental') {
|
|
240
384
|
ctx.send(ws, {
|
|
241
385
|
type: 'tabInitialized',
|
|
242
386
|
tabId,
|
|
@@ -245,29 +389,27 @@ function reattachSession(
|
|
|
245
389
|
resumedFromSeq: true,
|
|
246
390
|
isExecuting: session.isExecuting,
|
|
247
391
|
...(session.isExecuting && session.executionStartTimestamp ? { executionStartTimestamp: session.executionStartTimestamp } : {}),
|
|
392
|
+
...inflightEta,
|
|
248
393
|
...(worktreePath ? { worktreePath, worktreeBranch } : {}),
|
|
249
394
|
}
|
|
250
395
|
});
|
|
251
396
|
return;
|
|
252
397
|
}
|
|
253
398
|
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
: undefined;
|
|
260
|
-
|
|
399
|
+
// Snapshot path: either cold-start reattach (no prior seq) or replay-gap
|
|
400
|
+
// recovery (`hadGap=true`, no events sent). Both want a full snapshot so
|
|
401
|
+
// the web rebuilds from `outputHistory` + `executionEvents`. The
|
|
402
|
+
// `replayGap` flag distinguishes the two for telemetry — the wire
|
|
403
|
+
// payload shape is otherwise identical.
|
|
261
404
|
ctx.send(ws, {
|
|
262
405
|
type: 'tabInitialized',
|
|
263
406
|
tabId,
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
407
|
+
engine: resolveEngineForSession(session),
|
|
408
|
+
data: buildFullSnapshotData(session, {
|
|
409
|
+
worktreePath,
|
|
410
|
+
worktreeBranch,
|
|
411
|
+
engineOverride: regTab?.engineOverride,
|
|
412
|
+
replayGap: replay.hadGap,
|
|
413
|
+
}),
|
|
272
414
|
});
|
|
273
415
|
}
|