lattice-orchestrator 0.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/LICENSE +201 -0
- package/README.md +58 -0
- package/config/logrotate.conf +15 -0
- package/dist/cli-parser.d.ts +11 -0
- package/dist/cli-parser.d.ts.map +1 -0
- package/dist/cli-parser.js +48 -0
- package/dist/cli-parser.js.map +1 -0
- package/dist/lattice-server.d.ts +70 -0
- package/dist/lattice-server.d.ts.map +1 -0
- package/dist/lattice-server.js +969 -0
- package/dist/lattice-server.js.map +1 -0
- package/dist/mcp-server/index.d.ts +3 -0
- package/dist/mcp-server/index.d.ts.map +1 -0
- package/dist/mcp-server/index.js +190 -0
- package/dist/mcp-server/index.js.map +1 -0
- package/dist/mcp-server/lattice-tools.d.ts +15 -0
- package/dist/mcp-server/lattice-tools.d.ts.map +1 -0
- package/dist/mcp-server/lattice-tools.js +366 -0
- package/dist/mcp-server/lattice-tools.js.map +1 -0
- package/dist/middleware/cors-setup.d.ts +7 -0
- package/dist/middleware/cors-setup.d.ts.map +1 -0
- package/dist/middleware/cors-setup.js +8 -0
- package/dist/middleware/cors-setup.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +4 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +27 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/query-parser.d.ts +11 -0
- package/dist/middleware/query-parser.d.ts.map +1 -0
- package/dist/middleware/query-parser.js +68 -0
- package/dist/middleware/query-parser.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +4 -0
- package/dist/middleware/request-logger.d.ts.map +1 -0
- package/dist/middleware/request-logger.js +6 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/process-daemon/index.d.ts +14 -0
- package/dist/process-daemon/index.d.ts.map +1 -0
- package/dist/process-daemon/index.js +51 -0
- package/dist/process-daemon/index.js.map +1 -0
- package/dist/process-daemon/process-daemon.d.ts +101 -0
- package/dist/process-daemon/process-daemon.d.ts.map +1 -0
- package/dist/process-daemon/process-daemon.js +846 -0
- package/dist/process-daemon/process-daemon.js.map +1 -0
- package/dist/process-daemon/process-manager-client.d.ts +123 -0
- package/dist/process-daemon/process-manager-client.d.ts.map +1 -0
- package/dist/process-daemon/process-manager-client.js +329 -0
- package/dist/process-daemon/process-manager-client.js.map +1 -0
- package/dist/process-daemon/process-manager-interface.d.ts +61 -0
- package/dist/process-daemon/process-manager-interface.d.ts.map +1 -0
- package/dist/process-daemon/process-manager-interface.js +8 -0
- package/dist/process-daemon/process-manager-interface.js.map +1 -0
- package/dist/process-daemon/test-daemon.d.ts +12 -0
- package/dist/process-daemon/test-daemon.d.ts.map +1 -0
- package/dist/process-daemon/test-daemon.js +81 -0
- package/dist/process-daemon/test-daemon.js.map +1 -0
- package/dist/process-daemon/types.d.ts +97 -0
- package/dist/process-daemon/types.d.ts.map +1 -0
- package/dist/process-daemon/types.js +8 -0
- package/dist/process-daemon/types.js.map +1 -0
- package/dist/routes/analysis.routes.d.ts +13 -0
- package/dist/routes/analysis.routes.d.ts.map +1 -0
- package/dist/routes/analysis.routes.js +520 -0
- package/dist/routes/analysis.routes.js.map +1 -0
- package/dist/routes/config.routes.d.ts +4 -0
- package/dist/routes/config.routes.d.ts.map +1 -0
- package/dist/routes/config.routes.js +27 -0
- package/dist/routes/config.routes.js.map +1 -0
- package/dist/routes/conversation.routes.d.ts +43 -0
- package/dist/routes/conversation.routes.d.ts.map +1 -0
- package/dist/routes/conversation.routes.js +79 -0
- package/dist/routes/conversation.routes.js.map +1 -0
- package/dist/routes/filesystem.routes.d.ts +4 -0
- package/dist/routes/filesystem.routes.d.ts.map +1 -0
- package/dist/routes/filesystem.routes.js +86 -0
- package/dist/routes/filesystem.routes.js.map +1 -0
- package/dist/routes/insights.routes.d.ts +17 -0
- package/dist/routes/insights.routes.d.ts.map +1 -0
- package/dist/routes/insights.routes.js +633 -0
- package/dist/routes/insights.routes.js.map +1 -0
- package/dist/routes/lattice.routes.d.ts +10 -0
- package/dist/routes/lattice.routes.d.ts.map +1 -0
- package/dist/routes/lattice.routes.js +123 -0
- package/dist/routes/lattice.routes.js.map +1 -0
- package/dist/routes/license.routes.d.ts +3 -0
- package/dist/routes/license.routes.d.ts.map +1 -0
- package/dist/routes/license.routes.js +95 -0
- package/dist/routes/license.routes.js.map +1 -0
- package/dist/routes/log.routes.d.ts +3 -0
- package/dist/routes/log.routes.d.ts.map +1 -0
- package/dist/routes/log.routes.js +184 -0
- package/dist/routes/log.routes.js.map +1 -0
- package/dist/routes/pending-question.routes.d.ts +9 -0
- package/dist/routes/pending-question.routes.d.ts.map +1 -0
- package/dist/routes/pending-question.routes.js +162 -0
- package/dist/routes/pending-question.routes.js.map +1 -0
- package/dist/routes/permission.routes.d.ts +18 -0
- package/dist/routes/permission.routes.d.ts.map +1 -0
- package/dist/routes/permission.routes.js +370 -0
- package/dist/routes/permission.routes.js.map +1 -0
- package/dist/routes/process-events.routes.d.ts +9 -0
- package/dist/routes/process-events.routes.d.ts.map +1 -0
- package/dist/routes/process-events.routes.js +141 -0
- package/dist/routes/process-events.routes.js.map +1 -0
- package/dist/routes/prototype.routes.d.ts +9 -0
- package/dist/routes/prototype.routes.d.ts.map +1 -0
- package/dist/routes/prototype.routes.js +757 -0
- package/dist/routes/prototype.routes.js.map +1 -0
- package/dist/routes/question.routes.d.ts +8 -0
- package/dist/routes/question.routes.d.ts.map +1 -0
- package/dist/routes/question.routes.js +83 -0
- package/dist/routes/question.routes.js.map +1 -0
- package/dist/routes/session-control.routes.d.ts +29 -0
- package/dist/routes/session-control.routes.d.ts.map +1 -0
- package/dist/routes/session-control.routes.js +455 -0
- package/dist/routes/session-control.routes.js.map +1 -0
- package/dist/routes/session-lifecycle.routes.d.ts +21 -0
- package/dist/routes/session-lifecycle.routes.d.ts.map +1 -0
- package/dist/routes/session-lifecycle.routes.js +256 -0
- package/dist/routes/session-lifecycle.routes.js.map +1 -0
- package/dist/routes/session-query.routes.d.ts +25 -0
- package/dist/routes/session-query.routes.d.ts.map +1 -0
- package/dist/routes/session-query.routes.js +363 -0
- package/dist/routes/session-query.routes.js.map +1 -0
- package/dist/routes/session-stream.routes.d.ts +21 -0
- package/dist/routes/session-stream.routes.d.ts.map +1 -0
- package/dist/routes/session-stream.routes.js +235 -0
- package/dist/routes/session-stream.routes.js.map +1 -0
- package/dist/routes/streaming.routes.d.ts +4 -0
- package/dist/routes/streaming.routes.d.ts.map +1 -0
- package/dist/routes/streaming.routes.js +33 -0
- package/dist/routes/streaming.routes.js.map +1 -0
- package/dist/routes/system.routes.d.ts +7 -0
- package/dist/routes/system.routes.d.ts.map +1 -0
- package/dist/routes/system.routes.js +214 -0
- package/dist/routes/system.routes.js.map +1 -0
- package/dist/routes/walkthrough.routes.d.ts +19 -0
- package/dist/routes/walkthrough.routes.d.ts.map +1 -0
- package/dist/routes/walkthrough.routes.js +688 -0
- package/dist/routes/walkthrough.routes.js.map +1 -0
- package/dist/routes/working-directories.routes.d.ts +4 -0
- package/dist/routes/working-directories.routes.d.ts.map +1 -0
- package/dist/routes/working-directories.routes.js +25 -0
- package/dist/routes/working-directories.routes.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +34 -0
- package/dist/server.js.map +1 -0
- package/dist/services/ToolMetricsService.d.ts +53 -0
- package/dist/services/ToolMetricsService.d.ts.map +1 -0
- package/dist/services/ToolMetricsService.js +230 -0
- package/dist/services/ToolMetricsService.js.map +1 -0
- package/dist/services/claude-router-service.d.ts +19 -0
- package/dist/services/claude-router-service.d.ts.map +1 -0
- package/dist/services/claude-router-service.js +160 -0
- package/dist/services/claude-router-service.js.map +1 -0
- package/dist/services/commands-service.d.ts +20 -0
- package/dist/services/commands-service.d.ts.map +1 -0
- package/dist/services/commands-service.js +115 -0
- package/dist/services/commands-service.js.map +1 -0
- package/dist/services/connection-debug-logger.d.ts +85 -0
- package/dist/services/connection-debug-logger.d.ts.map +1 -0
- package/dist/services/connection-debug-logger.js +221 -0
- package/dist/services/connection-debug-logger.js.map +1 -0
- package/dist/services/debug-log.d.ts +6 -0
- package/dist/services/debug-log.d.ts.map +1 -0
- package/dist/services/debug-log.js +27 -0
- package/dist/services/debug-log.js.map +1 -0
- package/dist/services/gemini-service.d.ts +35 -0
- package/dist/services/gemini-service.d.ts.map +1 -0
- package/dist/services/gemini-service.js +256 -0
- package/dist/services/gemini-service.js.map +1 -0
- package/dist/services/infrastructure/config-service.d.ts +79 -0
- package/dist/services/infrastructure/config-service.d.ts.map +1 -0
- package/dist/services/infrastructure/config-service.js +431 -0
- package/dist/services/infrastructure/config-service.js.map +1 -0
- package/dist/services/infrastructure/cost-tracker.d.ts +112 -0
- package/dist/services/infrastructure/cost-tracker.d.ts.map +1 -0
- package/dist/services/infrastructure/cost-tracker.js +423 -0
- package/dist/services/infrastructure/cost-tracker.js.map +1 -0
- package/dist/services/infrastructure/file-system-service.d.ts +61 -0
- package/dist/services/infrastructure/file-system-service.d.ts.map +1 -0
- package/dist/services/infrastructure/file-system-service.js +348 -0
- package/dist/services/infrastructure/file-system-service.js.map +1 -0
- package/dist/services/infrastructure/log-formatter.d.ts +5 -0
- package/dist/services/infrastructure/log-formatter.d.ts.map +1 -0
- package/dist/services/infrastructure/log-formatter.js +77 -0
- package/dist/services/infrastructure/log-formatter.js.map +1 -0
- package/dist/services/infrastructure/log-stream-buffer.d.ts +11 -0
- package/dist/services/infrastructure/log-stream-buffer.d.ts.map +1 -0
- package/dist/services/infrastructure/log-stream-buffer.js +36 -0
- package/dist/services/infrastructure/log-stream-buffer.js.map +1 -0
- package/dist/services/infrastructure/logger.d.ts +71 -0
- package/dist/services/infrastructure/logger.d.ts.map +1 -0
- package/dist/services/infrastructure/logger.js +215 -0
- package/dist/services/infrastructure/logger.js.map +1 -0
- package/dist/services/infrastructure/service-registry.d.ts +86 -0
- package/dist/services/infrastructure/service-registry.d.ts.map +1 -0
- package/dist/services/infrastructure/service-registry.js +162 -0
- package/dist/services/infrastructure/service-registry.js.map +1 -0
- package/dist/services/infrastructure/stream-manager.d.ts +87 -0
- package/dist/services/infrastructure/stream-manager.d.ts.map +1 -0
- package/dist/services/infrastructure/stream-manager.js +436 -0
- package/dist/services/infrastructure/stream-manager.js.map +1 -0
- package/dist/services/infrastructure/timing.d.ts +72 -0
- package/dist/services/infrastructure/timing.d.ts.map +1 -0
- package/dist/services/infrastructure/timing.js +115 -0
- package/dist/services/infrastructure/timing.js.map +1 -0
- package/dist/services/insights/anthropic-service.d.ts +224 -0
- package/dist/services/insights/anthropic-service.d.ts.map +1 -0
- package/dist/services/insights/anthropic-service.js +1062 -0
- package/dist/services/insights/anthropic-service.js.map +1 -0
- package/dist/services/insights/insight-audit-repository.d.ts +119 -0
- package/dist/services/insights/insight-audit-repository.d.ts.map +1 -0
- package/dist/services/insights/insight-audit-repository.js +242 -0
- package/dist/services/insights/insight-audit-repository.js.map +1 -0
- package/dist/services/insights/insight-queue.d.ts +99 -0
- package/dist/services/insights/insight-queue.d.ts.map +1 -0
- package/dist/services/insights/insight-queue.js +277 -0
- package/dist/services/insights/insight-queue.js.map +1 -0
- package/dist/services/insights/insights-computer.d.ts +132 -0
- package/dist/services/insights/insights-computer.d.ts.map +1 -0
- package/dist/services/insights/insights-computer.js +936 -0
- package/dist/services/insights/insights-computer.js.map +1 -0
- package/dist/services/insights/insights-coordinator.d.ts +165 -0
- package/dist/services/insights/insights-coordinator.d.ts.map +1 -0
- package/dist/services/insights/insights-coordinator.js +1678 -0
- package/dist/services/insights/insights-coordinator.js.map +1 -0
- package/dist/services/insights/insights-event-log.d.ts +196 -0
- package/dist/services/insights/insights-event-log.d.ts.map +1 -0
- package/dist/services/insights/insights-event-log.js +319 -0
- package/dist/services/insights/insights-event-log.js.map +1 -0
- package/dist/services/lattice-service.d.ts +77 -0
- package/dist/services/lattice-service.d.ts.map +1 -0
- package/dist/services/lattice-service.js +195 -0
- package/dist/services/lattice-service.js.map +1 -0
- package/dist/services/license-service.d.ts +69 -0
- package/dist/services/license-service.d.ts.map +1 -0
- package/dist/services/license-service.js +330 -0
- package/dist/services/license-service.js.map +1 -0
- package/dist/services/mcp-config-generator.d.ts +32 -0
- package/dist/services/mcp-config-generator.d.ts.map +1 -0
- package/dist/services/mcp-config-generator.js +126 -0
- package/dist/services/mcp-config-generator.js.map +1 -0
- package/dist/services/message-filter.d.ts +22 -0
- package/dist/services/message-filter.d.ts.map +1 -0
- package/dist/services/message-filter.js +57 -0
- package/dist/services/message-filter.js.map +1 -0
- package/dist/services/notification-service.d.ts +45 -0
- package/dist/services/notification-service.d.ts.map +1 -0
- package/dist/services/notification-service.js +184 -0
- package/dist/services/notification-service.js.map +1 -0
- package/dist/services/pending-question-service.d.ts +97 -0
- package/dist/services/pending-question-service.d.ts.map +1 -0
- package/dist/services/pending-question-service.js +223 -0
- package/dist/services/pending-question-service.js.map +1 -0
- package/dist/services/permission-event-log.d.ts +136 -0
- package/dist/services/permission-event-log.d.ts.map +1 -0
- package/dist/services/permission-event-log.js +252 -0
- package/dist/services/permission-event-log.js.map +1 -0
- package/dist/services/permission-pattern-matcher.d.ts +82 -0
- package/dist/services/permission-pattern-matcher.d.ts.map +1 -0
- package/dist/services/permission-pattern-matcher.js +294 -0
- package/dist/services/permission-pattern-matcher.js.map +1 -0
- package/dist/services/permission-tracker.d.ts +67 -0
- package/dist/services/permission-tracker.d.ts.map +1 -0
- package/dist/services/permission-tracker.js +162 -0
- package/dist/services/permission-tracker.js.map +1 -0
- package/dist/services/process/claude-process-manager.d.ts +142 -0
- package/dist/services/process/claude-process-manager.d.ts.map +1 -0
- package/dist/services/process/claude-process-manager.js +1218 -0
- package/dist/services/process/claude-process-manager.js.map +1 -0
- package/dist/services/process/conversation-status-manager.d.ts +110 -0
- package/dist/services/process/conversation-status-manager.d.ts.map +1 -0
- package/dist/services/process/conversation-status-manager.js +349 -0
- package/dist/services/process/conversation-status-manager.js.map +1 -0
- package/dist/services/process/json-lines-parser.d.ts +19 -0
- package/dist/services/process/json-lines-parser.d.ts.map +1 -0
- package/dist/services/process/json-lines-parser.js +59 -0
- package/dist/services/process/json-lines-parser.js.map +1 -0
- package/dist/services/process/process-event-log.d.ts +263 -0
- package/dist/services/process/process-event-log.d.ts.map +1 -0
- package/dist/services/process/process-event-log.js +509 -0
- package/dist/services/process/process-event-log.js.map +1 -0
- package/dist/services/process/process-manager-factory.d.ts +109 -0
- package/dist/services/process/process-manager-factory.d.ts.map +1 -0
- package/dist/services/process/process-manager-factory.js +338 -0
- package/dist/services/process/process-manager-factory.js.map +1 -0
- package/dist/services/question-tracker.d.ts +51 -0
- package/dist/services/question-tracker.d.ts.map +1 -0
- package/dist/services/question-tracker.js +111 -0
- package/dist/services/question-tracker.js.map +1 -0
- package/dist/services/sessions/claude-history-reader.d.ts +139 -0
- package/dist/services/sessions/claude-history-reader.d.ts.map +1 -0
- package/dist/services/sessions/claude-history-reader.js +864 -0
- package/dist/services/sessions/claude-history-reader.js.map +1 -0
- package/dist/services/sessions/conversation-cache.d.ts +98 -0
- package/dist/services/sessions/conversation-cache.d.ts.map +1 -0
- package/dist/services/sessions/conversation-cache.js +329 -0
- package/dist/services/sessions/conversation-cache.js.map +1 -0
- package/dist/services/sessions/session-activity-watcher.d.ts +67 -0
- package/dist/services/sessions/session-activity-watcher.d.ts.map +1 -0
- package/dist/services/sessions/session-activity-watcher.js +236 -0
- package/dist/services/sessions/session-activity-watcher.js.map +1 -0
- package/dist/services/sessions/session-analysis-service.d.ts +72 -0
- package/dist/services/sessions/session-analysis-service.d.ts.map +1 -0
- package/dist/services/sessions/session-analysis-service.js +373 -0
- package/dist/services/sessions/session-analysis-service.js.map +1 -0
- package/dist/services/sessions/session-branch-service.d.ts +76 -0
- package/dist/services/sessions/session-branch-service.d.ts.map +1 -0
- package/dist/services/sessions/session-branch-service.js +355 -0
- package/dist/services/sessions/session-branch-service.js.map +1 -0
- package/dist/services/sessions/session-info-service.d.ts +455 -0
- package/dist/services/sessions/session-info-service.d.ts.map +1 -0
- package/dist/services/sessions/session-info-service.js +1640 -0
- package/dist/services/sessions/session-info-service.js.map +1 -0
- package/dist/services/sessions/session-marks-repository.d.ts +78 -0
- package/dist/services/sessions/session-marks-repository.d.ts.map +1 -0
- package/dist/services/sessions/session-marks-repository.js +263 -0
- package/dist/services/sessions/session-marks-repository.js.map +1 -0
- package/dist/services/sessions/session-marks-service.d.ts +137 -0
- package/dist/services/sessions/session-marks-service.d.ts.map +1 -0
- package/dist/services/sessions/session-marks-service.js +562 -0
- package/dist/services/sessions/session-marks-service.js.map +1 -0
- package/dist/services/sessions/session-review-service.d.ts +98 -0
- package/dist/services/sessions/session-review-service.d.ts.map +1 -0
- package/dist/services/sessions/session-review-service.js +629 -0
- package/dist/services/sessions/session-review-service.js.map +1 -0
- package/dist/services/sessions/turn-capture-service.d.ts +83 -0
- package/dist/services/sessions/turn-capture-service.d.ts.map +1 -0
- package/dist/services/sessions/turn-capture-service.js +477 -0
- package/dist/services/sessions/turn-capture-service.js.map +1 -0
- package/dist/services/sessions/turn-repository.d.ts +48 -0
- package/dist/services/sessions/turn-repository.d.ts.map +1 -0
- package/dist/services/sessions/turn-repository.js +103 -0
- package/dist/services/sessions/turn-repository.js.map +1 -0
- package/dist/services/walkthrough-service.d.ts +226 -0
- package/dist/services/walkthrough-service.d.ts.map +1 -0
- package/dist/services/walkthrough-service.js +1112 -0
- package/dist/services/walkthrough-service.js.map +1 -0
- package/dist/services/walkthrough-skill-prompt.d.ts +34 -0
- package/dist/services/walkthrough-skill-prompt.d.ts.map +1 -0
- package/dist/services/walkthrough-skill-prompt.js +313 -0
- package/dist/services/walkthrough-skill-prompt.js.map +1 -0
- package/dist/services/web-push-service.d.ts +48 -0
- package/dist/services/web-push-service.d.ts.map +1 -0
- package/dist/services/web-push-service.js +186 -0
- package/dist/services/web-push-service.js.map +1 -0
- package/dist/services/working-directories-service.d.ts +19 -0
- package/dist/services/working-directories-service.d.ts.map +1 -0
- package/dist/services/working-directories-service.js +103 -0
- package/dist/services/working-directories-service.js.map +1 -0
- package/dist/types/config.d.ts +122 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +21 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/express.d.ts +5 -0
- package/dist/types/express.d.ts.map +1 -0
- package/dist/types/express.js +2 -0
- package/dist/types/express.js.map +1 -0
- package/dist/types/index.d.ts +400 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +41 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/insights.d.ts +176 -0
- package/dist/types/insights.d.ts.map +1 -0
- package/dist/types/insights.js +23 -0
- package/dist/types/insights.js.map +1 -0
- package/dist/types/license.d.ts +70 -0
- package/dist/types/license.d.ts.map +1 -0
- package/dist/types/license.js +5 -0
- package/dist/types/license.js.map +1 -0
- package/dist/types/router-config.d.ts +13 -0
- package/dist/types/router-config.d.ts.map +1 -0
- package/dist/types/router-config.js +2 -0
- package/dist/types/router-config.js.map +1 -0
- package/dist/utils/constants.d.ts +26 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +28 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/machine-id.d.ts +7 -0
- package/dist/utils/machine-id.d.ts.map +1 -0
- package/dist/utils/machine-id.js +76 -0
- package/dist/utils/machine-id.js.map +1 -0
- package/dist/utils/server-startup.d.ts +11 -0
- package/dist/utils/server-startup.d.ts.map +1 -0
- package/dist/utils/server-startup.js +9 -0
- package/dist/utils/server-startup.js.map +1 -0
- package/dist/utils/update-check.d.ts +13 -0
- package/dist/utils/update-check.d.ts.map +1 -0
- package/dist/utils/update-check.js +90 -0
- package/dist/utils/update-check.js.map +1 -0
- package/dist/web/assets/ArchivedCardPrototype-S9ifiasa.js +5 -0
- package/dist/web/assets/BannerGallery-B__rJV6F.js +1 -0
- package/dist/web/assets/BannerPrototype-DBKP9Uiu.js +52 -0
- package/dist/web/assets/CodeHikeExperiment-B8jjWAFy.js +15 -0
- package/dist/web/assets/ContextTooltipVariations-DzklAFam.js +1 -0
- package/dist/web/assets/FontShowcasePrototype-KIMEWeP2.js +13 -0
- package/dist/web/assets/GeometricGallery-DddlWhHK.js +1 -0
- package/dist/web/assets/HistoryWalkthroughPrototype-DeniRRdw.js +18 -0
- package/dist/web/assets/InlineWalkthroughPrototype-Csd5r_Hk.js +1 -0
- package/dist/web/assets/MarkButtonPrototype-CxhxE0RP.js +1 -0
- package/dist/web/assets/MenuStylesPrototype-D7neA6YM.js +1 -0
- package/dist/web/assets/MomentCardVariations-2GT7GyFn.js +1 -0
- package/dist/web/assets/MomentHeaderVariations-DhGEw4XC.js +1 -0
- package/dist/web/assets/NarrativeWalkthroughDemo-B5C566fu.js +389 -0
- package/dist/web/assets/OutcomeVariations-BrZfsVcs.js +1 -0
- package/dist/web/assets/PermissionPatternPickerPrototype-CBOhe2Me.js +1 -0
- package/dist/web/assets/PermissionPrototype-BcF-a5an.js +1 -0
- package/dist/web/assets/PipelineGallery-BJhyM0rx.js +1 -0
- package/dist/web/assets/ScopeHeaderPrototype-GD1HNfaO.js +1 -0
- package/dist/web/assets/ScopeHeaderStylesPrototype-aa4uNJJ1.js +1 -0
- package/dist/web/assets/ScrollycodingPrototype-CKW1bf72.js +70 -0
- package/dist/web/assets/SectionHeaderVariations-DM8vUwfj.js +1 -0
- package/dist/web/assets/SemanticGallery-CtQEo7St.js +1 -0
- package/dist/web/assets/SessionCardPrototype-CgHCIMHe.js +1 -0
- package/dist/web/assets/SessionSidebarVariations-DMQL3Azj.js +3 -0
- package/dist/web/assets/SessionStartPrototype-Cwsv01Rr.js +1 -0
- package/dist/web/assets/SmartMenuPrototype-Br37Qbs_.js +1 -0
- package/dist/web/assets/StyleGallery-rZgrploB.js +1 -0
- package/dist/web/assets/TimelineCardPrototype-lzPc5mhe.js +19 -0
- package/dist/web/assets/ToolbarPrototype-Dm4BNZra.js +1 -0
- package/dist/web/assets/TooltipExperiment-Dy8QzTIP.js +13 -0
- package/dist/web/assets/WalkthroughCTAPrototype-uHoovujd.js +1 -0
- package/dist/web/assets/WalkthroughHeaderVariations-Do7Di1g1.js +1 -0
- package/dist/web/assets/WalkthroughShowcase-sGmRoPoM.js +112 -0
- package/dist/web/assets/arrow-right-D46Nx1mC.js +1 -0
- package/dist/web/assets/brain-BXIZKtOZ.js +1 -0
- package/dist/web/assets/grid-3x3-Cb81B62m.js +1 -0
- package/dist/web/assets/main-B1fyog77.js +321 -0
- package/dist/web/assets/main-C2PK2Klg.css +1 -0
- package/dist/web/assets/semantic-variations-Bd-W7ea2.js +1 -0
- package/dist/web/assets/target-Cf92wDTW.js +1 -0
- package/dist/web/favicon.png +0 -0
- package/dist/web/favicon.svg +22 -0
- package/dist/web/icon-192x192.png +0 -0
- package/dist/web/icon-512x512.png +0 -0
- package/dist/web/index.html +45 -0
- package/dist/web/manifest.json +61 -0
- package/package.json +192 -0
- package/scripts/postinstall.js +60 -0
|
@@ -0,0 +1,969 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname } from 'path';
|
|
5
|
+
import { displayServerStartup } from './utils/server-startup.js';
|
|
6
|
+
// Get the directory of this module for serving static files
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
import { createProcessManager } from './services/process/process-manager-factory.js';
|
|
10
|
+
import { StreamManager } from './services/infrastructure/stream-manager.js';
|
|
11
|
+
import { ClaudeHistoryReader } from './services/sessions/claude-history-reader.js';
|
|
12
|
+
import { PermissionTracker } from './services/permission-tracker.js';
|
|
13
|
+
import { QuestionTracker } from './services/question-tracker.js';
|
|
14
|
+
import { PendingQuestionService } from './services/pending-question-service.js';
|
|
15
|
+
import { MCPConfigGenerator } from './services/mcp-config-generator.js';
|
|
16
|
+
import { FileSystemService } from './services/infrastructure/file-system-service.js';
|
|
17
|
+
import { ConfigService } from './services/infrastructure/config-service.js';
|
|
18
|
+
import { SessionInfoService } from './services/sessions/session-info-service.js';
|
|
19
|
+
import { ConversationStatusManager } from './services/process/conversation-status-manager.js';
|
|
20
|
+
import { WorkingDirectoriesService } from './services/working-directories-service.js';
|
|
21
|
+
import { ToolMetricsService } from './services/ToolMetricsService.js';
|
|
22
|
+
import { NotificationService } from './services/notification-service.js';
|
|
23
|
+
import { WebPushService } from './services/web-push-service.js';
|
|
24
|
+
import { geminiService } from './services/gemini-service.js';
|
|
25
|
+
import { anthropicService } from './services/insights/anthropic-service.js';
|
|
26
|
+
import { ClaudeRouterService } from './services/claude-router-service.js';
|
|
27
|
+
import { LatticeError } from './types/index.js';
|
|
28
|
+
import { createLogger } from './services/infrastructure/logger.js';
|
|
29
|
+
import { createConversationRoutes } from './routes/conversation.routes.js';
|
|
30
|
+
import { createSystemRoutes } from './routes/system.routes.js';
|
|
31
|
+
import { createPermissionRoutes } from './routes/permission.routes.js';
|
|
32
|
+
import { createQuestionRoutes } from './routes/question.routes.js';
|
|
33
|
+
import { createPendingQuestionRoutes } from './routes/pending-question.routes.js';
|
|
34
|
+
import { createFileSystemRoutes } from './routes/filesystem.routes.js';
|
|
35
|
+
import { createLogRoutes } from './routes/log.routes.js';
|
|
36
|
+
import { createStreamingRoutes } from './routes/streaming.routes.js';
|
|
37
|
+
import { createWorkingDirectoriesRoutes } from './routes/working-directories.routes.js';
|
|
38
|
+
import { createConfigRoutes } from './routes/config.routes.js';
|
|
39
|
+
import { createInsightsRoutes } from './routes/insights.routes.js';
|
|
40
|
+
import { createLatticeRoutes } from './routes/lattice.routes.js';
|
|
41
|
+
import { LatticeService } from './services/lattice-service.js';
|
|
42
|
+
import { TurnCaptureService } from './services/sessions/turn-capture-service.js';
|
|
43
|
+
import { createLicenseRoutes } from './routes/license.routes.js';
|
|
44
|
+
import { createAnalysisRoutes } from './routes/analysis.routes.js';
|
|
45
|
+
import { createWalkthroughRoutes } from './routes/walkthrough.routes.js';
|
|
46
|
+
import { createPrototypeRoutes } from './routes/prototype.routes.js';
|
|
47
|
+
import processEventsRouter from './routes/process-events.routes.js';
|
|
48
|
+
import { errorHandler } from './middleware/error-handler.js';
|
|
49
|
+
import { requestLogger } from './middleware/request-logger.js';
|
|
50
|
+
import { createCorsMiddleware } from './middleware/cors-setup.js';
|
|
51
|
+
import { queryParser } from './middleware/query-parser.js';
|
|
52
|
+
import { checkForUpdatesOnStartup } from './utils/update-check.js';
|
|
53
|
+
import { registerStateProvider, startPeriodicStateLogging } from './services/connection-debug-logger.js';
|
|
54
|
+
import { serviceRegistry } from './services/infrastructure/service-registry.js';
|
|
55
|
+
// ViteExpress will be imported dynamically in initialize() if needed
|
|
56
|
+
let ViteExpress;
|
|
57
|
+
/**
|
|
58
|
+
* Main CUI server class
|
|
59
|
+
*/
|
|
60
|
+
export class LatticeServer {
|
|
61
|
+
app;
|
|
62
|
+
server;
|
|
63
|
+
processManager; // Initialized in initialize()
|
|
64
|
+
streamManager;
|
|
65
|
+
historyReader;
|
|
66
|
+
permissionTracker;
|
|
67
|
+
questionTracker;
|
|
68
|
+
pendingQuestionService;
|
|
69
|
+
mcpConfigGenerator;
|
|
70
|
+
fileSystemService;
|
|
71
|
+
configService;
|
|
72
|
+
sessionInfoService;
|
|
73
|
+
conversationStatusManager;
|
|
74
|
+
workingDirectoriesService;
|
|
75
|
+
toolMetricsService;
|
|
76
|
+
notificationService;
|
|
77
|
+
webPushService;
|
|
78
|
+
routerService;
|
|
79
|
+
turnCaptureService;
|
|
80
|
+
logger;
|
|
81
|
+
port;
|
|
82
|
+
host;
|
|
83
|
+
configOverrides;
|
|
84
|
+
orphanCleanupInterval;
|
|
85
|
+
constructor(configOverrides) {
|
|
86
|
+
this.app = express();
|
|
87
|
+
this.configOverrides = configOverrides;
|
|
88
|
+
this.logger = createLogger('LatticeServer');
|
|
89
|
+
// TEST: Add debug log right at the start
|
|
90
|
+
this.logger.debug('🔍 TEST: LatticeServer constructor started - this should be visible if debug logging works');
|
|
91
|
+
// Initialize config service first
|
|
92
|
+
this.configService = ConfigService.getInstance();
|
|
93
|
+
// Will be set after config is loaded
|
|
94
|
+
this.port = 0;
|
|
95
|
+
this.host = '';
|
|
96
|
+
this.logger.debug('Initializing LatticeServer', {
|
|
97
|
+
nodeEnv: process.env.NODE_ENV,
|
|
98
|
+
configOverrides
|
|
99
|
+
});
|
|
100
|
+
// Initialize services
|
|
101
|
+
this.logger.debug('Initializing services');
|
|
102
|
+
// Use singleton to ensure event handlers get the same initialized instance
|
|
103
|
+
this.sessionInfoService = SessionInfoService.getInstance();
|
|
104
|
+
this.historyReader = new ClaudeHistoryReader(this.sessionInfoService);
|
|
105
|
+
this.conversationStatusManager = new ConversationStatusManager();
|
|
106
|
+
this.toolMetricsService = new ToolMetricsService();
|
|
107
|
+
this.fileSystemService = new FileSystemService();
|
|
108
|
+
// processManager is initialized in initialize() to support daemon mode
|
|
109
|
+
this.streamManager = new StreamManager();
|
|
110
|
+
this.permissionTracker = new PermissionTracker();
|
|
111
|
+
this.questionTracker = new QuestionTracker();
|
|
112
|
+
this.pendingQuestionService = PendingQuestionService.getInstance();
|
|
113
|
+
this.mcpConfigGenerator = new MCPConfigGenerator(this.fileSystemService);
|
|
114
|
+
this.workingDirectoriesService = new WorkingDirectoriesService(this.historyReader, this.logger);
|
|
115
|
+
this.notificationService = new NotificationService();
|
|
116
|
+
this.webPushService = WebPushService.getInstance();
|
|
117
|
+
this.turnCaptureService = TurnCaptureService.getInstance();
|
|
118
|
+
// Wire up services that don't depend on processManager
|
|
119
|
+
this.permissionTracker.setNotificationService(this.notificationService);
|
|
120
|
+
this.permissionTracker.setConversationStatusManager(this.conversationStatusManager);
|
|
121
|
+
this.permissionTracker.setHistoryReader(this.historyReader);
|
|
122
|
+
this.logger.debug('Services initialized (processManager deferred to initialize())');
|
|
123
|
+
this.setupMiddleware();
|
|
124
|
+
// Routes and processManager integration are set up in initialize()
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get the Express app instance
|
|
128
|
+
*/
|
|
129
|
+
getApp() {
|
|
130
|
+
return this.app;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get the configured port
|
|
134
|
+
*/
|
|
135
|
+
getPort() {
|
|
136
|
+
return this.port;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get the configured host
|
|
140
|
+
*/
|
|
141
|
+
getHost() {
|
|
142
|
+
return this.host;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Initialize services without starting the HTTP server
|
|
146
|
+
*/
|
|
147
|
+
async initialize() {
|
|
148
|
+
this.logger.debug('Initialize method called');
|
|
149
|
+
try {
|
|
150
|
+
// =========================================================================
|
|
151
|
+
// PHASE 1: Register services with dependency graph
|
|
152
|
+
// This declares the initialization order - actual init happens below
|
|
153
|
+
// =========================================================================
|
|
154
|
+
serviceRegistry.register('ConfigService');
|
|
155
|
+
serviceRegistry.register('SessionInfoService', { dependsOn: ['ConfigService'] });
|
|
156
|
+
serviceRegistry.register('PendingQuestionService', { dependsOn: ['SessionInfoService'] });
|
|
157
|
+
serviceRegistry.register('ProcessManager', { dependsOn: ['SessionInfoService'] });
|
|
158
|
+
serviceRegistry.register('GeminiService', { dependsOn: ['ConfigService'] });
|
|
159
|
+
serviceRegistry.register('AnthropicService', { dependsOn: ['ConfigService'] });
|
|
160
|
+
serviceRegistry.register('CostTracker', { dependsOn: ['ConfigService'] });
|
|
161
|
+
serviceRegistry.register('InsightsCoordinator', {
|
|
162
|
+
dependsOn: ['SessionInfoService', 'AnthropicService']
|
|
163
|
+
});
|
|
164
|
+
// =========================================================================
|
|
165
|
+
// PHASE 2: Initialize services in dependency order
|
|
166
|
+
// =========================================================================
|
|
167
|
+
// Initialize configuration first
|
|
168
|
+
this.logger.debug('Initializing configuration');
|
|
169
|
+
await this.configService.initialize();
|
|
170
|
+
serviceRegistry.markInitialized('ConfigService');
|
|
171
|
+
// Set the invocation cwd if provided via CLI
|
|
172
|
+
if (this.configOverrides?.cwd) {
|
|
173
|
+
this.configService.setInvocationCwd(this.configOverrides.cwd);
|
|
174
|
+
}
|
|
175
|
+
const config = this.configService.getConfig();
|
|
176
|
+
// Initialize session info service
|
|
177
|
+
this.logger.debug('Initializing session info service');
|
|
178
|
+
await this.sessionInfoService.initialize();
|
|
179
|
+
serviceRegistry.markInitialized('SessionInfoService');
|
|
180
|
+
this.logger.debug('Session info service initialized successfully');
|
|
181
|
+
// Initialize pending question service (uses same DB as session info)
|
|
182
|
+
this.logger.debug('Initializing pending question service');
|
|
183
|
+
await this.pendingQuestionService.initialize();
|
|
184
|
+
serviceRegistry.markInitialized('PendingQuestionService');
|
|
185
|
+
this.logger.debug('Pending question service initialized successfully');
|
|
186
|
+
// Initialize process manager (skip if already set, e.g. by tests)
|
|
187
|
+
if (!this.processManager) {
|
|
188
|
+
this.logger.debug('Initializing process manager');
|
|
189
|
+
this.processManager = await createProcessManager({
|
|
190
|
+
historyReader: this.historyReader,
|
|
191
|
+
statusTracker: this.conversationStatusManager,
|
|
192
|
+
toolMetricsService: this.toolMetricsService,
|
|
193
|
+
sessionInfoService: this.sessionInfoService,
|
|
194
|
+
fileSystemService: this.fileSystemService
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
this.logger.debug('Process manager already set, skipping initialization');
|
|
199
|
+
}
|
|
200
|
+
this.processManager.setNotificationService(this.notificationService);
|
|
201
|
+
this.processManager.setConversationStatusManager(this.conversationStatusManager);
|
|
202
|
+
// Set up event listeners now that processManager exists
|
|
203
|
+
this.setupProcessManagerIntegration();
|
|
204
|
+
this.setupPermissionTrackerIntegration();
|
|
205
|
+
this.setupQuestionTrackerIntegration();
|
|
206
|
+
serviceRegistry.markInitialized('ProcessManager');
|
|
207
|
+
this.logger.debug('Process manager initialized successfully');
|
|
208
|
+
this.logger.debug('Initializing Gemini service');
|
|
209
|
+
await geminiService.initialize();
|
|
210
|
+
serviceRegistry.markInitialized('GeminiService');
|
|
211
|
+
this.logger.debug('Gemini service initialized successfully');
|
|
212
|
+
this.logger.debug('Initializing Anthropic service');
|
|
213
|
+
await anthropicService.initialize();
|
|
214
|
+
serviceRegistry.markInitialized('AnthropicService');
|
|
215
|
+
this.logger.debug('Anthropic service initialized successfully');
|
|
216
|
+
// Initialize cost tracker for LLM usage monitoring
|
|
217
|
+
this.logger.debug('Initializing cost tracker');
|
|
218
|
+
const { getCostTracker } = await import('./services/infrastructure/cost-tracker.js');
|
|
219
|
+
await getCostTracker().initialize();
|
|
220
|
+
serviceRegistry.markInitialized('CostTracker');
|
|
221
|
+
this.logger.debug('Cost tracker initialized successfully');
|
|
222
|
+
// Initialize insights coordinator (unified event-driven system)
|
|
223
|
+
this.logger.debug('Initializing insights coordinator');
|
|
224
|
+
const { initializeInsightsCoordinator, getInsightsCoordinator } = await import('./services/insights/insights-coordinator.js');
|
|
225
|
+
initializeInsightsCoordinator();
|
|
226
|
+
serviceRegistry.markInitialized('InsightsCoordinator');
|
|
227
|
+
// Wire up session-ended to expedite insight patches
|
|
228
|
+
this.conversationStatusManager.on('session-ended', ({ claudeSessionId }) => {
|
|
229
|
+
// handleSessionEnded is async - fire and forget but log errors
|
|
230
|
+
getInsightsCoordinator().handleSessionEnded(claudeSessionId).catch(err => {
|
|
231
|
+
this.logger.error('Failed to handle session ended for insights', {
|
|
232
|
+
sessionId: claudeSessionId.slice(0, 8),
|
|
233
|
+
error: err.message
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
this.logger.debug('Insights coordinator initialized successfully');
|
|
238
|
+
// Initialize router service if configured
|
|
239
|
+
await this.initializeOrReloadRouter(config);
|
|
240
|
+
// Apply overrides if provided (for tests and CLI options)
|
|
241
|
+
this.port = this.configOverrides?.port ?? config.server.port;
|
|
242
|
+
this.host = this.configOverrides?.host ?? config.server.host;
|
|
243
|
+
this.logger.info('Configuration loaded', {
|
|
244
|
+
machineId: config.machine_id,
|
|
245
|
+
port: this.port,
|
|
246
|
+
host: this.host,
|
|
247
|
+
overrides: this.configOverrides ? Object.keys(this.configOverrides) : []
|
|
248
|
+
});
|
|
249
|
+
// Set up routes after services are initialized
|
|
250
|
+
// This allows tests to override services before routes are created
|
|
251
|
+
this.logger.debug('Setting up routes');
|
|
252
|
+
this.setupRoutes();
|
|
253
|
+
// Register connection state provider for debug logging
|
|
254
|
+
const conversationStatusManager = this.conversationStatusManager;
|
|
255
|
+
const streamManager = this.streamManager;
|
|
256
|
+
const processManager = this.processManager;
|
|
257
|
+
registerStateProvider({
|
|
258
|
+
getConnectionState: () => ({
|
|
259
|
+
conversationStatusManager: {
|
|
260
|
+
activeSessions: conversationStatusManager.getStats().activeSessions,
|
|
261
|
+
},
|
|
262
|
+
streamManager: {
|
|
263
|
+
activeSessions: streamManager.getActiveSessions(),
|
|
264
|
+
clientCounts: Object.fromEntries(streamManager.getActiveSessions().map(s => [s, streamManager.getClientCount(s)])),
|
|
265
|
+
},
|
|
266
|
+
processManager: {
|
|
267
|
+
activeSessions: processManager.getActiveSessions(),
|
|
268
|
+
},
|
|
269
|
+
}),
|
|
270
|
+
});
|
|
271
|
+
startPeriodicStateLogging(30000); // Check every 30 seconds
|
|
272
|
+
// Generate MCP config before starting server
|
|
273
|
+
try {
|
|
274
|
+
const mcpConfigPath = await this.mcpConfigGenerator.generateConfig(this.port);
|
|
275
|
+
this.processManager.setMCPConfigPath(mcpConfigPath);
|
|
276
|
+
this.logger.debug('MCP config generated and set', { path: mcpConfigPath });
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
const isNonProduction = process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development';
|
|
280
|
+
if (isNonProduction) {
|
|
281
|
+
this.logger.warn('MCP config generation failed in non-production environment, proceeding without MCP', {
|
|
282
|
+
error: error instanceof Error ? error.message : String(error),
|
|
283
|
+
nodeEnv: process.env.NODE_ENV
|
|
284
|
+
});
|
|
285
|
+
// Don't set MCP config path - conversations will run without MCP
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
this.logger.error('MCP config generation failed in production environment', {
|
|
289
|
+
error: error instanceof Error ? error.message : String(error)
|
|
290
|
+
});
|
|
291
|
+
throw new LatticeError('MCP_CONFIG_REQUIRED', `MCP server files are required in production but not found: ${error instanceof Error ? error.message : String(error)}`, 500);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Subscribe to configuration changes to hot-reload router when needed
|
|
295
|
+
this.configService.onChange(async (newConfig) => {
|
|
296
|
+
try {
|
|
297
|
+
await this.initializeOrReloadRouter(newConfig);
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
this.logger.error('Failed to reload router after config change', error);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
// Check for updates (non-blocking, just logs if update available)
|
|
304
|
+
checkForUpdatesOnStartup();
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
this.logger.error('Failed to initialize server:', error, {
|
|
308
|
+
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
309
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
310
|
+
});
|
|
311
|
+
if (error instanceof LatticeError) {
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
throw new LatticeError('SERVER_INIT_FAILED', `Server initialization failed: ${error}`, 500);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Start the server
|
|
321
|
+
*/
|
|
322
|
+
async start() {
|
|
323
|
+
this.logger.debug('Start method called');
|
|
324
|
+
try {
|
|
325
|
+
// Initialize all services
|
|
326
|
+
await this.initialize();
|
|
327
|
+
// Start Express server
|
|
328
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
329
|
+
this.logger.debug('Creating HTTP server listener', {
|
|
330
|
+
useViteExpress: isDev,
|
|
331
|
+
environment: process.env.NODE_ENV
|
|
332
|
+
});
|
|
333
|
+
// Import ViteExpress dynamically if in development mode
|
|
334
|
+
if (isDev && !ViteExpress) {
|
|
335
|
+
const viteExpressModule = await import('vite-express');
|
|
336
|
+
ViteExpress = viteExpressModule.default;
|
|
337
|
+
}
|
|
338
|
+
await new Promise((resolve, reject) => {
|
|
339
|
+
// Use ViteExpress only in development
|
|
340
|
+
if (isDev && ViteExpress) {
|
|
341
|
+
try {
|
|
342
|
+
this.server = this.app.listen(this.port, this.host, () => {
|
|
343
|
+
this.logger.debug('Server successfully bound to port (dev mode)', {
|
|
344
|
+
port: this.port,
|
|
345
|
+
host: this.host,
|
|
346
|
+
address: this.server?.address()
|
|
347
|
+
});
|
|
348
|
+
// Display startup info now that server is actually listening
|
|
349
|
+
displayServerStartup({
|
|
350
|
+
host: this.host,
|
|
351
|
+
port: this.port,
|
|
352
|
+
logger: this.logger
|
|
353
|
+
});
|
|
354
|
+
// Configure and bind ViteExpress AFTER server is listening
|
|
355
|
+
ViteExpress.config({
|
|
356
|
+
mode: 'development',
|
|
357
|
+
viteConfigFile: 'vite.config.mts'
|
|
358
|
+
});
|
|
359
|
+
ViteExpress.bind(this.app, this.server);
|
|
360
|
+
this.logger.info(`CUI development server running on http://${this.host}:${this.port}`);
|
|
361
|
+
resolve();
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
this.logger.error('Failed to start ViteExpress server', error);
|
|
366
|
+
reject(error);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
// Production/test mode - regular Express server
|
|
371
|
+
this.server = this.app.listen(this.port, this.host, () => {
|
|
372
|
+
this.logger.debug('Server successfully bound to port', {
|
|
373
|
+
port: this.port,
|
|
374
|
+
host: this.host,
|
|
375
|
+
address: this.server?.address(),
|
|
376
|
+
mode: process.env.NODE_ENV || 'production'
|
|
377
|
+
});
|
|
378
|
+
// Display startup info now that server is actually listening
|
|
379
|
+
displayServerStartup({
|
|
380
|
+
host: this.host,
|
|
381
|
+
port: this.port,
|
|
382
|
+
logger: this.logger
|
|
383
|
+
});
|
|
384
|
+
resolve();
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
if (this.server) {
|
|
388
|
+
this.server.on('error', (error) => {
|
|
389
|
+
this.logger.error('Failed to start HTTP server:', error, {
|
|
390
|
+
errorCode: error.code,
|
|
391
|
+
errorSyscall: error.syscall,
|
|
392
|
+
port: this.port,
|
|
393
|
+
host: this.host
|
|
394
|
+
});
|
|
395
|
+
reject(new LatticeError('HTTP_SERVER_START_FAILED', `Failed to start HTTP server: ${error.message}`, 500));
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
this.logger.error('Failed to start server:', error, {
|
|
402
|
+
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
403
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
404
|
+
});
|
|
405
|
+
// Attempt cleanup on startup failure
|
|
406
|
+
await this.cleanup();
|
|
407
|
+
if (error instanceof LatticeError) {
|
|
408
|
+
throw error;
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
throw new LatticeError('SERVER_START_FAILED', `Server startup failed: ${error}`, 500);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Stop the server gracefully
|
|
417
|
+
*/
|
|
418
|
+
async stop() {
|
|
419
|
+
this.logger.debug('Stop method called', {
|
|
420
|
+
hasServer: !!this.server,
|
|
421
|
+
activeSessions: this.processManager.getActiveSessions().length,
|
|
422
|
+
connectedClients: this.streamManager.getTotalClientCount()
|
|
423
|
+
});
|
|
424
|
+
if (this.routerService) {
|
|
425
|
+
await this.routerService.stop();
|
|
426
|
+
}
|
|
427
|
+
// Stop all active Claude processes (only in direct mode - daemon keeps them alive)
|
|
428
|
+
if (this.processManager.mode === 'direct') {
|
|
429
|
+
const activeSessions = this.processManager.getActiveSessions();
|
|
430
|
+
if (activeSessions.length > 0) {
|
|
431
|
+
this.logger.info(`Stopping ${activeSessions.length} active sessions...`);
|
|
432
|
+
this.logger.debug('Active sessions to stop', { sessionIds: activeSessions });
|
|
433
|
+
const stopResults = await Promise.allSettled(activeSessions.map(streamingId => this.processManager.stopConversation(streamingId)
|
|
434
|
+
.catch(error => this.logger.error(`Error stopping session ${streamingId}:`, error))));
|
|
435
|
+
this.logger.debug('Session stop results', {
|
|
436
|
+
total: stopResults.length,
|
|
437
|
+
fulfilled: stopResults.filter(r => r.status === 'fulfilled').length,
|
|
438
|
+
rejected: stopResults.filter(r => r.status === 'rejected').length
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
this.logger.info('Daemon mode: keeping Claude processes alive through restart');
|
|
444
|
+
}
|
|
445
|
+
// Stop orphan cleanup interval
|
|
446
|
+
if (this.orphanCleanupInterval) {
|
|
447
|
+
clearInterval(this.orphanCleanupInterval);
|
|
448
|
+
this.orphanCleanupInterval = undefined;
|
|
449
|
+
}
|
|
450
|
+
// Disconnect from daemon to stop reconnection loop
|
|
451
|
+
this.processManager.disconnect();
|
|
452
|
+
// Disconnect all streaming clients (sends close events and ends responses)
|
|
453
|
+
this.logger.debug('Disconnecting all streaming clients');
|
|
454
|
+
this.streamManager.disconnectAll();
|
|
455
|
+
// Clean up MCP config
|
|
456
|
+
this.logger.debug('Cleaning up MCP config');
|
|
457
|
+
this.mcpConfigGenerator.cleanup();
|
|
458
|
+
// Close HTTP server to release port
|
|
459
|
+
if (this.server) {
|
|
460
|
+
this.logger.debug('Closing HTTP server');
|
|
461
|
+
// Force close all connections after ending SSE streams
|
|
462
|
+
// This ensures we don't wait for clients that haven't acknowledged the close
|
|
463
|
+
if (typeof this.server.closeAllConnections === 'function') {
|
|
464
|
+
this.server.closeAllConnections();
|
|
465
|
+
}
|
|
466
|
+
// Close with timeout - don't block shutdown forever
|
|
467
|
+
await new Promise((resolve) => {
|
|
468
|
+
const timeout = setTimeout(() => {
|
|
469
|
+
this.logger.warn('HTTP server close timed out after 2s, forcing shutdown');
|
|
470
|
+
resolve();
|
|
471
|
+
}, 2000);
|
|
472
|
+
this.server.close(() => {
|
|
473
|
+
clearTimeout(timeout);
|
|
474
|
+
this.logger.info('HTTP server closed successfully');
|
|
475
|
+
resolve();
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Cleanup resources during failed startup
|
|
482
|
+
*/
|
|
483
|
+
async cleanup() {
|
|
484
|
+
this.logger.info('Performing cleanup after startup failure...');
|
|
485
|
+
this.logger.debug('Cleanup initiated', {
|
|
486
|
+
hasServer: !!this.server,
|
|
487
|
+
hasActiveStreams: this.streamManager.getTotalClientCount() > 0
|
|
488
|
+
});
|
|
489
|
+
try {
|
|
490
|
+
// Close HTTP server if it was started
|
|
491
|
+
if (this.server) {
|
|
492
|
+
await new Promise((resolve) => {
|
|
493
|
+
this.server.close(() => {
|
|
494
|
+
this.logger.info('HTTP server closed during cleanup');
|
|
495
|
+
resolve();
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
// Disconnect streaming clients
|
|
500
|
+
this.streamManager.disconnectAll();
|
|
501
|
+
this.logger.info('Cleanup completed');
|
|
502
|
+
}
|
|
503
|
+
catch (error) {
|
|
504
|
+
this.logger.error('Error during cleanup:', error, {
|
|
505
|
+
errorType: error instanceof Error ? error.constructor.name : typeof error
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
setupMiddleware() {
|
|
510
|
+
this.app.use(createCorsMiddleware());
|
|
511
|
+
this.app.use(express.json({ limit: '10mb' }));
|
|
512
|
+
// Static file serving
|
|
513
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
514
|
+
if (!isDev) {
|
|
515
|
+
// In production/test, serve built static files
|
|
516
|
+
// In production, __dirname will be /path/to/node_modules/cui-server/dist
|
|
517
|
+
// We need to serve from dist/web
|
|
518
|
+
const staticPath = path.join(__dirname, 'web');
|
|
519
|
+
this.logger.debug('Serving static files from', { path: staticPath });
|
|
520
|
+
this.app.use(express.static(staticPath));
|
|
521
|
+
}
|
|
522
|
+
// In development, ViteExpress handles static file serving
|
|
523
|
+
// Request logging
|
|
524
|
+
this.app.use(requestLogger);
|
|
525
|
+
// Query parameter parsing - convert strings to proper types
|
|
526
|
+
this.app.use(queryParser);
|
|
527
|
+
}
|
|
528
|
+
setupRoutes() {
|
|
529
|
+
// System routes (includes health check) - before auth
|
|
530
|
+
this.app.use('/api/system', createSystemRoutes(this.processManager, this.historyReader, this.conversationStatusManager, this.streamManager));
|
|
531
|
+
this.app.use('/', createSystemRoutes(this.processManager, this.historyReader, this.conversationStatusManager, this.streamManager)); // For /health at root
|
|
532
|
+
// Permission routes (needed for MCP server communication)
|
|
533
|
+
this.app.use('/api/permissions', createPermissionRoutes(this.permissionTracker));
|
|
534
|
+
// API routes
|
|
535
|
+
// Insights routes first: has specific paths like /activity-check
|
|
536
|
+
// Conversation routes second: has /:sessionId catch-all that would match anything
|
|
537
|
+
this.app.use('/api/conversations', createInsightsRoutes(this.historyReader, this.sessionInfoService));
|
|
538
|
+
this.app.use('/api/conversations', createConversationRoutes(this.processManager, this.historyReader, this.conversationStatusManager, this.sessionInfoService, this.toolMetricsService));
|
|
539
|
+
this.app.use('/api/filesystem', createFileSystemRoutes(this.fileSystemService));
|
|
540
|
+
this.app.use('/api/logs', createLogRoutes());
|
|
541
|
+
this.app.use('/api/stream', createStreamingRoutes(this.streamManager));
|
|
542
|
+
this.app.use('/api/working-directories', createWorkingDirectoriesRoutes(this.workingDirectoriesService));
|
|
543
|
+
this.app.use('/api/config', createConfigRoutes(this.configService));
|
|
544
|
+
this.app.use('/api/questions', createQuestionRoutes(this.questionTracker, this.processManager));
|
|
545
|
+
this.app.use('/api/pending-questions', createPendingQuestionRoutes(this.pendingQuestionService, this.processManager, this.historyReader));
|
|
546
|
+
// Lattice orchestrator routes
|
|
547
|
+
const latticeService = LatticeService.getInstance({
|
|
548
|
+
processManager: this.processManager,
|
|
549
|
+
historyReader: this.historyReader,
|
|
550
|
+
statusManager: this.conversationStatusManager
|
|
551
|
+
});
|
|
552
|
+
this.app.use('/api/lattice', createLatticeRoutes(latticeService));
|
|
553
|
+
// License routes
|
|
554
|
+
this.app.use('/api/license', createLicenseRoutes());
|
|
555
|
+
// Post-session analysis routes
|
|
556
|
+
this.app.use('/api/analysis', createAnalysisRoutes());
|
|
557
|
+
// Walkthrough generation routes
|
|
558
|
+
this.app.use('/api/walkthrough', createWalkthroughRoutes({
|
|
559
|
+
historyReader: this.historyReader,
|
|
560
|
+
processManager: this.processManager,
|
|
561
|
+
streamManager: this.streamManager,
|
|
562
|
+
conversationStatusManager: this.conversationStatusManager,
|
|
563
|
+
}));
|
|
564
|
+
// Process event log routes (observability)
|
|
565
|
+
this.app.use('/api/process-events', processEventsRouter);
|
|
566
|
+
// Prototype routes (experimental features)
|
|
567
|
+
this.app.use('/api/prototype', createPrototypeRoutes());
|
|
568
|
+
// React Router catch-all - must be after all API routes
|
|
569
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
570
|
+
if (!isDev) {
|
|
571
|
+
// In production/test, serve index.html for all non-API routes
|
|
572
|
+
this.app.get('*', (req, res) => {
|
|
573
|
+
res.sendFile(path.join(__dirname, 'web', 'index.html'));
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
// In development, ViteExpress handles React routing
|
|
577
|
+
// Error handling - MUST be last
|
|
578
|
+
this.app.use(errorHandler);
|
|
579
|
+
}
|
|
580
|
+
setupProcessManagerIntegration() {
|
|
581
|
+
this.logger.debug('Setting up ProcessManager integration with StreamManager');
|
|
582
|
+
// Set up tool metrics service to listen to claude messages
|
|
583
|
+
this.toolMetricsService.listenToClaudeMessages(this.processManager);
|
|
584
|
+
// Local map to track streamingId -> sessionId (populated from system_init messages)
|
|
585
|
+
// This is a simple, reliable source of truth that doesn't depend on external services
|
|
586
|
+
const streamingSessionMap = new Map();
|
|
587
|
+
// Repopulate from daemon's active sessions on server restart
|
|
588
|
+
// This handles the case where the server restarts while sessions are still running
|
|
589
|
+
const existingMappings = this.processManager.getActiveSessionMappings();
|
|
590
|
+
for (const { streamingId, sessionId } of existingMappings) {
|
|
591
|
+
streamingSessionMap.set(streamingId, sessionId);
|
|
592
|
+
}
|
|
593
|
+
if (existingMappings.length > 0) {
|
|
594
|
+
this.logger.info('[SESSION-MAP] Repopulated from daemon active sessions', {
|
|
595
|
+
count: existingMappings.length,
|
|
596
|
+
mappings: existingMappings.map(m => ({
|
|
597
|
+
streamingId: m.streamingId.slice(0, 8),
|
|
598
|
+
sessionId: m.sessionId.slice(0, 8)
|
|
599
|
+
}))
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
// Start buffering events when a process spawns
|
|
603
|
+
// This handles the race condition where events are broadcast before the SSE client connects
|
|
604
|
+
this.processManager.on('process-spawned', ({ streamingId }) => {
|
|
605
|
+
this.logger.info('[BUFFERING] Process spawned, starting event buffering', { streamingId: streamingId.slice(0, 8) });
|
|
606
|
+
this.streamManager.startBuffering(streamingId);
|
|
607
|
+
});
|
|
608
|
+
// Forward Claude messages to stream
|
|
609
|
+
this.processManager.on('claude-message', ({ streamingId, message }) => {
|
|
610
|
+
this.logger.debug('Received claude-message event', {
|
|
611
|
+
streamingId,
|
|
612
|
+
messageType: message?.type,
|
|
613
|
+
messageSubtype: message?.subtype,
|
|
614
|
+
hasContent: !!message?.content,
|
|
615
|
+
contentLength: message?.content?.length || 0,
|
|
616
|
+
messageKeys: message ? Object.keys(message) : []
|
|
617
|
+
});
|
|
618
|
+
// Capture sessionId from system init messages (this is our source of truth)
|
|
619
|
+
if (message && message.type === 'system' && message.subtype === 'init' && message.session_id) {
|
|
620
|
+
streamingSessionMap.set(streamingId, message.session_id);
|
|
621
|
+
this.logger.info('[SESSION-MAP] Captured sessionId from system_init', {
|
|
622
|
+
streamingId: streamingId.slice(0, 8),
|
|
623
|
+
sessionId: message.session_id.slice(0, 8),
|
|
624
|
+
mapSize: streamingSessionMap.size
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
// Skip broadcasting system init messages as they're now included in API response
|
|
628
|
+
if (message && message.type === 'system' && message.subtype === 'init') {
|
|
629
|
+
this.logger.debug('Skipping broadcast of system init message (included in API response)', {
|
|
630
|
+
streamingId,
|
|
631
|
+
sessionId: message.session_id
|
|
632
|
+
});
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
// Detect AskUserQuestion tool_use and register with QuestionTracker + persist to DB
|
|
636
|
+
// Debug: log all assistant messages to see what structure we're getting
|
|
637
|
+
if (message && message.type === 'assistant') {
|
|
638
|
+
this.logger.debug('Assistant message received', {
|
|
639
|
+
streamingId,
|
|
640
|
+
hasMessage: !!message.message,
|
|
641
|
+
hasContent: !!message.message?.content,
|
|
642
|
+
contentIsArray: Array.isArray(message.message?.content),
|
|
643
|
+
contentLength: Array.isArray(message.message?.content) ? message.message.content.length : 0
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
if (message && message.type === 'assistant' && message.message?.content) {
|
|
647
|
+
const content = message.message.content;
|
|
648
|
+
if (Array.isArray(content)) {
|
|
649
|
+
for (const block of content) {
|
|
650
|
+
// Debug: log each block to see tool_use blocks
|
|
651
|
+
if (block.type === 'tool_use') {
|
|
652
|
+
this.logger.info('Tool use block detected', {
|
|
653
|
+
streamingId,
|
|
654
|
+
toolName: block.name,
|
|
655
|
+
blockId: block.id
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
if (block.type === 'tool_use' && block.name === 'AskUserQuestion') {
|
|
659
|
+
// Debug: log session state when AskUserQuestion arrives
|
|
660
|
+
const activeSessions = this.conversationStatusManager.getActiveSessionIds();
|
|
661
|
+
this.logger.info('AskUserQuestion detected - checking session state', {
|
|
662
|
+
incomingStreamingId: streamingId,
|
|
663
|
+
activeSessions,
|
|
664
|
+
activeCount: activeSessions.length
|
|
665
|
+
});
|
|
666
|
+
const input = block.input;
|
|
667
|
+
if (input.questions) {
|
|
668
|
+
// Add to in-memory tracker (for active session UI)
|
|
669
|
+
const questionRequest = this.questionTracker.addQuestionRequest(block.id, input.questions, streamingId);
|
|
670
|
+
// Persist to DB (for recovery after timeout/disconnect)
|
|
671
|
+
// Use local streamingSessionMap as primary source (populated from system_init)
|
|
672
|
+
// Fall back to other sources if needed
|
|
673
|
+
let claudeSessionId = streamingSessionMap.get(streamingId);
|
|
674
|
+
if (!claudeSessionId) {
|
|
675
|
+
claudeSessionId = this.conversationStatusManager.getSessionId(streamingId);
|
|
676
|
+
if (claudeSessionId) {
|
|
677
|
+
this.logger.debug('Using conversationStatusManager sessionId fallback', { streamingId, claudeSessionId });
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
if (!claudeSessionId) {
|
|
681
|
+
claudeSessionId = this.processManager.getClaudeSessionId(streamingId);
|
|
682
|
+
if (claudeSessionId) {
|
|
683
|
+
this.logger.debug('Using processManager sessionId fallback', { streamingId, claudeSessionId });
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (claudeSessionId) {
|
|
687
|
+
this.pendingQuestionService.addQuestion(questionRequest.id, claudeSessionId, streamingId, block.id, input.questions);
|
|
688
|
+
this.logger.info('AskUserQuestion persisted for recovery', {
|
|
689
|
+
questionId: questionRequest.id,
|
|
690
|
+
sessionId: claudeSessionId,
|
|
691
|
+
streamingId,
|
|
692
|
+
questionCount: input.questions.length
|
|
693
|
+
});
|
|
694
|
+
// Stop the session immediately to prevent Claude from continuing
|
|
695
|
+
// The session will be resumed when the user answers the question
|
|
696
|
+
this.logger.info('Stopping session to await user answer', { streamingId, claudeSessionId });
|
|
697
|
+
this.processManager.stopConversation(streamingId).catch((err) => {
|
|
698
|
+
this.logger.warn('Failed to stop session after AskUserQuestion', {
|
|
699
|
+
streamingId,
|
|
700
|
+
error: err instanceof Error ? err.message : String(err)
|
|
701
|
+
});
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
this.logger.warn('AskUserQuestion NOT persisted - claudeSessionId not found for streamingId', {
|
|
706
|
+
questionId: questionRequest.id,
|
|
707
|
+
streamingId,
|
|
708
|
+
questionCount: input.questions.length,
|
|
709
|
+
activeSessionCount: this.conversationStatusManager.getActiveSessionIds().length
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
// Stream other Claude messages as normal
|
|
718
|
+
this.logger.debug('Broadcasting message to StreamManager', {
|
|
719
|
+
streamingId,
|
|
720
|
+
messageType: message?.type,
|
|
721
|
+
messageSubtype: message?.subtype
|
|
722
|
+
});
|
|
723
|
+
this.streamManager.broadcast(streamingId, message);
|
|
724
|
+
});
|
|
725
|
+
// Handle process closure
|
|
726
|
+
this.processManager.on('process-closed', ({ streamingId, code }) => {
|
|
727
|
+
// Derive termination reason from exit code
|
|
728
|
+
const terminationReason = code === 0 ? 'normal_completion' :
|
|
729
|
+
code === 143 ? 'user_stop' : // SIGTERM = 128 + 15
|
|
730
|
+
code === 137 ? 'force_killed' : // SIGKILL = 128 + 9
|
|
731
|
+
'process_crash';
|
|
732
|
+
this.logger.info('[PROCESS-CLOSED] Received process-closed event', {
|
|
733
|
+
streamingId: streamingId.slice(0, 8),
|
|
734
|
+
exitCode: code,
|
|
735
|
+
terminationReason,
|
|
736
|
+
clientCount: this.streamManager.getClientCount(streamingId),
|
|
737
|
+
wasSuccessful: code === 0
|
|
738
|
+
});
|
|
739
|
+
// Capture turn BEFORE cleaning up (need sessionId from map)
|
|
740
|
+
// Use local streamingSessionMap as primary source (populated from system_init)
|
|
741
|
+
// Fall back to conversationStatusManager if map is empty (e.g., after server restart)
|
|
742
|
+
let sessionIdForTurn = streamingSessionMap.get(streamingId);
|
|
743
|
+
if (!sessionIdForTurn) {
|
|
744
|
+
sessionIdForTurn = this.conversationStatusManager.getSessionId(streamingId);
|
|
745
|
+
if (sessionIdForTurn) {
|
|
746
|
+
this.logger.debug('[TURN-CAPTURE] Used fallback sessionId from conversationStatusManager', {
|
|
747
|
+
streamingId: streamingId.slice(0, 8),
|
|
748
|
+
sessionId: sessionIdForTurn.slice(0, 8)
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
if (sessionIdForTurn) {
|
|
753
|
+
// Fire and forget - don't block cleanup on turn capture
|
|
754
|
+
this.turnCaptureService.captureTurn(sessionIdForTurn, code, terminationReason)
|
|
755
|
+
.then(turn => {
|
|
756
|
+
if (turn) {
|
|
757
|
+
// Emit SSE event for real-time UI updates
|
|
758
|
+
import('./services/sessions/session-activity-watcher.js').then(({ getSessionActivityWatcher }) => {
|
|
759
|
+
getSessionActivityWatcher().emitTurnCaptured({
|
|
760
|
+
sessionId: sessionIdForTurn,
|
|
761
|
+
turn: {
|
|
762
|
+
id: turn.id,
|
|
763
|
+
turnNumber: turn.turnNumber,
|
|
764
|
+
timestamp: turn.timestamp,
|
|
765
|
+
headline: turn.headline,
|
|
766
|
+
actions: turn.actions,
|
|
767
|
+
tag: turn.tag,
|
|
768
|
+
icon: turn.icon,
|
|
769
|
+
toolCount: turn.toolCount,
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
})
|
|
775
|
+
.catch(err => {
|
|
776
|
+
this.logger.debug('Turn capture failed (non-blocking)', { error: err });
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
this.logger.warn('[TURN-CAPTURE] Could not find sessionId for turn capture', {
|
|
781
|
+
streamingId: streamingId.slice(0, 8),
|
|
782
|
+
exitCode: code,
|
|
783
|
+
terminationReason,
|
|
784
|
+
mapSize: streamingSessionMap.size
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
// Check for pending questions BEFORE unregistering (we need the sessionId mapping)
|
|
788
|
+
// If there are pending questions, this was likely an AskUserQuestion timeout
|
|
789
|
+
const pendingQuestions = this.pendingQuestionService.getPendingByStreamingId(streamingId);
|
|
790
|
+
if (pendingQuestions.length > 0) {
|
|
791
|
+
this.logger.info('Session closed with pending AskUserQuestion - questions preserved for later resume', {
|
|
792
|
+
streamingId,
|
|
793
|
+
pendingQuestionCount: pendingQuestions.length,
|
|
794
|
+
questionIds: pendingQuestions.map(q => q.id)
|
|
795
|
+
});
|
|
796
|
+
// Questions remain in DB with status='pending' for later retrieval
|
|
797
|
+
// The in-memory tracker will be cleaned up below, but DB persists
|
|
798
|
+
}
|
|
799
|
+
// Clean up local session map
|
|
800
|
+
streamingSessionMap.delete(streamingId);
|
|
801
|
+
// Unregister session from status tracker
|
|
802
|
+
this.logger.debug('Unregistering session from status tracker', { streamingId });
|
|
803
|
+
this.conversationStatusManager.unregisterActiveSession(streamingId);
|
|
804
|
+
// Clean up conversation context (handled automatically in unregisterActiveSession)
|
|
805
|
+
// Clean up permissions for this streaming session
|
|
806
|
+
const removedCount = this.permissionTracker.removePermissionsByStreamingId(streamingId);
|
|
807
|
+
if (removedCount > 0) {
|
|
808
|
+
this.logger.debug('Cleaned up permissions for closed session', {
|
|
809
|
+
streamingId,
|
|
810
|
+
removedPermissions: removedCount
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
// Check for in-memory questions BEFORE removing them
|
|
814
|
+
// If there are pending questions, we need to keep them for error suppression
|
|
815
|
+
// They'll be cleaned up when answered or expired, not on session close
|
|
816
|
+
const inMemoryQuestions = this.questionTracker.getQuestionsByStreamingId(streamingId);
|
|
817
|
+
if (inMemoryQuestions.length > 0) {
|
|
818
|
+
this.logger.info('Preserving in-memory questions for error suppression (awaiting user answer)', {
|
|
819
|
+
streamingId,
|
|
820
|
+
inMemoryQuestionCount: inMemoryQuestions.length,
|
|
821
|
+
questionIds: inMemoryQuestions.map(q => q.id)
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
// No pending questions - safe to clean up
|
|
826
|
+
const removedQuestions = this.questionTracker.removeQuestionsByStreamingId(streamingId);
|
|
827
|
+
if (removedQuestions > 0) {
|
|
828
|
+
this.logger.debug('Cleaned up in-memory questions for closed session', {
|
|
829
|
+
streamingId,
|
|
830
|
+
removedQuestions
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
this.streamManager.closeSession(streamingId);
|
|
835
|
+
});
|
|
836
|
+
// Handle process errors
|
|
837
|
+
this.processManager.on('process-error', ({ streamingId, error }) => {
|
|
838
|
+
this.logger.info('[PROCESS-ERROR] Received process-error event', {
|
|
839
|
+
streamingId: streamingId.slice(0, 8),
|
|
840
|
+
error: error?.toString().slice(0, 200),
|
|
841
|
+
clientCount: this.streamManager.getClientCount(streamingId)
|
|
842
|
+
});
|
|
843
|
+
// Check if there's a pending question for this streaming session
|
|
844
|
+
// If so, suppress the error - the session was intentionally stopped to wait for user answer
|
|
845
|
+
const pendingQuestions = this.questionTracker.getQuestionsByStreamingId(streamingId);
|
|
846
|
+
if (pendingQuestions.length > 0) {
|
|
847
|
+
this.logger.info('Suppressing process error due to pending AskUserQuestion', {
|
|
848
|
+
streamingId,
|
|
849
|
+
pendingQuestionCount: pendingQuestions.length,
|
|
850
|
+
questionIds: pendingQuestions.map(q => q.id)
|
|
851
|
+
});
|
|
852
|
+
// Still unregister the session but don't broadcast the error
|
|
853
|
+
this.conversationStatusManager.unregisterActiveSession(streamingId);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
// Unregister session from status tracker on error
|
|
857
|
+
this.logger.debug('Unregistering session from status tracker due to error', { streamingId });
|
|
858
|
+
this.conversationStatusManager.unregisterActiveSession(streamingId);
|
|
859
|
+
// Clean up conversation context on error (handled automatically in unregisterActiveSession)
|
|
860
|
+
const errorEvent = {
|
|
861
|
+
type: 'error',
|
|
862
|
+
error: error.toString(),
|
|
863
|
+
streamingId: streamingId,
|
|
864
|
+
timestamp: new Date().toISOString()
|
|
865
|
+
};
|
|
866
|
+
this.logger.debug('Broadcasting error event to clients', {
|
|
867
|
+
streamingId,
|
|
868
|
+
errorEventKeys: Object.keys(errorEvent)
|
|
869
|
+
});
|
|
870
|
+
this.streamManager.broadcast(streamingId, errorEvent);
|
|
871
|
+
});
|
|
872
|
+
// Start periodic cleanup of orphaned SSE sessions (every 60 seconds)
|
|
873
|
+
// This catches edge cases where process-closed event didn't fire or was missed
|
|
874
|
+
this.orphanCleanupInterval = setInterval(() => {
|
|
875
|
+
const activeProcessIds = new Set(this.processManager.getActiveSessions());
|
|
876
|
+
const cleaned = this.streamManager.cleanupOrphanedSessions(activeProcessIds);
|
|
877
|
+
if (cleaned > 0) {
|
|
878
|
+
this.logger.info('Periodic orphan cleanup removed stale SSE sessions', { count: cleaned });
|
|
879
|
+
}
|
|
880
|
+
}, 60_000);
|
|
881
|
+
this.logger.debug('ProcessManager integration setup complete', {
|
|
882
|
+
totalEventListeners: this.processManager.listenerCount('claude-message') +
|
|
883
|
+
this.processManager.listenerCount('process-closed') +
|
|
884
|
+
this.processManager.listenerCount('process-error')
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
setupPermissionTrackerIntegration() {
|
|
888
|
+
this.logger.debug('Setting up PermissionTracker integration');
|
|
889
|
+
// Forward permission events to stream
|
|
890
|
+
this.permissionTracker.on('permission_request', (request) => {
|
|
891
|
+
this.logger.debug('Permission request event received', {
|
|
892
|
+
id: request.id,
|
|
893
|
+
toolName: request.toolName,
|
|
894
|
+
streamingId: request.streamingId
|
|
895
|
+
});
|
|
896
|
+
// Broadcast to the appropriate streaming session
|
|
897
|
+
if (request.streamingId && request.streamingId !== 'unknown') {
|
|
898
|
+
const event = {
|
|
899
|
+
type: 'permission_request',
|
|
900
|
+
data: request,
|
|
901
|
+
streamingId: request.streamingId,
|
|
902
|
+
timestamp: new Date().toISOString()
|
|
903
|
+
};
|
|
904
|
+
this.streamManager.broadcast(request.streamingId, event);
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
this.logger.debug('PermissionTracker integration setup complete');
|
|
908
|
+
}
|
|
909
|
+
setupQuestionTrackerIntegration() {
|
|
910
|
+
this.logger.debug('Setting up QuestionTracker integration');
|
|
911
|
+
// Forward question events to stream
|
|
912
|
+
this.questionTracker.on('question_request', (request) => {
|
|
913
|
+
// Log at info level to diagnose question flow issues
|
|
914
|
+
this.logger.info('Question request event received, broadcasting to stream', {
|
|
915
|
+
id: request.id,
|
|
916
|
+
toolUseId: request.toolUseId,
|
|
917
|
+
streamingId: request.streamingId,
|
|
918
|
+
questionCount: request.questions.length,
|
|
919
|
+
clientCount: this.streamManager.getClientCount(request.streamingId)
|
|
920
|
+
});
|
|
921
|
+
// Broadcast to the appropriate streaming session
|
|
922
|
+
if (request.streamingId && request.streamingId !== 'unknown') {
|
|
923
|
+
const event = {
|
|
924
|
+
type: 'question_request',
|
|
925
|
+
data: request,
|
|
926
|
+
streamingId: request.streamingId,
|
|
927
|
+
timestamp: new Date().toISOString()
|
|
928
|
+
};
|
|
929
|
+
this.streamManager.broadcast(request.streamingId, event);
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
this.logger.debug('QuestionTracker integration setup complete');
|
|
933
|
+
}
|
|
934
|
+
async initializeOrReloadRouter(config) {
|
|
935
|
+
// If router is disabled, ensure it is stopped
|
|
936
|
+
if (!config.router?.enabled) {
|
|
937
|
+
if (this.routerService) {
|
|
938
|
+
this.logger.info('Router disabled in configuration, stopping router service');
|
|
939
|
+
await this.routerService.stop();
|
|
940
|
+
this.routerService = undefined;
|
|
941
|
+
this.processManager.setRouterService(undefined);
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
this.logger.info('Router service is disabled');
|
|
945
|
+
}
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
// If router is enabled
|
|
949
|
+
try {
|
|
950
|
+
// If there is an existing router, stop it first
|
|
951
|
+
if (this.routerService) {
|
|
952
|
+
this.logger.info('Reloading router service due to configuration change...');
|
|
953
|
+
await this.routerService.stop();
|
|
954
|
+
this.routerService = undefined;
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
this.logger.debug('Router service is enabled, attempting to initialize...');
|
|
958
|
+
}
|
|
959
|
+
this.routerService = new ClaudeRouterService(config.router);
|
|
960
|
+
await this.routerService.initialize();
|
|
961
|
+
this.processManager.setRouterService(this.routerService);
|
|
962
|
+
this.logger.info('Router service initialized');
|
|
963
|
+
}
|
|
964
|
+
catch (error) {
|
|
965
|
+
this.logger.error('Router initialization failed, continuing without router', error);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
//# sourceMappingURL=lattice-server.js.map
|