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,1062 @@
|
|
|
1
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import { LatticeError, } from '../../types/index.js';
|
|
3
|
+
import { createLogger } from '../../services/infrastructure/logger.js';
|
|
4
|
+
import { ConfigService } from '../infrastructure/config-service.js';
|
|
5
|
+
import { getCostTracker } from '../infrastructure/cost-tracker.js';
|
|
6
|
+
const DEFAULT_RETRY_CONFIG = {
|
|
7
|
+
maxRetries: 3,
|
|
8
|
+
baseDelayMs: 1000,
|
|
9
|
+
maxDelayMs: 10000,
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Determines if an error is retryable (transient network/rate limit issues).
|
|
13
|
+
* Non-retryable: auth errors, credit exhaustion, invalid requests.
|
|
14
|
+
*/
|
|
15
|
+
function isRetryableError(error) {
|
|
16
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
17
|
+
const errorName = error instanceof Error ? error.name : '';
|
|
18
|
+
// Non-retryable errors - don't waste retries on these
|
|
19
|
+
if (message.includes('credit balance') || message.includes('billing'))
|
|
20
|
+
return false;
|
|
21
|
+
if (message.includes('invalid_api_key') || message.includes('authentication'))
|
|
22
|
+
return false;
|
|
23
|
+
if (message.includes('invalid_request_error'))
|
|
24
|
+
return false;
|
|
25
|
+
// Retryable: rate limits, overloaded, server errors, network issues
|
|
26
|
+
if (message.includes('rate_limit') || message.includes('overloaded'))
|
|
27
|
+
return true;
|
|
28
|
+
if (message.includes('529') || message.includes('503') || message.includes('500'))
|
|
29
|
+
return true;
|
|
30
|
+
if (errorName === 'APIConnectionError' || message.includes('ECONNRESET'))
|
|
31
|
+
return true;
|
|
32
|
+
if (errorName === 'RateLimitError' || errorName === 'InternalServerError')
|
|
33
|
+
return true;
|
|
34
|
+
// Default: retry on unknown errors (might be transient)
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Calculates exponential backoff delay with jitter.
|
|
39
|
+
*/
|
|
40
|
+
function calculateBackoff(attempt, config) {
|
|
41
|
+
const exponentialDelay = config.baseDelayMs * Math.pow(2, attempt);
|
|
42
|
+
const jitter = Math.random() * 0.3 * exponentialDelay; // 0-30% jitter
|
|
43
|
+
return Math.min(exponentialDelay + jitter, config.maxDelayMs);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Sleep for a specified duration.
|
|
47
|
+
*/
|
|
48
|
+
function sleep(ms) {
|
|
49
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
50
|
+
}
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Model Configuration
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Centralized model IDs - update these when Anthropic releases new versions.
|
|
55
|
+
// These are defaults; can be overridden via config.anthropic.models.*
|
|
56
|
+
const DEFAULT_MODELS = {
|
|
57
|
+
/** Full insight generation - Sonnet balances quality/cost well for structured extraction */
|
|
58
|
+
generation: 'claude-sonnet-4-5-20250929',
|
|
59
|
+
/** Quick staleness checks - needs speed over capability */
|
|
60
|
+
quickCheck: 'claude-haiku-4-5-20251001',
|
|
61
|
+
/** Fast patch generation - speed over quality */
|
|
62
|
+
patch: 'claude-haiku-4-5-20251001',
|
|
63
|
+
/** Full patch generation (fallback) - for complex patches when fast path fails */
|
|
64
|
+
fullPatch: 'claude-haiku-4-5-20251001',
|
|
65
|
+
/** Proposition extraction - factual extraction task */
|
|
66
|
+
propositions: 'claude-haiku-4-5-20251001',
|
|
67
|
+
};
|
|
68
|
+
export class AnthropicService {
|
|
69
|
+
logger;
|
|
70
|
+
client = null;
|
|
71
|
+
// Model configuration - populated from config or defaults
|
|
72
|
+
// Uses explicit mutable type rather than `typeof DEFAULT_MODELS` which is readonly due to `as const`
|
|
73
|
+
models;
|
|
74
|
+
constructor() {
|
|
75
|
+
this.logger = createLogger('AnthropicService');
|
|
76
|
+
this.models = { ...DEFAULT_MODELS };
|
|
77
|
+
}
|
|
78
|
+
async initialize() {
|
|
79
|
+
const config = ConfigService.getInstance().getConfig();
|
|
80
|
+
const apiKey = config.anthropic?.apiKey || process.env.ANTHROPIC_API_KEY;
|
|
81
|
+
if (!apiKey) {
|
|
82
|
+
this.logger.warn('Anthropic API key not configured');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
this.client = new Anthropic({ apiKey });
|
|
87
|
+
// Load model overrides from config
|
|
88
|
+
const configModels = config.anthropic?.models;
|
|
89
|
+
if (configModels) {
|
|
90
|
+
if (configModels.generation)
|
|
91
|
+
this.models.generation = configModels.generation;
|
|
92
|
+
if (configModels.quickCheck)
|
|
93
|
+
this.models.quickCheck = configModels.quickCheck;
|
|
94
|
+
if (configModels.patch)
|
|
95
|
+
this.models.patch = configModels.patch;
|
|
96
|
+
if (configModels.fullPatch)
|
|
97
|
+
this.models.fullPatch = configModels.fullPatch;
|
|
98
|
+
if (configModels.propositions)
|
|
99
|
+
this.models.propositions = configModels.propositions;
|
|
100
|
+
}
|
|
101
|
+
// Also support legacy config.anthropic.model for generation
|
|
102
|
+
if (config.anthropic?.model) {
|
|
103
|
+
this.models.generation = config.anthropic.model;
|
|
104
|
+
}
|
|
105
|
+
this.logger.info('Anthropic service initialized', { models: this.models });
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
this.logger.error('Failed to initialize Anthropic service', { error });
|
|
109
|
+
throw new LatticeError('ANTHROPIC_INIT_ERROR', 'Failed to initialize Anthropic service', 500);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Execute a function with exponential backoff retry on transient errors.
|
|
114
|
+
*/
|
|
115
|
+
async withRetry(operation, fn, config = DEFAULT_RETRY_CONFIG) {
|
|
116
|
+
let lastError;
|
|
117
|
+
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
118
|
+
try {
|
|
119
|
+
return await fn();
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
lastError = error;
|
|
123
|
+
// Don't retry non-retryable errors
|
|
124
|
+
if (!isRetryableError(error)) {
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
// Don't retry after max attempts
|
|
128
|
+
if (attempt >= config.maxRetries) {
|
|
129
|
+
this.logger.warn(`${operation}: All ${config.maxRetries} retries exhausted`, {
|
|
130
|
+
error: error instanceof Error ? error.message : String(error)
|
|
131
|
+
});
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
const delayMs = calculateBackoff(attempt, config);
|
|
135
|
+
this.logger.info(`${operation}: Retry ${attempt + 1}/${config.maxRetries} after ${Math.round(delayMs)}ms`, {
|
|
136
|
+
error: error instanceof Error ? error.message : String(error)
|
|
137
|
+
});
|
|
138
|
+
await sleep(delayMs);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
throw lastError;
|
|
142
|
+
}
|
|
143
|
+
async checkHealth() {
|
|
144
|
+
if (!this.client) {
|
|
145
|
+
return {
|
|
146
|
+
status: 'unhealthy',
|
|
147
|
+
message: 'Anthropic API key not configured',
|
|
148
|
+
apiKeyValid: false
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const response = await this.withRetry('checkHealth', () => this.client.messages.create({
|
|
153
|
+
model: this.models.generation,
|
|
154
|
+
max_tokens: 50,
|
|
155
|
+
messages: [{ role: 'user', content: 'Say "ok" and nothing else.' }]
|
|
156
|
+
}));
|
|
157
|
+
const text = response.content[0]?.type === 'text' ? response.content[0].text : null;
|
|
158
|
+
if (text) {
|
|
159
|
+
return {
|
|
160
|
+
status: 'healthy',
|
|
161
|
+
message: 'Anthropic API is accessible',
|
|
162
|
+
apiKeyValid: true
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
status: 'unhealthy',
|
|
167
|
+
message: 'Unexpected response from Anthropic API',
|
|
168
|
+
apiKeyValid: true
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
this.logger.error('Health check failed', { error });
|
|
173
|
+
return {
|
|
174
|
+
status: 'unhealthy',
|
|
175
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
176
|
+
apiKeyValid: false
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Log cost for an LLM API call.
|
|
182
|
+
* Call this after each API call to track costs.
|
|
183
|
+
*/
|
|
184
|
+
logCost(response, operation, model, durationMs, sessionId) {
|
|
185
|
+
try {
|
|
186
|
+
const costTracker = getCostTracker();
|
|
187
|
+
costTracker.log({
|
|
188
|
+
sessionId: sessionId || 'unknown',
|
|
189
|
+
operation,
|
|
190
|
+
model,
|
|
191
|
+
inputTokens: response.usage?.input_tokens || 0,
|
|
192
|
+
outputTokens: response.usage?.output_tokens || 0,
|
|
193
|
+
durationMs,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
// Don't let cost logging failures break the main flow
|
|
198
|
+
this.logger.debug('Failed to log cost', { error, operation });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Extract rich, structured session insights using Opus 4.5
|
|
203
|
+
*/
|
|
204
|
+
async extractSessionInsights(conversationText, sessionId) {
|
|
205
|
+
if (!this.client) {
|
|
206
|
+
throw new LatticeError('ANTHROPIC_API_KEY_MISSING', 'Anthropic API key not configured', 400);
|
|
207
|
+
}
|
|
208
|
+
const systemPrompt = `You are analyzing a coding session transcript to extract structured information for a dashboard display.
|
|
209
|
+
|
|
210
|
+
The goal: Someone glancing at this dashboard should immediately understand what this session is about.
|
|
211
|
+
|
|
212
|
+
Extract the following:
|
|
213
|
+
|
|
214
|
+
1. CONTEXT - Identity of the session:
|
|
215
|
+
- project: The codebase/repo name
|
|
216
|
+
- area: The specific component/module/domain if applicable (null if general)
|
|
217
|
+
- mission: The overarching goal in one sentence - be specific
|
|
218
|
+
- scope: "minor" (quick fix), "feature" (meaningful addition), "major" (significant change)
|
|
219
|
+
|
|
220
|
+
2. THEME - A single word capturing the session's current mode/vibe:
|
|
221
|
+
Examples: "debugging", "building", "exploring", "firefighting", "refactoring", "designing", "investigating", "polishing"
|
|
222
|
+
|
|
223
|
+
3. TAGS - Quick categorization for filtering/display:
|
|
224
|
+
- workType: Array of what's happening - pick ONLY from: "implementing", "debugging", "refactoring", "exploring", "designing", "investigating"
|
|
225
|
+
(implementing=writing new code, debugging=fixing bugs, refactoring=restructuring, exploring=reading/understanding, designing=planning architecture, investigating=diagnosing/researching)
|
|
226
|
+
- domain: Array of technical areas - e.g., "UI", "backend", "database", "API", "infra", "auth", "testing"
|
|
227
|
+
- collaboration: How the session is proceeding - "autonomous" (Claude working independently), "iterative" (back-and-forth), "guided" (user directing each step)
|
|
228
|
+
- complexity: Session difficulty - "routine" (straightforward), "tricky" (requires care), "gnarly" (complex/hairy - reserved for multi-system debugging or architecture-spanning changes)
|
|
229
|
+
|
|
230
|
+
CRITICAL OUTPUT INSTRUCTIONS:
|
|
231
|
+
- Respond with ONLY valid JSON
|
|
232
|
+
- Do NOT include explanatory text before or after the JSON
|
|
233
|
+
- Do NOT include markdown code blocks or formatting
|
|
234
|
+
- Start your response with { and end with }
|
|
235
|
+
- No prose, no commentary, ONLY the JSON object
|
|
236
|
+
|
|
237
|
+
JSON Structure:
|
|
238
|
+
{
|
|
239
|
+
"context": { "project": "string", "area": "string|null", "mission": "string", "scope": "minor|feature|major" },
|
|
240
|
+
"theme": "string",
|
|
241
|
+
"tags": { "workType": ["string", ...], "domain": ["string", ...], "collaboration": "autonomous|iterative|guided", "complexity": "routine|tricky|gnarly" }
|
|
242
|
+
}`;
|
|
243
|
+
let responseText = null;
|
|
244
|
+
const startTime = Date.now();
|
|
245
|
+
try {
|
|
246
|
+
const response = await this.withRetry('extractSessionInsights', () => this.client.messages.create({
|
|
247
|
+
model: this.models.generation,
|
|
248
|
+
max_tokens: 500, // Reduced - only extracting context, theme, tags now
|
|
249
|
+
system: systemPrompt,
|
|
250
|
+
messages: [
|
|
251
|
+
{
|
|
252
|
+
role: 'user',
|
|
253
|
+
content: `Analyze this coding session and extract structured insights:\n\n${conversationText}`
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
role: 'assistant',
|
|
257
|
+
content: '{' // Prefill to force JSON output
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
}));
|
|
261
|
+
const durationMs = Date.now() - startTime;
|
|
262
|
+
// Log cost for GENERATE operation
|
|
263
|
+
this.logCost(response, 'GENERATE', this.models.generation, durationMs, sessionId);
|
|
264
|
+
responseText = response.content[0]?.type === 'text' ? response.content[0].text : null;
|
|
265
|
+
if (!responseText) {
|
|
266
|
+
throw new LatticeError('ANTHROPIC_INSIGHTS_ERROR', 'No response text returned', 500);
|
|
267
|
+
}
|
|
268
|
+
// Prepend the '{' we used for prefill
|
|
269
|
+
responseText = '{' + responseText;
|
|
270
|
+
// Extract JSON from response with multiple fallback strategies
|
|
271
|
+
let jsonText = responseText.trim();
|
|
272
|
+
// Strategy 1: Try to extract from markdown code blocks
|
|
273
|
+
const codeBlockMatch = responseText.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
274
|
+
if (codeBlockMatch) {
|
|
275
|
+
jsonText = codeBlockMatch[1].trim();
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
// Strategy 2: Look for content between first { and last }
|
|
279
|
+
const firstBrace = jsonText.indexOf('{');
|
|
280
|
+
const lastBrace = jsonText.lastIndexOf('}');
|
|
281
|
+
if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {
|
|
282
|
+
jsonText = jsonText.substring(firstBrace, lastBrace + 1);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// Log the extracted JSON for debugging
|
|
286
|
+
this.logger.debug('Extracted JSON from LLM response', {
|
|
287
|
+
responsePreview: responseText.slice(0, 100),
|
|
288
|
+
extractedPreview: jsonText.slice(0, 100),
|
|
289
|
+
usedCodeBlock: !!codeBlockMatch
|
|
290
|
+
});
|
|
291
|
+
const result = JSON.parse(jsonText);
|
|
292
|
+
// Validate structure - be lenient, provide defaults
|
|
293
|
+
if (!result.context) {
|
|
294
|
+
throw new LatticeError('ANTHROPIC_INSIGHTS_ERROR', 'Invalid response structure', 500);
|
|
295
|
+
}
|
|
296
|
+
// Theme and tags come from the LLM prompt
|
|
297
|
+
result.theme = result.theme || 'working';
|
|
298
|
+
result.tags = result.tags || null;
|
|
299
|
+
// These fields are deprecated and no longer requested in the LLM prompt.
|
|
300
|
+
// They're set to empty defaults for type compatibility.
|
|
301
|
+
// V8 fields (milestones, notable, recentActions) were removed from prompt
|
|
302
|
+
// but types still require them for backward compat with cached data.
|
|
303
|
+
result.milestones = [];
|
|
304
|
+
result.notable = [];
|
|
305
|
+
result.recentActions = [];
|
|
306
|
+
result.currentState = null;
|
|
307
|
+
result.panels = [];
|
|
308
|
+
this.logger.info('Session insights extracted (Opus)', {
|
|
309
|
+
model: this.models.generation,
|
|
310
|
+
inputTokens: response.usage?.input_tokens,
|
|
311
|
+
outputTokens: response.usage?.output_tokens,
|
|
312
|
+
project: result.context.project,
|
|
313
|
+
theme: result.theme
|
|
314
|
+
});
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
if (error instanceof LatticeError) {
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
321
|
+
// Check for credit/billing errors and make them LOUD
|
|
322
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
323
|
+
if (errorMessage.includes('credit balance') || errorMessage.includes('billing')) {
|
|
324
|
+
this.logger.error('🚨 ANTHROPIC API CREDITS EXHAUSTED 🚨', {
|
|
325
|
+
errorMessage,
|
|
326
|
+
action: 'ADD CREDITS AT https://console.anthropic.com/settings/billing'
|
|
327
|
+
});
|
|
328
|
+
throw new LatticeError('ANTHROPIC_NO_CREDITS', '🚨 Anthropic API credits exhausted. Add credits at https://console.anthropic.com/settings/billing', 402);
|
|
329
|
+
}
|
|
330
|
+
// Log error with proper serialization AND the response that failed to parse
|
|
331
|
+
this.logger.error('Session insights extraction failed', {
|
|
332
|
+
errorMessage,
|
|
333
|
+
errorStack: error instanceof Error ? error.stack : undefined,
|
|
334
|
+
errorType: error?.constructor?.name,
|
|
335
|
+
// Include the actual response for debugging
|
|
336
|
+
responsePreview: responseText?.slice(0, 500) || 'no response'
|
|
337
|
+
});
|
|
338
|
+
throw new LatticeError('ANTHROPIC_INSIGHTS_ERROR', 'Failed to extract session insights', 500);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Generate a session name (simpler extraction for naming)
|
|
343
|
+
*/
|
|
344
|
+
async generateSessionName(conversationText) {
|
|
345
|
+
if (!this.client) {
|
|
346
|
+
throw new LatticeError('ANTHROPIC_API_KEY_MISSING', 'Anthropic API key not configured', 400);
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
const response = await this.withRetry('generateSessionName', () => this.client.messages.create({
|
|
350
|
+
model: this.models.generation,
|
|
351
|
+
max_tokens: 100,
|
|
352
|
+
messages: [{
|
|
353
|
+
role: 'user',
|
|
354
|
+
content: `Generate a brief, descriptive name (3-6 words) for this coding session. Include the project/area context. Focus on the main task or feature being worked on. Do not include generic words like "session", "conversation", or "chat".
|
|
355
|
+
|
|
356
|
+
Examples of good names:
|
|
357
|
+
- "CUI: Session insights UI redesign"
|
|
358
|
+
- "Auth: Fix redirect loop bug"
|
|
359
|
+
- "API: Add rate limiting middleware"
|
|
360
|
+
|
|
361
|
+
Respond with ONLY the name, nothing else.
|
|
362
|
+
|
|
363
|
+
Conversation:
|
|
364
|
+
${conversationText}`
|
|
365
|
+
}]
|
|
366
|
+
}));
|
|
367
|
+
const name = response.content[0]?.type === 'text' ? response.content[0].text.trim() : null;
|
|
368
|
+
if (!name) {
|
|
369
|
+
throw new LatticeError('ANTHROPIC_NAME_ERROR', 'No name returned', 500);
|
|
370
|
+
}
|
|
371
|
+
return { name };
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
if (error instanceof LatticeError) {
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
this.logger.error('Session name generation failed', { error });
|
|
378
|
+
throw new LatticeError('ANTHROPIC_NAME_ERROR', 'Failed to generate session name', 500);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
isConfigured() {
|
|
382
|
+
return this.client !== null;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Quick check using Haiku to determine if insights need patching.
|
|
386
|
+
*
|
|
387
|
+
* This is a fast, cheap call that just answers "has anything meaningful changed?"
|
|
388
|
+
* based on recent actions. If yes, we'll follow up with Sonnet for actual patching.
|
|
389
|
+
*/
|
|
390
|
+
async quickCheckInsightsStale(currentInsights, recentActions, sessionId) {
|
|
391
|
+
if (!this.client) {
|
|
392
|
+
return { needsPatch: false, reason: 'Anthropic not configured' };
|
|
393
|
+
}
|
|
394
|
+
const startTime = Date.now();
|
|
395
|
+
try {
|
|
396
|
+
const milestonesStr = currentInsights.milestones
|
|
397
|
+
.map(m => `[${m.status}] ${m.label}`)
|
|
398
|
+
.join(', ');
|
|
399
|
+
const actionsStr = recentActions.join('\n');
|
|
400
|
+
const prompt = `Current session insights (a living summary of the conversation):
|
|
401
|
+
- Mission: "${currentInsights.mission}"
|
|
402
|
+
- Milestones: ${milestonesStr || '(none)'}
|
|
403
|
+
|
|
404
|
+
Recent activity:
|
|
405
|
+
${actionsStr}
|
|
406
|
+
|
|
407
|
+
Should these insights be updated to reflect the recent activity?
|
|
408
|
+
|
|
409
|
+
Say YES (needsPatch: true) if ANY of these apply:
|
|
410
|
+
- Progress was made on any task or milestone
|
|
411
|
+
- Focus shifted to a new area or topic
|
|
412
|
+
- Something notable happened worth capturing
|
|
413
|
+
- The summary feels stale compared to what just happened
|
|
414
|
+
|
|
415
|
+
Say NO only if the recent actions are trivial or already reflected in current insights.
|
|
416
|
+
|
|
417
|
+
Err on the side of updating - the insights should feel LIVE and current.
|
|
418
|
+
|
|
419
|
+
Respond with JSON only:
|
|
420
|
+
{"needsPatch": true/false, "reason": "brief explanation"}`;
|
|
421
|
+
const response = await this.withRetry('quickCheckInsightsStale', () => this.client.messages.create({
|
|
422
|
+
model: this.models.quickCheck,
|
|
423
|
+
max_tokens: 100,
|
|
424
|
+
messages: [{ role: 'user', content: prompt }]
|
|
425
|
+
}));
|
|
426
|
+
const duration = Date.now() - startTime;
|
|
427
|
+
const text = response.content[0]?.type === 'text' ? response.content[0].text : '';
|
|
428
|
+
// Log cost for QUICK_CHECK operation
|
|
429
|
+
this.logCost(response, 'QUICK_CHECK', this.models.quickCheck, duration, sessionId);
|
|
430
|
+
// Log for observability
|
|
431
|
+
this.logger.info('Quick check (Haiku) completed', {
|
|
432
|
+
durationMs: duration,
|
|
433
|
+
inputTokens: response.usage?.input_tokens,
|
|
434
|
+
outputTokens: response.usage?.output_tokens,
|
|
435
|
+
recentActionsCount: recentActions.length,
|
|
436
|
+
responsePreview: text.slice(0, 100)
|
|
437
|
+
});
|
|
438
|
+
try {
|
|
439
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
440
|
+
const result = JSON.parse(jsonMatch?.[0] || text);
|
|
441
|
+
return {
|
|
442
|
+
needsPatch: result.needsPatch === true,
|
|
443
|
+
reason: result.reason || 'No reason provided'
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
this.logger.debug('Failed to parse quick check response', { text });
|
|
448
|
+
return { needsPatch: false, reason: 'Parse error - keeping cached' };
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
// Check for credit/billing errors and make them LOUD
|
|
453
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
454
|
+
if (errorMessage.includes('credit balance') || errorMessage.includes('billing')) {
|
|
455
|
+
this.logger.error('🚨 ANTHROPIC API CREDITS EXHAUSTED 🚨', {
|
|
456
|
+
errorMessage,
|
|
457
|
+
action: 'ADD CREDITS AT https://console.anthropic.com/settings/billing',
|
|
458
|
+
context: 'Quick check failed due to billing issue'
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
this.logger.error('Quick check failed', { error });
|
|
463
|
+
}
|
|
464
|
+
return { needsPatch: false, reason: 'Check error - keeping cached' };
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Decompose recent activity into exhaustive propositional facts.
|
|
469
|
+
* Separates "what happened" (facts) from "what does this mean" (interpretation).
|
|
470
|
+
* Returns structured bullet points of all tool uses, user messages, and Claude messages.
|
|
471
|
+
*/
|
|
472
|
+
async decomposeActivityIntoPropositions(previousState, recentActivity, sessionId) {
|
|
473
|
+
if (!this.client) {
|
|
474
|
+
return [];
|
|
475
|
+
}
|
|
476
|
+
const previousStateStr = previousState
|
|
477
|
+
? `${previousState.icon} [${previousState.color}] ${previousState.content}`
|
|
478
|
+
: '(no previous state)';
|
|
479
|
+
const activityStr = recentActivity
|
|
480
|
+
.map(a => {
|
|
481
|
+
// Format timestamp as HH:MM:SS for readability
|
|
482
|
+
const timeStr = a.timestamp
|
|
483
|
+
? new Date(a.timestamp).toLocaleTimeString('en-US', { hour12: false })
|
|
484
|
+
: '';
|
|
485
|
+
const prefix = timeStr ? `[${timeStr}] ` : '';
|
|
486
|
+
return `${prefix}[${a.type}] ${a.content}`;
|
|
487
|
+
})
|
|
488
|
+
.join('\n\n');
|
|
489
|
+
const prompt = `You are analyzing a transcript of a coding session between a user and Claude (an AI assistant).
|
|
490
|
+
|
|
491
|
+
**Previous Current State:**
|
|
492
|
+
${previousStateStr}
|
|
493
|
+
|
|
494
|
+
**Recent Activity:**
|
|
495
|
+
${activityStr}
|
|
496
|
+
|
|
497
|
+
**Your Task:**
|
|
498
|
+
Create an exhaustive list of propositional facts about what happened in this activity.
|
|
499
|
+
Be thorough and explicit - list EVERYTHING that was done and said.
|
|
500
|
+
|
|
501
|
+
Format as bullet points:
|
|
502
|
+
- Tool uses: "Claude used [tool] to [action]"
|
|
503
|
+
- Claude statements: "Claude said/stated/explained [fact]"
|
|
504
|
+
- User messages: "User asked/requested/said [fact]"
|
|
505
|
+
|
|
506
|
+
Be exhaustive - every action, every statement, every request should become a bullet point.
|
|
507
|
+
Keep each bullet point factual and specific, not interpretive.
|
|
508
|
+
|
|
509
|
+
Examples:
|
|
510
|
+
✓ "Claude used Grep to search for 'validateUser' in the codebase"
|
|
511
|
+
✓ "Claude stated that the bug was caused by missing null checks"
|
|
512
|
+
✓ "Claude said the fix is ready to test after server restart"
|
|
513
|
+
✓ "User asked about the performance impact"
|
|
514
|
+
✗ "Claude made progress on the bug" (too vague)
|
|
515
|
+
✗ "The session is going well" (interpretive, not factual)
|
|
516
|
+
|
|
517
|
+
Return ONLY a JSON array of strings, one per proposition:
|
|
518
|
+
["proposition 1", "proposition 2", ...]`;
|
|
519
|
+
let rawResponse = '';
|
|
520
|
+
const startTime = Date.now();
|
|
521
|
+
try {
|
|
522
|
+
const response = await this.withRetry('decomposeActivityIntoPropositions', () => this.client.messages.create({
|
|
523
|
+
model: this.models.propositions, // Use Haiku - faster for factual extraction
|
|
524
|
+
max_tokens: 2000,
|
|
525
|
+
temperature: 0,
|
|
526
|
+
messages: [{
|
|
527
|
+
role: 'user',
|
|
528
|
+
content: prompt
|
|
529
|
+
}]
|
|
530
|
+
}));
|
|
531
|
+
const durationMs = Date.now() - startTime;
|
|
532
|
+
// Log cost for PROPOSITIONS operation (Haiku)
|
|
533
|
+
this.logCost(response, 'PROPOSITIONS', this.models.propositions, durationMs, sessionId);
|
|
534
|
+
const textContent = response.content.find(block => block.type === 'text');
|
|
535
|
+
if (!textContent || textContent.type !== 'text') {
|
|
536
|
+
return [];
|
|
537
|
+
}
|
|
538
|
+
rawResponse = textContent.text;
|
|
539
|
+
let jsonText = textContent.text.trim();
|
|
540
|
+
// Strip markdown code blocks if present (```json ... ```)
|
|
541
|
+
if (jsonText.startsWith('```')) {
|
|
542
|
+
jsonText = jsonText.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, '').trim();
|
|
543
|
+
}
|
|
544
|
+
// Strip markdown headings and prose before JSON (e.g., "# Summary\nHere are...")
|
|
545
|
+
// Always try to extract JSON array first, even if text looks like it starts with [
|
|
546
|
+
const jsonMatch = jsonText.match(/\[[\s\S]*?\](?=\s*$)/);
|
|
547
|
+
if (jsonMatch) {
|
|
548
|
+
jsonText = jsonMatch[0];
|
|
549
|
+
}
|
|
550
|
+
else if (!jsonText.startsWith('[')) {
|
|
551
|
+
// No JSON array found and doesn't start with [
|
|
552
|
+
this.logger.debug('Decomposition response is not JSON', { preview: jsonText.slice(0, 200) });
|
|
553
|
+
return [];
|
|
554
|
+
}
|
|
555
|
+
const propositions = JSON.parse(jsonText);
|
|
556
|
+
// Log token usage for cost tracking
|
|
557
|
+
this.logger.info('Propositional decomposition (Haiku)', {
|
|
558
|
+
model: this.models.propositions,
|
|
559
|
+
inputTokens: response.usage?.input_tokens,
|
|
560
|
+
outputTokens: response.usage?.output_tokens,
|
|
561
|
+
propositionCount: Array.isArray(propositions) ? propositions.length : 0,
|
|
562
|
+
activityCount: recentActivity.length
|
|
563
|
+
});
|
|
564
|
+
return Array.isArray(propositions) ? propositions : [];
|
|
565
|
+
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
this.logger.debug('Failed to decompose activity into propositions', {
|
|
568
|
+
error: error instanceof Error ? error.message : String(error),
|
|
569
|
+
responsePreview: rawResponse.slice(0, 200),
|
|
570
|
+
});
|
|
571
|
+
return [];
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Generate fast patch for session purpose only.
|
|
576
|
+
*
|
|
577
|
+
* Theme/tags are now evaluated on user message (evaluateSessionMetadata),
|
|
578
|
+
* so this method only updates the evolving session purpose based on
|
|
579
|
+
* what Claude actually did (vs what user asked for).
|
|
580
|
+
*/
|
|
581
|
+
async generateFastPatch(currentInsights, propositions, _recentActivity, sessionId) {
|
|
582
|
+
if (!this.client || propositions.length === 0) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
const missionStr = currentInsights.mission || '(not set)';
|
|
586
|
+
const purposeStr = currentInsights.purpose || missionStr;
|
|
587
|
+
const propositionsStr = propositions.map((p, i) => `${i + 1}. ${p}`).join('\n');
|
|
588
|
+
// Simplified prompt - only evaluate purpose, not theme/tags
|
|
589
|
+
// Theme/tags are now evaluated on user message to prevent flip-flopping
|
|
590
|
+
const prompt = `You are updating a coding session dashboard based on recent activity.
|
|
591
|
+
|
|
592
|
+
ORIGINAL SESSION MISSION (frozen at start):
|
|
593
|
+
${missionStr}
|
|
594
|
+
|
|
595
|
+
CURRENT PURPOSE (what session has evolved to):
|
|
596
|
+
${purposeStr}
|
|
597
|
+
|
|
598
|
+
RECENT ACTIVITY (propositional facts):
|
|
599
|
+
${propositionsStr}
|
|
600
|
+
|
|
601
|
+
Based on what was just accomplished, update the session purpose:
|
|
602
|
+
|
|
603
|
+
**purpose** (30-50 chars): What is this session about NOW?
|
|
604
|
+
- The session's current identity/focus
|
|
605
|
+
- Keep concise: "Building auth system" not "We are currently..."
|
|
606
|
+
- May match the original mission, or may have evolved
|
|
607
|
+
|
|
608
|
+
Respond with ONLY valid JSON:
|
|
609
|
+
{"purpose": "current session focus"}`;
|
|
610
|
+
let rawResponse = '';
|
|
611
|
+
const startTime = Date.now();
|
|
612
|
+
try {
|
|
613
|
+
const response = await this.withRetry('generateFastPatch', () => this.client.messages.create({
|
|
614
|
+
model: this.models.patch,
|
|
615
|
+
max_tokens: 100, // Reduced - only extracting purpose now
|
|
616
|
+
temperature: 0,
|
|
617
|
+
messages: [{
|
|
618
|
+
role: 'user',
|
|
619
|
+
content: prompt
|
|
620
|
+
}]
|
|
621
|
+
}));
|
|
622
|
+
const durationMs = Date.now() - startTime;
|
|
623
|
+
// Log cost for FAST_PATCH operation (Haiku)
|
|
624
|
+
this.logCost(response, 'FAST_PATCH', this.models.patch, durationMs, sessionId);
|
|
625
|
+
const textContent = response.content.find(block => block.type === 'text');
|
|
626
|
+
if (!textContent || textContent.type !== 'text') {
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
rawResponse = textContent.text;
|
|
630
|
+
let jsonText = textContent.text.trim();
|
|
631
|
+
// Strip markdown code blocks if present
|
|
632
|
+
if (jsonText.startsWith('```')) {
|
|
633
|
+
jsonText = jsonText.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, '').trim();
|
|
634
|
+
}
|
|
635
|
+
// Extract JSON object
|
|
636
|
+
const jsonMatch = jsonText.match(/\{[\s\S]*\}(?=\s*$)/);
|
|
637
|
+
if (jsonMatch) {
|
|
638
|
+
jsonText = jsonMatch[0];
|
|
639
|
+
}
|
|
640
|
+
else if (!jsonText.startsWith('{')) {
|
|
641
|
+
this.logger.debug('FastPatch response is not JSON', { preview: jsonText.slice(0, 200) });
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
const result = JSON.parse(jsonText);
|
|
645
|
+
// Validate structure - purpose is required
|
|
646
|
+
if (!result.purpose || typeof result.purpose !== 'string') {
|
|
647
|
+
this.logger.debug('FastPatch missing purpose', { keys: Object.keys(result) });
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
return {
|
|
651
|
+
milestones: [],
|
|
652
|
+
recentActions: [],
|
|
653
|
+
purpose: result.purpose,
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
catch (error) {
|
|
657
|
+
this.logger.debug('FastPatch generation failed', {
|
|
658
|
+
error: error instanceof Error ? error.message : String(error),
|
|
659
|
+
responsePreview: rawResponse.slice(0, 200),
|
|
660
|
+
});
|
|
661
|
+
return null;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Generate targeted patches to insights using Sonnet.
|
|
666
|
+
*
|
|
667
|
+
* Called when quickCheckInsightsStale returns needsPatch=true.
|
|
668
|
+
* Returns specific patches to apply rather than regenerating everything.
|
|
669
|
+
*
|
|
670
|
+
* @param useFastPath - If true, only generate currentState using fast Haiku-only pipeline (~5s vs ~12s)
|
|
671
|
+
*/
|
|
672
|
+
async generateInsightsPatch(currentInsights, recentActivity, useFastPath = true, // Default to fast path for better UX
|
|
673
|
+
sessionId) {
|
|
674
|
+
if (!this.client) {
|
|
675
|
+
return { patches: {}, reason: 'Anthropic not configured', propositions: [] };
|
|
676
|
+
}
|
|
677
|
+
const startTime = Date.now();
|
|
678
|
+
try {
|
|
679
|
+
// Step 1: Decompose activity into propositional facts
|
|
680
|
+
const decompositionStart = Date.now();
|
|
681
|
+
const propositions = await this.decomposeActivityIntoPropositions(null, recentActivity, sessionId);
|
|
682
|
+
const decompositionTime = Date.now() - decompositionStart;
|
|
683
|
+
this.logger.debug('Propositional decomposition completed', {
|
|
684
|
+
durationMs: decompositionTime,
|
|
685
|
+
propositionCount: propositions.length,
|
|
686
|
+
});
|
|
687
|
+
// Fast path: Generate purpose, theme, tags using Haiku (~1-2s vs ~12s full patch)
|
|
688
|
+
// This is the "Haiku-heavy" strategy from Architecture V2
|
|
689
|
+
if (useFastPath) {
|
|
690
|
+
this.logger.debug('Using fast path for patch generation');
|
|
691
|
+
const fastPatch = await this.generateFastPatch({
|
|
692
|
+
milestones: currentInsights.milestones,
|
|
693
|
+
recentActions: currentInsights.recentActions,
|
|
694
|
+
notable: currentInsights.notable, // For deduplication
|
|
695
|
+
mission: currentInsights.mission, // For pivot detection
|
|
696
|
+
purpose: currentInsights.purpose, // Current evolving purpose
|
|
697
|
+
theme: currentInsights.theme, // Current theme for re-evaluation
|
|
698
|
+
tags: currentInsights.tags, // Current tags for re-evaluation
|
|
699
|
+
}, propositions, recentActivity, // Pass activity for intent extraction (Strategy 3)
|
|
700
|
+
sessionId);
|
|
701
|
+
const totalTime = Date.now() - startTime;
|
|
702
|
+
if (fastPatch && fastPatch.purpose) {
|
|
703
|
+
this.logger.info('Fast path patch completed', {
|
|
704
|
+
durationMs: totalTime,
|
|
705
|
+
purpose: fastPatch.purpose,
|
|
706
|
+
});
|
|
707
|
+
return {
|
|
708
|
+
patches: { purpose: fastPatch.purpose },
|
|
709
|
+
reason: `Fast path: Updated purpose based on propositional analysis`,
|
|
710
|
+
propositions
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
// Fall through to full path if fast path fails
|
|
714
|
+
this.logger.debug('Fast path returned null, falling back to full patch generation');
|
|
715
|
+
}
|
|
716
|
+
// Use propositions if available, fallback to raw activity
|
|
717
|
+
const activityStr = propositions.length > 0
|
|
718
|
+
? propositions.map(p => `• ${p}`).join('\n')
|
|
719
|
+
: recentActivity.slice(-10).map(a => `[${a.type}] ${a.content}`).join('\n');
|
|
720
|
+
const prompt = `You are analyzing a coding session transcript to update a dashboard display.
|
|
721
|
+
|
|
722
|
+
CURRENT SESSION:
|
|
723
|
+
Mission: "${currentInsights.mission}"
|
|
724
|
+
Theme: ${currentInsights.theme}
|
|
725
|
+
Purpose: ${currentInsights.purpose || currentInsights.mission}
|
|
726
|
+
|
|
727
|
+
RECENT ACTIVITY:
|
|
728
|
+
${activityStr}
|
|
729
|
+
|
|
730
|
+
Based on what happened, extract the session's current **purpose** (what it's about NOW).
|
|
731
|
+
|
|
732
|
+
**purpose**: 30-50 chars describing the session's current focus
|
|
733
|
+
- What someone would say if asked "what are you working on?"
|
|
734
|
+
- Keep it concise: "Building auth system" not "We are currently..."
|
|
735
|
+
- May match the original mission, or may have pivoted
|
|
736
|
+
|
|
737
|
+
Response format (JSON only):
|
|
738
|
+
{
|
|
739
|
+
"reason": "brief explanation",
|
|
740
|
+
"patches": {
|
|
741
|
+
"purpose": "current session focus"
|
|
742
|
+
}
|
|
743
|
+
}`;
|
|
744
|
+
const patchGenStart = Date.now();
|
|
745
|
+
const response = await this.withRetry('generateInsightsPatch', () => this.client.messages.create({
|
|
746
|
+
model: this.models.fullPatch,
|
|
747
|
+
max_tokens: 150, // Only extracting purpose now
|
|
748
|
+
temperature: 0,
|
|
749
|
+
messages: [{ role: 'user', content: prompt }]
|
|
750
|
+
}));
|
|
751
|
+
const duration = Date.now() - startTime;
|
|
752
|
+
const text = response.content[0]?.type === 'text' ? response.content[0].text : '';
|
|
753
|
+
// Log cost for PATCH operation (full path)
|
|
754
|
+
this.logCost(response, 'PATCH', this.models.fullPatch, duration, sessionId);
|
|
755
|
+
this.logger.info('Fallback patch generation (Sonnet) completed', {
|
|
756
|
+
durationMs: duration,
|
|
757
|
+
inputTokens: response.usage?.input_tokens,
|
|
758
|
+
outputTokens: response.usage?.output_tokens,
|
|
759
|
+
});
|
|
760
|
+
try {
|
|
761
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
762
|
+
const jsonToParse = jsonMatch?.[0] || text;
|
|
763
|
+
const result = JSON.parse(jsonToParse);
|
|
764
|
+
const patches = result.patches || {};
|
|
765
|
+
return {
|
|
766
|
+
patches,
|
|
767
|
+
reason: result.reason || 'Fallback patch generated',
|
|
768
|
+
propositions
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
catch (parseError) {
|
|
772
|
+
this.logger.warn('Failed to parse fallback patch response', {
|
|
773
|
+
error: parseError instanceof Error ? parseError.message : String(parseError),
|
|
774
|
+
textPreview: text.slice(0, 200),
|
|
775
|
+
});
|
|
776
|
+
return { patches: {}, reason: 'Parse error - no patches applied', propositions };
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
catch (error) {
|
|
780
|
+
// Check for credit/billing errors and make them LOUD
|
|
781
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
782
|
+
if (errorMessage.includes('credit balance') || errorMessage.includes('billing')) {
|
|
783
|
+
this.logger.error('🚨 ANTHROPIC API CREDITS EXHAUSTED 🚨', {
|
|
784
|
+
errorMessage,
|
|
785
|
+
action: 'ADD CREDITS AT https://console.anthropic.com/settings/billing',
|
|
786
|
+
context: 'Patch generation failed due to billing issue'
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
else {
|
|
790
|
+
this.logger.error('Patch generation failed', { error });
|
|
791
|
+
}
|
|
792
|
+
return { patches: {}, reason: 'Generation error - no patches applied', propositions: [] };
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Summarize what the user is currently asking Claude to do.
|
|
797
|
+
*
|
|
798
|
+
* Called immediately when a user message is detected, this generates a brief
|
|
799
|
+
* description that makes sense even if the user just said "do it" - because
|
|
800
|
+
* we provide recent conversation context.
|
|
801
|
+
*
|
|
802
|
+
* Uses Haiku for speed (~1-2s). The goal is "what would you tell someone
|
|
803
|
+
* who walked in and asked what you're working on right now?"
|
|
804
|
+
*/
|
|
805
|
+
async summarizeCurrentWork(userMessage, recentContext, sessionMission, sessionId) {
|
|
806
|
+
if (!this.client) {
|
|
807
|
+
return null;
|
|
808
|
+
}
|
|
809
|
+
const startTime = Date.now();
|
|
810
|
+
try {
|
|
811
|
+
// Build context from recent conversation (last few exchanges)
|
|
812
|
+
const contextStr = recentContext.length > 0
|
|
813
|
+
? recentContext.map(m => `[${m.type === 'user' ? 'User' : 'Claude'}]: ${m.content.slice(0, 500)}`).join('\n\n')
|
|
814
|
+
: '(new conversation)';
|
|
815
|
+
const missionStr = sessionMission ? `Session mission: ${sessionMission}\n\n` : '';
|
|
816
|
+
const prompt = `${missionStr}Recent conversation context:
|
|
817
|
+
${contextStr}
|
|
818
|
+
|
|
819
|
+
The user just sent this message:
|
|
820
|
+
"${userMessage}"
|
|
821
|
+
|
|
822
|
+
In 5-10 words, what is the user asking Claude to do RIGHT NOW?
|
|
823
|
+
- Be specific and concrete (not "continuing work" or "helping with code")
|
|
824
|
+
- If the user said something vague like "do it" or "yes" or "go ahead", infer from context what "it" refers to
|
|
825
|
+
- Use action verbs: "Implementing...", "Fixing...", "Adding...", "Debugging..."
|
|
826
|
+
- If truly unclear, describe what can be inferred
|
|
827
|
+
|
|
828
|
+
Respond with ONLY the summary text, nothing else. No quotes, no explanation.`;
|
|
829
|
+
const response = await this.withRetry('summarizeCurrentWork', () => this.client.messages.create({
|
|
830
|
+
model: this.models.quickCheck, // Haiku - speed is critical
|
|
831
|
+
max_tokens: 50,
|
|
832
|
+
temperature: 0,
|
|
833
|
+
messages: [{ role: 'user', content: prompt }]
|
|
834
|
+
}));
|
|
835
|
+
const durationMs = Date.now() - startTime;
|
|
836
|
+
// Log cost
|
|
837
|
+
this.logCost(response, 'CURRENT_WORK', this.models.quickCheck, durationMs, sessionId);
|
|
838
|
+
// Strip trailing period - we want consistency with history items which never end with periods
|
|
839
|
+
const rawText = response.content[0]?.type === 'text' ? response.content[0].text.trim() : null;
|
|
840
|
+
const text = rawText?.replace(/\.+$/, '') || null;
|
|
841
|
+
if (!text) {
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
this.logger.info('Current work summarized', {
|
|
845
|
+
durationMs,
|
|
846
|
+
inputTokens: response.usage?.input_tokens,
|
|
847
|
+
outputTokens: response.usage?.output_tokens,
|
|
848
|
+
summary: text.slice(0, 50),
|
|
849
|
+
});
|
|
850
|
+
return { summary: text };
|
|
851
|
+
}
|
|
852
|
+
catch (error) {
|
|
853
|
+
this.logger.debug('Failed to summarize current work', {
|
|
854
|
+
error: error instanceof Error ? error.message : String(error),
|
|
855
|
+
});
|
|
856
|
+
return null;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Evaluate session metadata (purpose/theme/tags) based on user message and context.
|
|
861
|
+
* Called on user message to update session classification. This replaces the
|
|
862
|
+
* timer-based fast patch approach with explicit user-message triggering.
|
|
863
|
+
*/
|
|
864
|
+
async evaluateSessionMetadata(userMessage, recentContext, current, sessionId) {
|
|
865
|
+
if (!this.client) {
|
|
866
|
+
return null;
|
|
867
|
+
}
|
|
868
|
+
const startTime = Date.now();
|
|
869
|
+
try {
|
|
870
|
+
// Build context from recent conversation
|
|
871
|
+
const contextStr = recentContext.length > 0
|
|
872
|
+
? recentContext.map(m => `[${m.type === 'user' ? 'User' : 'Claude'}]: ${m.content.slice(0, 400)}`).join('\n\n')
|
|
873
|
+
: '(new conversation)';
|
|
874
|
+
const missionStr = current.mission || '(not set)';
|
|
875
|
+
const purposeStr = current.purpose || missionStr;
|
|
876
|
+
const currentTheme = current.theme || 'exploring';
|
|
877
|
+
const currentTags = current.tags || { workType: ['exploring'], collaboration: 'iterative', complexity: 'routine' };
|
|
878
|
+
const prompt = `You classify coding session activity based on what the user just asked for.
|
|
879
|
+
|
|
880
|
+
SESSION MISSION: ${missionStr}
|
|
881
|
+
CURRENT PURPOSE: ${purposeStr}
|
|
882
|
+
CURRENT THEME: ${currentTheme}
|
|
883
|
+
CURRENT TAGS: workType=${JSON.stringify(currentTags.workType)}, collaboration=${currentTags.collaboration}, complexity=${currentTags.complexity}
|
|
884
|
+
|
|
885
|
+
Recent conversation:
|
|
886
|
+
${contextStr}
|
|
887
|
+
|
|
888
|
+
User just sent: "${userMessage.slice(0, 500)}"
|
|
889
|
+
|
|
890
|
+
Based on what the user is now asking Claude to do, update:
|
|
891
|
+
|
|
892
|
+
1. **purpose** (30-50 chars): Session's current focus. Keep concise.
|
|
893
|
+
2. **theme** (single word): "debugging" | "building" | "exploring" | "firefighting" | "refactoring" | "designing" | "investigating" | "polishing"
|
|
894
|
+
3. **tags**:
|
|
895
|
+
- workType: ["implementing" | "debugging" | "refactoring" | "exploring" | "designing" | "investigating"]
|
|
896
|
+
- collaboration: "autonomous" | "iterative" | "guided"
|
|
897
|
+
- complexity: "routine" | "tricky" | "gnarly"
|
|
898
|
+
|
|
899
|
+
IMPORTANT: Only change values if this message clearly shifts the session's focus. Preserve current values for continuation messages like "yes", "do it", "continue".
|
|
900
|
+
|
|
901
|
+
Respond with ONLY valid JSON:
|
|
902
|
+
{"purpose": "...", "theme": "...", "tags": {"workType": [...], "collaboration": "...", "complexity": "..."}}`;
|
|
903
|
+
const response = await this.withRetry('evaluateSessionMetadata', () => this.client.messages.create({
|
|
904
|
+
model: this.models.quickCheck, // Haiku - speed is critical
|
|
905
|
+
max_tokens: 150,
|
|
906
|
+
temperature: 0,
|
|
907
|
+
messages: [{ role: 'user', content: prompt }]
|
|
908
|
+
}));
|
|
909
|
+
const durationMs = Date.now() - startTime;
|
|
910
|
+
// Log cost
|
|
911
|
+
this.logCost(response, 'METADATA_EVAL', this.models.quickCheck, durationMs, sessionId);
|
|
912
|
+
const text = response.content[0]?.type === 'text' ? response.content[0].text.trim() : null;
|
|
913
|
+
if (!text) {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
// Parse JSON response
|
|
917
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
918
|
+
if (!jsonMatch) {
|
|
919
|
+
this.logger.debug('No JSON found in metadata eval response', { text });
|
|
920
|
+
return null;
|
|
921
|
+
}
|
|
922
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
923
|
+
this.logger.info('Session metadata evaluated', {
|
|
924
|
+
durationMs,
|
|
925
|
+
inputTokens: response.usage?.input_tokens,
|
|
926
|
+
outputTokens: response.usage?.output_tokens,
|
|
927
|
+
theme: parsed.theme,
|
|
928
|
+
purpose: parsed.purpose?.slice(0, 30),
|
|
929
|
+
});
|
|
930
|
+
return {
|
|
931
|
+
purpose: parsed.purpose,
|
|
932
|
+
theme: parsed.theme,
|
|
933
|
+
tags: parsed.tags,
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
catch (error) {
|
|
937
|
+
this.logger.debug('Failed to evaluate session metadata', {
|
|
938
|
+
error: error instanceof Error ? error.message : String(error),
|
|
939
|
+
});
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Generate intelligent permission pattern suggestions using Haiku.
|
|
945
|
+
*
|
|
946
|
+
* Called immediately when a permission request arrives, this generates
|
|
947
|
+
* contextually-aware pattern options. By the time the user clicks
|
|
948
|
+
* "Similar", suggestions should already be ready.
|
|
949
|
+
*
|
|
950
|
+
* @param toolName - The tool being invoked (e.g., "Bash", "Write")
|
|
951
|
+
* @param toolInput - The tool input parameters (e.g., { command: "..." })
|
|
952
|
+
* @returns Array of suggested patterns from most specific to broadest
|
|
953
|
+
*/
|
|
954
|
+
async suggestPermissionPatterns(toolName, toolInput) {
|
|
955
|
+
if (!this.client) {
|
|
956
|
+
return [];
|
|
957
|
+
}
|
|
958
|
+
const startTime = Date.now();
|
|
959
|
+
try {
|
|
960
|
+
// Build context based on tool type
|
|
961
|
+
let contextStr;
|
|
962
|
+
let valueStr;
|
|
963
|
+
if (toolName === 'Bash') {
|
|
964
|
+
const command = toolInput.command || '';
|
|
965
|
+
valueStr = command;
|
|
966
|
+
contextStr = `Bash command: ${command}`;
|
|
967
|
+
}
|
|
968
|
+
else if (['Write', 'Read', 'Edit'].includes(toolName)) {
|
|
969
|
+
const filePath = toolInput.file_path || '';
|
|
970
|
+
valueStr = filePath;
|
|
971
|
+
contextStr = `${toolName} file: ${filePath}`;
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
// Unknown tool - return basic suggestions
|
|
975
|
+
return [
|
|
976
|
+
`${toolName}(${JSON.stringify(toolInput).slice(0, 50)})`,
|
|
977
|
+
toolName
|
|
978
|
+
];
|
|
979
|
+
}
|
|
980
|
+
const prompt = `You help users create permission patterns for a coding assistant tool.
|
|
981
|
+
|
|
982
|
+
The user is being asked to approve: ${contextStr}
|
|
983
|
+
|
|
984
|
+
Generate permission patterns from MOST SPECIFIC (safest) to LEAST SPECIFIC (broadest).
|
|
985
|
+
Each pattern should be meaningfully different and useful.
|
|
986
|
+
|
|
987
|
+
For Bash commands:
|
|
988
|
+
- Understand the INTENT behind the command, not just the literal text
|
|
989
|
+
- Extract the core tool and action (e.g., "git commit" from "git commit -m 'msg'")
|
|
990
|
+
- For piped commands like "fd -e tsx | head", focus on the primary command (fd)
|
|
991
|
+
- For chained commands like "npm install && npm run build", focus on the first command (npm install)
|
|
992
|
+
|
|
993
|
+
Pattern syntax:
|
|
994
|
+
- Bash(exact command) - matches only this exact command
|
|
995
|
+
- Bash(git commit *) - matches any git commit command
|
|
996
|
+
- Bash(git *) - matches any git command
|
|
997
|
+
- Bash - matches all bash commands
|
|
998
|
+
|
|
999
|
+
For file operations:
|
|
1000
|
+
- Write(/exact/path/file.tsx) - exact file only
|
|
1001
|
+
- Write(/path/to/dir/*.tsx) - any .tsx in that directory
|
|
1002
|
+
- Write(/path/**/*.tsx) - any .tsx recursively under path
|
|
1003
|
+
- Write(*.tsx) - any .tsx file anywhere
|
|
1004
|
+
- Write - all write operations
|
|
1005
|
+
|
|
1006
|
+
Generate 3-5 patterns, ordered from safest to broadest.
|
|
1007
|
+
IMPORTANT: Only return patterns that make semantic sense for this operation.
|
|
1008
|
+
|
|
1009
|
+
Respond with ONLY a JSON array of pattern strings:
|
|
1010
|
+
["pattern1", "pattern2", ...]`;
|
|
1011
|
+
const response = await this.withRetry('suggestPermissionPatterns', () => this.client.messages.create({
|
|
1012
|
+
model: this.models.quickCheck, // Haiku - speed is critical
|
|
1013
|
+
max_tokens: 200,
|
|
1014
|
+
temperature: 0,
|
|
1015
|
+
messages: [{ role: 'user', content: prompt }]
|
|
1016
|
+
}));
|
|
1017
|
+
const durationMs = Date.now() - startTime;
|
|
1018
|
+
// Log for observability (no cost tracking category for this yet)
|
|
1019
|
+
this.logger.info('Permission patterns suggested', {
|
|
1020
|
+
durationMs,
|
|
1021
|
+
inputTokens: response.usage?.input_tokens,
|
|
1022
|
+
outputTokens: response.usage?.output_tokens,
|
|
1023
|
+
toolName,
|
|
1024
|
+
valuePreview: valueStr.slice(0, 50),
|
|
1025
|
+
});
|
|
1026
|
+
const text = response.content[0]?.type === 'text' ? response.content[0].text.trim() : null;
|
|
1027
|
+
if (!text) {
|
|
1028
|
+
return [];
|
|
1029
|
+
}
|
|
1030
|
+
// Parse JSON array response
|
|
1031
|
+
let jsonText = text;
|
|
1032
|
+
// Strip markdown code blocks if present
|
|
1033
|
+
if (jsonText.startsWith('```')) {
|
|
1034
|
+
jsonText = jsonText.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, '').trim();
|
|
1035
|
+
}
|
|
1036
|
+
// Extract JSON array
|
|
1037
|
+
const jsonMatch = jsonText.match(/\[[\s\S]*\]/);
|
|
1038
|
+
if (!jsonMatch) {
|
|
1039
|
+
this.logger.debug('No JSON array found in pattern response', { text });
|
|
1040
|
+
return [];
|
|
1041
|
+
}
|
|
1042
|
+
const patterns = JSON.parse(jsonMatch[0]);
|
|
1043
|
+
// Validate patterns - ensure they're strings and not empty
|
|
1044
|
+
const validPatterns = patterns.filter(p => typeof p === 'string' && p.length > 0);
|
|
1045
|
+
// Always ensure the broadest pattern is included
|
|
1046
|
+
if (validPatterns.length > 0 && !validPatterns.includes(toolName)) {
|
|
1047
|
+
validPatterns.push(toolName);
|
|
1048
|
+
}
|
|
1049
|
+
return validPatterns;
|
|
1050
|
+
}
|
|
1051
|
+
catch (error) {
|
|
1052
|
+
this.logger.debug('Failed to suggest permission patterns', {
|
|
1053
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1054
|
+
toolName,
|
|
1055
|
+
});
|
|
1056
|
+
return [];
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
// Export singleton instance
|
|
1061
|
+
export const anthropicService = new AnthropicService();
|
|
1062
|
+
//# sourceMappingURL=anthropic-service.js.map
|