hammoc 1.5.0 → 1.7.0
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/README.md +12 -4
- package/package.json +2 -2
- package/packages/client/dist/assets/{agentExampleHighlight-BgwTm15v.js → agentExampleHighlight-7Olvqdpf.js} +1 -1
- package/packages/client/dist/assets/{commandTokenHighlight-BljHwnrK.js → commandTokenHighlight-DtKwh4lx.js} +1 -1
- package/packages/client/dist/assets/index-B09doO8H.js +139 -0
- package/packages/client/dist/assets/index-Bee52CKG.js +1569 -0
- package/packages/client/dist/assets/index-CVy582lU.css +32 -0
- package/packages/client/dist/assets/{index-D3LxqW3f.js → index-DLHBUw8c.js} +1 -1
- package/packages/client/dist/assets/{snippetTokenHighlight-DWsaQXX0.js → snippetTokenHighlight-C8O688f4.js} +1 -1
- package/packages/client/dist/index.html +2 -2
- package/packages/client/dist/sw.js +2 -2
- package/packages/server/dist/_dbg_agent_rows.d.ts +2 -0
- package/packages/server/dist/_dbg_agent_rows.d.ts.map +1 -0
- package/packages/server/dist/_dbg_agent_rows.js +60 -0
- package/packages/server/dist/_dbg_agent_rows.js.map +1 -0
- package/packages/server/dist/_dbg_flicker.d.ts +2 -0
- package/packages/server/dist/_dbg_flicker.d.ts.map +1 -0
- package/packages/server/dist/_dbg_flicker.js +38 -0
- package/packages/server/dist/_dbg_flicker.js.map +1 -0
- package/packages/server/dist/_harness_cli.d.ts +2 -0
- package/packages/server/dist/_harness_cli.d.ts.map +1 -0
- package/packages/server/dist/_harness_cli.js +81 -0
- package/packages/server/dist/_harness_cli.js.map +1 -0
- package/packages/server/dist/_harness_provcard.d.ts +2 -0
- package/packages/server/dist/_harness_provcard.d.ts.map +1 -0
- package/packages/server/dist/_harness_provcard.js +67 -0
- package/packages/server/dist/_harness_provcard.js.map +1 -0
- package/packages/server/dist/_replay_dump.d.ts +2 -0
- package/packages/server/dist/_replay_dump.d.ts.map +1 -0
- package/packages/server/dist/_replay_dump.js +62 -0
- package/packages/server/dist/_replay_dump.js.map +1 -0
- package/packages/server/dist/app.d.ts.map +1 -1
- package/packages/server/dist/app.js +6 -0
- package/packages/server/dist/app.js.map +1 -1
- package/packages/server/dist/config/index.d.ts.map +1 -1
- package/packages/server/dist/config/index.js.map +1 -1
- package/packages/server/dist/controllers/bmadCoreConfigController.d.ts +41 -0
- package/packages/server/dist/controllers/bmadCoreConfigController.d.ts.map +1 -0
- package/packages/server/dist/controllers/bmadCoreConfigController.js +172 -0
- package/packages/server/dist/controllers/bmadCoreConfigController.js.map +1 -0
- package/packages/server/dist/controllers/contextBuilderController.d.ts +43 -0
- package/packages/server/dist/controllers/contextBuilderController.d.ts.map +1 -0
- package/packages/server/dist/controllers/contextBuilderController.js +159 -0
- package/packages/server/dist/controllers/contextBuilderController.js.map +1 -0
- package/packages/server/dist/controllers/harnessAgentController.d.ts +7 -0
- package/packages/server/dist/controllers/harnessAgentController.d.ts.map +1 -1
- package/packages/server/dist/controllers/harnessAgentController.js +33 -0
- package/packages/server/dist/controllers/harnessAgentController.js.map +1 -1
- package/packages/server/dist/controllers/harnessBundleController.d.ts +37 -0
- package/packages/server/dist/controllers/harnessBundleController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessBundleController.js +312 -0
- package/packages/server/dist/controllers/harnessBundleController.js.map +1 -0
- package/packages/server/dist/controllers/harnessCommandController.d.ts +7 -0
- package/packages/server/dist/controllers/harnessCommandController.d.ts.map +1 -1
- package/packages/server/dist/controllers/harnessCommandController.js +33 -0
- package/packages/server/dist/controllers/harnessCommandController.js.map +1 -1
- package/packages/server/dist/controllers/harnessHookController.d.ts.map +1 -1
- package/packages/server/dist/controllers/harnessHookController.js +44 -1
- package/packages/server/dist/controllers/harnessHookController.js.map +1 -1
- package/packages/server/dist/controllers/harnessMcpController.d.ts.map +1 -1
- package/packages/server/dist/controllers/harnessMcpController.js +62 -1
- package/packages/server/dist/controllers/harnessMcpController.js.map +1 -1
- package/packages/server/dist/controllers/harnessShareScopeController.d.ts +9 -0
- package/packages/server/dist/controllers/harnessShareScopeController.d.ts.map +1 -1
- package/packages/server/dist/controllers/harnessShareScopeController.js +48 -1
- package/packages/server/dist/controllers/harnessShareScopeController.js.map +1 -1
- package/packages/server/dist/controllers/marketplaceController.d.ts +19 -0
- package/packages/server/dist/controllers/marketplaceController.d.ts.map +1 -0
- package/packages/server/dist/controllers/marketplaceController.js +74 -0
- package/packages/server/dist/controllers/marketplaceController.js.map +1 -0
- package/packages/server/dist/controllers/observabilityController.d.ts +32 -0
- package/packages/server/dist/controllers/observabilityController.d.ts.map +1 -0
- package/packages/server/dist/controllers/observabilityController.js +148 -0
- package/packages/server/dist/controllers/observabilityController.js.map +1 -0
- package/packages/server/dist/controllers/projectController.d.ts.map +1 -1
- package/packages/server/dist/controllers/projectController.js +7 -1
- package/packages/server/dist/controllers/projectController.js.map +1 -1
- package/packages/server/dist/controllers/sessionController.d.ts.map +1 -1
- package/packages/server/dist/controllers/sessionController.js +16 -4
- package/packages/server/dist/controllers/sessionController.js.map +1 -1
- package/packages/server/dist/controllers/systemBrowseController.d.ts +33 -0
- package/packages/server/dist/controllers/systemBrowseController.d.ts.map +1 -0
- package/packages/server/dist/controllers/systemBrowseController.js +105 -0
- package/packages/server/dist/controllers/systemBrowseController.js.map +1 -0
- package/packages/server/dist/handlers/streamCallbacks.d.ts +10 -0
- package/packages/server/dist/handlers/streamCallbacks.d.ts.map +1 -1
- package/packages/server/dist/handlers/streamCallbacks.js +25 -4
- package/packages/server/dist/handlers/streamCallbacks.js.map +1 -1
- package/packages/server/dist/handlers/websocket.d.ts +43 -3
- package/packages/server/dist/handlers/websocket.d.ts.map +1 -1
- package/packages/server/dist/handlers/websocket.js +385 -93
- package/packages/server/dist/handlers/websocket.js.map +1 -1
- package/packages/server/dist/index.js +2 -0
- package/packages/server/dist/index.js.map +1 -1
- package/packages/server/dist/locales/en/server.json +17 -0
- package/packages/server/dist/locales/es/server.json +17 -0
- package/packages/server/dist/locales/ja/server.json +17 -0
- package/packages/server/dist/locales/ko/server.json +17 -0
- package/packages/server/dist/locales/pt/server.json +17 -0
- package/packages/server/dist/locales/zh-CN/server.json +17 -0
- package/packages/server/dist/routes/harness.d.ts.map +1 -1
- package/packages/server/dist/routes/harness.js +58 -0
- package/packages/server/dist/routes/harness.js.map +1 -1
- package/packages/server/dist/routes/preferences.d.ts.map +1 -1
- package/packages/server/dist/routes/preferences.js +12 -2
- package/packages/server/dist/routes/preferences.js.map +1 -1
- package/packages/server/dist/routes/systemBrowse.d.ts +17 -0
- package/packages/server/dist/routes/systemBrowse.d.ts.map +1 -0
- package/packages/server/dist/routes/systemBrowse.js +22 -0
- package/packages/server/dist/routes/systemBrowse.js.map +1 -0
- package/packages/server/dist/services/bmadCoreConfigService.d.ts +86 -0
- package/packages/server/dist/services/bmadCoreConfigService.d.ts.map +1 -0
- package/packages/server/dist/services/bmadCoreConfigService.js +175 -0
- package/packages/server/dist/services/bmadCoreConfigService.js.map +1 -0
- package/packages/server/dist/services/bmadStatusService.d.ts +9 -0
- package/packages/server/dist/services/bmadStatusService.d.ts.map +1 -1
- package/packages/server/dist/services/bmadStatusService.js +77 -13
- package/packages/server/dist/services/bmadStatusService.js.map +1 -1
- package/packages/server/dist/services/chatEngine.d.ts +81 -0
- package/packages/server/dist/services/chatEngine.d.ts.map +1 -0
- package/packages/server/dist/services/chatEngine.js +2 -0
- package/packages/server/dist/services/chatEngine.js.map +1 -0
- package/packages/server/dist/services/chatEngineFactory.d.ts +15 -0
- package/packages/server/dist/services/chatEngineFactory.d.ts.map +1 -0
- package/packages/server/dist/services/chatEngineFactory.js +28 -0
- package/packages/server/dist/services/chatEngineFactory.js.map +1 -0
- package/packages/server/dist/services/chatService.d.ts +34 -26
- package/packages/server/dist/services/chatService.d.ts.map +1 -1
- package/packages/server/dist/services/chatService.js +44 -104
- package/packages/server/dist/services/chatService.js.map +1 -1
- package/packages/server/dist/services/cliChatEngine.d.ts +198 -0
- package/packages/server/dist/services/cliChatEngine.d.ts.map +1 -0
- package/packages/server/dist/services/cliChatEngine.js +2977 -0
- package/packages/server/dist/services/cliChatEngine.js.map +1 -0
- package/packages/server/dist/services/cliGridCards.d.ts +87 -0
- package/packages/server/dist/services/cliGridCards.d.ts.map +1 -0
- package/packages/server/dist/services/cliGridCards.js +298 -0
- package/packages/server/dist/services/cliGridCards.js.map +1 -0
- package/packages/server/dist/services/cliGridRegion.d.ts +44 -0
- package/packages/server/dist/services/cliGridRegion.d.ts.map +1 -0
- package/packages/server/dist/services/cliGridRegion.js +94 -0
- package/packages/server/dist/services/cliGridRegion.js.map +1 -0
- package/packages/server/dist/services/cliModalDetect.d.ts +299 -0
- package/packages/server/dist/services/cliModalDetect.d.ts.map +1 -0
- package/packages/server/dist/services/cliModalDetect.js +583 -0
- package/packages/server/dist/services/cliModalDetect.js.map +1 -0
- package/packages/server/dist/services/cliScreenCache.d.ts +33 -0
- package/packages/server/dist/services/cliScreenCache.d.ts.map +1 -0
- package/packages/server/dist/services/cliScreenCache.js +48 -0
- package/packages/server/dist/services/cliScreenCache.js.map +1 -0
- package/packages/server/dist/services/cliScreenModel.d.ts +141 -0
- package/packages/server/dist/services/cliScreenModel.d.ts.map +1 -0
- package/packages/server/dist/services/cliScreenModel.js +196 -0
- package/packages/server/dist/services/cliScreenModel.js.map +1 -0
- package/packages/server/dist/services/cliScreenStallWatchdog.d.ts +41 -0
- package/packages/server/dist/services/cliScreenStallWatchdog.d.ts.map +1 -0
- package/packages/server/dist/services/cliScreenStallWatchdog.js +45 -0
- package/packages/server/dist/services/cliScreenStallWatchdog.js.map +1 -0
- package/packages/server/dist/services/cliSessionPool.d.ts +97 -0
- package/packages/server/dist/services/cliSessionPool.d.ts.map +1 -0
- package/packages/server/dist/services/cliSessionPool.js +278 -0
- package/packages/server/dist/services/cliSessionPool.js.map +1 -0
- package/packages/server/dist/services/cliSpinnerProgress.d.ts +41 -0
- package/packages/server/dist/services/cliSpinnerProgress.d.ts.map +1 -0
- package/packages/server/dist/services/cliSpinnerProgress.js +131 -0
- package/packages/server/dist/services/cliSpinnerProgress.js.map +1 -0
- package/packages/server/dist/services/contextBuilderScriptTemplate.d.ts +24 -0
- package/packages/server/dist/services/contextBuilderScriptTemplate.d.ts.map +1 -0
- package/packages/server/dist/services/contextBuilderScriptTemplate.js +181 -0
- package/packages/server/dist/services/contextBuilderScriptTemplate.js.map +1 -0
- package/packages/server/dist/services/contextBuilderService.d.ts +68 -0
- package/packages/server/dist/services/contextBuilderService.d.ts.map +1 -0
- package/packages/server/dist/services/contextBuilderService.js +345 -0
- package/packages/server/dist/services/contextBuilderService.js.map +1 -0
- package/packages/server/dist/services/fileRewind.d.ts +46 -0
- package/packages/server/dist/services/fileRewind.d.ts.map +1 -0
- package/packages/server/dist/services/fileRewind.js +77 -0
- package/packages/server/dist/services/fileRewind.js.map +1 -0
- package/packages/server/dist/services/fileWatcherService.d.ts.map +1 -1
- package/packages/server/dist/services/fileWatcherService.js +40 -0
- package/packages/server/dist/services/fileWatcherService.js.map +1 -1
- package/packages/server/dist/services/harnessAgentService.d.ts +18 -0
- package/packages/server/dist/services/harnessAgentService.d.ts.map +1 -1
- package/packages/server/dist/services/harnessAgentService.js +55 -0
- package/packages/server/dist/services/harnessAgentService.js.map +1 -1
- package/packages/server/dist/services/harnessBundleService.d.ts +145 -0
- package/packages/server/dist/services/harnessBundleService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessBundleService.js +1318 -0
- package/packages/server/dist/services/harnessBundleService.js.map +1 -0
- package/packages/server/dist/services/harnessCommandService.d.ts +21 -0
- package/packages/server/dist/services/harnessCommandService.d.ts.map +1 -1
- package/packages/server/dist/services/harnessCommandService.js +64 -0
- package/packages/server/dist/services/harnessCommandService.js.map +1 -1
- package/packages/server/dist/services/harnessHookService.d.ts +27 -0
- package/packages/server/dist/services/harnessHookService.d.ts.map +1 -1
- package/packages/server/dist/services/harnessHookService.js +52 -0
- package/packages/server/dist/services/harnessHookService.js.map +1 -1
- package/packages/server/dist/services/harnessMcpService.d.ts +24 -1
- package/packages/server/dist/services/harnessMcpService.d.ts.map +1 -1
- package/packages/server/dist/services/harnessMcpService.js +70 -0
- package/packages/server/dist/services/harnessMcpService.js.map +1 -1
- package/packages/server/dist/services/harnessShareScopeService.d.ts +19 -0
- package/packages/server/dist/services/harnessShareScopeService.d.ts.map +1 -1
- package/packages/server/dist/services/harnessShareScopeService.js +65 -0
- package/packages/server/dist/services/harnessShareScopeService.js.map +1 -1
- package/packages/server/dist/services/historyParser.d.ts +11 -0
- package/packages/server/dist/services/historyParser.d.ts.map +1 -1
- package/packages/server/dist/services/historyParser.js +10 -8
- package/packages/server/dist/services/historyParser.js.map +1 -1
- package/packages/server/dist/services/imageStorageService.d.ts +12 -0
- package/packages/server/dist/services/imageStorageService.d.ts.map +1 -1
- package/packages/server/dist/services/imageStorageService.js +22 -0
- package/packages/server/dist/services/imageStorageService.js.map +1 -1
- package/packages/server/dist/services/issueService.d.ts.map +1 -1
- package/packages/server/dist/services/issueService.js +1 -0
- package/packages/server/dist/services/issueService.js.map +1 -1
- package/packages/server/dist/services/marketplaceService.d.ts +50 -0
- package/packages/server/dist/services/marketplaceService.d.ts.map +1 -0
- package/packages/server/dist/services/marketplaceService.js +326 -0
- package/packages/server/dist/services/marketplaceService.js.map +1 -0
- package/packages/server/dist/services/observabilityService.d.ts +87 -0
- package/packages/server/dist/services/observabilityService.d.ts.map +1 -0
- package/packages/server/dist/services/observabilityService.js +0 -0
- package/packages/server/dist/services/observabilityService.js.map +1 -0
- package/packages/server/dist/services/projectService.d.ts +13 -1
- package/packages/server/dist/services/projectService.d.ts.map +1 -1
- package/packages/server/dist/services/projectService.js +32 -0
- package/packages/server/dist/services/projectService.js.map +1 -1
- package/packages/server/dist/services/queueService.d.ts.map +1 -1
- package/packages/server/dist/services/queueService.js +12 -3
- package/packages/server/dist/services/queueService.js.map +1 -1
- package/packages/server/dist/services/rateLimitProbeService.d.ts +15 -0
- package/packages/server/dist/services/rateLimitProbeService.d.ts.map +1 -1
- package/packages/server/dist/services/rateLimitProbeService.js +48 -19
- package/packages/server/dist/services/rateLimitProbeService.js.map +1 -1
- package/packages/server/dist/services/sessionService.d.ts +16 -0
- package/packages/server/dist/services/sessionService.d.ts.map +1 -1
- package/packages/server/dist/services/sessionService.js +125 -0
- package/packages/server/dist/services/sessionService.js.map +1 -1
- package/packages/server/dist/services/streamHandler.d.ts +8 -1
- package/packages/server/dist/services/streamHandler.d.ts.map +1 -1
- package/packages/server/dist/services/streamHandler.js +12 -5
- package/packages/server/dist/services/streamHandler.js.map +1 -1
- package/packages/server/dist/services/systemBrowseService.d.ts +91 -0
- package/packages/server/dist/services/systemBrowseService.d.ts.map +1 -0
- package/packages/server/dist/services/systemBrowseService.js +372 -0
- package/packages/server/dist/services/systemBrowseService.js.map +1 -0
- package/packages/server/dist/services/tokenCountService.d.ts +71 -0
- package/packages/server/dist/services/tokenCountService.d.ts.map +1 -0
- package/packages/server/dist/services/tokenCountService.js +313 -0
- package/packages/server/dist/services/tokenCountService.js.map +1 -0
- package/packages/server/dist/services/workspaceContext.d.ts +24 -0
- package/packages/server/dist/services/workspaceContext.d.ts.map +1 -0
- package/packages/server/dist/services/workspaceContext.js +109 -0
- package/packages/server/dist/services/workspaceContext.js.map +1 -0
- package/packages/server/dist/snippets/apply-qa-fixes +7 -5
- package/packages/server/dist/snippets/qa-review +7 -1
- package/packages/server/dist/utils/assertSafeBundlePath.d.ts +29 -0
- package/packages/server/dist/utils/assertSafeBundlePath.d.ts.map +1 -0
- package/packages/server/dist/utils/assertSafeBundlePath.js +53 -0
- package/packages/server/dist/utils/assertSafeBundlePath.js.map +1 -0
- package/packages/server/dist/utils/backgroundBlock.d.ts +29 -0
- package/packages/server/dist/utils/backgroundBlock.d.ts.map +1 -0
- package/packages/server/dist/utils/backgroundBlock.js +33 -0
- package/packages/server/dist/utils/backgroundBlock.js.map +1 -0
- package/packages/server/dist/utils/backgroundTaskTracker.d.ts +20 -0
- package/packages/server/dist/utils/backgroundTaskTracker.d.ts.map +1 -0
- package/packages/server/dist/utils/backgroundTaskTracker.js +40 -0
- package/packages/server/dist/utils/backgroundTaskTracker.js.map +1 -0
- package/packages/server/dist/utils/bundledBinaryModelSupport.d.ts +14 -0
- package/packages/server/dist/utils/bundledBinaryModelSupport.d.ts.map +1 -0
- package/packages/server/dist/utils/bundledBinaryModelSupport.js +112 -0
- package/packages/server/dist/utils/bundledBinaryModelSupport.js.map +1 -0
- package/packages/server/dist/utils/cliDebugLog.d.ts +38 -0
- package/packages/server/dist/utils/cliDebugLog.d.ts.map +1 -0
- package/packages/server/dist/utils/cliDebugLog.js +65 -0
- package/packages/server/dist/utils/cliDebugLog.js.map +1 -0
- package/packages/server/dist/utils/cliEngineUtils.d.ts +35 -0
- package/packages/server/dist/utils/cliEngineUtils.d.ts.map +1 -0
- package/packages/server/dist/utils/cliEngineUtils.js +38 -0
- package/packages/server/dist/utils/cliEngineUtils.js.map +1 -0
- package/packages/server/dist/utils/effortUtils.d.ts +2 -2
- package/packages/server/dist/utils/effortUtils.d.ts.map +1 -1
- package/packages/server/dist/utils/effortUtils.js +7 -5
- package/packages/server/dist/utils/effortUtils.js.map +1 -1
- package/packages/server/dist/utils/errors.d.ts +1 -0
- package/packages/server/dist/utils/errors.d.ts.map +1 -1
- package/packages/server/dist/utils/errors.js +17 -0
- package/packages/server/dist/utils/errors.js.map +1 -1
- package/packages/server/dist/utils/harnessBundleSchema.d.ts +14 -12
- package/packages/server/dist/utils/harnessBundleSchema.d.ts.map +1 -1
- package/packages/server/dist/utils/harnessBundleSchema.js +11 -1
- package/packages/server/dist/utils/harnessBundleSchema.js.map +1 -1
- package/packages/server/dist/utils/harnessPaths.d.ts +40 -0
- package/packages/server/dist/utils/harnessPaths.d.ts.map +1 -1
- package/packages/server/dist/utils/harnessPaths.js +123 -0
- package/packages/server/dist/utils/harnessPaths.js.map +1 -1
- package/packages/server/dist/utils/trailingThrottle.d.ts +21 -0
- package/packages/server/dist/utils/trailingThrottle.d.ts.map +1 -0
- package/packages/server/dist/utils/trailingThrottle.js +36 -0
- package/packages/server/dist/utils/trailingThrottle.js.map +1 -0
- package/packages/server/package.json +61 -58
- package/packages/server/resources/internals/INDEX.md +3 -1
- package/packages/server/resources/internals/bmad-qa-fix-marker.md +32 -0
- package/packages/server/resources/internals/harness-files.md +22 -0
- package/packages/server/resources/internals/image-storage.md +9 -0
- package/packages/server/resources/internals/observability-storage.md +23 -0
- package/packages/server/resources/manual/02-chat.md +23 -4
- package/packages/server/resources/manual/03-sessions.md +1 -0
- package/packages/server/resources/manual/05-projects.md +15 -2
- package/packages/server/resources/manual/10-project-board.md +4 -3
- package/packages/server/resources/manual/11-bmad-method-integration.md +12 -8
- package/packages/server/resources/manual/12-harness-workbench.md +82 -1
- package/packages/server/resources/manual/13-settings.md +61 -9
- package/packages/shared/dist/index.d.ts +7 -0
- package/packages/shared/dist/index.d.ts.map +1 -1
- package/packages/shared/dist/index.js +14 -0
- package/packages/shared/dist/index.js.map +1 -1
- package/packages/shared/dist/types/bmadCoreConfig.d.ts +71 -0
- package/packages/shared/dist/types/bmadCoreConfig.d.ts.map +1 -0
- package/packages/shared/dist/types/bmadCoreConfig.js +30 -0
- package/packages/shared/dist/types/bmadCoreConfig.js.map +1 -0
- package/packages/shared/dist/types/bmadStatus.d.ts +26 -0
- package/packages/shared/dist/types/bmadStatus.d.ts.map +1 -1
- package/packages/shared/dist/types/bmadStatus.js.map +1 -1
- package/packages/shared/dist/types/board.d.ts +6 -0
- package/packages/shared/dist/types/board.d.ts.map +1 -1
- package/packages/shared/dist/types/contextBuilder.d.ts +102 -0
- package/packages/shared/dist/types/contextBuilder.d.ts.map +1 -0
- package/packages/shared/dist/types/contextBuilder.js +55 -0
- package/packages/shared/dist/types/contextBuilder.js.map +1 -0
- package/packages/shared/dist/types/harnessBundle.d.ts +35 -0
- package/packages/shared/dist/types/harnessBundle.d.ts.map +1 -1
- package/packages/shared/dist/types/marketplace.d.ts +83 -0
- package/packages/shared/dist/types/marketplace.d.ts.map +1 -0
- package/packages/shared/dist/types/marketplace.js +18 -0
- package/packages/shared/dist/types/marketplace.js.map +1 -0
- package/packages/shared/dist/types/observability.d.ts +148 -0
- package/packages/shared/dist/types/observability.d.ts.map +1 -0
- package/packages/shared/dist/types/observability.js +24 -0
- package/packages/shared/dist/types/observability.js.map +1 -0
- package/packages/shared/dist/types/preferences.d.ts +16 -2
- package/packages/shared/dist/types/preferences.d.ts.map +1 -1
- package/packages/shared/dist/types/preferences.js +1 -0
- package/packages/shared/dist/types/preferences.js.map +1 -1
- package/packages/shared/dist/types/project.d.ts +7 -1
- package/packages/shared/dist/types/project.d.ts.map +1 -1
- package/packages/shared/dist/types/project.js.map +1 -1
- package/packages/shared/dist/types/sdk.d.ts +124 -12
- package/packages/shared/dist/types/sdk.d.ts.map +1 -1
- package/packages/shared/dist/types/sdk.js +64 -19
- package/packages/shared/dist/types/sdk.js.map +1 -1
- package/packages/shared/dist/types/streaming.d.ts +2 -2
- package/packages/shared/dist/types/streaming.d.ts.map +1 -1
- package/packages/shared/dist/types/streaming.js.map +1 -1
- package/packages/shared/dist/types/systemBrowse.d.ts +120 -0
- package/packages/shared/dist/types/systemBrowse.d.ts.map +1 -0
- package/packages/shared/dist/types/systemBrowse.js +57 -0
- package/packages/shared/dist/types/systemBrowse.js.map +1 -0
- package/packages/shared/dist/types/websocket.d.ts +38 -0
- package/packages/shared/dist/types/websocket.d.ts.map +1 -1
- package/packages/shared/dist/utils/interruptFiller.d.ts +30 -0
- package/packages/shared/dist/utils/interruptFiller.d.ts.map +1 -0
- package/packages/shared/dist/utils/interruptFiller.js +38 -0
- package/packages/shared/dist/utils/interruptFiller.js.map +1 -0
- package/packages/shared/dist/utils/toolResultContent.d.ts +22 -0
- package/packages/shared/dist/utils/toolResultContent.d.ts.map +1 -0
- package/packages/shared/dist/utils/toolResultContent.js +29 -0
- package/packages/shared/dist/utils/toolResultContent.js.map +1 -0
- package/scripts/run-integration-test.mjs +362 -362
- package/packages/client/dist/assets/index-CjyjnXB8.css +0 -32
- package/packages/client/dist/assets/index-NqJdhlek.js +0 -1498
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliScreenCache.d.ts","sourceRoot":"","sources":["../../src/services/cliScreenCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAgBH;kGACkG;AAClG,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAGnE;AAED,6FAA6F;AAC7F,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElE;AAED;wGACwG;AACxG,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAI3E;AAED,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,yFAAyF;AACzF,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAEvD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI screen cache (Story 37.7, reworked in 37.8) — a session-lifetime snapshot of the
|
|
3
|
+
* current claude TUI screen, used to restore late-joining / refreshed / collapse-expanded
|
|
4
|
+
* browsers in CLI mode.
|
|
5
|
+
*
|
|
6
|
+
* The Story 37.1 headless emulator is per-TURN (spawned with the PTY, disposed at teardown).
|
|
7
|
+
* But the CLI engine is turn-per-process, so between turns there is no emulator and no live
|
|
8
|
+
* PTY. This cache lives at the SESSION scope: while a turn streams, the engine refreshes it
|
|
9
|
+
* with the CURRENT serialized screen on the same ~100ms throttle as the live `cli:screen-frame`
|
|
10
|
+
* (so a late-join mid-turn restores the live screen, not a stale one), and hands off the FINAL
|
|
11
|
+
* serialized screen at teardown. A `session:join` or `cli:request-screen-frame` arriving
|
|
12
|
+
* between turns then has a "current screen" to send.
|
|
13
|
+
*
|
|
14
|
+
* Story 37.8: the entry is now a single SERIALIZED frame string (ANSI/color intact, produced
|
|
15
|
+
* by the screen model's serialize addon), not a plain-text grid — the client restores it via
|
|
16
|
+
* reset()+write(frame), identical to a live frame, so restore and live share one render path.
|
|
17
|
+
*
|
|
18
|
+
* Pure module-level Map. CLI-only; SDK mode never writes it (no PTY / screen model), so `get`
|
|
19
|
+
* returns undefined for SDK sessions and the frame push is naturally a no-op.
|
|
20
|
+
*/
|
|
21
|
+
const cache = new Map();
|
|
22
|
+
/** Store (or replace) the current serialized screen frame for a session. `ts` records recency.
|
|
23
|
+
* Preserves the existing `stalled` flag — only the watchdog (setCliScreenStall) mutates that. */
|
|
24
|
+
export function setCliScreen(sessionId, frame) {
|
|
25
|
+
const prev = cache.get(sessionId);
|
|
26
|
+
cache.set(sessionId, { frame, stalled: prev?.stalled ?? false, ts: Date.now() });
|
|
27
|
+
}
|
|
28
|
+
/** Return the cached serialized screen frame for a session, or undefined on a cache miss. */
|
|
29
|
+
export function getCliScreen(sessionId) {
|
|
30
|
+
return cache.get(sessionId)?.frame;
|
|
31
|
+
}
|
|
32
|
+
/** Update the soft screen-stall flag on an EXISTING screen entry. No-op on a cache miss, so a
|
|
33
|
+
* frame-less (leak-prone) entry is never created — a cached screen frame is what defines a session. */
|
|
34
|
+
export function setCliScreenStall(sessionId, stalled) {
|
|
35
|
+
const prev = cache.get(sessionId);
|
|
36
|
+
if (!prev)
|
|
37
|
+
return;
|
|
38
|
+
cache.set(sessionId, { ...prev, stalled });
|
|
39
|
+
}
|
|
40
|
+
/** Current cached stall flag for a session (false on a miss). Used to resync late joiners. */
|
|
41
|
+
export function getCliScreenStall(sessionId) {
|
|
42
|
+
return cache.get(sessionId)?.stalled ?? false;
|
|
43
|
+
}
|
|
44
|
+
/** Drop a session's cached screen (called when its socket room empties — leak guard). */
|
|
45
|
+
export function deleteCliScreen(sessionId) {
|
|
46
|
+
cache.delete(sessionId);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=cliScreenCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliScreenCache.js","sourceRoot":"","sources":["../../src/services/cliScreenCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAcH,MAAM,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAC;AAEhD;kGACkG;AAClG,MAAM,UAAU,YAAY,CAAC,SAAiB,EAAE,KAAa;IAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAClC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AACnF,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,OAAO,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC;AACrC,CAAC;AAED;wGACwG;AACxG,MAAM,UAAU,iBAAiB,CAAC,SAAiB,EAAE,OAAgB;IACnE,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,OAAO,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,OAAO,IAAI,KAAK,CAAC;AAChD,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Screen Model (Epic 37 — Story 37.1)
|
|
3
|
+
*
|
|
4
|
+
* A thin server-side **headless terminal** that reconstructs the *final screen grid*
|
|
5
|
+
* the interactive `claude` TUI paints into its PTY. The CLI engine's existing
|
|
6
|
+
* extraction (Story 32.7 spinner / 32.6 permission / 32.8 question / usage-limit)
|
|
7
|
+
* runs regex over a **linear** ANSI-stripped buffer (`stripAnsiForDetect` →
|
|
8
|
+
* `progressBuffer` etc.). A linear buffer has no cursor-movement information, so a
|
|
9
|
+
* counter that claude *overwrites in place* (the spinner's "365" → "366" on the same
|
|
10
|
+
* column) gets **concatenated** ("365" + "366" → "365366" fusion). The accumulated
|
|
11
|
+
* workarounds (digit caps, implausible-jump guards) only paper over that.
|
|
12
|
+
*
|
|
13
|
+
* This model feeds the raw PTY frames (ANSI intact) into a real `@xterm/headless`
|
|
14
|
+
* `Terminal` — the **same terminal core** (`@xterm/xterm` 5.5.x) the client mirror
|
|
15
|
+
* (`CliPtyMirror.tsx`) also renders into, now via the serialized `cli:screen-frame`. The
|
|
16
|
+
* emulator *applies* the cursor moves claude intended, leaving only the final grid,
|
|
17
|
+
* where same-cell updates are overwrites — so fusion is **structurally impossible**.
|
|
18
|
+
*
|
|
19
|
+
* Story 37.1 is a *spike-bearing gate*: it adds this grid model as a **pure
|
|
20
|
+
* foundation** (no production consumer is moved here yet — that is 37.2~37.4) and
|
|
21
|
+
* proves real-claude frames reconstruct faithfully (GO/NO-GO). The screen model is
|
|
22
|
+
* fed *unconditionally* on every turn ("reconstruct always / display-only toggle",
|
|
23
|
+
* AC3) — unlike the mirror passthrough which is gated by the `cliPtyMirror` pref.
|
|
24
|
+
*
|
|
25
|
+
* Geometry is **120×80** — the engine passes `cols`/`rows` to `cliSessionPool.spawnClaude`
|
|
26
|
+
* explicitly, matching this model and `CliPtyMirror` (COLS=120 / ROWS=80) — so the
|
|
27
|
+
* coordinates claude addresses line up with what we read. (Story 37.8 raised rows 40→80 so
|
|
28
|
+
* the mirror shows more of claude's screen at once.)
|
|
29
|
+
*
|
|
30
|
+
* API surface (intentionally thin):
|
|
31
|
+
* createCliScreenModel(cols=120, rows=40) → { write, flush, readGrid, readScreenText, serialize, dispose }
|
|
32
|
+
*
|
|
33
|
+
* @see docs/stories/37.1.story.md
|
|
34
|
+
* [Source: docs/prd/epic-37-cli-terminal-emulation.md#story-371]
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Default CLI terminal geometry — MUST match `cliSessionPool.spawnClaude` (the engine passes
|
|
38
|
+
* these explicitly) and `CliPtyMirror` (COLS=120 / ROWS=80). claude draws its in-place redraws
|
|
39
|
+
* at *these* coordinates; a mismatch desyncs the grid. (Story 37.8 raised ROWS 40→80 so the
|
|
40
|
+
* mirror shows more of claude's screen at once.)
|
|
41
|
+
*/
|
|
42
|
+
export declare const CLI_SCREEN_COLS = 120;
|
|
43
|
+
export declare const CLI_SCREEN_ROWS = 80;
|
|
44
|
+
/**
|
|
45
|
+
* Scrollback retention (lines kept ABOVE the viewport). Story 37.11 AC5: a single block (a long tool
|
|
46
|
+
* diff / reasoning) taller than the viewport scrolls its HEAD — the opening glyph the card parser keys
|
|
47
|
+
* on — off the top. With `scrollback:0` that head was discarded, so the parser saw only the glyph-less
|
|
48
|
+
* TAIL and could not classify the block; it fell back to the late JSONL (a screen↔file timing race).
|
|
49
|
+
* Retaining scrollback lets `readGrid(scrollbackRows)` recover the head so the SCREEN stays the single
|
|
50
|
+
* primary card source. The mirror is kept viewport-only via `serialize({scrollback:0})` so this
|
|
51
|
+
* retention never bloats the `cli:screen-frame` payload.
|
|
52
|
+
*/
|
|
53
|
+
export declare const CLI_SCREEN_SCROLLBACK = 5000;
|
|
54
|
+
/**
|
|
55
|
+
* The foreground color CLASS of a leading `●` card bullet (Story 37.10 — tool running/done).
|
|
56
|
+
* The interactive claude TUI paints the card bullet in a TRUECOLOR that encodes tool status,
|
|
57
|
+
* which the plain-text `readGrid()` (translateToString) discards. Verified against real frames
|
|
58
|
+
* (cli-real-pty-dump): a COMPLETED tool's bullet is green (78,186,101), an IN-PROGRESS tool's
|
|
59
|
+
* bullet is gray (153,153,153), and an assistant TEXT body bullet is white (255,255,255). The
|
|
60
|
+
* class — not the exact RGB — is the stable signal (claude may retune the shade), so we bucket:
|
|
61
|
+
* - 'green' : tool COMPLETE (g clearly dominates r & b)
|
|
62
|
+
* - 'white' : assistant text body bullet (all channels high ≥200)
|
|
63
|
+
* - 'gray' : tool RUNNING / dim (the remaining low-saturation case)
|
|
64
|
+
* - 'other' : a non-RGB / palette / default fg (older TUI or unexpected) — treated as not-green
|
|
65
|
+
* - null : the row has no leading `●` glyph
|
|
66
|
+
*/
|
|
67
|
+
export type CliBulletColor = 'green' | 'white' | 'gray' | 'other' | null;
|
|
68
|
+
/** The screen model's read/write surface consumed by later stories (37.2~37.4). */
|
|
69
|
+
export interface CliScreenModel {
|
|
70
|
+
/**
|
|
71
|
+
* Feed a raw PTY frame (ANSI intact) straight into the emulator. **No linear
|
|
72
|
+
* strip** — the whole point is that the emulator *interprets* the ANSI. Cheap and
|
|
73
|
+
* fire-and-forget: xterm buffers the write and parses it asynchronously, so the
|
|
74
|
+
* grid is not guaranteed settled until `flush()` resolves.
|
|
75
|
+
*/
|
|
76
|
+
write(data: string): void;
|
|
77
|
+
/**
|
|
78
|
+
* Resolve once every preceding `write` has been parsed into the buffer. xterm's
|
|
79
|
+
* write pipeline is asynchronous/buffered (verified — an immediate read after
|
|
80
|
+
* `write` returns the pre-write grid), so a reader that needs a *settled* grid
|
|
81
|
+
* must `await flush()` first. Implemented via xterm's own write callback (writes
|
|
82
|
+
* are FIFO, so an empty write's callback fires after all prior writes are parsed).
|
|
83
|
+
*/
|
|
84
|
+
flush(): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* The active screen (viewport) as one trimmed string per row — exactly `rows`
|
|
87
|
+
* entries, blank rows included as ''. Later detectors (37.2~37.4) scan these rows
|
|
88
|
+
* for the spinner line / modal box / permission-mode status row.
|
|
89
|
+
*
|
|
90
|
+
* `scrollbackRows` (Story 37.11 AC5, default 0 = viewport-only / unchanged): when > 0, PREPEND up
|
|
91
|
+
* to that many scrolled-off rows ABOVE the viewport so the card parser can read a block whose head
|
|
92
|
+
* scrolled off the top. Footer detectors pass 0 (they want only the live bottom region); only the
|
|
93
|
+
* card scraper passes a window.
|
|
94
|
+
*/
|
|
95
|
+
readGrid(scrollbackRows?: number): string[];
|
|
96
|
+
/** `readGrid().join('\n')` — convenience surface for line-spanning detectors. */
|
|
97
|
+
readScreenText(): string;
|
|
98
|
+
/**
|
|
99
|
+
* Per-row foreground-color CLASS of each row's leading `●` card bullet (Story 37.10).
|
|
100
|
+
* One entry per row, index-aligned with `readGrid(scrollbackRows)` for the SAME `scrollbackRows`
|
|
101
|
+
* (so a caller can slice both identically). `null` where a row has no leading `●`. This is the COLOR
|
|
102
|
+
* channel `readGrid()` throws away — used to read tool running(gray)/done(green) status off the
|
|
103
|
+
* bullet, far more robust than the `⎿ Waiting…`/`⎿ Running…` placeholder TEXT (misread as results).
|
|
104
|
+
*/
|
|
105
|
+
readBulletColors(scrollbackRows?: number): CliBulletColor[];
|
|
106
|
+
/**
|
|
107
|
+
* Serialize the CURRENT screen to a string WITH ANSI/color escapes (via the serialize
|
|
108
|
+
* addon) — suitable for the client mirror to `reset()` + `write()` into an identical
|
|
109
|
+
* screen. Call AFTER `flush()` so the serialized state is settled. Covers normal +
|
|
110
|
+
* alternate buffers (claude TUI is a full-screen / alt-buffer app); an alt screen is
|
|
111
|
+
* prefixed with the alt-buffer-enter + cursor-home sequence, which the client absorbs
|
|
112
|
+
* via `reset()` before each write. Unlike `readGrid` (plain text for detectors), this
|
|
113
|
+
* preserves color — it is the mirror's content source, not a detection source. VIEWPORT-ONLY
|
|
114
|
+
* (`serialize({scrollback:0})`): the card parser's scrollback retention (Story 37.11 AC5) must NOT
|
|
115
|
+
* bloat the mirror frame, so only the active screen is serialized (its pre-37.11 scope).
|
|
116
|
+
*/
|
|
117
|
+
serialize(): string;
|
|
118
|
+
/**
|
|
119
|
+
* Story 37.11 AC3: mark the CURRENT TURN's start at the live cursor (call once, at generation
|
|
120
|
+
* start). Floors `readGrid(scrollbackRows>0)`/`readBulletColors(scrollbackRows>0)` so a resume
|
|
121
|
+
* boot-repaint of the prior conversation (ABOVE this mark) is excluded from the card scrape.
|
|
122
|
+
* 실측 2026-06-16: registered at generation-start ⇒ ZERO prior-turn leak across FIT and overflow
|
|
123
|
+
* frames (a 130-line turn-2 that scrolled the prior repaint into scrollback leaked it 0 times).
|
|
124
|
+
*/
|
|
125
|
+
markTurnStart(): void;
|
|
126
|
+
/** Dispose the underlying emulator. Idempotent-safe at the call sites (single teardown path). */
|
|
127
|
+
dispose(): void;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Create a headless screen model owning one `@xterm/headless` Terminal.
|
|
131
|
+
*
|
|
132
|
+
* `disableStdin: true` (we never type into this emulator — it is read-only), `scrollback:
|
|
133
|
+
* CLI_SCREEN_SCROLLBACK` (Story 37.11 AC5 — retain scrolled-off lines so `readGrid(scrollbackRows)`
|
|
134
|
+
* can recover the HEAD of a block taller than the viewport; was 0, which discarded the head and
|
|
135
|
+
* forced the card parser to fall back to the late JSONL), and `allowProposedApi: true` (the `buffer`
|
|
136
|
+
* namespace used to read the grid is proposed API in 5.x — verified to throw otherwise).
|
|
137
|
+
* `Terminal.open()` is **never** called: headless has no DOM/renderer; we read the buffer model
|
|
138
|
+
* directly. The mirror's `serialize()` is scoped to the viewport so this retention never bloats it.
|
|
139
|
+
*/
|
|
140
|
+
export declare function createCliScreenModel(cols?: number, rows?: number): CliScreenModel;
|
|
141
|
+
//# sourceMappingURL=cliScreenModel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliScreenModel.d.ts","sourceRoot":"","sources":["../../src/services/cliScreenModel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAiBH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,MAAM,CAAC;AACnC,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAEzE,mFAAmF;AACnF,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;;;OAMG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB;;;;;;;;;OASG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5C,iFAAiF;IACjF,cAAc,IAAI,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,gBAAgB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,CAAC;IAC5D;;;;;;;;;;OAUG;IACH,SAAS,IAAI,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,aAAa,IAAI,IAAI,CAAC;IACtB,iGAAiG;IACjG,OAAO,IAAI,IAAI,CAAC;CACjB;AAwBD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,GAAE,MAAwB,EAC9B,IAAI,GAAE,MAAwB,GAC7B,cAAc,CA8FhB"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Screen Model (Epic 37 — Story 37.1)
|
|
3
|
+
*
|
|
4
|
+
* A thin server-side **headless terminal** that reconstructs the *final screen grid*
|
|
5
|
+
* the interactive `claude` TUI paints into its PTY. The CLI engine's existing
|
|
6
|
+
* extraction (Story 32.7 spinner / 32.6 permission / 32.8 question / usage-limit)
|
|
7
|
+
* runs regex over a **linear** ANSI-stripped buffer (`stripAnsiForDetect` →
|
|
8
|
+
* `progressBuffer` etc.). A linear buffer has no cursor-movement information, so a
|
|
9
|
+
* counter that claude *overwrites in place* (the spinner's "365" → "366" on the same
|
|
10
|
+
* column) gets **concatenated** ("365" + "366" → "365366" fusion). The accumulated
|
|
11
|
+
* workarounds (digit caps, implausible-jump guards) only paper over that.
|
|
12
|
+
*
|
|
13
|
+
* This model feeds the raw PTY frames (ANSI intact) into a real `@xterm/headless`
|
|
14
|
+
* `Terminal` — the **same terminal core** (`@xterm/xterm` 5.5.x) the client mirror
|
|
15
|
+
* (`CliPtyMirror.tsx`) also renders into, now via the serialized `cli:screen-frame`. The
|
|
16
|
+
* emulator *applies* the cursor moves claude intended, leaving only the final grid,
|
|
17
|
+
* where same-cell updates are overwrites — so fusion is **structurally impossible**.
|
|
18
|
+
*
|
|
19
|
+
* Story 37.1 is a *spike-bearing gate*: it adds this grid model as a **pure
|
|
20
|
+
* foundation** (no production consumer is moved here yet — that is 37.2~37.4) and
|
|
21
|
+
* proves real-claude frames reconstruct faithfully (GO/NO-GO). The screen model is
|
|
22
|
+
* fed *unconditionally* on every turn ("reconstruct always / display-only toggle",
|
|
23
|
+
* AC3) — unlike the mirror passthrough which is gated by the `cliPtyMirror` pref.
|
|
24
|
+
*
|
|
25
|
+
* Geometry is **120×80** — the engine passes `cols`/`rows` to `cliSessionPool.spawnClaude`
|
|
26
|
+
* explicitly, matching this model and `CliPtyMirror` (COLS=120 / ROWS=80) — so the
|
|
27
|
+
* coordinates claude addresses line up with what we read. (Story 37.8 raised rows 40→80 so
|
|
28
|
+
* the mirror shows more of claude's screen at once.)
|
|
29
|
+
*
|
|
30
|
+
* API surface (intentionally thin):
|
|
31
|
+
* createCliScreenModel(cols=120, rows=40) → { write, flush, readGrid, readScreenText, serialize, dispose }
|
|
32
|
+
*
|
|
33
|
+
* @see docs/stories/37.1.story.md
|
|
34
|
+
* [Source: docs/prd/epic-37-cli-terminal-emulation.md#story-371]
|
|
35
|
+
*/
|
|
36
|
+
// `@xterm/headless` ships CommonJS; under Node ESM the named export `Terminal`
|
|
37
|
+
// is not detected by the cjs-module-lexer, so a named import
|
|
38
|
+
// (`import { Terminal } from ...`) fails at runtime. The default import resolves
|
|
39
|
+
// to the module's `module.exports`, off which `Terminal` is destructured.
|
|
40
|
+
// (Verified empirically — not assumed; see Story 37.1 Task 1.)
|
|
41
|
+
import headless from '@xterm/headless';
|
|
42
|
+
const { Terminal } = headless;
|
|
43
|
+
// @xterm/addon-serialize ships CJS as well, so it hits the same named-import caveat as
|
|
44
|
+
// headless above — default-import and destructure. SerializeAddon reads the buffer model
|
|
45
|
+
// directly (no DOM/renderer), which is exactly the `@xterm/headless` + serialize-addon
|
|
46
|
+
// combo xterm.js documents for server-side state capture and reconnection restore.
|
|
47
|
+
import serializeMod from '@xterm/addon-serialize';
|
|
48
|
+
const { SerializeAddon } = serializeMod;
|
|
49
|
+
/**
|
|
50
|
+
* Default CLI terminal geometry — MUST match `cliSessionPool.spawnClaude` (the engine passes
|
|
51
|
+
* these explicitly) and `CliPtyMirror` (COLS=120 / ROWS=80). claude draws its in-place redraws
|
|
52
|
+
* at *these* coordinates; a mismatch desyncs the grid. (Story 37.8 raised ROWS 40→80 so the
|
|
53
|
+
* mirror shows more of claude's screen at once.)
|
|
54
|
+
*/
|
|
55
|
+
export const CLI_SCREEN_COLS = 120;
|
|
56
|
+
export const CLI_SCREEN_ROWS = 80;
|
|
57
|
+
/**
|
|
58
|
+
* Scrollback retention (lines kept ABOVE the viewport). Story 37.11 AC5: a single block (a long tool
|
|
59
|
+
* diff / reasoning) taller than the viewport scrolls its HEAD — the opening glyph the card parser keys
|
|
60
|
+
* on — off the top. With `scrollback:0` that head was discarded, so the parser saw only the glyph-less
|
|
61
|
+
* TAIL and could not classify the block; it fell back to the late JSONL (a screen↔file timing race).
|
|
62
|
+
* Retaining scrollback lets `readGrid(scrollbackRows)` recover the head so the SCREEN stays the single
|
|
63
|
+
* primary card source. The mirror is kept viewport-only via `serialize({scrollback:0})` so this
|
|
64
|
+
* retention never bloats the `cli:screen-frame` payload.
|
|
65
|
+
*/
|
|
66
|
+
export const CLI_SCREEN_SCROLLBACK = 5000;
|
|
67
|
+
/** The assistant card bullet glyph (U+25CF) — its fg color encodes tool status (Story 37.10). */
|
|
68
|
+
const CARD_BULLET = '●';
|
|
69
|
+
/**
|
|
70
|
+
* Classify a `●` cell's foreground color into a {@link CliBulletColor} bucket. Only TRUECOLOR
|
|
71
|
+
* (claude paints the bullet via `\x1b[38;2;r;g;bm`) carries the status signal; a non-RGB fg is
|
|
72
|
+
* 'other' (not-green → treated as running, the safe default). Thresholds bucket by hue/brightness,
|
|
73
|
+
* not exact RGB, so a TUI shade retune still classifies (green: g clearly dominates; white: all
|
|
74
|
+
* channels bright; gray: the remaining low-saturation case). Verified RGBs: green 78,186,101 /
|
|
75
|
+
* gray 153,153,153 / white 255,255,255 (cli-real-pty-dump).
|
|
76
|
+
*/
|
|
77
|
+
function classifyBulletFg(cell) {
|
|
78
|
+
if (!cell.isFgRGB())
|
|
79
|
+
return 'other';
|
|
80
|
+
const c = cell.getFgColor();
|
|
81
|
+
const r = (c >> 16) & 0xff;
|
|
82
|
+
const g = (c >> 8) & 0xff;
|
|
83
|
+
const b = c & 0xff;
|
|
84
|
+
if (g - r >= 30 && g - b >= 30)
|
|
85
|
+
return 'green'; // done — green dominates (78,186,101)
|
|
86
|
+
if (r >= 200 && g >= 200 && b >= 200)
|
|
87
|
+
return 'white'; // assistant text body (255,255,255)
|
|
88
|
+
return 'gray'; // running / dim (153,153,153) and any other low-saturation fg
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Create a headless screen model owning one `@xterm/headless` Terminal.
|
|
92
|
+
*
|
|
93
|
+
* `disableStdin: true` (we never type into this emulator — it is read-only), `scrollback:
|
|
94
|
+
* CLI_SCREEN_SCROLLBACK` (Story 37.11 AC5 — retain scrolled-off lines so `readGrid(scrollbackRows)`
|
|
95
|
+
* can recover the HEAD of a block taller than the viewport; was 0, which discarded the head and
|
|
96
|
+
* forced the card parser to fall back to the late JSONL), and `allowProposedApi: true` (the `buffer`
|
|
97
|
+
* namespace used to read the grid is proposed API in 5.x — verified to throw otherwise).
|
|
98
|
+
* `Terminal.open()` is **never** called: headless has no DOM/renderer; we read the buffer model
|
|
99
|
+
* directly. The mirror's `serialize()` is scoped to the viewport so this retention never bloats it.
|
|
100
|
+
*/
|
|
101
|
+
export function createCliScreenModel(cols = CLI_SCREEN_COLS, rows = CLI_SCREEN_ROWS) {
|
|
102
|
+
const terminal = new Terminal({
|
|
103
|
+
cols,
|
|
104
|
+
rows,
|
|
105
|
+
disableStdin: true,
|
|
106
|
+
scrollback: CLI_SCREEN_SCROLLBACK,
|
|
107
|
+
allowProposedApi: true,
|
|
108
|
+
});
|
|
109
|
+
// Load the serialize addon onto the headless terminal. headless has no renderer, but
|
|
110
|
+
// serialize only reads the buffer model, so `open()` is unnecessary (same as readGrid).
|
|
111
|
+
// headless and addon-serialize ship separate (structurally identical) ITerminalAddon
|
|
112
|
+
// types, so the load is cast through `unknown` to bridge the two module declarations.
|
|
113
|
+
const serializeAddon = new SerializeAddon();
|
|
114
|
+
terminal.loadAddon(serializeAddon);
|
|
115
|
+
// Story 37.11 AC3: marks the current turn's start (set via markTurnStart at generation start). The
|
|
116
|
+
// card scraper's scrollback read is FLOORED here so a resume boot-repaint of the PRIOR conversation
|
|
117
|
+
// (painted into THIS turn's buffer ABOVE the mark) is never read as a live card. The marker rides
|
|
118
|
+
// scroll/eviction (xterm IMarker — probe-verified). null/absent ⇒ floor 0 (read full window).
|
|
119
|
+
let turnMarker;
|
|
120
|
+
const turnFloor = () => (turnMarker && !turnMarker.isDisposed ? turnMarker.line : 0);
|
|
121
|
+
return {
|
|
122
|
+
write(data) {
|
|
123
|
+
terminal.write(data);
|
|
124
|
+
},
|
|
125
|
+
flush() {
|
|
126
|
+
return new Promise((resolve) => {
|
|
127
|
+
terminal.write('', resolve);
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
readGrid(scrollbackRows = 0) {
|
|
131
|
+
const buffer = terminal.buffer.active;
|
|
132
|
+
const grid = [];
|
|
133
|
+
// The active viewport is the absolute range [baseY, baseY + rows). Story 37.11 AC5: when a
|
|
134
|
+
// caller asks for scrollback, START up to `scrollbackRows` lines higher so the scrolled-off HEAD
|
|
135
|
+
// of an over-tall block is included; 0 = viewport-only (unchanged). The start is clamped two ways:
|
|
136
|
+
// `max(turnFloor(), …)` keeps the read AT/below the current-turn mark (AC3 — a resume repaint that
|
|
137
|
+
// scrolled into scrollback ABOVE the mark is excluded), and `min(baseY, …)` keeps it AT/above baseY
|
|
138
|
+
// so the VIEWPORT is ALWAYS fully read (a mark inside the viewport never hides a live card — only
|
|
139
|
+
// the scrollback portion is floored). `translateToString(true)` trims trailing whitespace.
|
|
140
|
+
const startY = scrollbackRows > 0 ? Math.min(buffer.baseY, Math.max(turnFloor(), buffer.baseY - scrollbackRows)) : buffer.baseY;
|
|
141
|
+
for (let absY = startY; absY < buffer.baseY + rows; absY++) {
|
|
142
|
+
const line = buffer.getLine(absY);
|
|
143
|
+
grid.push(line ? line.translateToString(true) : '');
|
|
144
|
+
}
|
|
145
|
+
return grid;
|
|
146
|
+
},
|
|
147
|
+
readScreenText() {
|
|
148
|
+
return this.readGrid().join('\n');
|
|
149
|
+
},
|
|
150
|
+
readBulletColors(scrollbackRows = 0) {
|
|
151
|
+
const buffer = terminal.buffer.active;
|
|
152
|
+
const out = [];
|
|
153
|
+
// Same window as readGrid(scrollbackRows) so the two stay index-aligned (Story 37.11 AC5).
|
|
154
|
+
const startY = scrollbackRows > 0 ? Math.min(buffer.baseY, Math.max(turnFloor(), buffer.baseY - scrollbackRows)) : buffer.baseY;
|
|
155
|
+
for (let absY = startY; absY < buffer.baseY + rows; absY++) {
|
|
156
|
+
const line = buffer.getLine(absY);
|
|
157
|
+
let cls = null;
|
|
158
|
+
if (line) {
|
|
159
|
+
for (let x = 0; x < cols; x++) {
|
|
160
|
+
const cell = line.getCell(x);
|
|
161
|
+
if (!cell)
|
|
162
|
+
continue;
|
|
163
|
+
const ch = cell.getChars();
|
|
164
|
+
if (ch === CARD_BULLET) {
|
|
165
|
+
cls = classifyBulletFg(cell);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
// The bullet must be the LEADING glyph of the row (after indent spaces); a non-blank
|
|
169
|
+
// non-bullet first glyph (e.g. `⎿`, box-drawing, prose) means this row has no card bullet.
|
|
170
|
+
if (ch && ch.trim().length > 0)
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
out.push(cls);
|
|
175
|
+
}
|
|
176
|
+
return out;
|
|
177
|
+
},
|
|
178
|
+
serialize() {
|
|
179
|
+
// VIEWPORT-ONLY (Story 37.11 AC5): the card parser's scrollback retention must not bloat the
|
|
180
|
+
// mirror frame, so serialize just the active screen (its pre-37.11 scope). Probe-verified: the
|
|
181
|
+
// addon honors `{scrollback:0}` (default incl-scrollback payload was ~2x the viewport-only one).
|
|
182
|
+
return serializeAddon.serialize({ scrollback: 0 });
|
|
183
|
+
},
|
|
184
|
+
markTurnStart() {
|
|
185
|
+
// Drop a marker at the live cursor = the current turn's start. Replaces any prior mark so a
|
|
186
|
+
// re-entrant call can't drift the floor downward (it must stay at the FIRST generation frame).
|
|
187
|
+
turnMarker?.dispose();
|
|
188
|
+
turnMarker = terminal.registerMarker(0);
|
|
189
|
+
},
|
|
190
|
+
dispose() {
|
|
191
|
+
turnMarker?.dispose();
|
|
192
|
+
terminal.dispose();
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=cliScreenModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliScreenModel.js","sourceRoot":"","sources":["../../src/services/cliScreenModel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Soft CLI screen-stall watchdog.
|
|
3
|
+
*
|
|
4
|
+
* The reconstructed claude screen changes ~1×/second while a turn is healthy (the spinner glyph
|
|
5
|
+
* rotates and the elapsed clock ticks), so a long flat-line of NO content change is a reliable
|
|
6
|
+
* "claude froze" signal — far more reliable than the removed inactivity timeout, which watched JSONL
|
|
7
|
+
* activity that legitimately pauses during deep thinking. This pure helper turns a stream of screen
|
|
8
|
+
* frames into a single stalled↔live state:
|
|
9
|
+
*
|
|
10
|
+
* - a CHANGED frame proves liveness → clears any active stall and (re)arms the timer;
|
|
11
|
+
* - an identical repaint is ignored (no real change);
|
|
12
|
+
* - after `stallMs` with no change the timer fires `onStallChange(true)` — but ONLY if `isActive()`
|
|
13
|
+
* still holds (a running, non-modal turn), so a finished turn or a pending permission/question
|
|
14
|
+
* modal can never false-trigger;
|
|
15
|
+
* - `dispose()` (turn end) clears the timer so a stale fire can't leak into the next turn, and
|
|
16
|
+
* drops any active stall.
|
|
17
|
+
*
|
|
18
|
+
* Advisory ONLY: the caller decides what to do with the signal — Hammoc surfaces a "looks stuck —
|
|
19
|
+
* Stop?" affordance and never auto-aborts. The scheduler is injectable so the timing is unit-testable
|
|
20
|
+
* without real timers.
|
|
21
|
+
*/
|
|
22
|
+
export interface ScreenStallWatchdog {
|
|
23
|
+
/** Feed a screen frame. A changed frame clears the stall and re-arms; an identical one is ignored. */
|
|
24
|
+
noteFrame(frame: string): void;
|
|
25
|
+
/** Tear down at turn end: clear the timer and drop any active stall. */
|
|
26
|
+
dispose(): void;
|
|
27
|
+
}
|
|
28
|
+
export interface ScreenStallWatchdogOptions {
|
|
29
|
+
/** No-change window in ms. `<= 0` disables the watchdog entirely (noteFrame/dispose become no-ops). */
|
|
30
|
+
stallMs: number;
|
|
31
|
+
/** Fire-guard, re-checked when the timer elapses: is this still a running, non-modal turn? */
|
|
32
|
+
isActive: () => boolean;
|
|
33
|
+
/** Called only on a stalled↔live transition (never repeated for the same state). */
|
|
34
|
+
onStallChange: (stalled: boolean) => void;
|
|
35
|
+
/** Injectable scheduler (tests). Default setTimeout. */
|
|
36
|
+
schedule?: (cb: () => void, ms: number) => ReturnType<typeof setTimeout>;
|
|
37
|
+
/** Injectable canceller (tests). Default clearTimeout. */
|
|
38
|
+
cancel?: (id: ReturnType<typeof setTimeout>) => void;
|
|
39
|
+
}
|
|
40
|
+
export declare function createScreenStallWatchdog(opts: ScreenStallWatchdogOptions): ScreenStallWatchdog;
|
|
41
|
+
//# sourceMappingURL=cliScreenStallWatchdog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliScreenStallWatchdog.d.ts","sourceRoot":"","sources":["../../src/services/cliScreenStallWatchdog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,mBAAmB;IAClC,sGAAsG;IACtG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,wEAAwE;IACxE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,0BAA0B;IACzC,uGAAuG;IACvG,OAAO,EAAE,MAAM,CAAC;IAChB,8FAA8F;IAC9F,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,oFAAoF;IACpF,aAAa,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1C,wDAAwD;IACxD,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;IACzE,0DAA0D;IAC1D,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,CAAC;CACtD;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,0BAA0B,GAAG,mBAAmB,CA2C/F"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export function createScreenStallWatchdog(opts) {
|
|
2
|
+
const schedule = opts.schedule ?? ((cb, ms) => setTimeout(cb, ms));
|
|
3
|
+
const cancel = opts.cancel ?? ((id) => clearTimeout(id));
|
|
4
|
+
let timer = null;
|
|
5
|
+
let lastFrame = null;
|
|
6
|
+
let stalled = false;
|
|
7
|
+
const setStalled = (next) => {
|
|
8
|
+
if (stalled === next)
|
|
9
|
+
return;
|
|
10
|
+
stalled = next;
|
|
11
|
+
opts.onStallChange(next);
|
|
12
|
+
};
|
|
13
|
+
const clearTimer = () => {
|
|
14
|
+
if (timer !== null) {
|
|
15
|
+
cancel(timer);
|
|
16
|
+
timer = null;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const arm = () => {
|
|
20
|
+
clearTimer();
|
|
21
|
+
if (opts.stallMs <= 0)
|
|
22
|
+
return;
|
|
23
|
+
timer = schedule(() => {
|
|
24
|
+
timer = null;
|
|
25
|
+
if (opts.isActive())
|
|
26
|
+
setStalled(true);
|
|
27
|
+
}, opts.stallMs);
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
noteFrame(frame) {
|
|
31
|
+
if (opts.stallMs <= 0)
|
|
32
|
+
return;
|
|
33
|
+
if (frame === lastFrame)
|
|
34
|
+
return; // identical repaint — not a real change
|
|
35
|
+
lastFrame = frame;
|
|
36
|
+
setStalled(false); // content moved → live (clears any active stall)
|
|
37
|
+
arm();
|
|
38
|
+
},
|
|
39
|
+
dispose() {
|
|
40
|
+
clearTimer();
|
|
41
|
+
setStalled(false);
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=cliScreenStallWatchdog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliScreenStallWatchdog.js","sourceRoot":"","sources":["../../src/services/cliScreenStallWatchdog.ts"],"names":[],"mappings":"AAyCA,MAAM,UAAU,yBAAyB,CAAC,IAAgC;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;IAEzD,IAAI,KAAK,GAAyC,IAAI,CAAC;IACvD,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,MAAM,UAAU,GAAG,CAAC,IAAa,EAAE,EAAE;QACnC,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO;QAC7B,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,CAAC,KAAK,CAAC,CAAC;YACd,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,GAAG,GAAG,GAAG,EAAE;QACf,UAAU,EAAE,CAAC;QACb,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC;YAAE,OAAO;QAC9B,KAAK,GAAG,QAAQ,CAAC,GAAG,EAAE;YACpB,KAAK,GAAG,IAAI,CAAC;YACb,IAAI,IAAI,CAAC,QAAQ,EAAE;gBAAE,UAAU,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACnB,CAAC,CAAC;IAEF,OAAO;QACL,SAAS,CAAC,KAAa;YACrB,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC;gBAAE,OAAO;YAC9B,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,CAAC,wCAAwC;YACzE,SAAS,GAAG,KAAK,CAAC;YAClB,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,iDAAiD;YACpE,GAAG,EAAE,CAAC;QACR,CAAC;QACD,OAAO;YACL,UAAU,EAAE,CAAC;YACb,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Session Pool (Epic 32 — Story 32.4)
|
|
3
|
+
*
|
|
4
|
+
* Owns the PTY processes for the CLI conversation engine (`CliChatEngine`), one
|
|
5
|
+
* per in-flight turn. This pool is **deliberately a separate module singleton
|
|
6
|
+
* from `ptyService`** (the web terminal pool): the dashboard's "active terminals"
|
|
7
|
+
* count is derived from `ptyService`'s session map, so routing CLI-engine PTYs
|
|
8
|
+
* through `ptyService` would pollute that count (Sprint Change Proposal §9.2 #3,
|
|
9
|
+
* spike §9-3). Keeping a distinct instance guarantees the CLI engine's PTYs never
|
|
10
|
+
* leak into the terminal count — isolation by construction.
|
|
11
|
+
*
|
|
12
|
+
* The Windows PATH normalization and binary-discovery patterns are *borrowed*
|
|
13
|
+
* from `ptyService` (which stays unmodified). They are duplicated here on purpose
|
|
14
|
+
* for file independence and isolation — the same convention `chatService.ts` and
|
|
15
|
+
* `streamHandler.ts` already use for `extractContextWindow`.
|
|
16
|
+
*/
|
|
17
|
+
import * as pty from 'node-pty';
|
|
18
|
+
/** Handle returned to the engine for a spawned CLI PTY. */
|
|
19
|
+
export interface CliPtyHandle {
|
|
20
|
+
/** Opaque key used to address this PTY in the pool (not the claude session id). */
|
|
21
|
+
handle: string;
|
|
22
|
+
pty: pty.IPty;
|
|
23
|
+
}
|
|
24
|
+
interface SpawnClaudeOptions {
|
|
25
|
+
/** Working directory for the claude session (the project root). */
|
|
26
|
+
cwd?: string;
|
|
27
|
+
/** Arguments passed to claude (interactive — no `--print`/`--output-format`). */
|
|
28
|
+
args: string[];
|
|
29
|
+
cols?: number;
|
|
30
|
+
rows?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Epic 33 (Story 33.3): user-configured `claude` binary path. When set and valid it
|
|
33
|
+
* takes precedence over auto-detection; an invalid value falls back gracefully (see
|
|
34
|
+
* `resolveClaudeBinary`). Empty / undefined = auto-detect.
|
|
35
|
+
*/
|
|
36
|
+
binaryPathOverride?: string;
|
|
37
|
+
}
|
|
38
|
+
declare class CliSessionPool {
|
|
39
|
+
private sessions;
|
|
40
|
+
private readonly windowsSystemPath;
|
|
41
|
+
/**
|
|
42
|
+
* Convert MINGW64 Unix-style PATH entries to Windows format and merge in the
|
|
43
|
+
* real Windows system PATH. (Borrowed verbatim in spirit from ptyService.)
|
|
44
|
+
*/
|
|
45
|
+
private normalizePathForWindows;
|
|
46
|
+
/**
|
|
47
|
+
* Build the spawn environment: process.env minus undefined values and minus
|
|
48
|
+
* the "inside claude" signal vars, with Windows PATH normalized.
|
|
49
|
+
*/
|
|
50
|
+
private buildEnv;
|
|
51
|
+
/**
|
|
52
|
+
* Best-effort sanity check for a user-supplied binary override (Story 33.3): exists,
|
|
53
|
+
* is a regular file, and (POSIX only) is executable by this process. Windows has no
|
|
54
|
+
* comparable execute bit, so `.exe`/file existence is sufficient there. Intentionally
|
|
55
|
+
* shallow — no signature / "is this really claude" inspection (out of scope).
|
|
56
|
+
*/
|
|
57
|
+
private isValidBinaryOverride;
|
|
58
|
+
/**
|
|
59
|
+
* Resolve an executable path for `claude`. PATH lookup can fail in the server's
|
|
60
|
+
* child process (spike §9-5: `where claude` failed; the fallback resolved it),
|
|
61
|
+
* so known install locations are tried first, then a PATH probe with the
|
|
62
|
+
* corrected env, then a bare name as a last resort (pty resolves via env PATH).
|
|
63
|
+
*
|
|
64
|
+
* Story 33.3: when a non-empty `override` is supplied it is checked first and used
|
|
65
|
+
* if valid. An *invalid* override never hard-fails the turn — it logs a warning and
|
|
66
|
+
* falls through to the normal chain (auto-detect is the better UX than blocking CLI
|
|
67
|
+
* mode over a bad path). Empty / undefined skips the override entirely.
|
|
68
|
+
*/
|
|
69
|
+
private resolveClaudeBinary;
|
|
70
|
+
/**
|
|
71
|
+
* Spawn an interactive claude TUI in a PTY and register it in the pool.
|
|
72
|
+
* The caller drives stdin (prompt injection), wires `pty.onData`, and attaches
|
|
73
|
+
* a watcher disposer via `registerDisposer`.
|
|
74
|
+
*/
|
|
75
|
+
spawnClaude(opts: SpawnClaudeOptions): CliPtyHandle;
|
|
76
|
+
/** Attach the watcher teardown so dispose() tears down PTY + watcher together. */
|
|
77
|
+
registerDisposer(handle: string, dispose: () => void): void;
|
|
78
|
+
/** Send Ctrl+C (interrupt) to the PTY without killing it. */
|
|
79
|
+
interrupt(handle: string): void;
|
|
80
|
+
/**
|
|
81
|
+
* Tear down a CLI PTY: run the watcher disposer, kill the process, remove from
|
|
82
|
+
* the pool. Removal happens first so any onExit listener's dispose is a no-op.
|
|
83
|
+
* The kill is wrapped in try/catch to absorb Windows ConPTY shutdown noise
|
|
84
|
+
* (`AttachConsole failed`, spike §9-6 — `ptyService.killSession` pattern).
|
|
85
|
+
*/
|
|
86
|
+
dispose(handle: string): void;
|
|
87
|
+
/** Destroy every CLI PTY (server shutdown). Symmetric with ptyService.destroyAll. */
|
|
88
|
+
destroyAll(): void;
|
|
89
|
+
/** Number of live CLI PTYs (for tests / introspection). */
|
|
90
|
+
get size(): number;
|
|
91
|
+
/** Whether a handle is still live in the pool. */
|
|
92
|
+
has(handle: string): boolean;
|
|
93
|
+
}
|
|
94
|
+
/** Module singleton — isolated from the web terminal pool (`ptyService`). */
|
|
95
|
+
export declare const cliSessionPool: CliSessionPool;
|
|
96
|
+
export {};
|
|
97
|
+
//# sourceMappingURL=cliSessionPool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliSessionPool.d.ts","sourceRoot":"","sources":["../../src/services/cliSessionPool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AA+BhC,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC3B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC;CACf;AAUD,UAAU,kBAAkB;IAC1B,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,cAAM,cAAc;IAClB,OAAO,CAAC,QAAQ,CAAkC;IAMlD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAU7B;IAEL;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAgD/B;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAUhB;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAY7B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,mBAAmB;IAkC3B;;;;OAIG;IACH,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,YAAY;IAkBnD,kFAAkF;IAClF,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAK3D,6DAA6D;IAC7D,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAU/B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAiB7B,qFAAqF;IACrF,UAAU,IAAI,IAAI;IASlB,2DAA2D;IAC3D,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,kDAAkD;IAClD,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;CAG7B;AAED,6EAA6E;AAC7E,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
|