attocode 0.1.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/CHANGELOG.md +48 -0
- package/LICENSE +21 -0
- package/README.md +164 -0
- package/dist/src/adapters.d.ts +83 -0
- package/dist/src/adapters.d.ts.map +1 -0
- package/dist/src/adapters.js +221 -0
- package/dist/src/adapters.js.map +1 -0
- package/dist/src/agent-tools/index.d.ts +7 -0
- package/dist/src/agent-tools/index.d.ts.map +1 -0
- package/dist/src/agent-tools/index.js +8 -0
- package/dist/src/agent-tools/index.js.map +1 -0
- package/dist/src/agent-tools/lsp-file-tools.d.ts +33 -0
- package/dist/src/agent-tools/lsp-file-tools.d.ts.map +1 -0
- package/dist/src/agent-tools/lsp-file-tools.js +200 -0
- package/dist/src/agent-tools/lsp-file-tools.js.map +1 -0
- package/dist/src/agent.d.ts +667 -0
- package/dist/src/agent.d.ts.map +1 -0
- package/dist/src/agent.js +2824 -0
- package/dist/src/agent.js.map +1 -0
- package/dist/src/cli.d.ts +36 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +176 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/handler.d.ts +22 -0
- package/dist/src/commands/handler.d.ts.map +1 -0
- package/dist/src/commands/handler.js +1320 -0
- package/dist/src/commands/handler.js.map +1 -0
- package/dist/src/commands/init.d.ts +7 -0
- package/dist/src/commands/init.d.ts.map +1 -0
- package/dist/src/commands/init.js +153 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/types.d.ts +70 -0
- package/dist/src/commands/types.d.ts.map +1 -0
- package/dist/src/commands/types.js +8 -0
- package/dist/src/commands/types.js.map +1 -0
- package/dist/src/config.d.ts +22 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +25 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/core/index.d.ts +32 -0
- package/dist/src/core/index.d.ts.map +1 -0
- package/dist/src/core/index.js +35 -0
- package/dist/src/core/index.js.map +1 -0
- package/dist/src/core/process-handlers.d.ts +43 -0
- package/dist/src/core/process-handlers.d.ts.map +1 -0
- package/dist/src/core/process-handlers.js +117 -0
- package/dist/src/core/process-handlers.js.map +1 -0
- package/dist/src/core/protocol/bridge.d.ts +117 -0
- package/dist/src/core/protocol/bridge.d.ts.map +1 -0
- package/dist/src/core/protocol/bridge.js +149 -0
- package/dist/src/core/protocol/bridge.js.map +1 -0
- package/dist/src/core/protocol/index.d.ts +8 -0
- package/dist/src/core/protocol/index.d.ts.map +1 -0
- package/dist/src/core/protocol/index.js +8 -0
- package/dist/src/core/protocol/index.js.map +1 -0
- package/dist/src/core/protocol/types.d.ts +539 -0
- package/dist/src/core/protocol/types.d.ts.map +1 -0
- package/dist/src/core/protocol/types.js +149 -0
- package/dist/src/core/protocol/types.js.map +1 -0
- package/dist/src/core/queues/atomic-counter.d.ts +36 -0
- package/dist/src/core/queues/atomic-counter.d.ts.map +1 -0
- package/dist/src/core/queues/atomic-counter.js +46 -0
- package/dist/src/core/queues/atomic-counter.js.map +1 -0
- package/dist/src/core/queues/event-queue.d.ts +126 -0
- package/dist/src/core/queues/event-queue.d.ts.map +1 -0
- package/dist/src/core/queues/event-queue.js +208 -0
- package/dist/src/core/queues/event-queue.js.map +1 -0
- package/dist/src/core/queues/index.d.ts +12 -0
- package/dist/src/core/queues/index.d.ts.map +1 -0
- package/dist/src/core/queues/index.js +15 -0
- package/dist/src/core/queues/index.js.map +1 -0
- package/dist/src/core/queues/submission-queue.d.ts +116 -0
- package/dist/src/core/queues/submission-queue.d.ts.map +1 -0
- package/dist/src/core/queues/submission-queue.js +236 -0
- package/dist/src/core/queues/submission-queue.js.map +1 -0
- package/dist/src/costs/index.d.ts +22 -0
- package/dist/src/costs/index.d.ts.map +1 -0
- package/dist/src/costs/index.js +22 -0
- package/dist/src/costs/index.js.map +1 -0
- package/dist/src/costs/model-registry.d.ts +80 -0
- package/dist/src/costs/model-registry.d.ts.map +1 -0
- package/dist/src/costs/model-registry.js +237 -0
- package/dist/src/costs/model-registry.js.map +1 -0
- package/dist/src/costs/types.d.ts +50 -0
- package/dist/src/costs/types.d.ts.map +1 -0
- package/dist/src/costs/types.js +2 -0
- package/dist/src/costs/types.js.map +1 -0
- package/dist/src/defaults.d.ts +114 -0
- package/dist/src/defaults.d.ts.map +1 -0
- package/dist/src/defaults.js +457 -0
- package/dist/src/defaults.js.map +1 -0
- package/dist/src/first-run.d.ts +35 -0
- package/dist/src/first-run.d.ts.map +1 -0
- package/dist/src/first-run.js +94 -0
- package/dist/src/first-run.js.map +1 -0
- package/dist/src/hello.d.ts +2 -0
- package/dist/src/hello.d.ts.map +1 -0
- package/dist/src/hello.js +4 -0
- package/dist/src/hello.js.map +1 -0
- package/dist/src/integrations/agent-registry.d.ts +160 -0
- package/dist/src/integrations/agent-registry.d.ts.map +1 -0
- package/dist/src/integrations/agent-registry.js +446 -0
- package/dist/src/integrations/agent-registry.js.map +1 -0
- package/dist/src/integrations/auto-compaction.d.ts +177 -0
- package/dist/src/integrations/auto-compaction.d.ts.map +1 -0
- package/dist/src/integrations/auto-compaction.js +428 -0
- package/dist/src/integrations/auto-compaction.js.map +1 -0
- package/dist/src/integrations/cancellation.d.ts +162 -0
- package/dist/src/integrations/cancellation.d.ts.map +1 -0
- package/dist/src/integrations/cancellation.js +339 -0
- package/dist/src/integrations/cancellation.js.map +1 -0
- package/dist/src/integrations/codebase-context.d.ts +319 -0
- package/dist/src/integrations/codebase-context.d.ts.map +1 -0
- package/dist/src/integrations/codebase-context.js +816 -0
- package/dist/src/integrations/codebase-context.js.map +1 -0
- package/dist/src/integrations/compaction.d.ts +192 -0
- package/dist/src/integrations/compaction.d.ts.map +1 -0
- package/dist/src/integrations/compaction.js +376 -0
- package/dist/src/integrations/compaction.js.map +1 -0
- package/dist/src/integrations/context-engineering.d.ts +246 -0
- package/dist/src/integrations/context-engineering.d.ts.map +1 -0
- package/dist/src/integrations/context-engineering.js +394 -0
- package/dist/src/integrations/context-engineering.js.map +1 -0
- package/dist/src/integrations/diff-utils.d.ts +105 -0
- package/dist/src/integrations/diff-utils.d.ts.map +1 -0
- package/dist/src/integrations/diff-utils.js +497 -0
- package/dist/src/integrations/diff-utils.js.map +1 -0
- package/dist/src/integrations/economics.d.ts +192 -0
- package/dist/src/integrations/economics.d.ts.map +1 -0
- package/dist/src/integrations/economics.js +431 -0
- package/dist/src/integrations/economics.js.map +1 -0
- package/dist/src/integrations/execution-policy.d.ts +189 -0
- package/dist/src/integrations/execution-policy.d.ts.map +1 -0
- package/dist/src/integrations/execution-policy.js +352 -0
- package/dist/src/integrations/execution-policy.js.map +1 -0
- package/dist/src/integrations/file-change-tracker.d.ts +161 -0
- package/dist/src/integrations/file-change-tracker.d.ts.map +1 -0
- package/dist/src/integrations/file-change-tracker.js +520 -0
- package/dist/src/integrations/file-change-tracker.js.map +1 -0
- package/dist/src/integrations/hierarchical-config.d.ts +212 -0
- package/dist/src/integrations/hierarchical-config.d.ts.map +1 -0
- package/dist/src/integrations/hierarchical-config.js +484 -0
- package/dist/src/integrations/hierarchical-config.js.map +1 -0
- package/dist/src/integrations/hooks.d.ts +114 -0
- package/dist/src/integrations/hooks.d.ts.map +1 -0
- package/dist/src/integrations/hooks.js +326 -0
- package/dist/src/integrations/hooks.js.map +1 -0
- package/dist/src/integrations/ignore.d.ts +143 -0
- package/dist/src/integrations/ignore.d.ts.map +1 -0
- package/dist/src/integrations/ignore.js +417 -0
- package/dist/src/integrations/ignore.js.map +1 -0
- package/dist/src/integrations/image-renderer.d.ts +119 -0
- package/dist/src/integrations/image-renderer.d.ts.map +1 -0
- package/dist/src/integrations/image-renderer.js +306 -0
- package/dist/src/integrations/image-renderer.js.map +1 -0
- package/dist/src/integrations/index.d.ts +42 -0
- package/dist/src/integrations/index.d.ts.map +1 -0
- package/dist/src/integrations/index.js +73 -0
- package/dist/src/integrations/index.js.map +1 -0
- package/dist/src/integrations/lsp.d.ts +196 -0
- package/dist/src/integrations/lsp.d.ts.map +1 -0
- package/dist/src/integrations/lsp.js +582 -0
- package/dist/src/integrations/lsp.js.map +1 -0
- package/dist/src/integrations/mcp-client.d.ts +270 -0
- package/dist/src/integrations/mcp-client.d.ts.map +1 -0
- package/dist/src/integrations/mcp-client.js +698 -0
- package/dist/src/integrations/mcp-client.js.map +1 -0
- package/dist/src/integrations/mcp-tool-search.d.ts +77 -0
- package/dist/src/integrations/mcp-tool-search.d.ts.map +1 -0
- package/dist/src/integrations/mcp-tool-search.js +220 -0
- package/dist/src/integrations/mcp-tool-search.js.map +1 -0
- package/dist/src/integrations/memory.d.ts +108 -0
- package/dist/src/integrations/memory.d.ts.map +1 -0
- package/dist/src/integrations/memory.js +288 -0
- package/dist/src/integrations/memory.js.map +1 -0
- package/dist/src/integrations/multi-agent.d.ts +150 -0
- package/dist/src/integrations/multi-agent.d.ts.map +1 -0
- package/dist/src/integrations/multi-agent.js +306 -0
- package/dist/src/integrations/multi-agent.js.map +1 -0
- package/dist/src/integrations/observability.d.ts +162 -0
- package/dist/src/integrations/observability.d.ts.map +1 -0
- package/dist/src/integrations/observability.js +406 -0
- package/dist/src/integrations/observability.js.map +1 -0
- package/dist/src/integrations/openrouter-pricing.d.ts +42 -0
- package/dist/src/integrations/openrouter-pricing.d.ts.map +1 -0
- package/dist/src/integrations/openrouter-pricing.js +124 -0
- package/dist/src/integrations/openrouter-pricing.js.map +1 -0
- package/dist/src/integrations/pending-plan.d.ts +171 -0
- package/dist/src/integrations/pending-plan.d.ts.map +1 -0
- package/dist/src/integrations/pending-plan.js +244 -0
- package/dist/src/integrations/pending-plan.js.map +1 -0
- package/dist/src/integrations/persistence.d.ts +48 -0
- package/dist/src/integrations/persistence.d.ts.map +1 -0
- package/dist/src/integrations/persistence.js +196 -0
- package/dist/src/integrations/persistence.js.map +1 -0
- package/dist/src/integrations/planning.d.ts +96 -0
- package/dist/src/integrations/planning.d.ts.map +1 -0
- package/dist/src/integrations/planning.js +338 -0
- package/dist/src/integrations/planning.js.map +1 -0
- package/dist/src/integrations/pty-shell.d.ts +169 -0
- package/dist/src/integrations/pty-shell.d.ts.map +1 -0
- package/dist/src/integrations/pty-shell.js +367 -0
- package/dist/src/integrations/pty-shell.js.map +1 -0
- package/dist/src/integrations/react.d.ts +139 -0
- package/dist/src/integrations/react.d.ts.map +1 -0
- package/dist/src/integrations/react.js +273 -0
- package/dist/src/integrations/react.js.map +1 -0
- package/dist/src/integrations/resources.d.ts +177 -0
- package/dist/src/integrations/resources.d.ts.map +1 -0
- package/dist/src/integrations/resources.js +311 -0
- package/dist/src/integrations/resources.js.map +1 -0
- package/dist/src/integrations/result-synthesizer.d.ts +389 -0
- package/dist/src/integrations/result-synthesizer.d.ts.map +1 -0
- package/dist/src/integrations/result-synthesizer.js +951 -0
- package/dist/src/integrations/result-synthesizer.js.map +1 -0
- package/dist/src/integrations/routing.d.ts +117 -0
- package/dist/src/integrations/routing.d.ts.map +1 -0
- package/dist/src/integrations/routing.js +347 -0
- package/dist/src/integrations/routing.js.map +1 -0
- package/dist/src/integrations/rules.d.ts +131 -0
- package/dist/src/integrations/rules.d.ts.map +1 -0
- package/dist/src/integrations/rules.js +284 -0
- package/dist/src/integrations/rules.js.map +1 -0
- package/dist/src/integrations/safety.d.ts +142 -0
- package/dist/src/integrations/safety.d.ts.map +1 -0
- package/dist/src/integrations/safety.js +342 -0
- package/dist/src/integrations/safety.js.map +1 -0
- package/dist/src/integrations/sandbox/basic.d.ts +74 -0
- package/dist/src/integrations/sandbox/basic.d.ts.map +1 -0
- package/dist/src/integrations/sandbox/basic.js +310 -0
- package/dist/src/integrations/sandbox/basic.js.map +1 -0
- package/dist/src/integrations/sandbox/docker.d.ts +94 -0
- package/dist/src/integrations/sandbox/docker.d.ts.map +1 -0
- package/dist/src/integrations/sandbox/docker.js +293 -0
- package/dist/src/integrations/sandbox/docker.js.map +1 -0
- package/dist/src/integrations/sandbox/index.d.ts +182 -0
- package/dist/src/integrations/sandbox/index.d.ts.map +1 -0
- package/dist/src/integrations/sandbox/index.js +382 -0
- package/dist/src/integrations/sandbox/index.js.map +1 -0
- package/dist/src/integrations/sandbox/landlock.d.ts +59 -0
- package/dist/src/integrations/sandbox/landlock.d.ts.map +1 -0
- package/dist/src/integrations/sandbox/landlock.js +326 -0
- package/dist/src/integrations/sandbox/landlock.js.map +1 -0
- package/dist/src/integrations/sandbox/seatbelt.d.ts +68 -0
- package/dist/src/integrations/sandbox/seatbelt.d.ts.map +1 -0
- package/dist/src/integrations/sandbox/seatbelt.js +298 -0
- package/dist/src/integrations/sandbox/seatbelt.js.map +1 -0
- package/dist/src/integrations/semantic-cache.d.ts +178 -0
- package/dist/src/integrations/semantic-cache.d.ts.map +1 -0
- package/dist/src/integrations/semantic-cache.js +372 -0
- package/dist/src/integrations/semantic-cache.js.map +1 -0
- package/dist/src/integrations/session-store.d.ts +183 -0
- package/dist/src/integrations/session-store.d.ts.map +1 -0
- package/dist/src/integrations/session-store.js +345 -0
- package/dist/src/integrations/session-store.js.map +1 -0
- package/dist/src/integrations/shared-blackboard.d.ts +403 -0
- package/dist/src/integrations/shared-blackboard.d.ts.map +1 -0
- package/dist/src/integrations/shared-blackboard.js +710 -0
- package/dist/src/integrations/shared-blackboard.js.map +1 -0
- package/dist/src/integrations/skills.d.ts +171 -0
- package/dist/src/integrations/skills.d.ts.map +1 -0
- package/dist/src/integrations/skills.js +403 -0
- package/dist/src/integrations/skills.js.map +1 -0
- package/dist/src/integrations/smart-decomposer.d.ts +322 -0
- package/dist/src/integrations/smart-decomposer.d.ts.map +1 -0
- package/dist/src/integrations/smart-decomposer.js +856 -0
- package/dist/src/integrations/smart-decomposer.js.map +1 -0
- package/dist/src/integrations/sourcegraph.d.ts +169 -0
- package/dist/src/integrations/sourcegraph.d.ts.map +1 -0
- package/dist/src/integrations/sourcegraph.js +379 -0
- package/dist/src/integrations/sourcegraph.js.map +1 -0
- package/dist/src/integrations/sqlite-store.d.ts +518 -0
- package/dist/src/integrations/sqlite-store.d.ts.map +1 -0
- package/dist/src/integrations/sqlite-store.js +1423 -0
- package/dist/src/integrations/sqlite-store.js.map +1 -0
- package/dist/src/integrations/streaming.d.ts +102 -0
- package/dist/src/integrations/streaming.d.ts.map +1 -0
- package/dist/src/integrations/streaming.js +362 -0
- package/dist/src/integrations/streaming.js.map +1 -0
- package/dist/src/integrations/thread-manager.d.ts +199 -0
- package/dist/src/integrations/thread-manager.d.ts.map +1 -0
- package/dist/src/integrations/thread-manager.js +357 -0
- package/dist/src/integrations/thread-manager.js.map +1 -0
- package/dist/src/main.d.ts +26 -0
- package/dist/src/main.d.ts.map +1 -0
- package/dist/src/main.js +170 -0
- package/dist/src/main.js.map +1 -0
- package/dist/src/modes/index.d.ts +10 -0
- package/dist/src/modes/index.d.ts.map +1 -0
- package/dist/src/modes/index.js +10 -0
- package/dist/src/modes/index.js.map +1 -0
- package/dist/src/modes/repl.d.ts +19 -0
- package/dist/src/modes/repl.d.ts.map +1 -0
- package/dist/src/modes/repl.js +393 -0
- package/dist/src/modes/repl.js.map +1 -0
- package/dist/src/modes/tui.d.ts +29 -0
- package/dist/src/modes/tui.d.ts.map +1 -0
- package/dist/src/modes/tui.js +272 -0
- package/dist/src/modes/tui.js.map +1 -0
- package/dist/src/modes.d.ts +179 -0
- package/dist/src/modes.d.ts.map +1 -0
- package/dist/src/modes.js +385 -0
- package/dist/src/modes.js.map +1 -0
- package/dist/src/observability/tracer.d.ts +111 -0
- package/dist/src/observability/tracer.d.ts.map +1 -0
- package/dist/src/observability/tracer.js +300 -0
- package/dist/src/observability/tracer.js.map +1 -0
- package/dist/src/observability/types.d.ts +271 -0
- package/dist/src/observability/types.d.ts.map +1 -0
- package/dist/src/observability/types.js +24 -0
- package/dist/src/observability/types.js.map +1 -0
- package/dist/src/paths.d.ts +101 -0
- package/dist/src/paths.d.ts.map +1 -0
- package/dist/src/paths.js +148 -0
- package/dist/src/paths.js.map +1 -0
- package/dist/src/persistence/index.d.ts +38 -0
- package/dist/src/persistence/index.d.ts.map +1 -0
- package/dist/src/persistence/index.js +48 -0
- package/dist/src/persistence/index.js.map +1 -0
- package/dist/src/persistence/migrator.d.ts +135 -0
- package/dist/src/persistence/migrator.d.ts.map +1 -0
- package/dist/src/persistence/migrator.js +303 -0
- package/dist/src/persistence/migrator.js.map +1 -0
- package/dist/src/persistence/schema.d.ts +101 -0
- package/dist/src/persistence/schema.d.ts.map +1 -0
- package/dist/src/persistence/schema.js +395 -0
- package/dist/src/persistence/schema.js.map +1 -0
- package/dist/src/providers/adapters/anthropic.d.ts +20 -0
- package/dist/src/providers/adapters/anthropic.d.ts.map +1 -0
- package/dist/src/providers/adapters/anthropic.js +124 -0
- package/dist/src/providers/adapters/anthropic.js.map +1 -0
- package/dist/src/providers/adapters/mock.d.ts +25 -0
- package/dist/src/providers/adapters/mock.d.ts.map +1 -0
- package/dist/src/providers/adapters/mock.js +133 -0
- package/dist/src/providers/adapters/mock.js.map +1 -0
- package/dist/src/providers/adapters/openai.d.ts +21 -0
- package/dist/src/providers/adapters/openai.d.ts.map +1 -0
- package/dist/src/providers/adapters/openai.js +126 -0
- package/dist/src/providers/adapters/openai.js.map +1 -0
- package/dist/src/providers/adapters/openrouter.d.ts +49 -0
- package/dist/src/providers/adapters/openrouter.d.ts.map +1 -0
- package/dist/src/providers/adapters/openrouter.js +363 -0
- package/dist/src/providers/adapters/openrouter.js.map +1 -0
- package/dist/src/providers/provider.d.ts +54 -0
- package/dist/src/providers/provider.d.ts.map +1 -0
- package/dist/src/providers/provider.js +111 -0
- package/dist/src/providers/provider.js.map +1 -0
- package/dist/src/providers/resilient-fetch.d.ts +99 -0
- package/dist/src/providers/resilient-fetch.d.ts.map +1 -0
- package/dist/src/providers/resilient-fetch.js +208 -0
- package/dist/src/providers/resilient-fetch.js.map +1 -0
- package/dist/src/providers/types.d.ts +227 -0
- package/dist/src/providers/types.d.ts.map +1 -0
- package/dist/src/providers/types.js +24 -0
- package/dist/src/providers/types.js.map +1 -0
- package/dist/src/session-picker.d.ts +28 -0
- package/dist/src/session-picker.d.ts.map +1 -0
- package/dist/src/session-picker.js +256 -0
- package/dist/src/session-picker.js.map +1 -0
- package/dist/src/test-sqlite.d.ts +2 -0
- package/dist/src/test-sqlite.d.ts.map +1 -0
- package/dist/src/test-sqlite.js +114 -0
- package/dist/src/test-sqlite.js.map +1 -0
- package/dist/src/tools/agent.d.ts +44 -0
- package/dist/src/tools/agent.d.ts.map +1 -0
- package/dist/src/tools/agent.js +110 -0
- package/dist/src/tools/agent.js.map +1 -0
- package/dist/src/tools/bash.d.ts +52 -0
- package/dist/src/tools/bash.d.ts.map +1 -0
- package/dist/src/tools/bash.js +141 -0
- package/dist/src/tools/bash.js.map +1 -0
- package/dist/src/tools/file.d.ts +47 -0
- package/dist/src/tools/file.d.ts.map +1 -0
- package/dist/src/tools/file.js +263 -0
- package/dist/src/tools/file.js.map +1 -0
- package/dist/src/tools/permission.d.ts +43 -0
- package/dist/src/tools/permission.d.ts.map +1 -0
- package/dist/src/tools/permission.js +216 -0
- package/dist/src/tools/permission.js.map +1 -0
- package/dist/src/tools/registry.d.ts +63 -0
- package/dist/src/tools/registry.d.ts.map +1 -0
- package/dist/src/tools/registry.js +250 -0
- package/dist/src/tools/registry.js.map +1 -0
- package/dist/src/tools/standard.d.ts +57 -0
- package/dist/src/tools/standard.d.ts.map +1 -0
- package/dist/src/tools/standard.js +113 -0
- package/dist/src/tools/standard.js.map +1 -0
- package/dist/src/tools/types.d.ts +146 -0
- package/dist/src/tools/types.d.ts.map +1 -0
- package/dist/src/tools/types.js +28 -0
- package/dist/src/tools/types.js.map +1 -0
- package/dist/src/tools/undo.d.ts +71 -0
- package/dist/src/tools/undo.d.ts.map +1 -0
- package/dist/src/tools/undo.js +123 -0
- package/dist/src/tools/undo.js.map +1 -0
- package/dist/src/tracing/cache-boundary-tracker.d.ts +189 -0
- package/dist/src/tracing/cache-boundary-tracker.d.ts.map +1 -0
- package/dist/src/tracing/cache-boundary-tracker.js +411 -0
- package/dist/src/tracing/cache-boundary-tracker.js.map +1 -0
- package/dist/src/tracing/trace-collector.d.ts +274 -0
- package/dist/src/tracing/trace-collector.d.ts.map +1 -0
- package/dist/src/tracing/trace-collector.js +727 -0
- package/dist/src/tracing/trace-collector.js.map +1 -0
- package/dist/src/tracing/types.d.ts +657 -0
- package/dist/src/tracing/types.d.ts.map +1 -0
- package/dist/src/tracing/types.js +39 -0
- package/dist/src/tracing/types.js.map +1 -0
- package/dist/src/tricks/failure-evidence.d.ts +268 -0
- package/dist/src/tricks/failure-evidence.d.ts.map +1 -0
- package/dist/src/tricks/failure-evidence.js +544 -0
- package/dist/src/tricks/failure-evidence.js.map +1 -0
- package/dist/src/tricks/json-utils.d.ts +77 -0
- package/dist/src/tricks/json-utils.d.ts.map +1 -0
- package/dist/src/tricks/json-utils.js +247 -0
- package/dist/src/tricks/json-utils.js.map +1 -0
- package/dist/src/tricks/kv-cache-context.d.ts +227 -0
- package/dist/src/tricks/kv-cache-context.d.ts.map +1 -0
- package/dist/src/tricks/kv-cache-context.js +377 -0
- package/dist/src/tricks/kv-cache-context.js.map +1 -0
- package/dist/src/tricks/recitation.d.ts +208 -0
- package/dist/src/tricks/recitation.d.ts.map +1 -0
- package/dist/src/tricks/recitation.js +374 -0
- package/dist/src/tricks/recitation.js.map +1 -0
- package/dist/src/tricks/reversible-compaction.d.ts +251 -0
- package/dist/src/tricks/reversible-compaction.d.ts.map +1 -0
- package/dist/src/tricks/reversible-compaction.js +555 -0
- package/dist/src/tricks/reversible-compaction.js.map +1 -0
- package/dist/src/tricks/serialization-diversity.d.ts +197 -0
- package/dist/src/tricks/serialization-diversity.d.ts.map +1 -0
- package/dist/src/tricks/serialization-diversity.js +460 -0
- package/dist/src/tricks/serialization-diversity.js.map +1 -0
- package/dist/src/tui/app.d.ts +42 -0
- package/dist/src/tui/app.d.ts.map +1 -0
- package/dist/src/tui/app.js +1076 -0
- package/dist/src/tui/app.js.map +1 -0
- package/dist/src/tui/components/ApprovalDialog.d.ts +28 -0
- package/dist/src/tui/components/ApprovalDialog.d.ts.map +1 -0
- package/dist/src/tui/components/ApprovalDialog.js +59 -0
- package/dist/src/tui/components/ApprovalDialog.js.map +1 -0
- package/dist/src/tui/components/InputArea.d.ts +35 -0
- package/dist/src/tui/components/InputArea.d.ts.map +1 -0
- package/dist/src/tui/components/InputArea.js +144 -0
- package/dist/src/tui/components/InputArea.js.map +1 -0
- package/dist/src/tui/components/MessageItem.d.ts +28 -0
- package/dist/src/tui/components/MessageItem.d.ts.map +1 -0
- package/dist/src/tui/components/MessageItem.js +27 -0
- package/dist/src/tui/components/MessageItem.js.map +1 -0
- package/dist/src/tui/components/ScrollableBox.d.ts +41 -0
- package/dist/src/tui/components/ScrollableBox.d.ts.map +1 -0
- package/dist/src/tui/components/ScrollableBox.js +101 -0
- package/dist/src/tui/components/ScrollableBox.js.map +1 -0
- package/dist/src/tui/components/ToolCallItem.d.ts +33 -0
- package/dist/src/tui/components/ToolCallItem.d.ts.map +1 -0
- package/dist/src/tui/components/ToolCallItem.js +91 -0
- package/dist/src/tui/components/ToolCallItem.js.map +1 -0
- package/dist/src/tui/components/index.d.ts +13 -0
- package/dist/src/tui/components/index.d.ts.map +1 -0
- package/dist/src/tui/components/index.js +15 -0
- package/dist/src/tui/components/index.js.map +1 -0
- package/dist/src/tui/event-display.d.ts +19 -0
- package/dist/src/tui/event-display.d.ts.map +1 -0
- package/dist/src/tui/event-display.js +178 -0
- package/dist/src/tui/event-display.js.map +1 -0
- package/dist/src/tui/index.d.ts +105 -0
- package/dist/src/tui/index.d.ts.map +1 -0
- package/dist/src/tui/index.js +214 -0
- package/dist/src/tui/index.js.map +1 -0
- package/dist/src/tui/input/CommandPalette.d.ts +55 -0
- package/dist/src/tui/input/CommandPalette.d.ts.map +1 -0
- package/dist/src/tui/input/CommandPalette.js +135 -0
- package/dist/src/tui/input/CommandPalette.js.map +1 -0
- package/dist/src/tui/input/index.d.ts +7 -0
- package/dist/src/tui/input/index.d.ts.map +1 -0
- package/dist/src/tui/input/index.js +7 -0
- package/dist/src/tui/input/index.js.map +1 -0
- package/dist/src/tui/theme/index.d.ts +45 -0
- package/dist/src/tui/theme/index.d.ts.map +1 -0
- package/dist/src/tui/theme/index.js +215 -0
- package/dist/src/tui/theme/index.js.map +1 -0
- package/dist/src/tui/types.d.ts +214 -0
- package/dist/src/tui/types.d.ts.map +1 -0
- package/dist/src/tui/types.js +27 -0
- package/dist/src/tui/types.js.map +1 -0
- package/dist/src/types.d.ts +905 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +9 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,1423 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Session Store
|
|
3
|
+
*
|
|
4
|
+
* Alternative backend for session persistence using SQLite.
|
|
5
|
+
* Provides better query performance and ACID guarantees.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const store = await createSQLiteStore({ dbPath: '.agent/sessions.db' });
|
|
10
|
+
* await store.createSession('My Session');
|
|
11
|
+
* await store.appendMessage({ role: 'user', content: 'Hello' });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
import Database from 'better-sqlite3';
|
|
15
|
+
import { join, dirname } from 'node:path';
|
|
16
|
+
import { mkdir } from 'node:fs/promises';
|
|
17
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
18
|
+
import { applyMigrations, getMigrationStatus, detectFeatures, } from '../persistence/schema.js';
|
|
19
|
+
// =============================================================================
|
|
20
|
+
// SQLITE STORE
|
|
21
|
+
// =============================================================================
|
|
22
|
+
/**
|
|
23
|
+
* SQLite-backed session store.
|
|
24
|
+
*/
|
|
25
|
+
export class SQLiteStore {
|
|
26
|
+
db;
|
|
27
|
+
config;
|
|
28
|
+
currentSessionId = null;
|
|
29
|
+
listeners = [];
|
|
30
|
+
/** Available schema features (detected after migration) */
|
|
31
|
+
features = {
|
|
32
|
+
core: false,
|
|
33
|
+
costs: false,
|
|
34
|
+
hierarchy: false,
|
|
35
|
+
compaction: false,
|
|
36
|
+
fileChanges: false,
|
|
37
|
+
goals: false,
|
|
38
|
+
workerResults: false,
|
|
39
|
+
pendingPlans: false,
|
|
40
|
+
};
|
|
41
|
+
// Prepared statements for performance
|
|
42
|
+
// Goal-related statements are optional (only prepared if feature is available)
|
|
43
|
+
stmts;
|
|
44
|
+
constructor(config = {}) {
|
|
45
|
+
this.config = {
|
|
46
|
+
baseDir: config.baseDir || '.agent/sessions',
|
|
47
|
+
dbPath: config.dbPath || join(config.baseDir || '.agent/sessions', 'sessions.db'),
|
|
48
|
+
autoSave: config.autoSave ?? true,
|
|
49
|
+
maxSessions: config.maxSessions || 50,
|
|
50
|
+
walMode: config.walMode ?? true,
|
|
51
|
+
};
|
|
52
|
+
// Initialize database
|
|
53
|
+
this.db = new Database(this.config.dbPath);
|
|
54
|
+
// Enable WAL mode for better concurrent access
|
|
55
|
+
if (this.config.walMode) {
|
|
56
|
+
this.db.pragma('journal_mode = WAL');
|
|
57
|
+
}
|
|
58
|
+
// Enable foreign keys
|
|
59
|
+
this.db.pragma('foreign_keys = ON');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Initialize the store (create tables, prepare statements).
|
|
63
|
+
*/
|
|
64
|
+
async initialize() {
|
|
65
|
+
// Ensure directory exists
|
|
66
|
+
const dbDir = dirname(this.config.dbPath);
|
|
67
|
+
if (!existsSync(dbDir)) {
|
|
68
|
+
await mkdir(dbDir, { recursive: true });
|
|
69
|
+
}
|
|
70
|
+
// Apply embedded migrations (no file I/O needed)
|
|
71
|
+
const isDebug = process.env.DEBUG || process.argv.includes('--debug');
|
|
72
|
+
if (isDebug) {
|
|
73
|
+
const status = getMigrationStatus(this.db);
|
|
74
|
+
process.stderr.write(`[DEBUG] [SQLite] DB version: ${status.currentVersion}, latest: ${status.latestVersion}, pending: ${status.pendingCount}\n`);
|
|
75
|
+
}
|
|
76
|
+
const result = applyMigrations(this.db);
|
|
77
|
+
if (isDebug && result.applied > 0) {
|
|
78
|
+
process.stderr.write(`[DEBUG] [SQLite] Applied ${result.applied} migrations: ${result.appliedMigrations.join(', ')}\n`);
|
|
79
|
+
}
|
|
80
|
+
// Detect available features after migrations
|
|
81
|
+
this.features = detectFeatures(this.db);
|
|
82
|
+
if (isDebug) {
|
|
83
|
+
const enabled = Object.entries(this.features)
|
|
84
|
+
.filter(([, v]) => v)
|
|
85
|
+
.map(([k]) => k);
|
|
86
|
+
process.stderr.write(`[DEBUG] [SQLite] Features: ${enabled.join(', ')}\n`);
|
|
87
|
+
}
|
|
88
|
+
// Prepare statements for available features
|
|
89
|
+
this.prepareStatements();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Prepare SQL statements for reuse.
|
|
93
|
+
*/
|
|
94
|
+
prepareStatements() {
|
|
95
|
+
this.stmts = {
|
|
96
|
+
insertSession: this.db.prepare(`
|
|
97
|
+
INSERT INTO sessions (id, name, created_at, last_active_at, message_count, token_count)
|
|
98
|
+
VALUES (@id, @name, @createdAt, @lastActiveAt, @messageCount, @tokenCount)
|
|
99
|
+
`),
|
|
100
|
+
updateSession: this.db.prepare(`
|
|
101
|
+
UPDATE sessions SET
|
|
102
|
+
name = COALESCE(@name, name),
|
|
103
|
+
last_active_at = COALESCE(@lastActiveAt, last_active_at),
|
|
104
|
+
message_count = COALESCE(@messageCount, message_count),
|
|
105
|
+
token_count = COALESCE(@tokenCount, token_count),
|
|
106
|
+
summary = COALESCE(@summary, summary)
|
|
107
|
+
WHERE id = @id
|
|
108
|
+
`),
|
|
109
|
+
deleteSession: this.db.prepare(`DELETE FROM sessions WHERE id = ?`),
|
|
110
|
+
getSession: this.db.prepare(`
|
|
111
|
+
SELECT id, name, created_at as createdAt,
|
|
112
|
+
COALESCE(last_active_at, created_at) as lastActiveAt,
|
|
113
|
+
message_count as messageCount, token_count as tokenCount, summary,
|
|
114
|
+
parent_session_id as parentSessionId, session_type as sessionType,
|
|
115
|
+
prompt_tokens as promptTokens, completion_tokens as completionTokens,
|
|
116
|
+
cost_usd as costUsd
|
|
117
|
+
FROM sessions WHERE id = ?
|
|
118
|
+
`),
|
|
119
|
+
listSessions: this.db.prepare(`
|
|
120
|
+
SELECT id, name, created_at as createdAt,
|
|
121
|
+
COALESCE(last_active_at, created_at) as lastActiveAt,
|
|
122
|
+
message_count as messageCount, token_count as tokenCount, summary,
|
|
123
|
+
parent_session_id as parentSessionId, session_type as sessionType,
|
|
124
|
+
prompt_tokens as promptTokens, completion_tokens as completionTokens,
|
|
125
|
+
cost_usd as costUsd
|
|
126
|
+
FROM sessions ORDER BY COALESCE(last_active_at, created_at) DESC
|
|
127
|
+
`),
|
|
128
|
+
insertEntry: this.db.prepare(`
|
|
129
|
+
INSERT INTO entries (session_id, timestamp, type, data)
|
|
130
|
+
VALUES (@sessionId, @timestamp, @type, @data)
|
|
131
|
+
`),
|
|
132
|
+
getEntries: this.db.prepare(`
|
|
133
|
+
SELECT timestamp, type, data FROM entries
|
|
134
|
+
WHERE session_id = ? ORDER BY id ASC
|
|
135
|
+
`),
|
|
136
|
+
insertToolCall: this.db.prepare(`
|
|
137
|
+
INSERT INTO tool_calls (id, session_id, name, arguments, status, created_at)
|
|
138
|
+
VALUES (@id, @sessionId, @name, @arguments, @status, @createdAt)
|
|
139
|
+
`),
|
|
140
|
+
updateToolCall: this.db.prepare(`
|
|
141
|
+
UPDATE tool_calls SET
|
|
142
|
+
status = @status,
|
|
143
|
+
result = @result,
|
|
144
|
+
error = @error,
|
|
145
|
+
duration_ms = @durationMs,
|
|
146
|
+
completed_at = @completedAt
|
|
147
|
+
WHERE id = @id
|
|
148
|
+
`),
|
|
149
|
+
insertCheckpoint: this.db.prepare(`
|
|
150
|
+
INSERT INTO checkpoints (id, session_id, state_json, created_at, description)
|
|
151
|
+
VALUES (@id, @sessionId, @stateJson, @createdAt, @description)
|
|
152
|
+
`),
|
|
153
|
+
getLatestCheckpoint: this.db.prepare(`
|
|
154
|
+
SELECT id, session_id as sessionId, state_json as stateJson,
|
|
155
|
+
created_at as createdAt, description
|
|
156
|
+
FROM checkpoints WHERE session_id = ?
|
|
157
|
+
ORDER BY created_at DESC, rowid DESC LIMIT 1
|
|
158
|
+
`),
|
|
159
|
+
// Cost tracking statements
|
|
160
|
+
insertUsageLog: this.db.prepare(`
|
|
161
|
+
INSERT INTO usage_logs (session_id, model_id, prompt_tokens, completion_tokens, cost_usd, timestamp)
|
|
162
|
+
VALUES (@sessionId, @modelId, @promptTokens, @completionTokens, @costUsd, @timestamp)
|
|
163
|
+
`),
|
|
164
|
+
updateSessionCosts: this.db.prepare(`
|
|
165
|
+
UPDATE sessions SET
|
|
166
|
+
prompt_tokens = COALESCE(prompt_tokens, 0) + @promptTokens,
|
|
167
|
+
completion_tokens = COALESCE(completion_tokens, 0) + @completionTokens,
|
|
168
|
+
cost_usd = COALESCE(cost_usd, 0) + @costUsd
|
|
169
|
+
WHERE id = @sessionId
|
|
170
|
+
`),
|
|
171
|
+
getSessionUsage: this.db.prepare(`
|
|
172
|
+
SELECT
|
|
173
|
+
COALESCE(SUM(prompt_tokens), 0) as promptTokens,
|
|
174
|
+
COALESCE(SUM(completion_tokens), 0) as completionTokens,
|
|
175
|
+
COALESCE(SUM(cost_usd), 0) as costUsd
|
|
176
|
+
FROM usage_logs WHERE session_id = ?
|
|
177
|
+
`),
|
|
178
|
+
// Session hierarchy statements
|
|
179
|
+
insertChildSession: this.db.prepare(`
|
|
180
|
+
INSERT INTO sessions (id, name, created_at, last_active_at, message_count, token_count, parent_session_id, session_type)
|
|
181
|
+
VALUES (@id, @name, @createdAt, @lastActiveAt, @messageCount, @tokenCount, @parentSessionId, @sessionType)
|
|
182
|
+
`),
|
|
183
|
+
getChildSessions: this.db.prepare(`
|
|
184
|
+
SELECT id, name, created_at as createdAt, last_active_at as lastActiveAt,
|
|
185
|
+
message_count as messageCount, token_count as tokenCount, summary,
|
|
186
|
+
parent_session_id as parentSessionId, session_type as sessionType,
|
|
187
|
+
prompt_tokens as promptTokens, completion_tokens as completionTokens,
|
|
188
|
+
cost_usd as costUsd
|
|
189
|
+
FROM sessions WHERE parent_session_id = ?
|
|
190
|
+
ORDER BY created_at ASC
|
|
191
|
+
`),
|
|
192
|
+
getSessionTree: this.db.prepare(`
|
|
193
|
+
WITH RECURSIVE session_tree AS (
|
|
194
|
+
SELECT id, name, created_at as createdAt, last_active_at as lastActiveAt,
|
|
195
|
+
message_count as messageCount, token_count as tokenCount, summary,
|
|
196
|
+
parent_session_id as parentSessionId, session_type as sessionType,
|
|
197
|
+
prompt_tokens as promptTokens, completion_tokens as completionTokens,
|
|
198
|
+
cost_usd as costUsd, 0 as depth
|
|
199
|
+
FROM sessions WHERE id = ?
|
|
200
|
+
UNION ALL
|
|
201
|
+
SELECT s.id, s.name, s.created_at, s.last_active_at,
|
|
202
|
+
s.message_count, s.token_count, s.summary,
|
|
203
|
+
s.parent_session_id, s.session_type,
|
|
204
|
+
s.prompt_tokens, s.completion_tokens,
|
|
205
|
+
s.cost_usd, st.depth + 1
|
|
206
|
+
FROM sessions s
|
|
207
|
+
INNER JOIN session_tree st ON s.parent_session_id = st.id
|
|
208
|
+
)
|
|
209
|
+
SELECT * FROM session_tree ORDER BY depth, createdAt ASC
|
|
210
|
+
`),
|
|
211
|
+
};
|
|
212
|
+
// Goal integrity statements - only prepare if feature is available
|
|
213
|
+
if (this.features.goals) {
|
|
214
|
+
this.stmts.insertGoal = this.db.prepare(`
|
|
215
|
+
INSERT INTO goals (id, session_id, goal_text, status, priority, parent_goal_id,
|
|
216
|
+
progress_current, progress_total, created_at, updated_at, metadata)
|
|
217
|
+
VALUES (@id, @sessionId, @goalText, @status, @priority, @parentGoalId,
|
|
218
|
+
@progressCurrent, @progressTotal, @createdAt, @updatedAt, @metadata)
|
|
219
|
+
`);
|
|
220
|
+
this.stmts.updateGoal = this.db.prepare(`
|
|
221
|
+
UPDATE goals SET
|
|
222
|
+
goal_text = COALESCE(@goalText, goal_text),
|
|
223
|
+
status = COALESCE(@status, status),
|
|
224
|
+
priority = COALESCE(@priority, priority),
|
|
225
|
+
progress_current = COALESCE(@progressCurrent, progress_current),
|
|
226
|
+
progress_total = COALESCE(@progressTotal, progress_total),
|
|
227
|
+
updated_at = @updatedAt,
|
|
228
|
+
completed_at = @completedAt,
|
|
229
|
+
metadata = COALESCE(@metadata, metadata)
|
|
230
|
+
WHERE id = @id
|
|
231
|
+
`);
|
|
232
|
+
this.stmts.getGoal = this.db.prepare(`
|
|
233
|
+
SELECT id, session_id as sessionId, goal_text as goalText, status, priority,
|
|
234
|
+
parent_goal_id as parentGoalId, progress_current as progressCurrent,
|
|
235
|
+
progress_total as progressTotal, created_at as createdAt,
|
|
236
|
+
updated_at as updatedAt, completed_at as completedAt, metadata
|
|
237
|
+
FROM goals WHERE id = ?
|
|
238
|
+
`);
|
|
239
|
+
this.stmts.listGoals = this.db.prepare(`
|
|
240
|
+
SELECT id, session_id as sessionId, goal_text as goalText, status, priority,
|
|
241
|
+
parent_goal_id as parentGoalId, progress_current as progressCurrent,
|
|
242
|
+
progress_total as progressTotal, created_at as createdAt,
|
|
243
|
+
updated_at as updatedAt, completed_at as completedAt, metadata
|
|
244
|
+
FROM goals WHERE session_id = ? ORDER BY priority ASC, created_at ASC
|
|
245
|
+
`);
|
|
246
|
+
this.stmts.listActiveGoals = this.db.prepare(`
|
|
247
|
+
SELECT id, session_id as sessionId, goal_text as goalText, status, priority,
|
|
248
|
+
parent_goal_id as parentGoalId, progress_current as progressCurrent,
|
|
249
|
+
progress_total as progressTotal, created_at as createdAt,
|
|
250
|
+
updated_at as updatedAt, completed_at as completedAt, metadata
|
|
251
|
+
FROM goals WHERE session_id = ? AND status = 'active'
|
|
252
|
+
ORDER BY priority ASC, created_at ASC
|
|
253
|
+
`);
|
|
254
|
+
this.stmts.insertJuncture = this.db.prepare(`
|
|
255
|
+
INSERT INTO junctures (session_id, goal_id, type, description, outcome,
|
|
256
|
+
importance, context, created_at)
|
|
257
|
+
VALUES (@sessionId, @goalId, @type, @description, @outcome,
|
|
258
|
+
@importance, @context, @createdAt)
|
|
259
|
+
`);
|
|
260
|
+
this.stmts.listJunctures = this.db.prepare(`
|
|
261
|
+
SELECT id, session_id as sessionId, goal_id as goalId, type, description,
|
|
262
|
+
outcome, importance, context, created_at as createdAt
|
|
263
|
+
FROM junctures WHERE session_id = ? ORDER BY created_at DESC
|
|
264
|
+
`);
|
|
265
|
+
}
|
|
266
|
+
// Worker result statements - only prepare if feature is available
|
|
267
|
+
if (this.features.workerResults) {
|
|
268
|
+
this.stmts.insertWorkerResult = this.db.prepare(`
|
|
269
|
+
INSERT INTO worker_results (id, session_id, worker_id, task_description, model_used,
|
|
270
|
+
status, summary, full_output, artifacts, metrics, error,
|
|
271
|
+
created_at, completed_at)
|
|
272
|
+
VALUES (@id, @sessionId, @workerId, @taskDescription, @modelUsed,
|
|
273
|
+
@status, @summary, @fullOutput, @artifacts, @metrics, @error,
|
|
274
|
+
@createdAt, @completedAt)
|
|
275
|
+
`);
|
|
276
|
+
this.stmts.updateWorkerResult = this.db.prepare(`
|
|
277
|
+
UPDATE worker_results SET
|
|
278
|
+
status = COALESCE(@status, status),
|
|
279
|
+
summary = COALESCE(@summary, summary),
|
|
280
|
+
full_output = COALESCE(@fullOutput, full_output),
|
|
281
|
+
artifacts = COALESCE(@artifacts, artifacts),
|
|
282
|
+
metrics = COALESCE(@metrics, metrics),
|
|
283
|
+
error = @error,
|
|
284
|
+
completed_at = @completedAt
|
|
285
|
+
WHERE id = @id
|
|
286
|
+
`);
|
|
287
|
+
this.stmts.getWorkerResult = this.db.prepare(`
|
|
288
|
+
SELECT id, session_id as sessionId, worker_id as workerId,
|
|
289
|
+
task_description as taskDescription, model_used as modelUsed,
|
|
290
|
+
status, summary, full_output as fullOutput, artifacts, metrics,
|
|
291
|
+
error, created_at as createdAt, completed_at as completedAt
|
|
292
|
+
FROM worker_results WHERE id = ?
|
|
293
|
+
`);
|
|
294
|
+
this.stmts.listWorkerResults = this.db.prepare(`
|
|
295
|
+
SELECT id, session_id as sessionId, worker_id as workerId,
|
|
296
|
+
task_description as taskDescription, model_used as modelUsed,
|
|
297
|
+
status, summary, full_output as fullOutput, artifacts, metrics,
|
|
298
|
+
error, created_at as createdAt, completed_at as completedAt
|
|
299
|
+
FROM worker_results WHERE session_id = ?
|
|
300
|
+
ORDER BY created_at DESC
|
|
301
|
+
`);
|
|
302
|
+
this.stmts.listPendingWorkerResults = this.db.prepare(`
|
|
303
|
+
SELECT id, session_id as sessionId, worker_id as workerId,
|
|
304
|
+
task_description as taskDescription, model_used as modelUsed,
|
|
305
|
+
status, summary, created_at as createdAt
|
|
306
|
+
FROM worker_results WHERE session_id = ? AND status = 'pending'
|
|
307
|
+
ORDER BY created_at ASC
|
|
308
|
+
`);
|
|
309
|
+
}
|
|
310
|
+
// Pending plan statements (optional - only if pendingPlans feature available)
|
|
311
|
+
if (this.features.pendingPlans) {
|
|
312
|
+
this.stmts.insertPendingPlan = this.db.prepare(`
|
|
313
|
+
INSERT INTO pending_plans (id, session_id, task, proposed_changes, exploration_summary, status, created_at, updated_at)
|
|
314
|
+
VALUES (@id, @sessionId, @task, @proposedChanges, @explorationSummary, @status, @createdAt, @updatedAt)
|
|
315
|
+
`);
|
|
316
|
+
this.stmts.updatePendingPlan = this.db.prepare(`
|
|
317
|
+
UPDATE pending_plans SET
|
|
318
|
+
proposed_changes = @proposedChanges,
|
|
319
|
+
exploration_summary = @explorationSummary,
|
|
320
|
+
status = @status,
|
|
321
|
+
updated_at = @updatedAt
|
|
322
|
+
WHERE id = @id
|
|
323
|
+
`);
|
|
324
|
+
this.stmts.getPendingPlan = this.db.prepare(`
|
|
325
|
+
SELECT id, session_id as sessionId, task, proposed_changes as proposedChanges,
|
|
326
|
+
exploration_summary as explorationSummary, status, created_at as createdAt, updated_at as updatedAt
|
|
327
|
+
FROM pending_plans WHERE session_id = ? AND status = 'pending'
|
|
328
|
+
ORDER BY created_at DESC LIMIT 1
|
|
329
|
+
`);
|
|
330
|
+
this.stmts.deletePendingPlan = this.db.prepare(`
|
|
331
|
+
DELETE FROM pending_plans WHERE id = ?
|
|
332
|
+
`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// ===========================================================================
|
|
336
|
+
// SESSION MANAGEMENT (compatible with SessionStore interface)
|
|
337
|
+
// ===========================================================================
|
|
338
|
+
/**
|
|
339
|
+
* Create a new session.
|
|
340
|
+
*/
|
|
341
|
+
createSession(name) {
|
|
342
|
+
const id = `session-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
343
|
+
const now = new Date().toISOString();
|
|
344
|
+
this.stmts.insertSession.run({
|
|
345
|
+
id,
|
|
346
|
+
name: name || null,
|
|
347
|
+
createdAt: now,
|
|
348
|
+
lastActiveAt: now,
|
|
349
|
+
messageCount: 0,
|
|
350
|
+
tokenCount: 0,
|
|
351
|
+
});
|
|
352
|
+
this.currentSessionId = id;
|
|
353
|
+
// Prune old sessions
|
|
354
|
+
this.pruneOldSessions();
|
|
355
|
+
this.emit({ type: 'session.created', sessionId: id });
|
|
356
|
+
return id;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Get current session ID.
|
|
360
|
+
*/
|
|
361
|
+
getCurrentSessionId() {
|
|
362
|
+
return this.currentSessionId;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Set current session ID.
|
|
366
|
+
*/
|
|
367
|
+
setCurrentSessionId(sessionId) {
|
|
368
|
+
this.currentSessionId = sessionId;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Append an entry to the current session.
|
|
372
|
+
*/
|
|
373
|
+
appendEntry(entry) {
|
|
374
|
+
if (!this.currentSessionId) {
|
|
375
|
+
this.createSession();
|
|
376
|
+
}
|
|
377
|
+
const timestamp = new Date().toISOString();
|
|
378
|
+
this.stmts.insertEntry.run({
|
|
379
|
+
sessionId: this.currentSessionId,
|
|
380
|
+
timestamp,
|
|
381
|
+
type: entry.type,
|
|
382
|
+
data: JSON.stringify(entry.data),
|
|
383
|
+
});
|
|
384
|
+
// Update session metadata
|
|
385
|
+
if (entry.type === 'message') {
|
|
386
|
+
const msg = entry.data;
|
|
387
|
+
// Set summary from first user message (for session picker display)
|
|
388
|
+
if (msg.role === 'user' && typeof msg.content === 'string') {
|
|
389
|
+
// Only set if no summary yet
|
|
390
|
+
const session = this.stmts.getSession.get(this.currentSessionId);
|
|
391
|
+
if (session && !session.summary) {
|
|
392
|
+
// Extract first line or first ~50 chars as summary
|
|
393
|
+
const firstLine = msg.content.split('\n')[0].trim();
|
|
394
|
+
const summary = firstLine.length > 60 ? firstLine.slice(0, 57) + '...' : firstLine;
|
|
395
|
+
this.db.prepare(`UPDATE sessions SET summary = ? WHERE id = ?`).run(summary, this.currentSessionId);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
this.db.prepare(`
|
|
399
|
+
UPDATE sessions SET
|
|
400
|
+
last_active_at = ?,
|
|
401
|
+
message_count = message_count + 1
|
|
402
|
+
WHERE id = ?
|
|
403
|
+
`).run(timestamp, this.currentSessionId);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
this.db.prepare(`
|
|
407
|
+
UPDATE sessions SET last_active_at = ? WHERE id = ?
|
|
408
|
+
`).run(timestamp, this.currentSessionId);
|
|
409
|
+
}
|
|
410
|
+
this.emit({ type: 'entry.appended', sessionId: this.currentSessionId, entryType: entry.type });
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Append a message to the current session.
|
|
414
|
+
*/
|
|
415
|
+
appendMessage(message) {
|
|
416
|
+
this.appendEntry({ type: 'message', data: message });
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Append a tool call to the current session.
|
|
420
|
+
*/
|
|
421
|
+
appendToolCall(toolCall) {
|
|
422
|
+
this.appendEntry({ type: 'tool_call', data: toolCall });
|
|
423
|
+
// Also insert into tool_calls table for fast lookup
|
|
424
|
+
if ('id' in toolCall && toolCall.id) {
|
|
425
|
+
this.stmts.insertToolCall.run({
|
|
426
|
+
id: toolCall.id,
|
|
427
|
+
sessionId: this.currentSessionId,
|
|
428
|
+
name: toolCall.name,
|
|
429
|
+
arguments: JSON.stringify(toolCall.arguments),
|
|
430
|
+
status: 'pending',
|
|
431
|
+
createdAt: new Date().toISOString(),
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Append a tool result to the current session.
|
|
437
|
+
*/
|
|
438
|
+
appendToolResult(callId, result) {
|
|
439
|
+
this.appendEntry({ type: 'tool_result', data: { callId, result } });
|
|
440
|
+
// Update tool_calls table
|
|
441
|
+
this.stmts.updateToolCall.run({
|
|
442
|
+
id: callId,
|
|
443
|
+
status: 'success',
|
|
444
|
+
result: JSON.stringify(result),
|
|
445
|
+
error: null,
|
|
446
|
+
durationMs: null,
|
|
447
|
+
completedAt: new Date().toISOString(),
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Append a compaction summary.
|
|
452
|
+
*/
|
|
453
|
+
appendCompaction(summary, compactedCount) {
|
|
454
|
+
this.appendEntry({
|
|
455
|
+
type: 'compaction',
|
|
456
|
+
data: { summary, compactedCount, compactedAt: new Date().toISOString() },
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Load a session by ID.
|
|
461
|
+
*/
|
|
462
|
+
loadSession(sessionId) {
|
|
463
|
+
const rows = this.stmts.getEntries.all(sessionId);
|
|
464
|
+
const entries = rows.map(row => ({
|
|
465
|
+
timestamp: row.timestamp,
|
|
466
|
+
type: row.type,
|
|
467
|
+
data: JSON.parse(row.data),
|
|
468
|
+
}));
|
|
469
|
+
this.currentSessionId = sessionId;
|
|
470
|
+
this.emit({ type: 'session.loaded', sessionId, entryCount: entries.length });
|
|
471
|
+
return entries;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Reconstruct messages from session entries.
|
|
475
|
+
*/
|
|
476
|
+
loadSessionMessages(sessionId) {
|
|
477
|
+
const entries = this.loadSession(sessionId);
|
|
478
|
+
const messages = [];
|
|
479
|
+
for (const entry of entries) {
|
|
480
|
+
if (entry.type === 'message') {
|
|
481
|
+
messages.push(entry.data);
|
|
482
|
+
}
|
|
483
|
+
else if (entry.type === 'compaction') {
|
|
484
|
+
const compaction = entry.data;
|
|
485
|
+
messages.push({
|
|
486
|
+
role: 'system',
|
|
487
|
+
content: `[Previous conversation summary]\n${compaction.summary}`,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return messages;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Delete a session.
|
|
495
|
+
*/
|
|
496
|
+
deleteSession(sessionId) {
|
|
497
|
+
this.stmts.deleteSession.run(sessionId);
|
|
498
|
+
if (this.currentSessionId === sessionId) {
|
|
499
|
+
this.currentSessionId = null;
|
|
500
|
+
}
|
|
501
|
+
this.emit({ type: 'session.deleted', sessionId });
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* List all sessions.
|
|
505
|
+
*/
|
|
506
|
+
listSessions() {
|
|
507
|
+
return this.stmts.listSessions.all();
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Get the most recent session.
|
|
511
|
+
*/
|
|
512
|
+
getRecentSession() {
|
|
513
|
+
const sessions = this.listSessions();
|
|
514
|
+
return sessions[0] || null;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Get session metadata by ID.
|
|
518
|
+
*/
|
|
519
|
+
getSessionMetadata(sessionId) {
|
|
520
|
+
return this.stmts.getSession.get(sessionId);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Update session metadata.
|
|
524
|
+
*/
|
|
525
|
+
updateSessionMetadata(sessionId, updates) {
|
|
526
|
+
this.stmts.updateSession.run({
|
|
527
|
+
id: sessionId,
|
|
528
|
+
name: updates.name || null,
|
|
529
|
+
lastActiveAt: null,
|
|
530
|
+
messageCount: null,
|
|
531
|
+
tokenCount: updates.tokenCount || null,
|
|
532
|
+
summary: updates.summary || null,
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
// ===========================================================================
|
|
536
|
+
// SQLITE-SPECIFIC FEATURES
|
|
537
|
+
// ===========================================================================
|
|
538
|
+
/**
|
|
539
|
+
* Save a checkpoint for state restoration.
|
|
540
|
+
*/
|
|
541
|
+
saveCheckpoint(state, description) {
|
|
542
|
+
if (!this.currentSessionId) {
|
|
543
|
+
this.createSession();
|
|
544
|
+
}
|
|
545
|
+
const id = `ckpt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
546
|
+
this.stmts.insertCheckpoint.run({
|
|
547
|
+
id,
|
|
548
|
+
sessionId: this.currentSessionId,
|
|
549
|
+
stateJson: JSON.stringify(state),
|
|
550
|
+
createdAt: new Date().toISOString(),
|
|
551
|
+
description: description || null,
|
|
552
|
+
});
|
|
553
|
+
return id;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Load the latest checkpoint for a session.
|
|
557
|
+
*/
|
|
558
|
+
loadLatestCheckpoint(sessionId) {
|
|
559
|
+
const row = this.stmts.getLatestCheckpoint.get(sessionId);
|
|
560
|
+
if (!row)
|
|
561
|
+
return null;
|
|
562
|
+
return {
|
|
563
|
+
id: row.id,
|
|
564
|
+
state: JSON.parse(row.stateJson),
|
|
565
|
+
createdAt: row.createdAt,
|
|
566
|
+
description: row.description ?? undefined,
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Query entries with SQL (advanced usage).
|
|
571
|
+
*/
|
|
572
|
+
query(sql, params = []) {
|
|
573
|
+
return this.db.prepare(sql).all(...params);
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Get database statistics.
|
|
577
|
+
*/
|
|
578
|
+
getStats() {
|
|
579
|
+
const sessionCount = this.db.prepare('SELECT COUNT(*) as count FROM sessions').get().count;
|
|
580
|
+
const entryCount = this.db.prepare('SELECT COUNT(*) as count FROM entries').get().count;
|
|
581
|
+
const toolCallCount = this.db.prepare('SELECT COUNT(*) as count FROM tool_calls').get().count;
|
|
582
|
+
const checkpointCount = this.db.prepare('SELECT COUNT(*) as count FROM checkpoints').get().count;
|
|
583
|
+
const dbSizeBytes = this.db.prepare('SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()').get().size;
|
|
584
|
+
return { sessionCount, entryCount, toolCallCount, checkpointCount, dbSizeBytes };
|
|
585
|
+
}
|
|
586
|
+
// ===========================================================================
|
|
587
|
+
// COST TRACKING
|
|
588
|
+
// ===========================================================================
|
|
589
|
+
/**
|
|
590
|
+
* Log API usage for cost tracking.
|
|
591
|
+
* Inserts a usage log entry and updates session totals atomically.
|
|
592
|
+
*/
|
|
593
|
+
logUsage(usage) {
|
|
594
|
+
// Use transaction to ensure atomicity of insert + update
|
|
595
|
+
this.db.transaction(() => {
|
|
596
|
+
// Insert into usage_logs table
|
|
597
|
+
this.stmts.insertUsageLog.run({
|
|
598
|
+
sessionId: usage.sessionId,
|
|
599
|
+
modelId: usage.modelId,
|
|
600
|
+
promptTokens: usage.promptTokens,
|
|
601
|
+
completionTokens: usage.completionTokens,
|
|
602
|
+
costUsd: usage.costUsd,
|
|
603
|
+
timestamp: usage.timestamp,
|
|
604
|
+
});
|
|
605
|
+
// Update session totals
|
|
606
|
+
this.stmts.updateSessionCosts.run({
|
|
607
|
+
sessionId: usage.sessionId,
|
|
608
|
+
promptTokens: usage.promptTokens,
|
|
609
|
+
completionTokens: usage.completionTokens,
|
|
610
|
+
costUsd: usage.costUsd,
|
|
611
|
+
});
|
|
612
|
+
})();
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Get aggregated usage for a session.
|
|
616
|
+
*/
|
|
617
|
+
getSessionUsage(sessionId) {
|
|
618
|
+
const result = this.stmts.getSessionUsage.get(sessionId);
|
|
619
|
+
return result || { promptTokens: 0, completionTokens: 0, costUsd: 0 };
|
|
620
|
+
}
|
|
621
|
+
// ===========================================================================
|
|
622
|
+
// SESSION HIERARCHY
|
|
623
|
+
// ===========================================================================
|
|
624
|
+
/**
|
|
625
|
+
* Create a child session linked to a parent.
|
|
626
|
+
*/
|
|
627
|
+
createChildSession(parentId, name, type = 'subagent') {
|
|
628
|
+
const id = `session-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
629
|
+
const now = new Date().toISOString();
|
|
630
|
+
this.stmts.insertChildSession.run({
|
|
631
|
+
id,
|
|
632
|
+
name: name || null,
|
|
633
|
+
createdAt: now,
|
|
634
|
+
lastActiveAt: now,
|
|
635
|
+
messageCount: 0,
|
|
636
|
+
tokenCount: 0,
|
|
637
|
+
parentSessionId: parentId,
|
|
638
|
+
sessionType: type,
|
|
639
|
+
});
|
|
640
|
+
this.emit({ type: 'session.created', sessionId: id });
|
|
641
|
+
return id;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Get all direct child sessions of a parent.
|
|
645
|
+
*/
|
|
646
|
+
getChildSessions(parentId) {
|
|
647
|
+
return this.stmts.getChildSessions.all(parentId);
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Get the full session tree starting from a root session.
|
|
651
|
+
* Uses a recursive CTE to traverse the hierarchy.
|
|
652
|
+
*/
|
|
653
|
+
getSessionTree(rootId) {
|
|
654
|
+
const rows = this.stmts.getSessionTree.all(rootId);
|
|
655
|
+
// Remove the depth field from results (used only for ordering)
|
|
656
|
+
return rows.map(({ depth, ...session }) => session);
|
|
657
|
+
}
|
|
658
|
+
// ===========================================================================
|
|
659
|
+
// GOAL INTEGRITY
|
|
660
|
+
// ===========================================================================
|
|
661
|
+
/**
|
|
662
|
+
* Check if goals feature is available.
|
|
663
|
+
*/
|
|
664
|
+
hasGoalsFeature() {
|
|
665
|
+
return this.features.goals;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Create a new goal for the current session.
|
|
669
|
+
* Goals persist outside of context and survive compaction.
|
|
670
|
+
* Returns undefined if goals feature is not available.
|
|
671
|
+
*/
|
|
672
|
+
createGoal(goalText, options = {}) {
|
|
673
|
+
if (!this.features.goals || !this.stmts.insertGoal) {
|
|
674
|
+
return undefined;
|
|
675
|
+
}
|
|
676
|
+
if (!this.currentSessionId) {
|
|
677
|
+
this.createSession();
|
|
678
|
+
}
|
|
679
|
+
const id = `goal-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
680
|
+
const now = new Date().toISOString();
|
|
681
|
+
this.stmts.insertGoal.run({
|
|
682
|
+
id,
|
|
683
|
+
sessionId: this.currentSessionId,
|
|
684
|
+
goalText,
|
|
685
|
+
status: 'active',
|
|
686
|
+
priority: options.priority ?? 2,
|
|
687
|
+
parentGoalId: options.parentGoalId ?? null,
|
|
688
|
+
progressCurrent: 0,
|
|
689
|
+
progressTotal: options.progressTotal ?? null,
|
|
690
|
+
createdAt: now,
|
|
691
|
+
updatedAt: now,
|
|
692
|
+
metadata: options.metadata ? JSON.stringify(options.metadata) : null,
|
|
693
|
+
});
|
|
694
|
+
return id;
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Update a goal's status or progress.
|
|
698
|
+
*/
|
|
699
|
+
updateGoal(goalId, updates) {
|
|
700
|
+
if (!this.features.goals || !this.stmts.updateGoal) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
const now = new Date().toISOString();
|
|
704
|
+
this.stmts.updateGoal.run({
|
|
705
|
+
id: goalId,
|
|
706
|
+
goalText: updates.goalText ?? null,
|
|
707
|
+
status: updates.status ?? null,
|
|
708
|
+
priority: updates.priority ?? null,
|
|
709
|
+
progressCurrent: updates.progressCurrent ?? null,
|
|
710
|
+
progressTotal: updates.progressTotal ?? null,
|
|
711
|
+
updatedAt: now,
|
|
712
|
+
completedAt: updates.status === 'completed' || updates.status === 'abandoned' ? now : null,
|
|
713
|
+
metadata: updates.metadata ? JSON.stringify(updates.metadata) : null,
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Mark a goal as completed.
|
|
718
|
+
*/
|
|
719
|
+
completeGoal(goalId) {
|
|
720
|
+
this.updateGoal(goalId, { status: 'completed' });
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Get a goal by ID.
|
|
724
|
+
*/
|
|
725
|
+
getGoal(goalId) {
|
|
726
|
+
if (!this.features.goals || !this.stmts.getGoal) {
|
|
727
|
+
return undefined;
|
|
728
|
+
}
|
|
729
|
+
return this.stmts.getGoal.get(goalId);
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* List all goals for a session.
|
|
733
|
+
*/
|
|
734
|
+
listGoals(sessionId) {
|
|
735
|
+
if (!this.features.goals || !this.stmts.listGoals) {
|
|
736
|
+
return [];
|
|
737
|
+
}
|
|
738
|
+
const sid = sessionId ?? this.currentSessionId;
|
|
739
|
+
if (!sid)
|
|
740
|
+
return [];
|
|
741
|
+
return this.stmts.listGoals.all(sid);
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* List active goals for a session.
|
|
745
|
+
*/
|
|
746
|
+
listActiveGoals(sessionId) {
|
|
747
|
+
if (!this.features.goals || !this.stmts.listActiveGoals) {
|
|
748
|
+
return [];
|
|
749
|
+
}
|
|
750
|
+
const sid = sessionId ?? this.currentSessionId;
|
|
751
|
+
if (!sid)
|
|
752
|
+
return [];
|
|
753
|
+
return this.stmts.listActiveGoals.all(sid);
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Get a summary of the current goals for context injection.
|
|
757
|
+
* This is what gets recited to maintain goal awareness.
|
|
758
|
+
*/
|
|
759
|
+
getGoalsSummary(sessionId) {
|
|
760
|
+
if (!this.features.goals) {
|
|
761
|
+
return 'Goals feature not available.';
|
|
762
|
+
}
|
|
763
|
+
const goals = this.listActiveGoals(sessionId);
|
|
764
|
+
if (goals.length === 0) {
|
|
765
|
+
return 'No active goals.';
|
|
766
|
+
}
|
|
767
|
+
const lines = ['Active Goals:'];
|
|
768
|
+
for (const goal of goals) {
|
|
769
|
+
const progress = goal.progressTotal
|
|
770
|
+
? ` (${goal.progressCurrent}/${goal.progressTotal})`
|
|
771
|
+
: '';
|
|
772
|
+
const priority = goal.priority === 1 ? ' [HIGH]' : goal.priority === 3 ? ' [low]' : '';
|
|
773
|
+
lines.push(`• ${goal.goalText}${progress}${priority}`);
|
|
774
|
+
}
|
|
775
|
+
return lines.join('\n');
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Log a critical juncture (decision, failure, breakthrough, pivot).
|
|
779
|
+
* Returns -1 if goals feature is not available.
|
|
780
|
+
*/
|
|
781
|
+
logJuncture(type, description, options = {}) {
|
|
782
|
+
if (!this.features.goals || !this.stmts.insertJuncture) {
|
|
783
|
+
return -1;
|
|
784
|
+
}
|
|
785
|
+
if (!this.currentSessionId) {
|
|
786
|
+
this.createSession();
|
|
787
|
+
}
|
|
788
|
+
const result = this.stmts.insertJuncture.run({
|
|
789
|
+
sessionId: this.currentSessionId,
|
|
790
|
+
goalId: options.goalId ?? null,
|
|
791
|
+
type,
|
|
792
|
+
description,
|
|
793
|
+
outcome: options.outcome ?? null,
|
|
794
|
+
importance: options.importance ?? 2,
|
|
795
|
+
context: options.context ? JSON.stringify(options.context) : null,
|
|
796
|
+
createdAt: new Date().toISOString(),
|
|
797
|
+
});
|
|
798
|
+
return Number(result.lastInsertRowid);
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* List junctures for a session.
|
|
802
|
+
*/
|
|
803
|
+
listJunctures(sessionId, limit) {
|
|
804
|
+
if (!this.features.goals || !this.stmts.listJunctures) {
|
|
805
|
+
return [];
|
|
806
|
+
}
|
|
807
|
+
const sid = sessionId ?? this.currentSessionId;
|
|
808
|
+
if (!sid)
|
|
809
|
+
return [];
|
|
810
|
+
const junctures = this.stmts.listJunctures.all(sid);
|
|
811
|
+
return limit ? junctures.slice(0, limit) : junctures;
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Get recent critical junctures for context.
|
|
815
|
+
*/
|
|
816
|
+
getJuncturesSummary(sessionId, limit = 5) {
|
|
817
|
+
if (!this.features.goals) {
|
|
818
|
+
return '';
|
|
819
|
+
}
|
|
820
|
+
const junctures = this.listJunctures(sessionId, limit);
|
|
821
|
+
if (junctures.length === 0) {
|
|
822
|
+
return '';
|
|
823
|
+
}
|
|
824
|
+
const lines = ['Recent Key Moments:'];
|
|
825
|
+
for (const j of junctures) {
|
|
826
|
+
const icon = j.type === 'failure' ? '✗' : j.type === 'breakthrough' ? '★' :
|
|
827
|
+
j.type === 'decision' ? '→' : '↻';
|
|
828
|
+
lines.push(`${icon} [${j.type}] ${j.description}`);
|
|
829
|
+
if (j.outcome) {
|
|
830
|
+
lines.push(` └─ ${j.outcome}`);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
return lines.join('\n');
|
|
834
|
+
}
|
|
835
|
+
// ===========================================================================
|
|
836
|
+
// WORKER RESULTS
|
|
837
|
+
// ===========================================================================
|
|
838
|
+
/**
|
|
839
|
+
* Check if worker results feature is available.
|
|
840
|
+
*/
|
|
841
|
+
hasWorkerResultsFeature() {
|
|
842
|
+
return this.features.workerResults;
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Create a pending worker result entry.
|
|
846
|
+
* Call this when spawning a worker to reserve the result slot.
|
|
847
|
+
* Returns the result ID for later reference.
|
|
848
|
+
*/
|
|
849
|
+
createWorkerResult(workerId, taskDescription, modelUsed) {
|
|
850
|
+
if (!this.features.workerResults || !this.stmts.insertWorkerResult) {
|
|
851
|
+
return undefined;
|
|
852
|
+
}
|
|
853
|
+
if (!this.currentSessionId) {
|
|
854
|
+
this.createSession();
|
|
855
|
+
}
|
|
856
|
+
const id = `wr-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
857
|
+
const now = new Date().toISOString();
|
|
858
|
+
this.stmts.insertWorkerResult.run({
|
|
859
|
+
id,
|
|
860
|
+
sessionId: this.currentSessionId,
|
|
861
|
+
workerId,
|
|
862
|
+
taskDescription,
|
|
863
|
+
modelUsed: modelUsed ?? null,
|
|
864
|
+
status: 'pending',
|
|
865
|
+
summary: null,
|
|
866
|
+
fullOutput: null,
|
|
867
|
+
artifacts: null,
|
|
868
|
+
metrics: null,
|
|
869
|
+
error: null,
|
|
870
|
+
createdAt: now,
|
|
871
|
+
completedAt: null,
|
|
872
|
+
});
|
|
873
|
+
return id;
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Complete a worker result with output.
|
|
877
|
+
* Stores full output in database, generates summary for context injection.
|
|
878
|
+
*/
|
|
879
|
+
completeWorkerResult(resultId, output) {
|
|
880
|
+
if (!this.features.workerResults || !this.stmts.updateWorkerResult) {
|
|
881
|
+
return undefined;
|
|
882
|
+
}
|
|
883
|
+
const now = new Date().toISOString();
|
|
884
|
+
// Auto-generate summary if not provided (first 200 chars)
|
|
885
|
+
const summary = output.summary ?? this.generateResultSummary(output.fullOutput);
|
|
886
|
+
this.stmts.updateWorkerResult.run({
|
|
887
|
+
id: resultId,
|
|
888
|
+
status: 'success',
|
|
889
|
+
summary,
|
|
890
|
+
fullOutput: output.fullOutput,
|
|
891
|
+
artifacts: output.artifacts ? JSON.stringify(output.artifacts) : null,
|
|
892
|
+
metrics: output.metrics ? JSON.stringify(output.metrics) : null,
|
|
893
|
+
error: null,
|
|
894
|
+
completedAt: now,
|
|
895
|
+
});
|
|
896
|
+
// Return reference for context injection
|
|
897
|
+
const result = this.getWorkerResult(resultId);
|
|
898
|
+
return result ? this.toResultRef(result) : undefined;
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Mark a worker result as failed.
|
|
902
|
+
*/
|
|
903
|
+
failWorkerResult(resultId, error) {
|
|
904
|
+
if (!this.features.workerResults || !this.stmts.updateWorkerResult) {
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
this.stmts.updateWorkerResult.run({
|
|
908
|
+
id: resultId,
|
|
909
|
+
status: 'error',
|
|
910
|
+
summary: `Failed: ${error.slice(0, 100)}`,
|
|
911
|
+
fullOutput: null,
|
|
912
|
+
artifacts: null,
|
|
913
|
+
metrics: null,
|
|
914
|
+
error,
|
|
915
|
+
completedAt: new Date().toISOString(),
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Get a worker result by ID (includes full output).
|
|
920
|
+
*/
|
|
921
|
+
getWorkerResult(resultId) {
|
|
922
|
+
if (!this.features.workerResults || !this.stmts.getWorkerResult) {
|
|
923
|
+
return undefined;
|
|
924
|
+
}
|
|
925
|
+
return this.stmts.getWorkerResult.get(resultId);
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Get a lightweight reference to a worker result (for context injection).
|
|
929
|
+
* Does NOT include full output - that stays in database.
|
|
930
|
+
*/
|
|
931
|
+
getWorkerResultRef(resultId) {
|
|
932
|
+
const result = this.getWorkerResult(resultId);
|
|
933
|
+
return result ? this.toResultRef(result) : undefined;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* List all worker results for a session.
|
|
937
|
+
*/
|
|
938
|
+
listWorkerResults(sessionId) {
|
|
939
|
+
if (!this.features.workerResults || !this.stmts.listWorkerResults) {
|
|
940
|
+
return [];
|
|
941
|
+
}
|
|
942
|
+
const sid = sessionId ?? this.currentSessionId;
|
|
943
|
+
if (!sid)
|
|
944
|
+
return [];
|
|
945
|
+
return this.stmts.listWorkerResults.all(sid);
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* List pending worker results (workers still running).
|
|
949
|
+
*/
|
|
950
|
+
listPendingWorkerResults(sessionId) {
|
|
951
|
+
if (!this.features.workerResults || !this.stmts.listPendingWorkerResults) {
|
|
952
|
+
return [];
|
|
953
|
+
}
|
|
954
|
+
const sid = sessionId ?? this.currentSessionId;
|
|
955
|
+
if (!sid)
|
|
956
|
+
return [];
|
|
957
|
+
return this.stmts.listPendingWorkerResults.all(sid);
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Get a summary of worker results for context injection.
|
|
961
|
+
* Returns lightweight references, not full outputs.
|
|
962
|
+
*/
|
|
963
|
+
getWorkerResultsSummary(sessionId) {
|
|
964
|
+
if (!this.features.workerResults) {
|
|
965
|
+
return '';
|
|
966
|
+
}
|
|
967
|
+
const results = this.listWorkerResults(sessionId);
|
|
968
|
+
if (results.length === 0) {
|
|
969
|
+
return '';
|
|
970
|
+
}
|
|
971
|
+
const lines = ['Worker Results:'];
|
|
972
|
+
for (const r of results.slice(0, 10)) {
|
|
973
|
+
const status = r.status === 'success' ? '✓' : r.status === 'error' ? '✗' : '⏳';
|
|
974
|
+
const task = r.taskDescription.length > 50
|
|
975
|
+
? r.taskDescription.slice(0, 47) + '...'
|
|
976
|
+
: r.taskDescription;
|
|
977
|
+
lines.push(`${status} [${r.id}] ${task}`);
|
|
978
|
+
if (r.summary) {
|
|
979
|
+
lines.push(` └─ ${r.summary}`);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
if (results.length > 10) {
|
|
983
|
+
lines.push(` ... and ${results.length - 10} more`);
|
|
984
|
+
}
|
|
985
|
+
return lines.join('\n');
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Convert a full WorkerResult to a lightweight reference.
|
|
989
|
+
*/
|
|
990
|
+
toResultRef(result) {
|
|
991
|
+
return {
|
|
992
|
+
id: result.id,
|
|
993
|
+
workerId: result.workerId,
|
|
994
|
+
taskDescription: result.taskDescription,
|
|
995
|
+
status: result.status,
|
|
996
|
+
summary: result.summary,
|
|
997
|
+
modelUsed: result.modelUsed,
|
|
998
|
+
retrievalHint: `Full output available: store.getWorkerResult('${result.id}')`,
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Generate a brief summary from full output.
|
|
1003
|
+
*/
|
|
1004
|
+
generateResultSummary(fullOutput) {
|
|
1005
|
+
const firstLine = fullOutput.split('\n')[0].trim();
|
|
1006
|
+
if (firstLine.length <= 150) {
|
|
1007
|
+
return firstLine;
|
|
1008
|
+
}
|
|
1009
|
+
return firstLine.slice(0, 147) + '...';
|
|
1010
|
+
}
|
|
1011
|
+
// ===========================================================================
|
|
1012
|
+
// PENDING PLANS (Plan Mode Support)
|
|
1013
|
+
// ===========================================================================
|
|
1014
|
+
/**
|
|
1015
|
+
* Check if pending plans feature is available.
|
|
1016
|
+
*/
|
|
1017
|
+
hasPendingPlansFeature() {
|
|
1018
|
+
return this.features.pendingPlans;
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
* Save a pending plan to the database.
|
|
1022
|
+
*/
|
|
1023
|
+
savePendingPlan(plan, sessionId) {
|
|
1024
|
+
if (!this.features.pendingPlans || !this.stmts.insertPendingPlan) {
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
const sid = sessionId ?? this.currentSessionId;
|
|
1028
|
+
if (!sid)
|
|
1029
|
+
return;
|
|
1030
|
+
const now = new Date().toISOString();
|
|
1031
|
+
// Check if plan already exists
|
|
1032
|
+
const existing = this.getPendingPlan(sid);
|
|
1033
|
+
if (existing && existing.id === plan.id) {
|
|
1034
|
+
// Update existing plan
|
|
1035
|
+
this.stmts.updatePendingPlan?.run({
|
|
1036
|
+
id: plan.id,
|
|
1037
|
+
proposedChanges: JSON.stringify(plan.proposedChanges),
|
|
1038
|
+
explorationSummary: plan.explorationSummary || null,
|
|
1039
|
+
status: plan.status,
|
|
1040
|
+
updatedAt: now,
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
else {
|
|
1044
|
+
// Delete any existing pending plan first
|
|
1045
|
+
if (existing) {
|
|
1046
|
+
this.stmts.deletePendingPlan?.run(existing.id);
|
|
1047
|
+
}
|
|
1048
|
+
// Insert new plan
|
|
1049
|
+
this.stmts.insertPendingPlan.run({
|
|
1050
|
+
id: plan.id,
|
|
1051
|
+
sessionId: sid,
|
|
1052
|
+
task: plan.task,
|
|
1053
|
+
proposedChanges: JSON.stringify(plan.proposedChanges),
|
|
1054
|
+
explorationSummary: plan.explorationSummary || null,
|
|
1055
|
+
status: plan.status,
|
|
1056
|
+
createdAt: plan.createdAt,
|
|
1057
|
+
updatedAt: now,
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Get the pending plan for a session.
|
|
1063
|
+
* Returns the most recent pending plan, or null if none.
|
|
1064
|
+
*/
|
|
1065
|
+
getPendingPlan(sessionId) {
|
|
1066
|
+
if (!this.features.pendingPlans || !this.stmts.getPendingPlan) {
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
const sid = sessionId ?? this.currentSessionId;
|
|
1070
|
+
if (!sid)
|
|
1071
|
+
return null;
|
|
1072
|
+
const row = this.stmts.getPendingPlan.get(sid);
|
|
1073
|
+
if (!row)
|
|
1074
|
+
return null;
|
|
1075
|
+
return {
|
|
1076
|
+
id: row.id,
|
|
1077
|
+
task: row.task,
|
|
1078
|
+
createdAt: row.createdAt,
|
|
1079
|
+
updatedAt: row.updatedAt,
|
|
1080
|
+
proposedChanges: JSON.parse(row.proposedChanges),
|
|
1081
|
+
explorationSummary: row.explorationSummary || '',
|
|
1082
|
+
status: row.status,
|
|
1083
|
+
sessionId: row.sessionId,
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Update the status of a pending plan.
|
|
1088
|
+
*/
|
|
1089
|
+
updatePlanStatus(planId, status) {
|
|
1090
|
+
if (!this.features.pendingPlans || !this.stmts.updatePendingPlan) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
const now = new Date().toISOString();
|
|
1094
|
+
// We need to get the plan first to preserve other fields
|
|
1095
|
+
// Since we update by id, we do a direct update here
|
|
1096
|
+
this.db.prepare(`
|
|
1097
|
+
UPDATE pending_plans SET status = ?, updated_at = ? WHERE id = ?
|
|
1098
|
+
`).run(status, now, planId);
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Delete a pending plan.
|
|
1102
|
+
*/
|
|
1103
|
+
deletePendingPlan(planId) {
|
|
1104
|
+
if (!this.features.pendingPlans || !this.stmts.deletePendingPlan) {
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
this.stmts.deletePendingPlan.run(planId);
|
|
1108
|
+
}
|
|
1109
|
+
// ===========================================================================
|
|
1110
|
+
// SESSION MANIFEST (Handoff Support)
|
|
1111
|
+
// ===========================================================================
|
|
1112
|
+
/**
|
|
1113
|
+
* Export a complete session manifest for handoff.
|
|
1114
|
+
* Contains all information needed for another agent/human to pick up the work.
|
|
1115
|
+
*/
|
|
1116
|
+
exportSessionManifest(sessionId) {
|
|
1117
|
+
const sid = sessionId ?? this.currentSessionId;
|
|
1118
|
+
if (!sid)
|
|
1119
|
+
return undefined;
|
|
1120
|
+
const session = this.getSessionMetadata(sid);
|
|
1121
|
+
if (!session)
|
|
1122
|
+
return undefined;
|
|
1123
|
+
// Collect all session state
|
|
1124
|
+
const goals = this.listGoals(sid);
|
|
1125
|
+
const activeGoals = goals.filter(g => g.status === 'active');
|
|
1126
|
+
const completedGoals = goals.filter(g => g.status === 'completed');
|
|
1127
|
+
const junctures = this.listJunctures(sid, 20);
|
|
1128
|
+
const workerResults = this.listWorkerResults(sid);
|
|
1129
|
+
const entries = this.loadSession(sid);
|
|
1130
|
+
// Count message types from entries
|
|
1131
|
+
let messageCount = entries.filter(e => e.type === 'message').length;
|
|
1132
|
+
const toolCallCount = entries.filter(e => e.type === 'tool_call').length;
|
|
1133
|
+
const compactionCount = entries.filter(e => e.type === 'compaction').length;
|
|
1134
|
+
// If no messages in entries, check the latest checkpoint (where messages are actually stored)
|
|
1135
|
+
if (messageCount === 0) {
|
|
1136
|
+
const checkpoint = this.loadLatestCheckpoint(sid);
|
|
1137
|
+
if (checkpoint?.state?.messages && Array.isArray(checkpoint.state.messages)) {
|
|
1138
|
+
messageCount = checkpoint.state.messages.length;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
return {
|
|
1142
|
+
version: '1.0',
|
|
1143
|
+
exportedAt: new Date().toISOString(),
|
|
1144
|
+
session: {
|
|
1145
|
+
id: session.id,
|
|
1146
|
+
name: session.name,
|
|
1147
|
+
createdAt: session.createdAt,
|
|
1148
|
+
lastActiveAt: session.lastActiveAt,
|
|
1149
|
+
summary: session.summary,
|
|
1150
|
+
},
|
|
1151
|
+
state: {
|
|
1152
|
+
messageCount,
|
|
1153
|
+
toolCallCount,
|
|
1154
|
+
compactionCount,
|
|
1155
|
+
tokenCount: session.tokenCount,
|
|
1156
|
+
costUsd: session.costUsd,
|
|
1157
|
+
},
|
|
1158
|
+
goals: {
|
|
1159
|
+
active: activeGoals.map(g => ({
|
|
1160
|
+
id: g.id,
|
|
1161
|
+
text: g.goalText,
|
|
1162
|
+
priority: g.priority,
|
|
1163
|
+
progress: g.progressTotal
|
|
1164
|
+
? `${g.progressCurrent}/${g.progressTotal}`
|
|
1165
|
+
: undefined,
|
|
1166
|
+
})),
|
|
1167
|
+
completed: completedGoals.map(g => ({
|
|
1168
|
+
id: g.id,
|
|
1169
|
+
text: g.goalText,
|
|
1170
|
+
completedAt: g.completedAt,
|
|
1171
|
+
})),
|
|
1172
|
+
},
|
|
1173
|
+
keyMoments: junctures.map(j => ({
|
|
1174
|
+
type: j.type,
|
|
1175
|
+
description: j.description,
|
|
1176
|
+
outcome: j.outcome,
|
|
1177
|
+
createdAt: j.createdAt,
|
|
1178
|
+
})),
|
|
1179
|
+
workerResults: workerResults.map(r => ({
|
|
1180
|
+
id: r.id,
|
|
1181
|
+
task: r.taskDescription,
|
|
1182
|
+
status: r.status,
|
|
1183
|
+
summary: r.summary,
|
|
1184
|
+
model: r.modelUsed,
|
|
1185
|
+
})),
|
|
1186
|
+
resumption: {
|
|
1187
|
+
currentSessionId: sid,
|
|
1188
|
+
canResume: true,
|
|
1189
|
+
hint: 'Load this session with /load ' + sid.slice(-8),
|
|
1190
|
+
},
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Export session as human-readable markdown.
|
|
1195
|
+
* Suitable for printing, sharing, or reviewing offline.
|
|
1196
|
+
*/
|
|
1197
|
+
exportSessionMarkdown(sessionId) {
|
|
1198
|
+
const manifest = this.exportSessionManifest(sessionId);
|
|
1199
|
+
if (!manifest)
|
|
1200
|
+
return '# Session Not Found\n';
|
|
1201
|
+
const lines = [];
|
|
1202
|
+
// Header
|
|
1203
|
+
lines.push(`# Session Handoff: ${manifest.session.name || manifest.session.id}`);
|
|
1204
|
+
lines.push('');
|
|
1205
|
+
lines.push(`> Exported: ${manifest.exportedAt}`);
|
|
1206
|
+
lines.push(`> Session ID: \`${manifest.session.id}\``);
|
|
1207
|
+
lines.push('');
|
|
1208
|
+
// Summary
|
|
1209
|
+
if (manifest.session.summary) {
|
|
1210
|
+
lines.push('## Summary');
|
|
1211
|
+
lines.push('');
|
|
1212
|
+
lines.push(manifest.session.summary);
|
|
1213
|
+
lines.push('');
|
|
1214
|
+
}
|
|
1215
|
+
// State
|
|
1216
|
+
lines.push('## Session State');
|
|
1217
|
+
lines.push('');
|
|
1218
|
+
lines.push(`- Messages: ${manifest.state.messageCount}`);
|
|
1219
|
+
lines.push(`- Tool Calls: ${manifest.state.toolCallCount}`);
|
|
1220
|
+
lines.push(`- Compactions: ${manifest.state.compactionCount}`);
|
|
1221
|
+
lines.push(`- Tokens: ${manifest.state.tokenCount?.toLocaleString() ?? 'N/A'}`);
|
|
1222
|
+
if (manifest.state.costUsd) {
|
|
1223
|
+
lines.push(`- Cost: $${manifest.state.costUsd.toFixed(4)}`);
|
|
1224
|
+
}
|
|
1225
|
+
lines.push('');
|
|
1226
|
+
// Active Goals
|
|
1227
|
+
if (manifest.goals.active.length > 0) {
|
|
1228
|
+
lines.push('## Active Goals');
|
|
1229
|
+
lines.push('');
|
|
1230
|
+
for (const goal of manifest.goals.active) {
|
|
1231
|
+
const priority = goal.priority === 1 ? ' **[HIGH]**' : goal.priority === 3 ? ' [low]' : '';
|
|
1232
|
+
const progress = goal.progress ? ` (${goal.progress})` : '';
|
|
1233
|
+
lines.push(`- [ ] ${goal.text}${progress}${priority}`);
|
|
1234
|
+
}
|
|
1235
|
+
lines.push('');
|
|
1236
|
+
}
|
|
1237
|
+
// Completed Goals
|
|
1238
|
+
if (manifest.goals.completed.length > 0) {
|
|
1239
|
+
lines.push('## Completed Goals');
|
|
1240
|
+
lines.push('');
|
|
1241
|
+
for (const goal of manifest.goals.completed) {
|
|
1242
|
+
lines.push(`- [x] ${goal.text}`);
|
|
1243
|
+
}
|
|
1244
|
+
lines.push('');
|
|
1245
|
+
}
|
|
1246
|
+
// Key Moments
|
|
1247
|
+
if (manifest.keyMoments.length > 0) {
|
|
1248
|
+
lines.push('## Key Moments');
|
|
1249
|
+
lines.push('');
|
|
1250
|
+
for (const moment of manifest.keyMoments) {
|
|
1251
|
+
const icon = moment.type === 'failure' ? '❌' :
|
|
1252
|
+
moment.type === 'breakthrough' ? '⭐' :
|
|
1253
|
+
moment.type === 'decision' ? '→' : '↻';
|
|
1254
|
+
lines.push(`### ${icon} ${moment.type.charAt(0).toUpperCase() + moment.type.slice(1)}`);
|
|
1255
|
+
lines.push('');
|
|
1256
|
+
lines.push(moment.description);
|
|
1257
|
+
if (moment.outcome) {
|
|
1258
|
+
lines.push('');
|
|
1259
|
+
lines.push(`**Outcome:** ${moment.outcome}`);
|
|
1260
|
+
}
|
|
1261
|
+
lines.push('');
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
// Worker Results
|
|
1265
|
+
if (manifest.workerResults.length > 0) {
|
|
1266
|
+
lines.push('## Worker Results');
|
|
1267
|
+
lines.push('');
|
|
1268
|
+
for (const result of manifest.workerResults) {
|
|
1269
|
+
const status = result.status === 'success' ? '✅' :
|
|
1270
|
+
result.status === 'error' ? '❌' : '⏳';
|
|
1271
|
+
lines.push(`- ${status} **${result.task}**`);
|
|
1272
|
+
if (result.summary) {
|
|
1273
|
+
lines.push(` - ${result.summary}`);
|
|
1274
|
+
}
|
|
1275
|
+
if (result.model) {
|
|
1276
|
+
lines.push(` - Model: ${result.model}`);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
lines.push('');
|
|
1280
|
+
}
|
|
1281
|
+
// Resumption
|
|
1282
|
+
lines.push('## How to Resume');
|
|
1283
|
+
lines.push('');
|
|
1284
|
+
lines.push('```bash');
|
|
1285
|
+
lines.push(`attocode --load ${manifest.resumption.currentSessionId}`);
|
|
1286
|
+
lines.push('```');
|
|
1287
|
+
lines.push('');
|
|
1288
|
+
lines.push('Or within attocode:');
|
|
1289
|
+
lines.push('```');
|
|
1290
|
+
lines.push(manifest.resumption.hint);
|
|
1291
|
+
lines.push('```');
|
|
1292
|
+
return lines.join('\n');
|
|
1293
|
+
}
|
|
1294
|
+
// ===========================================================================
|
|
1295
|
+
// MIGRATION
|
|
1296
|
+
// ===========================================================================
|
|
1297
|
+
/**
|
|
1298
|
+
* Migrate sessions from JSONL format to SQLite.
|
|
1299
|
+
*/
|
|
1300
|
+
async migrateFromJSONL(jsonlDir) {
|
|
1301
|
+
const indexPath = join(jsonlDir, 'index.json');
|
|
1302
|
+
let migrated = 0;
|
|
1303
|
+
let failed = 0;
|
|
1304
|
+
if (!existsSync(indexPath)) {
|
|
1305
|
+
return { migrated: 0, failed: 0 };
|
|
1306
|
+
}
|
|
1307
|
+
try {
|
|
1308
|
+
const indexContent = readFileSync(indexPath, 'utf-8');
|
|
1309
|
+
const index = JSON.parse(indexContent);
|
|
1310
|
+
for (const meta of index.sessions) {
|
|
1311
|
+
try {
|
|
1312
|
+
// Check if already migrated
|
|
1313
|
+
const existing = this.getSessionMetadata(meta.id);
|
|
1314
|
+
if (existing) {
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1317
|
+
// Insert session metadata
|
|
1318
|
+
this.stmts.insertSession.run({
|
|
1319
|
+
id: meta.id,
|
|
1320
|
+
name: meta.name || null,
|
|
1321
|
+
createdAt: meta.createdAt,
|
|
1322
|
+
lastActiveAt: meta.lastActiveAt,
|
|
1323
|
+
messageCount: meta.messageCount,
|
|
1324
|
+
tokenCount: meta.tokenCount,
|
|
1325
|
+
});
|
|
1326
|
+
// Load and migrate entries
|
|
1327
|
+
const sessionPath = join(jsonlDir, `${meta.id}.jsonl`);
|
|
1328
|
+
if (existsSync(sessionPath)) {
|
|
1329
|
+
const content = readFileSync(sessionPath, 'utf-8');
|
|
1330
|
+
for (const line of content.split('\n')) {
|
|
1331
|
+
if (line.trim()) {
|
|
1332
|
+
try {
|
|
1333
|
+
const entry = JSON.parse(line);
|
|
1334
|
+
this.stmts.insertEntry.run({
|
|
1335
|
+
sessionId: meta.id,
|
|
1336
|
+
timestamp: entry.timestamp,
|
|
1337
|
+
type: entry.type,
|
|
1338
|
+
data: JSON.stringify(entry.data),
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
catch {
|
|
1342
|
+
// Skip corrupted lines
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
migrated++;
|
|
1348
|
+
}
|
|
1349
|
+
catch (err) {
|
|
1350
|
+
console.error(`Failed to migrate session ${meta.id}:`, err);
|
|
1351
|
+
failed++;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
catch (err) {
|
|
1356
|
+
console.error('Failed to read JSONL index:', err);
|
|
1357
|
+
}
|
|
1358
|
+
return { migrated, failed };
|
|
1359
|
+
}
|
|
1360
|
+
// ===========================================================================
|
|
1361
|
+
// LIFECYCLE
|
|
1362
|
+
// ===========================================================================
|
|
1363
|
+
/**
|
|
1364
|
+
* Prune old sessions if over limit.
|
|
1365
|
+
*/
|
|
1366
|
+
pruneOldSessions() {
|
|
1367
|
+
const sessions = this.listSessions();
|
|
1368
|
+
if (sessions.length > this.config.maxSessions) {
|
|
1369
|
+
const toDelete = sessions.slice(this.config.maxSessions);
|
|
1370
|
+
for (const session of toDelete) {
|
|
1371
|
+
this.stmts.deleteSession.run(session.id);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Subscribe to events.
|
|
1377
|
+
*/
|
|
1378
|
+
on(listener) {
|
|
1379
|
+
this.listeners.push(listener);
|
|
1380
|
+
return () => {
|
|
1381
|
+
const idx = this.listeners.indexOf(listener);
|
|
1382
|
+
if (idx >= 0)
|
|
1383
|
+
this.listeners.splice(idx, 1);
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Emit an event.
|
|
1388
|
+
*/
|
|
1389
|
+
emit(event) {
|
|
1390
|
+
for (const listener of this.listeners) {
|
|
1391
|
+
try {
|
|
1392
|
+
listener(event);
|
|
1393
|
+
}
|
|
1394
|
+
catch {
|
|
1395
|
+
// Ignore listener errors
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Close the database connection.
|
|
1401
|
+
*/
|
|
1402
|
+
close() {
|
|
1403
|
+
this.db.close();
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* Cleanup - same as close for compatibility.
|
|
1407
|
+
*/
|
|
1408
|
+
async cleanup() {
|
|
1409
|
+
this.close();
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
// =============================================================================
|
|
1413
|
+
// FACTORY
|
|
1414
|
+
// =============================================================================
|
|
1415
|
+
/**
|
|
1416
|
+
* Create and initialize a SQLite session store.
|
|
1417
|
+
*/
|
|
1418
|
+
export async function createSQLiteStore(config) {
|
|
1419
|
+
const store = new SQLiteStore(config);
|
|
1420
|
+
await store.initialize();
|
|
1421
|
+
return store;
|
|
1422
|
+
}
|
|
1423
|
+
//# sourceMappingURL=sqlite-store.js.map
|