@vinaes/succ 1.4.0 → 1.5.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -10
- package/dist/cli.js +81 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/agents-md.d.ts.map +1 -1
- package/dist/commands/agents-md.js +3 -2
- package/dist/commands/agents-md.js.map +1 -1
- package/dist/commands/analyze-profile.d.ts.map +1 -1
- package/dist/commands/analyze-profile.js +32 -8
- package/dist/commands/analyze-profile.js.map +1 -1
- package/dist/commands/analyze-recursive.d.ts.map +1 -1
- package/dist/commands/analyze-recursive.js +6 -2
- package/dist/commands/analyze-recursive.js.map +1 -1
- package/dist/commands/analyze-utils.d.ts.map +1 -1
- package/dist/commands/analyze-utils.js +17 -4
- package/dist/commands/analyze-utils.js.map +1 -1
- package/dist/commands/benchmark-quality.d.ts.map +1 -1
- package/dist/commands/benchmark-quality.js +11 -4
- package/dist/commands/benchmark-quality.js.map +1 -1
- package/dist/commands/benchmark-sqlite-vec.d.ts.map +1 -1
- package/dist/commands/benchmark-sqlite-vec.js +4 -0
- package/dist/commands/benchmark-sqlite-vec.js.map +1 -1
- package/dist/commands/benchmark.d.ts.map +1 -1
- package/dist/commands/benchmark.js +5 -1
- package/dist/commands/benchmark.js.map +1 -1
- package/dist/commands/codex-chat.d.ts +8 -0
- package/dist/commands/codex-chat.d.ts.map +1 -0
- package/dist/commands/codex-chat.js +161 -0
- package/dist/commands/codex-chat.js.map +1 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +32 -4
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +13 -4
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/index-code.d.ts +4 -0
- package/dist/commands/index-code.d.ts.map +1 -1
- package/dist/commands/index-code.js +1 -1
- package/dist/commands/index-code.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +305 -203
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/memories.d.ts.map +1 -1
- package/dist/commands/memories.js +25 -14
- package/dist/commands/memories.js.map +1 -1
- package/dist/commands/progress.d.ts.map +1 -1
- package/dist/commands/progress.js +3 -2
- package/dist/commands/progress.js.map +1 -1
- package/dist/commands/reindex.d.ts.map +1 -1
- package/dist/commands/reindex.js +54 -36
- package/dist/commands/reindex.js.map +1 -1
- package/dist/commands/retention.d.ts.map +1 -1
- package/dist/commands/retention.js +7 -5
- package/dist/commands/retention.js.map +1 -1
- package/dist/commands/scan-code.d.ts +76 -0
- package/dist/commands/scan-code.d.ts.map +1 -0
- package/dist/commands/scan-code.js +385 -0
- package/dist/commands/scan-code.js.map +1 -0
- package/dist/commands/score.d.ts.map +1 -1
- package/dist/commands/score.js +3 -2
- package/dist/commands/score.js.map +1 -1
- package/dist/commands/session.d.ts +33 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +163 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +254 -15
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/soul.js +3 -2
- package/dist/commands/soul.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +14 -5
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +13 -4
- package/dist/commands/watch.js.map +1 -1
- package/dist/daemon/analyzer.d.ts.map +1 -1
- package/dist/daemon/analyzer.js +21 -5
- package/dist/daemon/analyzer.js.map +1 -1
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +32 -8
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/routes/analyzer.d.ts +3 -0
- package/dist/daemon/routes/analyzer.d.ts.map +1 -0
- package/dist/daemon/routes/analyzer.js +27 -0
- package/dist/daemon/routes/analyzer.js.map +1 -0
- package/dist/daemon/routes/hooks.d.ts +14 -0
- package/dist/daemon/routes/hooks.d.ts.map +1 -0
- package/dist/daemon/routes/hooks.js +1212 -0
- package/dist/daemon/routes/hooks.js.map +1 -0
- package/dist/daemon/routes/memory.d.ts +4 -0
- package/dist/daemon/routes/memory.d.ts.map +1 -0
- package/dist/daemon/routes/memory.js +71 -0
- package/dist/daemon/routes/memory.js.map +1 -0
- package/dist/daemon/routes/reflection.d.ts +10 -0
- package/dist/daemon/routes/reflection.d.ts.map +1 -0
- package/dist/daemon/routes/reflection.js +397 -0
- package/dist/daemon/routes/reflection.js.map +1 -0
- package/dist/daemon/routes/search.d.ts +5 -0
- package/dist/daemon/routes/search.d.ts.map +1 -0
- package/dist/daemon/routes/search.js +93 -0
- package/dist/daemon/routes/search.js.map +1 -0
- package/dist/daemon/routes/sessions.d.ts +3 -0
- package/dist/daemon/routes/sessions.d.ts.map +1 -0
- package/dist/daemon/routes/sessions.js +160 -0
- package/dist/daemon/routes/sessions.js.map +1 -0
- package/dist/daemon/routes/skills.d.ts +3 -0
- package/dist/daemon/routes/skills.d.ts.map +1 -0
- package/dist/daemon/routes/skills.js +36 -0
- package/dist/daemon/routes/skills.js.map +1 -0
- package/dist/daemon/routes/status.d.ts +3 -0
- package/dist/daemon/routes/status.d.ts.map +1 -0
- package/dist/daemon/routes/status.js +47 -0
- package/dist/daemon/routes/status.js.map +1 -0
- package/dist/daemon/routes/types.d.ts +240 -0
- package/dist/daemon/routes/types.d.ts.map +1 -0
- package/dist/daemon/routes/types.js +97 -0
- package/dist/daemon/routes/types.js.map +1 -0
- package/dist/daemon/routes/versioning.d.ts +27 -0
- package/dist/daemon/routes/versioning.d.ts.map +1 -0
- package/dist/daemon/routes/versioning.js +44 -0
- package/dist/daemon/routes/versioning.js.map +1 -0
- package/dist/daemon/routes/watcher.d.ts +3 -0
- package/dist/daemon/routes/watcher.d.ts.map +1 -0
- package/dist/daemon/routes/watcher.js +28 -0
- package/dist/daemon/routes/watcher.js.map +1 -0
- package/dist/daemon/service.d.ts +5 -23
- package/dist/daemon/service.d.ts.map +1 -1
- package/dist/daemon/service.js +201 -933
- package/dist/daemon/service.js.map +1 -1
- package/dist/daemon/session-processor.d.ts +4 -8
- package/dist/daemon/session-processor.d.ts.map +1 -1
- package/dist/daemon/session-processor.js +39 -38
- package/dist/daemon/session-processor.js.map +1 -1
- package/dist/lib/ai-readiness.d.ts.map +1 -1
- package/dist/lib/ai-readiness.js +33 -8
- package/dist/lib/ai-readiness.js.map +1 -1
- package/dist/lib/analyze-state.d.ts.map +1 -1
- package/dist/lib/analyze-state.js +25 -3
- package/dist/lib/analyze-state.js.map +1 -1
- package/dist/lib/auto-memory/consolidation.d.ts +41 -0
- package/dist/lib/auto-memory/consolidation.d.ts.map +1 -0
- package/dist/lib/auto-memory/consolidation.js +151 -0
- package/dist/lib/auto-memory/consolidation.js.map +1 -0
- package/dist/lib/bpe.d.ts.map +1 -1
- package/dist/lib/bpe.js +9 -10
- package/dist/lib/bpe.js.map +1 -1
- package/dist/lib/brain-export.d.ts +65 -0
- package/dist/lib/brain-export.d.ts.map +1 -0
- package/dist/lib/brain-export.js +413 -0
- package/dist/lib/brain-export.js.map +1 -0
- package/dist/lib/checkpoint.d.ts.map +1 -1
- package/dist/lib/checkpoint.js +22 -6
- package/dist/lib/checkpoint.js.map +1 -1
- package/dist/lib/chunker.d.ts.map +1 -1
- package/dist/lib/chunker.js +6 -1
- package/dist/lib/chunker.js.map +1 -1
- package/dist/lib/claude-ws-transport.d.ts.map +1 -1
- package/dist/lib/claude-ws-transport.js +12 -4
- package/dist/lib/claude-ws-transport.js.map +1 -1
- package/dist/lib/command-safety.d.ts +64 -0
- package/dist/lib/command-safety.d.ts.map +1 -0
- package/dist/lib/command-safety.js +625 -0
- package/dist/lib/command-safety.js.map +1 -0
- package/dist/lib/compact-briefing.d.ts.map +1 -1
- package/dist/lib/compact-briefing.js +10 -13
- package/dist/lib/compact-briefing.js.map +1 -1
- package/dist/lib/config-defaults.d.ts.map +1 -1
- package/dist/lib/config-defaults.js +3 -0
- package/dist/lib/config-defaults.js.map +1 -1
- package/dist/lib/config-display.d.ts +4 -0
- package/dist/lib/config-display.d.ts.map +1 -1
- package/dist/lib/config-display.js +6 -1
- package/dist/lib/config-display.js.map +1 -1
- package/dist/lib/config-types.d.ts +156 -0
- package/dist/lib/config-types.d.ts.map +1 -1
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config-validation.js +5 -0
- package/dist/lib/config-validation.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +92 -9
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/consolidate.d.ts.map +1 -1
- package/dist/lib/consolidate.js +66 -47
- package/dist/lib/consolidate.js.map +1 -1
- package/dist/lib/content-sanitizer.d.ts +29 -0
- package/dist/lib/content-sanitizer.d.ts.map +1 -0
- package/dist/lib/content-sanitizer.js +72 -0
- package/dist/lib/content-sanitizer.js.map +1 -0
- package/dist/lib/cross-repo.d.ts +44 -0
- package/dist/lib/cross-repo.d.ts.map +1 -0
- package/dist/lib/cross-repo.js +108 -0
- package/dist/lib/cross-repo.js.map +1 -0
- package/dist/lib/daemon-port.d.ts +12 -0
- package/dist/lib/daemon-port.d.ts.map +1 -0
- package/dist/lib/daemon-port.js +20 -0
- package/dist/lib/daemon-port.js.map +1 -0
- package/dist/lib/db/auto-memory.d.ts +40 -0
- package/dist/lib/db/auto-memory.d.ts.map +1 -0
- package/dist/lib/db/auto-memory.js +74 -0
- package/dist/lib/db/auto-memory.js.map +1 -0
- package/dist/lib/db/bm25-indexes.d.ts.map +1 -1
- package/dist/lib/db/bm25-indexes.js +16 -4
- package/dist/lib/db/bm25-indexes.js.map +1 -1
- package/dist/lib/db/documents.d.ts.map +1 -1
- package/dist/lib/db/documents.js +4 -1
- package/dist/lib/db/documents.js.map +1 -1
- package/dist/lib/db/global-memories.d.ts +2 -10
- package/dist/lib/db/global-memories.d.ts.map +1 -1
- package/dist/lib/db/global-memories.js +13 -6
- package/dist/lib/db/global-memories.js.map +1 -1
- package/dist/lib/db/graph.d.ts +5 -1
- package/dist/lib/db/graph.d.ts.map +1 -1
- package/dist/lib/db/graph.js +38 -8
- package/dist/lib/db/graph.js.map +1 -1
- package/dist/lib/db/hybrid-search.d.ts +4 -2
- package/dist/lib/db/hybrid-search.d.ts.map +1 -1
- package/dist/lib/db/hybrid-search.js +29 -11
- package/dist/lib/db/hybrid-search.js.map +1 -1
- package/dist/lib/db/index.d.ts +6 -1
- package/dist/lib/db/index.d.ts.map +1 -1
- package/dist/lib/db/index.js +5 -1
- package/dist/lib/db/index.js.map +1 -1
- package/dist/lib/db/memories.d.ts +19 -14
- package/dist/lib/db/memories.d.ts.map +1 -1
- package/dist/lib/db/memories.js +100 -37
- package/dist/lib/db/memories.js.map +1 -1
- package/dist/lib/db/parse-helpers.d.ts +14 -0
- package/dist/lib/db/parse-helpers.d.ts.map +1 -0
- package/dist/lib/db/parse-helpers.js +59 -0
- package/dist/lib/db/parse-helpers.js.map +1 -0
- package/dist/lib/db/recall-events.d.ts +49 -0
- package/dist/lib/db/recall-events.d.ts.map +1 -0
- package/dist/lib/db/recall-events.js +196 -0
- package/dist/lib/db/recall-events.js.map +1 -0
- package/dist/lib/db/retention.d.ts +4 -3
- package/dist/lib/db/retention.d.ts.map +1 -1
- package/dist/lib/db/retention.js +12 -1
- package/dist/lib/db/retention.js.map +1 -1
- package/dist/lib/db/schema.d.ts +2 -0
- package/dist/lib/db/schema.d.ts.map +1 -1
- package/dist/lib/db/schema.js +140 -80
- package/dist/lib/db/schema.js.map +1 -1
- package/dist/lib/db/skills.d.ts.map +1 -1
- package/dist/lib/db/skills.js +10 -6
- package/dist/lib/db/skills.js.map +1 -1
- package/dist/lib/diff-brain.d.ts +24 -0
- package/dist/lib/diff-brain.d.ts.map +1 -0
- package/dist/lib/diff-brain.js +114 -0
- package/dist/lib/diff-brain.js.map +1 -0
- package/dist/lib/diff-parser.d.ts +74 -0
- package/dist/lib/diff-parser.d.ts.map +1 -0
- package/dist/lib/diff-parser.js +200 -0
- package/dist/lib/diff-parser.js.map +1 -0
- package/dist/lib/embedding-pool.d.ts.map +1 -1
- package/dist/lib/embedding-pool.js +5 -1
- package/dist/lib/embedding-pool.js.map +1 -1
- package/dist/lib/embeddings.d.ts +12 -0
- package/dist/lib/embeddings.d.ts.map +1 -1
- package/dist/lib/embeddings.js +77 -19
- package/dist/lib/embeddings.js.map +1 -1
- package/dist/lib/errors.d.ts +2 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +4 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/fault-logger.d.ts.map +1 -1
- package/dist/lib/fault-logger.js +22 -7
- package/dist/lib/fault-logger.js.map +1 -1
- package/dist/lib/git/co-change.d.ts +39 -0
- package/dist/lib/git/co-change.d.ts.map +1 -0
- package/dist/lib/git/co-change.js +139 -0
- package/dist/lib/git/co-change.js.map +1 -0
- package/dist/lib/graph/bridge-edges.d.ts +93 -0
- package/dist/lib/graph/bridge-edges.d.ts.map +1 -0
- package/dist/lib/graph/bridge-edges.js +276 -0
- package/dist/lib/graph/bridge-edges.js.map +1 -0
- package/dist/lib/graph/centrality.d.ts +11 -0
- package/dist/lib/graph/centrality.d.ts.map +1 -1
- package/dist/lib/graph/centrality.js +51 -3
- package/dist/lib/graph/centrality.js.map +1 -1
- package/dist/lib/graph/cleanup.d.ts.map +1 -1
- package/dist/lib/graph/cleanup.js +2 -1
- package/dist/lib/graph/cleanup.js.map +1 -1
- package/dist/lib/graph/community-detection.d.ts +17 -2
- package/dist/lib/graph/community-detection.d.ts.map +1 -1
- package/dist/lib/graph/community-detection.js +147 -48
- package/dist/lib/graph/community-detection.js.map +1 -1
- package/dist/lib/graph/community-summaries.d.ts +26 -0
- package/dist/lib/graph/community-summaries.d.ts.map +1 -0
- package/dist/lib/graph/community-summaries.js +130 -0
- package/dist/lib/graph/community-summaries.js.map +1 -0
- package/dist/lib/graph/contextual-proximity.d.ts.map +1 -1
- package/dist/lib/graph/contextual-proximity.js +11 -4
- package/dist/lib/graph/contextual-proximity.js.map +1 -1
- package/dist/lib/graph/graphology-bridge.d.ts +101 -0
- package/dist/lib/graph/graphology-bridge.d.ts.map +1 -0
- package/dist/lib/graph/graphology-bridge.js +488 -0
- package/dist/lib/graph/graphology-bridge.js.map +1 -0
- package/dist/lib/graph/llm-relations.d.ts.map +1 -1
- package/dist/lib/graph/llm-relations.js +27 -5
- package/dist/lib/graph/llm-relations.js.map +1 -1
- package/dist/lib/graph-export.d.ts.map +1 -1
- package/dist/lib/graph-export.js +2 -2
- package/dist/lib/graph-export.js.map +1 -1
- package/dist/lib/graph-scheduler.d.ts +0 -5
- package/dist/lib/graph-scheduler.d.ts.map +1 -1
- package/dist/lib/graph-scheduler.js +5 -1
- package/dist/lib/graph-scheduler.js.map +1 -1
- package/dist/lib/guardrails.d.ts +50 -0
- package/dist/lib/guardrails.d.ts.map +1 -0
- package/dist/lib/guardrails.js +502 -0
- package/dist/lib/guardrails.js.map +1 -0
- package/dist/lib/hook-rules.d.ts +1 -1
- package/dist/lib/hook-rules.d.ts.map +1 -1
- package/dist/lib/hook-rules.js +8 -2
- package/dist/lib/hook-rules.js.map +1 -1
- package/dist/lib/ifc/file-labels.d.ts +35 -0
- package/dist/lib/ifc/file-labels.d.ts.map +1 -0
- package/dist/lib/ifc/file-labels.js +208 -0
- package/dist/lib/ifc/file-labels.js.map +1 -0
- package/dist/lib/ifc/label.d.ts +38 -0
- package/dist/lib/ifc/label.d.ts.map +1 -0
- package/dist/lib/ifc/label.js +80 -0
- package/dist/lib/ifc/label.js.map +1 -0
- package/dist/lib/ifc/session-ifc.d.ts +92 -0
- package/dist/lib/ifc/session-ifc.d.ts.map +1 -0
- package/dist/lib/ifc/session-ifc.js +222 -0
- package/dist/lib/ifc/session-ifc.js.map +1 -0
- package/dist/lib/indexer.js +2 -2
- package/dist/lib/indexer.js.map +1 -1
- package/dist/lib/injection-detector.d.ts +83 -0
- package/dist/lib/injection-detector.d.ts.map +1 -0
- package/dist/lib/injection-detector.js +586 -0
- package/dist/lib/injection-detector.js.map +1 -0
- package/dist/lib/injection-semantic.d.ts +31 -0
- package/dist/lib/injection-semantic.d.ts.map +1 -0
- package/dist/lib/injection-semantic.js +230 -0
- package/dist/lib/injection-semantic.js.map +1 -0
- package/dist/lib/llm.d.ts.map +1 -1
- package/dist/lib/llm.js +19 -4
- package/dist/lib/llm.js.map +1 -1
- package/dist/lib/lock.d.ts.map +1 -1
- package/dist/lib/lock.js +24 -3
- package/dist/lib/lock.js.map +1 -1
- package/dist/lib/md-fetch.d.ts.map +1 -1
- package/dist/lib/md-fetch.js +9 -2
- package/dist/lib/md-fetch.js.map +1 -1
- package/dist/lib/observability.d.ts +75 -0
- package/dist/lib/observability.d.ts.map +1 -0
- package/dist/lib/observability.js +201 -0
- package/dist/lib/observability.js.map +1 -0
- package/dist/lib/ort-session.d.ts +26 -0
- package/dist/lib/ort-session.d.ts.map +1 -1
- package/dist/lib/ort-session.js +107 -3
- package/dist/lib/ort-session.js.map +1 -1
- package/dist/lib/prd/codebase-context.d.ts.map +1 -1
- package/dist/lib/prd/codebase-context.js +9 -2
- package/dist/lib/prd/codebase-context.js.map +1 -1
- package/dist/lib/prd/context.d.ts.map +1 -1
- package/dist/lib/prd/context.js +11 -3
- package/dist/lib/prd/context.js.map +1 -1
- package/dist/lib/prd/export.js +1 -1
- package/dist/lib/prd/export.js.map +1 -1
- package/dist/lib/prd/generate.d.ts.map +1 -1
- package/dist/lib/prd/generate.js +17 -4
- package/dist/lib/prd/generate.js.map +1 -1
- package/dist/lib/prd/parse.d.ts.map +1 -1
- package/dist/lib/prd/parse.js +6 -1
- package/dist/lib/prd/parse.js.map +1 -1
- package/dist/lib/prd/runner.d.ts +1 -2
- package/dist/lib/prd/runner.d.ts.map +1 -1
- package/dist/lib/prd/runner.js +43 -32
- package/dist/lib/prd/runner.js.map +1 -1
- package/dist/lib/prd/worktree.d.ts +1 -2
- package/dist/lib/prd/worktree.d.ts.map +1 -1
- package/dist/lib/prd/worktree.js +62 -70
- package/dist/lib/prd/worktree.js.map +1 -1
- package/dist/lib/precompute-context.d.ts.map +1 -1
- package/dist/lib/precompute-context.js +15 -34
- package/dist/lib/precompute-context.js.map +1 -1
- package/dist/lib/pricing.d.ts.map +1 -1
- package/dist/lib/pricing.js +5 -1
- package/dist/lib/pricing.js.map +1 -1
- package/dist/lib/process-registry.js +3 -3
- package/dist/lib/process-registry.js.map +1 -1
- package/dist/lib/public-api.d.ts +41 -1
- package/dist/lib/public-api.d.ts.map +1 -1
- package/dist/lib/public-api.js +38 -0
- package/dist/lib/public-api.js.map +1 -1
- package/dist/lib/quality.d.ts.map +1 -1
- package/dist/lib/quality.js +15 -6
- package/dist/lib/quality.js.map +1 -1
- package/dist/lib/query-expansion.d.ts +32 -0
- package/dist/lib/query-expansion.d.ts.map +1 -1
- package/dist/lib/query-expansion.js +62 -1
- package/dist/lib/query-expansion.js.map +1 -1
- package/dist/lib/reference-embeddings.d.ts.map +1 -1
- package/dist/lib/reference-embeddings.js +17 -4
- package/dist/lib/reference-embeddings.js.map +1 -1
- package/dist/lib/reflection-synthesizer.d.ts.map +1 -1
- package/dist/lib/reflection-synthesizer.js +7 -1
- package/dist/lib/reflection-synthesizer.js.map +1 -1
- package/dist/lib/reranker.d.ts +41 -0
- package/dist/lib/reranker.d.ts.map +1 -0
- package/dist/lib/reranker.js +294 -0
- package/dist/lib/reranker.js.map +1 -0
- package/dist/lib/retrieval-feedback.d.ts +100 -0
- package/dist/lib/retrieval-feedback.d.ts.map +1 -0
- package/dist/lib/retrieval-feedback.js +174 -0
- package/dist/lib/retrieval-feedback.js.map +1 -0
- package/dist/lib/review/context-pack.d.ts +58 -0
- package/dist/lib/review/context-pack.d.ts.map +1 -0
- package/dist/lib/review/context-pack.js +300 -0
- package/dist/lib/review/context-pack.js.map +1 -0
- package/dist/lib/search/hierarchical-summaries.d.ts +65 -0
- package/dist/lib/search/hierarchical-summaries.d.ts.map +1 -0
- package/dist/lib/search/hierarchical-summaries.js +423 -0
- package/dist/lib/search/hierarchical-summaries.js.map +1 -0
- package/dist/lib/search/hyde.d.ts +27 -0
- package/dist/lib/search/hyde.d.ts.map +1 -0
- package/dist/lib/search/hyde.js +141 -0
- package/dist/lib/search/hyde.js.map +1 -0
- package/dist/lib/search/late-chunking.d.ts +53 -0
- package/dist/lib/search/late-chunking.d.ts.map +1 -0
- package/dist/lib/search/late-chunking.js +230 -0
- package/dist/lib/search/late-chunking.js.map +1 -0
- package/dist/lib/search/ppr-retrieval.d.ts +49 -0
- package/dist/lib/search/ppr-retrieval.d.ts.map +1 -0
- package/dist/lib/search/ppr-retrieval.js +135 -0
- package/dist/lib/search/ppr-retrieval.js.map +1 -0
- package/dist/lib/search/repo-map.d.ts +43 -0
- package/dist/lib/search/repo-map.d.ts.map +1 -0
- package/dist/lib/search/repo-map.js +165 -0
- package/dist/lib/search/repo-map.js.map +1 -0
- package/dist/lib/session-analyzer.d.ts +90 -0
- package/dist/lib/session-analyzer.d.ts.map +1 -0
- package/dist/lib/session-analyzer.js +467 -0
- package/dist/lib/session-analyzer.js.map +1 -0
- package/dist/lib/session-observations.d.ts.map +1 -1
- package/dist/lib/session-observations.js +13 -3
- package/dist/lib/session-observations.js.map +1 -1
- package/dist/lib/session-summary.d.ts.map +1 -1
- package/dist/lib/session-summary.js +57 -50
- package/dist/lib/session-summary.js.map +1 -1
- package/dist/lib/session-surgeon.d.ts +53 -0
- package/dist/lib/session-surgeon.d.ts.map +1 -0
- package/dist/lib/session-surgeon.js +501 -0
- package/dist/lib/session-surgeon.js.map +1 -0
- package/dist/lib/similarity-utils.d.ts +26 -0
- package/dist/lib/similarity-utils.d.ts.map +1 -0
- package/dist/lib/similarity-utils.js +66 -0
- package/dist/lib/similarity-utils.js.map +1 -0
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/skills.js +11 -11
- package/dist/lib/skills.js.map +1 -1
- package/dist/lib/storage/backends/interface.d.ts +13 -3
- package/dist/lib/storage/backends/interface.d.ts.map +1 -1
- package/dist/lib/storage/backends/postgresql.d.ts +52 -3
- package/dist/lib/storage/backends/postgresql.d.ts.map +1 -1
- package/dist/lib/storage/backends/postgresql.js +694 -49
- package/dist/lib/storage/backends/postgresql.js.map +1 -1
- package/dist/lib/storage/benchmark.js +2 -2
- package/dist/lib/storage/benchmark.js.map +1 -1
- package/dist/lib/storage/dispatcher/base.d.ts +114 -0
- package/dist/lib/storage/dispatcher/base.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/base.js +160 -0
- package/dist/lib/storage/dispatcher/base.js.map +1 -0
- package/dist/lib/storage/dispatcher/documents.d.ts +25 -0
- package/dist/lib/storage/dispatcher/documents.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/documents.js +194 -0
- package/dist/lib/storage/dispatcher/documents.js.map +1 -0
- package/dist/lib/storage/dispatcher/embeddings.d.ts +34 -0
- package/dist/lib/storage/dispatcher/embeddings.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/embeddings.js +144 -0
- package/dist/lib/storage/dispatcher/embeddings.js.map +1 -0
- package/dist/lib/storage/dispatcher/export-import.d.ts +139 -0
- package/dist/lib/storage/dispatcher/export-import.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/export-import.js +191 -0
- package/dist/lib/storage/dispatcher/export-import.js.map +1 -0
- package/dist/lib/storage/dispatcher/file-hashes.d.ts +13 -0
- package/dist/lib/storage/dispatcher/file-hashes.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/file-hashes.js +36 -0
- package/dist/lib/storage/dispatcher/file-hashes.js.map +1 -0
- package/dist/lib/storage/dispatcher/global-memories.d.ts +28 -0
- package/dist/lib/storage/dispatcher/global-memories.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/global-memories.js +151 -0
- package/dist/lib/storage/dispatcher/global-memories.js.map +1 -0
- package/dist/lib/storage/dispatcher/graph.d.ts +32 -0
- package/dist/lib/storage/dispatcher/graph.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/graph.js +146 -0
- package/dist/lib/storage/dispatcher/graph.js.map +1 -0
- package/dist/lib/storage/dispatcher/index.d.ts +34 -0
- package/dist/lib/storage/dispatcher/index.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/index.js +139 -0
- package/dist/lib/storage/dispatcher/index.js.map +1 -0
- package/dist/lib/storage/dispatcher/memories.d.ts +65 -0
- package/dist/lib/storage/dispatcher/memories.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/memories.js +466 -0
- package/dist/lib/storage/dispatcher/memories.js.map +1 -0
- package/dist/lib/storage/dispatcher/mixin-helper.d.ts +6 -0
- package/dist/lib/storage/dispatcher/mixin-helper.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/mixin-helper.js +10 -0
- package/dist/lib/storage/dispatcher/mixin-helper.js.map +1 -0
- package/dist/lib/storage/dispatcher/retention.d.ts +20 -0
- package/dist/lib/storage/dispatcher/retention.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/retention.js +123 -0
- package/dist/lib/storage/dispatcher/retention.js.map +1 -0
- package/dist/lib/storage/dispatcher/search.d.ts +34 -0
- package/dist/lib/storage/dispatcher/search.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/search.js +222 -0
- package/dist/lib/storage/dispatcher/search.js.map +1 -0
- package/dist/lib/storage/dispatcher/skills.d.ts +53 -0
- package/dist/lib/storage/dispatcher/skills.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/skills.js +98 -0
- package/dist/lib/storage/dispatcher/skills.js.map +1 -0
- package/dist/lib/storage/dispatcher/token-stats.d.ts +23 -0
- package/dist/lib/storage/dispatcher/token-stats.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/token-stats.js +92 -0
- package/dist/lib/storage/dispatcher/token-stats.js.map +1 -0
- package/dist/lib/storage/dispatcher/web-search.d.ts +10 -0
- package/dist/lib/storage/dispatcher/web-search.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/web-search.js +39 -0
- package/dist/lib/storage/dispatcher/web-search.js.map +1 -0
- package/dist/lib/storage/dispatcher-export.d.ts.map +1 -1
- package/dist/lib/storage/dispatcher-export.js +48 -39
- package/dist/lib/storage/dispatcher-export.js.map +1 -1
- package/dist/lib/storage/dispatcher.d.ts +1 -468
- package/dist/lib/storage/dispatcher.d.ts.map +1 -1
- package/dist/lib/storage/dispatcher.js +1 -1931
- package/dist/lib/storage/dispatcher.js.map +1 -1
- package/dist/lib/storage/index.d.ts +20 -5
- package/dist/lib/storage/index.d.ts.map +1 -1
- package/dist/lib/storage/index.js +36 -7
- package/dist/lib/storage/index.js.map +1 -1
- package/dist/lib/storage/migration/export-import.d.ts.map +1 -1
- package/dist/lib/storage/migration/export-import.js +9 -2
- package/dist/lib/storage/migration/export-import.js.map +1 -1
- package/dist/lib/storage/types.d.ts +152 -10
- package/dist/lib/storage/types.d.ts.map +1 -1
- package/dist/lib/storage/types.js +13 -0
- package/dist/lib/storage/types.js.map +1 -1
- package/dist/lib/storage/vector/interface.d.ts +4 -0
- package/dist/lib/storage/vector/interface.d.ts.map +1 -1
- package/dist/lib/storage/vector/qdrant.d.ts +13 -2
- package/dist/lib/storage/vector/qdrant.d.ts.map +1 -1
- package/dist/lib/storage/vector/qdrant.js +147 -61
- package/dist/lib/storage/vector/qdrant.js.map +1 -1
- package/dist/lib/token-budget.d.ts.map +1 -1
- package/dist/lib/token-budget.js +9 -2
- package/dist/lib/token-budget.js.map +1 -1
- package/dist/lib/transcript-utils.d.ts +60 -0
- package/dist/lib/transcript-utils.d.ts.map +1 -0
- package/dist/lib/transcript-utils.js +69 -0
- package/dist/lib/transcript-utils.js.map +1 -0
- package/dist/lib/tree-sitter/extractor.d.ts +1 -1
- package/dist/lib/tree-sitter/extractor.d.ts.map +1 -1
- package/dist/lib/tree-sitter/extractor.js +34 -9
- package/dist/lib/tree-sitter/extractor.js.map +1 -1
- package/dist/lib/tree-sitter/parser.d.ts.map +1 -1
- package/dist/lib/tree-sitter/parser.js +45 -11
- package/dist/lib/tree-sitter/parser.js.map +1 -1
- package/dist/lib/tree-sitter/public.d.ts +12 -0
- package/dist/lib/tree-sitter/public.d.ts.map +1 -1
- package/dist/lib/tree-sitter/public.js +33 -1
- package/dist/lib/tree-sitter/public.js.map +1 -1
- package/dist/lib/tree-sitter/queries.d.ts.map +1 -1
- package/dist/lib/tree-sitter/queries.js +8 -0
- package/dist/lib/tree-sitter/queries.js.map +1 -1
- package/dist/lib/version-check.d.ts +29 -0
- package/dist/lib/version-check.d.ts.map +1 -0
- package/dist/lib/version-check.js +187 -0
- package/dist/lib/version-check.js.map +1 -0
- package/dist/lib/working-memory-pipeline.d.ts.map +1 -1
- package/dist/lib/working-memory-pipeline.js +12 -3
- package/dist/lib/working-memory-pipeline.js.map +1 -1
- package/dist/lib/worktree-detect.d.ts +43 -0
- package/dist/lib/worktree-detect.d.ts.map +1 -0
- package/dist/lib/worktree-detect.js +154 -0
- package/dist/lib/worktree-detect.js.map +1 -0
- package/dist/lsp/client.d.ts +96 -0
- package/dist/lsp/client.d.ts.map +1 -0
- package/dist/lsp/client.js +435 -0
- package/dist/lsp/client.js.map +1 -0
- package/dist/lsp/installer.d.ts +39 -0
- package/dist/lsp/installer.d.ts.map +1 -0
- package/dist/lsp/installer.js +275 -0
- package/dist/lsp/installer.js.map +1 -0
- package/dist/lsp/manager.d.ts +62 -0
- package/dist/lsp/manager.d.ts.map +1 -0
- package/dist/lsp/manager.js +234 -0
- package/dist/lsp/manager.js.map +1 -0
- package/dist/lsp/servers.d.ts +52 -0
- package/dist/lsp/servers.d.ts.map +1 -0
- package/dist/lsp/servers.js +162 -0
- package/dist/lsp/servers.js.map +1 -0
- package/dist/mcp/helpers.d.ts.map +1 -1
- package/dist/mcp/helpers.js +8 -2
- package/dist/mcp/helpers.js.map +1 -1
- package/dist/mcp/profile.js +1 -1
- package/dist/mcp/server.d.ts +3 -2
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +19 -7
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/config.d.ts.map +1 -1
- package/dist/mcp/tools/config.js +28 -118
- package/dist/mcp/tools/config.js.map +1 -1
- package/dist/mcp/tools/dead-end.d.ts.map +1 -1
- package/dist/mcp/tools/dead-end.js +4 -3
- package/dist/mcp/tools/dead-end.js.map +1 -1
- package/dist/mcp/tools/debug.d.ts.map +1 -1
- package/dist/mcp/tools/debug.js +27 -112
- package/dist/mcp/tools/debug.js.map +1 -1
- package/dist/mcp/tools/graph.d.ts.map +1 -1
- package/dist/mcp/tools/graph.js +164 -176
- package/dist/mcp/tools/graph.js.map +1 -1
- package/dist/mcp/tools/indexing.d.ts +1 -1
- package/dist/mcp/tools/indexing.d.ts.map +1 -1
- package/dist/mcp/tools/indexing.js +63 -164
- package/dist/mcp/tools/indexing.js.map +1 -1
- package/dist/mcp/tools/memory/forget.d.ts +3 -0
- package/dist/mcp/tools/memory/forget.d.ts.map +1 -0
- package/dist/mcp/tools/memory/forget.js +175 -0
- package/dist/mcp/tools/memory/forget.js.map +1 -0
- package/dist/mcp/tools/memory/memory-helpers.d.ts +45 -0
- package/dist/mcp/tools/memory/memory-helpers.d.ts.map +1 -0
- package/dist/mcp/tools/memory/memory-helpers.js +291 -0
- package/dist/mcp/tools/memory/memory-helpers.js.map +1 -0
- package/dist/mcp/tools/memory/recall.d.ts +3 -0
- package/dist/mcp/tools/memory/recall.d.ts.map +1 -0
- package/dist/mcp/tools/memory/recall.js +495 -0
- package/dist/mcp/tools/memory/recall.js.map +1 -0
- package/dist/mcp/tools/memory/remember.d.ts +3 -0
- package/dist/mcp/tools/memory/remember.d.ts.map +1 -0
- package/dist/mcp/tools/memory/remember.js +256 -0
- package/dist/mcp/tools/memory/remember.js.map +1 -0
- package/dist/mcp/tools/memory/temporal-query.d.ts +8 -0
- package/dist/mcp/tools/memory/temporal-query.d.ts.map +1 -0
- package/dist/mcp/tools/memory/temporal-query.js +68 -0
- package/dist/mcp/tools/memory/temporal-query.js.map +1 -0
- package/dist/mcp/tools/memory.d.ts +0 -11
- package/dist/mcp/tools/memory.d.ts.map +1 -1
- package/dist/mcp/tools/memory.js +6 -1228
- package/dist/mcp/tools/memory.js.map +1 -1
- package/dist/mcp/tools/prd.d.ts.map +1 -1
- package/dist/mcp/tools/prd.js +19 -70
- package/dist/mcp/tools/prd.js.map +1 -1
- package/dist/mcp/tools/review.d.ts +8 -0
- package/dist/mcp/tools/review.d.ts.map +1 -0
- package/dist/mcp/tools/review.js +133 -0
- package/dist/mcp/tools/review.js.map +1 -0
- package/dist/mcp/tools/search.d.ts.map +1 -1
- package/dist/mcp/tools/search.js +79 -8
- package/dist/mcp/tools/search.js.map +1 -1
- package/dist/mcp/tools/status.d.ts.map +1 -1
- package/dist/mcp/tools/status.js +50 -75
- package/dist/mcp/tools/status.js.map +1 -1
- package/dist/mcp/tools/web-fetch.d.ts.map +1 -1
- package/dist/mcp/tools/web-fetch.js +5 -1
- package/dist/mcp/tools/web-fetch.js.map +1 -1
- package/dist/mcp/tools/web-search.d.ts.map +1 -1
- package/dist/mcp/tools/web-search.js +25 -103
- package/dist/mcp/tools/web-search.js.map +1 -1
- package/dist/prompts/guardrails.d.ts +14 -0
- package/dist/prompts/guardrails.d.ts.map +1 -0
- package/dist/prompts/guardrails.js +115 -0
- package/dist/prompts/guardrails.js.map +1 -0
- package/dist/prompts/index.d.ts +2 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +3 -1
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/prd.d.ts +0 -2
- package/dist/prompts/prd.d.ts.map +1 -1
- package/dist/prompts/prd.js +0 -2
- package/dist/prompts/prd.js.map +1 -1
- package/hooks/core/__tests__/adapter.test.cjs +340 -0
- package/hooks/core/adapter.cjs +463 -0
- package/hooks/core/config.cjs +83 -0
- package/hooks/core/daemon-boot.cjs +140 -0
- package/hooks/core/log.cjs +41 -0
- package/hooks/core/worktree.cjs +119 -0
- package/hooks/succ-post-tool.cjs +198 -134
- package/hooks/succ-pre-compact.cjs +262 -0
- package/hooks/succ-pre-tool.cjs +526 -182
- package/hooks/succ-session-end.cjs +40 -64
- package/hooks/succ-session-start.cjs +528 -427
- package/hooks/succ-stop-reflection.cjs +36 -62
- package/hooks/succ-user-prompt.cjs +137 -180
- package/package.json +17 -6
package/dist/daemon/service.js
CHANGED
|
@@ -16,52 +16,36 @@
|
|
|
16
16
|
import http from 'http';
|
|
17
17
|
import fs from 'fs';
|
|
18
18
|
import path from 'path';
|
|
19
|
-
import {
|
|
19
|
+
import { createIdleWatcher, createSessionManager } from './sessions.js';
|
|
20
20
|
import { logError, logWarn } from '../lib/fault-logger.js';
|
|
21
21
|
import { processRegistry } from '../lib/process-registry.js';
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
|
|
43
|
-
// Constants
|
|
44
|
-
// ============================================================================
|
|
22
|
+
import { NotFoundError, NetworkError, ValidationError } from '../lib/errors.js';
|
|
23
|
+
import { startWatcher, stopWatcher } from './watcher.js';
|
|
24
|
+
import { startAnalyzer, stopAnalyzer } from './analyzer.js';
|
|
25
|
+
import { getConfig, getIdleReflectionConfig, getIdleWatcherConfig, getProjectRoot, getSuccDir, } from '../lib/config.js';
|
|
26
|
+
import { getStablePort } from '../lib/daemon-port.js';
|
|
27
|
+
import { closeDb, closeGlobalDb, closeStorageDispatcher, initStorageDispatcher, } from '../lib/storage/index.js';
|
|
28
|
+
import { cleanupEmbeddings } from '../lib/embeddings.js';
|
|
29
|
+
import { cleanupReranker } from '../lib/reranker.js';
|
|
30
|
+
import { cleanupQualityScoring } from '../lib/quality.js';
|
|
31
|
+
import { loadBudgets } from '../lib/token-budget.js';
|
|
32
|
+
import { getErrorMessage, } from './routes/types.js';
|
|
33
|
+
import { sessionRoutes } from './routes/sessions.js';
|
|
34
|
+
import { searchRoutes, resetSearchRoutesState } from './routes/search.js';
|
|
35
|
+
import { memoryRoutes, resetMemoryRoutesState } from './routes/memory.js';
|
|
36
|
+
import { clearBriefingCache, initReflectionMaintenance, performReflection, preGenerateBriefing, reflectionRoutes, resetReflectionRoutesState, } from './routes/reflection.js';
|
|
37
|
+
import { statusRoutes } from './routes/status.js';
|
|
38
|
+
import { watcherRoutes } from './routes/watcher.js';
|
|
39
|
+
import { analyzerRoutes } from './routes/analyzer.js';
|
|
40
|
+
import { skillRoutes } from './routes/skills.js';
|
|
41
|
+
import { hookRoutes } from './routes/hooks.js';
|
|
42
|
+
import { addVersionedRoutes, getApiVersionInfo } from './routes/versioning.js';
|
|
45
43
|
const DEFAULT_PORT_RANGE_START = 37842;
|
|
46
44
|
const MAX_PORT_ATTEMPTS = 100;
|
|
47
|
-
//
|
|
48
|
-
// Daemon State
|
|
49
|
-
// ============================================================================
|
|
45
|
+
const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB
|
|
50
46
|
let state = null;
|
|
51
47
|
let sessionManager = null;
|
|
52
48
|
let idleWatcher = null;
|
|
53
|
-
const briefingCache = new Map();
|
|
54
|
-
const briefingGenerationInProgress = new Set();
|
|
55
|
-
// In-flight dedup: prevents race condition when identical /api/remember requests
|
|
56
|
-
// arrive within a short window (e.g. hook fires twice for same tool_use)
|
|
57
|
-
const rememberInFlight = new Map();
|
|
58
|
-
const REMEMBER_DEDUP_TTL_MS = 5000;
|
|
59
|
-
// Hook rules cache — stores all hook-rule memories, invalidated on remember with hook-rule tag
|
|
60
|
-
let hookRulesCache = null;
|
|
61
|
-
const HOOK_RULES_CACHE_TTL = 60_000; // 60s
|
|
62
|
-
// ============================================================================
|
|
63
|
-
// File Paths
|
|
64
|
-
// ============================================================================
|
|
65
49
|
function getDaemonPidFile() {
|
|
66
50
|
const succDir = getSuccDir();
|
|
67
51
|
const tmpDir = path.join(succDir, '.tmp');
|
|
@@ -70,13 +54,6 @@ function getDaemonPidFile() {
|
|
|
70
54
|
}
|
|
71
55
|
return path.join(tmpDir, 'daemon.pid');
|
|
72
56
|
}
|
|
73
|
-
// ============================================================================
|
|
74
|
-
// Progress File Management
|
|
75
|
-
// ============================================================================
|
|
76
|
-
/**
|
|
77
|
-
* Get path to session progress file
|
|
78
|
-
* Progress files accumulate idle reflection briefings for session-end processing
|
|
79
|
-
*/
|
|
80
57
|
function getProgressFilePath(sessionId) {
|
|
81
58
|
const succDir = getSuccDir();
|
|
82
59
|
const tmpDir = path.join(succDir, '.tmp');
|
|
@@ -85,10 +62,6 @@ function getProgressFilePath(sessionId) {
|
|
|
85
62
|
}
|
|
86
63
|
return path.join(tmpDir, `session-${sessionId}-progress.md`);
|
|
87
64
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Append a briefing to the session progress file
|
|
90
|
-
* Creates file with header if it doesn't exist
|
|
91
|
-
*/
|
|
92
65
|
export function appendToProgressFile(sessionId, briefing) {
|
|
93
66
|
const progressPath = getProgressFilePath(sessionId);
|
|
94
67
|
const timestamp = new Date().toISOString();
|
|
@@ -106,10 +79,6 @@ export function appendToProgressFile(sessionId, briefing) {
|
|
|
106
79
|
content += '\n\n---\n\n';
|
|
107
80
|
fs.appendFileSync(progressPath, content);
|
|
108
81
|
}
|
|
109
|
-
/**
|
|
110
|
-
* Read tail of transcript file (for fallback when no progress file)
|
|
111
|
-
* Returns the last maxBytes of the file, starting from a complete line
|
|
112
|
-
*/
|
|
113
82
|
export function readTailTranscript(transcriptPath, maxBytes = 2 * 1024 * 1024) {
|
|
114
83
|
if (!fs.existsSync(transcriptPath)) {
|
|
115
84
|
return '';
|
|
@@ -118,107 +87,14 @@ export function readTailTranscript(transcriptPath, maxBytes = 2 * 1024 * 1024) {
|
|
|
118
87
|
if (stats.size <= maxBytes) {
|
|
119
88
|
return fs.readFileSync(transcriptPath, 'utf8');
|
|
120
89
|
}
|
|
121
|
-
// Read only tail
|
|
122
90
|
const fd = fs.openSync(transcriptPath, 'r');
|
|
123
91
|
const buffer = Buffer.alloc(maxBytes);
|
|
124
92
|
fs.readSync(fd, buffer, 0, maxBytes, stats.size - maxBytes);
|
|
125
93
|
fs.closeSync(fd);
|
|
126
|
-
// Find first complete line (skip partial line at start)
|
|
127
94
|
const content = buffer.toString('utf8');
|
|
128
95
|
const firstNewline = content.indexOf('\n');
|
|
129
96
|
return firstNewline > 0 ? content.slice(firstNewline + 1) : content;
|
|
130
97
|
}
|
|
131
|
-
// ============================================================================
|
|
132
|
-
// Briefing Pre-Generation
|
|
133
|
-
// ============================================================================
|
|
134
|
-
const BRIEFING_CACHE_MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes
|
|
135
|
-
const BRIEFING_MIN_TRANSCRIPT_GROWTH = 5000; // Re-generate after 5KB growth
|
|
136
|
-
// const BRIEFING_PREGENERATE_IDLE_MS = 120 * 1000; // Pre-generate after 2 min idle
|
|
137
|
-
/**
|
|
138
|
-
* Pre-generate briefing for a session in background
|
|
139
|
-
* Called when session is idle or transcript grows significantly
|
|
140
|
-
*/
|
|
141
|
-
async function preGenerateBriefing(sessionId, transcriptPath) {
|
|
142
|
-
// Skip if already generating
|
|
143
|
-
if (briefingGenerationInProgress.has(sessionId)) {
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
if (!fs.existsSync(transcriptPath)) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
const stats = fs.statSync(transcriptPath);
|
|
150
|
-
const currentSize = stats.size;
|
|
151
|
-
// Check if we need to regenerate
|
|
152
|
-
const cached = briefingCache.get(sessionId);
|
|
153
|
-
if (cached) {
|
|
154
|
-
const age = Date.now() - cached.generatedAt;
|
|
155
|
-
const growth = currentSize - cached.transcriptSize;
|
|
156
|
-
// Skip if cache is fresh and transcript hasn't grown much
|
|
157
|
-
if (age < BRIEFING_CACHE_MAX_AGE_MS && growth < BRIEFING_MIN_TRANSCRIPT_GROWTH) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
briefingGenerationInProgress.add(sessionId);
|
|
162
|
-
log(`[briefing] Pre-generating for session ${sessionId.slice(0, 8)}...`);
|
|
163
|
-
try {
|
|
164
|
-
const transcriptContent = fs.readFileSync(transcriptPath, 'utf-8');
|
|
165
|
-
const result = await generateCompactBriefing(transcriptContent);
|
|
166
|
-
if (result.success && result.briefing) {
|
|
167
|
-
briefingCache.set(sessionId, {
|
|
168
|
-
briefing: result.briefing,
|
|
169
|
-
generatedAt: Date.now(),
|
|
170
|
-
transcriptSize: currentSize,
|
|
171
|
-
});
|
|
172
|
-
log(`[briefing] Pre-generated for session ${sessionId.slice(0, 8)} (${result.briefing.length} chars)`);
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
log(`[briefing] Pre-generation failed: ${result.error}`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
catch (error) {
|
|
179
|
-
log(`[briefing] Pre-generation error: ${error}`);
|
|
180
|
-
}
|
|
181
|
-
finally {
|
|
182
|
-
briefingGenerationInProgress.delete(sessionId);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Get cached briefing or generate on-demand
|
|
187
|
-
*/
|
|
188
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
189
|
-
async function getCachedBriefing(sessionId, transcriptPath) {
|
|
190
|
-
const cached = briefingCache.get(sessionId);
|
|
191
|
-
if (cached) {
|
|
192
|
-
// Check if cache is still valid
|
|
193
|
-
const age = Date.now() - cached.generatedAt;
|
|
194
|
-
if (age < BRIEFING_CACHE_MAX_AGE_MS) {
|
|
195
|
-
return { briefing: cached.briefing, cached: true };
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
// Cache miss or stale - generate fresh
|
|
199
|
-
if (!fs.existsSync(transcriptPath)) {
|
|
200
|
-
return { cached: false };
|
|
201
|
-
}
|
|
202
|
-
const transcriptContent = fs.readFileSync(transcriptPath, 'utf-8');
|
|
203
|
-
const result = await generateCompactBriefing(transcriptContent);
|
|
204
|
-
if (result.success && result.briefing) {
|
|
205
|
-
const stats = fs.statSync(transcriptPath);
|
|
206
|
-
briefingCache.set(sessionId, {
|
|
207
|
-
briefing: result.briefing,
|
|
208
|
-
generatedAt: Date.now(),
|
|
209
|
-
transcriptSize: stats.size,
|
|
210
|
-
});
|
|
211
|
-
return { briefing: result.briefing, cached: false };
|
|
212
|
-
}
|
|
213
|
-
return { cached: false };
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Clear briefing cache for a session (called when session ends)
|
|
217
|
-
*/
|
|
218
|
-
function clearBriefingCache(sessionId) {
|
|
219
|
-
briefingCache.delete(sessionId);
|
|
220
|
-
briefingGenerationInProgress.delete(sessionId);
|
|
221
|
-
}
|
|
222
98
|
function getDaemonPortFile() {
|
|
223
99
|
const succDir = getSuccDir();
|
|
224
100
|
const tmpDir = path.join(succDir, '.tmp');
|
|
@@ -231,13 +107,9 @@ function getDaemonLogFile() {
|
|
|
231
107
|
const succDir = getSuccDir();
|
|
232
108
|
return path.join(succDir, 'daemon.log');
|
|
233
109
|
}
|
|
234
|
-
// ============================================================================
|
|
235
|
-
// Logging
|
|
236
|
-
// ============================================================================
|
|
237
110
|
function log(message) {
|
|
238
111
|
const timestamp = new Date().toISOString();
|
|
239
112
|
const line = `[${timestamp}] ${message}\n`;
|
|
240
|
-
// Write to daemon.log
|
|
241
113
|
try {
|
|
242
114
|
fs.appendFileSync(getDaemonLogFile(), line);
|
|
243
115
|
}
|
|
@@ -246,291 +118,43 @@ function log(message) {
|
|
|
246
118
|
error: err instanceof Error ? err.message : String(err),
|
|
247
119
|
});
|
|
248
120
|
}
|
|
249
|
-
// Also write to stderr for debugging
|
|
250
121
|
process.stderr.write(line);
|
|
251
122
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
// ============================================================================
|
|
255
|
-
/**
|
|
256
|
-
* Write a human-like reflection to the brain vault
|
|
257
|
-
* Uses Claude CLI or local LLM to generate introspective text
|
|
258
|
-
*/
|
|
259
|
-
async function writeReflection(transcript, _idleConfig) {
|
|
260
|
-
const projectRoot = getProjectRoot();
|
|
261
|
-
const reflectionsDir = path.join(projectRoot, '.succ', 'brain', 'reflections');
|
|
262
|
-
// Create reflections directory if needed
|
|
263
|
-
if (!fs.existsSync(reflectionsDir)) {
|
|
264
|
-
fs.mkdirSync(reflectionsDir, { recursive: true });
|
|
265
|
-
}
|
|
266
|
-
const now = new Date();
|
|
267
|
-
const dateStr = now.toISOString().split('T')[0];
|
|
268
|
-
const timeStr = now.toTimeString().split(' ')[0].substring(0, 5);
|
|
269
|
-
const timestamp = `${dateStr} ${timeStr}`;
|
|
270
|
-
const prompt = REFLECTION_PROMPT.replace('{transcript}', transcript.substring(0, 3000));
|
|
271
|
-
let reflectionText = null;
|
|
272
|
-
// Use sleep agent for background reflection if enabled
|
|
273
|
-
try {
|
|
274
|
-
reflectionText = await callLLM(prompt, {
|
|
275
|
-
timeout: 60000,
|
|
276
|
-
useSleepAgent: true,
|
|
277
|
-
systemPrompt: REFLECTION_SYSTEM,
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
catch (err) {
|
|
281
|
-
log(`[reflection] LLM call failed: ${err}`);
|
|
282
|
-
reflectionText = null;
|
|
283
|
-
}
|
|
284
|
-
if (!reflectionText || reflectionText.trim().length < 50) {
|
|
285
|
-
log(`[reflection] Reflection text too short or empty, skipping`);
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
// Write reflection file with YAML frontmatter
|
|
289
|
-
const reflectionFile = path.join(reflectionsDir, `${timestamp}.md`);
|
|
290
|
-
const content = `---
|
|
291
|
-
date: ${dateStr}
|
|
292
|
-
time: ${timeStr}
|
|
293
|
-
trigger: idle
|
|
294
|
-
tags:
|
|
295
|
-
- reflection
|
|
296
|
-
---
|
|
297
|
-
|
|
298
|
-
# Reflection ${dateStr} ${timeStr}
|
|
299
|
-
|
|
300
|
-
${reflectionText.trim()}
|
|
301
|
-
`;
|
|
302
|
-
fs.writeFileSync(reflectionFile, content);
|
|
303
|
-
// Also save to memory (with dedup to prevent duplicate reflections)
|
|
304
|
-
const embedding = await getEmbedding(reflectionText.trim());
|
|
305
|
-
await saveMemory(reflectionText.trim(), embedding, ['reflection'], 'observation', {
|
|
306
|
-
qualityScore: { score: 0.6, factors: { hasContext: 1 } },
|
|
307
|
-
deduplicate: true,
|
|
308
|
-
});
|
|
123
|
+
function isErrnoException(error) {
|
|
124
|
+
return typeof error === 'object' && error !== null && 'code' in error;
|
|
309
125
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
catch {
|
|
340
|
-
/* transcript file gone — skip size check */
|
|
341
|
-
}
|
|
342
|
-
// Check memory count for consolidation skip
|
|
343
|
-
const memStats = await getMemoryStats();
|
|
344
|
-
const currentMemCount = memStats.total;
|
|
345
|
-
if (session.lastMemoryCount !== undefined && currentMemCount === session.lastMemoryCount) {
|
|
346
|
-
memoriesChanged = false;
|
|
347
|
-
}
|
|
348
|
-
session.lastMemoryCount = currentMemCount;
|
|
349
|
-
// ── Mid-conversation observer: extract facts when enough new content ──
|
|
350
|
-
const observerConfig = getObserverConfig();
|
|
351
|
-
if (observerConfig.enabled && transcriptChanged) {
|
|
352
|
-
try {
|
|
353
|
-
const currentSize = session.lastTranscriptSize ?? 0;
|
|
354
|
-
const lastObsSize = session.lastObservationSize ?? 0;
|
|
355
|
-
const lastObsTime = session.lastObservation ?? session.registeredAt;
|
|
356
|
-
const now = Date.now();
|
|
357
|
-
// Read new content and track real token count via budget
|
|
358
|
-
const newBytes = currentSize - lastObsSize;
|
|
359
|
-
const timeThresholdMs = observerConfig.max_minutes * 60 * 1000;
|
|
360
|
-
const enoughTime = now - lastObsTime >= timeThresholdMs;
|
|
361
|
-
// Use byte-estimated token check first (cheap), then verify with real count
|
|
362
|
-
const estimatedTokens = Math.ceil(newBytes / 3.5);
|
|
363
|
-
const enoughNewContent = estimatedTokens >= observerConfig.min_tokens;
|
|
364
|
-
if (enoughNewContent || enoughTime) {
|
|
365
|
-
const newContent = readTailTranscript(session.transcriptPath, newBytes);
|
|
366
|
-
// Track real tokens in budget
|
|
367
|
-
const realTokens = recordTranscriptTokens(sessionId, newContent);
|
|
368
|
-
log(`[observer] Triggering extraction (tokens: ~${realTokens}, time: ${Math.round((now - lastObsTime) / 60000)}min)`);
|
|
369
|
-
if (newContent.length > 200) {
|
|
370
|
-
const result = await extractSessionSummary(newContent, { verbose: false });
|
|
371
|
-
recordExtraction(sessionId, result.transcriptTokens ?? 0, result.summaryTokens ?? 0, result.factsExtracted, result.factsSaved);
|
|
372
|
-
resetTranscriptCounter(sessionId);
|
|
373
|
-
// Persist extraction metadata to session observations (append-only)
|
|
374
|
-
if (result.factsSaved > 0) {
|
|
375
|
-
appendObservations(sessionId, [
|
|
376
|
-
{
|
|
377
|
-
content: `Extracted ${result.factsExtracted} facts, saved ${result.factsSaved}`,
|
|
378
|
-
type: 'observation',
|
|
379
|
-
tags: ['mid-session'],
|
|
380
|
-
extractedAt: new Date().toISOString(),
|
|
381
|
-
source: 'mid-session-observer',
|
|
382
|
-
transcriptOffset: currentSize,
|
|
383
|
-
memoryId: null,
|
|
384
|
-
},
|
|
385
|
-
]);
|
|
386
|
-
}
|
|
387
|
-
log(`[observer] Extracted ${result.factsExtracted} facts, saved ${result.factsSaved} (skipped ${result.factsSkipped})`);
|
|
388
|
-
}
|
|
389
|
-
session.lastObservation = now;
|
|
390
|
-
session.lastObservationSize = currentSize;
|
|
391
|
-
flushBudgets();
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
catch (err) {
|
|
395
|
-
log(`[observer] Mid-session extraction failed: ${err}`);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
// ── Generate briefing (skip if transcript unchanged) ──
|
|
399
|
-
let briefingResult = { success: false };
|
|
400
|
-
if (transcriptChanged) {
|
|
401
|
-
const transcriptContent = readTailTranscript(session.transcriptPath, 100 * 1024); // 100KB max
|
|
402
|
-
briefingResult = await generateCompactBriefing(transcriptContent, {
|
|
403
|
-
format: 'structured',
|
|
404
|
-
include_memories: true,
|
|
405
|
-
max_memories: 3,
|
|
406
|
-
});
|
|
407
|
-
if (briefingResult.success && briefingResult.briefing) {
|
|
408
|
-
appendToProgressFile(sessionId, briefingResult.briefing);
|
|
409
|
-
log(`[reflection] Appended briefing to progress file (${briefingResult.briefing.length} chars)`);
|
|
410
|
-
}
|
|
411
|
-
else {
|
|
412
|
-
log(`[reflection] Failed to generate briefing for ${sessionId}`);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
// ── Parallel operations ──
|
|
416
|
-
const globalConfig = getConfig();
|
|
417
|
-
const parallelOps = [];
|
|
418
|
-
// memory_consolidation - skip if no new memories (disabled by default, opt-in only)
|
|
419
|
-
if (memoriesChanged && idleConfig.operations?.memory_consolidation === true) {
|
|
420
|
-
parallelOps.push((async () => {
|
|
421
|
-
const threshold = idleConfig.thresholds?.similarity_for_merge ?? 0.92;
|
|
422
|
-
const limit = idleConfig.max_memories_to_process ?? 50;
|
|
423
|
-
log(`[reflection] Running memory consolidation (threshold=${threshold}, limit=${limit})`);
|
|
424
|
-
const { consolidate } = await import('../commands/consolidate.js');
|
|
425
|
-
await consolidate({
|
|
426
|
-
threshold: String(threshold),
|
|
427
|
-
limit: String(limit),
|
|
428
|
-
llm: true,
|
|
429
|
-
verbose: false,
|
|
430
|
-
});
|
|
431
|
-
log(`[reflection] Memory consolidation complete`);
|
|
432
|
-
})());
|
|
433
|
-
}
|
|
434
|
-
// retention_cleanup - independent (always runs if enabled)
|
|
435
|
-
if (globalConfig.retention?.enabled && idleConfig.operations?.retention_cleanup !== false) {
|
|
436
|
-
parallelOps.push((async () => {
|
|
437
|
-
log(`[reflection] Running retention cleanup`);
|
|
438
|
-
const { retention } = await import('../commands/retention.js');
|
|
439
|
-
await retention({ apply: true, verbose: false });
|
|
440
|
-
log(`[reflection] Retention cleanup complete`);
|
|
441
|
-
})());
|
|
442
|
-
}
|
|
443
|
-
await Promise.all(parallelOps);
|
|
444
|
-
// ── Graph cleanup: prune → enrich → orphans → communities → centrality ──
|
|
445
|
-
if (idleConfig.operations?.graph_refinement !== false ||
|
|
446
|
-
idleConfig.operations?.graph_enrichment !== false) {
|
|
447
|
-
const shouldRun = memoriesChanged || session.lastLinkCount === undefined;
|
|
448
|
-
if (shouldRun) {
|
|
449
|
-
log(`[reflection] Running graph cleanup pipeline`);
|
|
450
|
-
try {
|
|
451
|
-
const { graphCleanup } = await import('../lib/graph/cleanup.js');
|
|
452
|
-
const cleanupResult = await graphCleanup({
|
|
453
|
-
skipEnrich: idleConfig.operations?.graph_enrichment === false,
|
|
454
|
-
onProgress: (step, detail) => log(`[reflection] [${step}] ${detail}`),
|
|
455
|
-
});
|
|
456
|
-
log(`[reflection] Cleanup: pruned ${cleanupResult.pruned}, enriched ${cleanupResult.enriched}, orphans ${cleanupResult.orphansConnected}, communities ${cleanupResult.communitiesDetected}, centrality ${cleanupResult.centralityUpdated}`);
|
|
457
|
-
// Proximity links from co-occurrence (not part of cleanup pipeline)
|
|
458
|
-
try {
|
|
459
|
-
const { createProximityLinks } = await import('../lib/graph/contextual-proximity.js');
|
|
460
|
-
const r = await createProximityLinks({ minCooccurrence: 2 });
|
|
461
|
-
log(`[reflection] Created ${r.created} proximity links`);
|
|
462
|
-
}
|
|
463
|
-
catch (err) {
|
|
464
|
-
log(`[reflection] Proximity failed: ${err}`);
|
|
465
|
-
}
|
|
466
|
-
// Synthesize patterns from community clusters (uses cleanup's community result)
|
|
467
|
-
if (cleanupResult.communityResult &&
|
|
468
|
-
cleanupResult.communityResult.communities.length > 0) {
|
|
469
|
-
try {
|
|
470
|
-
const { synthesizeFromCommunities } = await import('../lib/reflection-synthesizer.js');
|
|
471
|
-
const synthResult = await synthesizeFromCommunities(cleanupResult.communityResult, {
|
|
472
|
-
log,
|
|
473
|
-
});
|
|
474
|
-
const hasSynthActivity = synthResult.patternsCreated > 0 ||
|
|
475
|
-
synthResult.duplicatesSkipped > 0 ||
|
|
476
|
-
synthResult.reinforced > 0;
|
|
477
|
-
if (hasSynthActivity) {
|
|
478
|
-
log(`[reflection] Synthesized ${synthResult.patternsCreated} patterns from ${synthResult.clustersProcessed} clusters` +
|
|
479
|
-
(synthResult.reinforced > 0
|
|
480
|
-
? `, reinforced ${synthResult.reinforced} existing`
|
|
481
|
-
: '') +
|
|
482
|
-
(synthResult.duplicatesSkipped > 0
|
|
483
|
-
? `, skipped ${synthResult.duplicatesSkipped} duplicates`
|
|
484
|
-
: '') +
|
|
485
|
-
(synthResult.observationsMarked > 0
|
|
486
|
-
? `, marked ${synthResult.observationsMarked} as reflected`
|
|
487
|
-
: ''));
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
catch (err) {
|
|
491
|
-
log(`[reflection] Synthesis failed: ${err}`);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
session.lastLinkCount = (session.lastLinkCount ?? 0) + cleanupResult.orphansConnected;
|
|
495
|
-
}
|
|
496
|
-
catch (err) {
|
|
497
|
-
log(`[reflection] Graph cleanup failed: ${err}`);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
log(`[reflection] Skipping graph cleanup (no changes)`);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
// ── Write reflection (runs last, may use LLM) ──
|
|
505
|
-
if (idleConfig.operations?.write_reflection !== false) {
|
|
506
|
-
log(`[reflection] Writing reflection for ${sessionId}`);
|
|
507
|
-
try {
|
|
508
|
-
const progressPath = getProgressFilePath(sessionId);
|
|
509
|
-
const briefingContent = fs.existsSync(progressPath)
|
|
510
|
-
? fs.readFileSync(progressPath, 'utf-8')
|
|
511
|
-
: briefingResult.briefing || '';
|
|
512
|
-
if (briefingContent.length >= 100) {
|
|
513
|
-
await writeReflection(briefingContent, idleConfig);
|
|
514
|
-
log(`[reflection] Reflection written`);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
catch (err) {
|
|
518
|
-
log(`[reflection] Write reflection error: ${err}`);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
log(`[reflection] Completed reflection for session ${sessionId}`);
|
|
522
|
-
}
|
|
523
|
-
catch (err) {
|
|
524
|
-
log(`[reflection] Error for session ${sessionId}: ${err}`);
|
|
525
|
-
}
|
|
126
|
+
function createRouteContext() {
|
|
127
|
+
return {
|
|
128
|
+
state,
|
|
129
|
+
sessionManager,
|
|
130
|
+
log,
|
|
131
|
+
checkShutdown,
|
|
132
|
+
clearBriefingCache,
|
|
133
|
+
appendToProgressFile,
|
|
134
|
+
readTailTranscript,
|
|
135
|
+
getProgressFilePath,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function buildRoutes(ctx) {
|
|
139
|
+
const baseRoutes = {
|
|
140
|
+
...statusRoutes(ctx),
|
|
141
|
+
...sessionRoutes(ctx),
|
|
142
|
+
...searchRoutes(ctx),
|
|
143
|
+
...memoryRoutes(ctx),
|
|
144
|
+
...reflectionRoutes(ctx),
|
|
145
|
+
...watcherRoutes(ctx),
|
|
146
|
+
...analyzerRoutes(ctx),
|
|
147
|
+
...skillRoutes(ctx),
|
|
148
|
+
...hookRoutes(ctx),
|
|
149
|
+
// API version info endpoint
|
|
150
|
+
'GET /api/version': async () => getApiVersionInfo(),
|
|
151
|
+
};
|
|
152
|
+
// Add /v1/api/* aliases for all /api/* routes
|
|
153
|
+
return addVersionedRoutes(baseRoutes);
|
|
526
154
|
}
|
|
527
|
-
// ============================================================================
|
|
528
|
-
// HTTP Request Handler
|
|
529
|
-
// ============================================================================
|
|
530
155
|
async function handleRequest(req, res) {
|
|
531
156
|
const reqUrl = new URL(req.url || '/', `http://localhost`);
|
|
532
157
|
const method = req.method || 'GET';
|
|
533
|
-
// CORS headers (for potential web clients)
|
|
534
158
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
535
159
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
|
|
536
160
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
@@ -539,500 +163,140 @@ async function handleRequest(req, res) {
|
|
|
539
163
|
res.end();
|
|
540
164
|
return;
|
|
541
165
|
}
|
|
542
|
-
// Parse JSON body for POST requests
|
|
543
166
|
let body = null;
|
|
544
167
|
if (method === 'POST') {
|
|
545
|
-
|
|
168
|
+
try {
|
|
169
|
+
body = await parseBody(req);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
const message = getErrorMessage(error);
|
|
173
|
+
if (message.includes('too large')) {
|
|
174
|
+
res.writeHead(413, { 'Content-Type': 'application/json' });
|
|
175
|
+
res.end(JSON.stringify({ error: 'Request body too large' }));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
179
|
+
res.end(JSON.stringify({ error: 'Invalid request body' }));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
546
182
|
}
|
|
547
183
|
try {
|
|
548
|
-
// Route request
|
|
549
184
|
const result = await routeRequest(method, reqUrl.pathname, reqUrl.searchParams, body);
|
|
550
185
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
551
186
|
res.end(JSON.stringify(result));
|
|
552
187
|
}
|
|
553
188
|
catch (err) {
|
|
554
|
-
|
|
189
|
+
const message = getErrorMessage(err);
|
|
190
|
+
log(`[http] Error: ${message}`);
|
|
191
|
+
if (err instanceof ValidationError) {
|
|
192
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
193
|
+
res.end(JSON.stringify({ error: message }));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (err instanceof NotFoundError) {
|
|
197
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
198
|
+
res.end(JSON.stringify({ error: message }));
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
555
201
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
556
|
-
res.end(JSON.stringify({ error:
|
|
202
|
+
res.end(JSON.stringify({ error: 'Internal server error' }));
|
|
557
203
|
}
|
|
558
204
|
}
|
|
559
205
|
export async function parseBody(req) {
|
|
560
206
|
return new Promise((resolve, reject) => {
|
|
561
207
|
let data = '';
|
|
562
|
-
|
|
208
|
+
let size = 0;
|
|
209
|
+
let settled = false;
|
|
210
|
+
const settle = (fn) => {
|
|
211
|
+
if (settled)
|
|
212
|
+
return;
|
|
213
|
+
settled = true;
|
|
214
|
+
fn();
|
|
215
|
+
};
|
|
216
|
+
req.on('data', (chunk) => {
|
|
217
|
+
if (settled)
|
|
218
|
+
return;
|
|
219
|
+
const chunkSize = Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(String(chunk));
|
|
220
|
+
size += chunkSize;
|
|
221
|
+
if (size > MAX_BODY_SIZE) {
|
|
222
|
+
req.destroy();
|
|
223
|
+
settle(() => reject(new Error('Request body too large')));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
data += chunk;
|
|
227
|
+
});
|
|
563
228
|
req.on('end', () => {
|
|
229
|
+
if (settled)
|
|
230
|
+
return;
|
|
564
231
|
try {
|
|
565
|
-
|
|
232
|
+
const parsed = data ? JSON.parse(data) : {};
|
|
233
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
234
|
+
settle(() => reject(new Error('Invalid request body')));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
settle(() => resolve(parsed));
|
|
566
238
|
}
|
|
567
|
-
catch
|
|
568
|
-
|
|
569
|
-
error: err instanceof Error ? err.message : String(err),
|
|
570
|
-
});
|
|
571
|
-
resolve({});
|
|
239
|
+
catch {
|
|
240
|
+
settle(() => reject(new Error('Invalid request body')));
|
|
572
241
|
}
|
|
573
242
|
});
|
|
574
|
-
req.on('error',
|
|
243
|
+
req.on('error', (error) => {
|
|
244
|
+
settle(() => reject(error));
|
|
245
|
+
});
|
|
575
246
|
});
|
|
576
247
|
}
|
|
577
248
|
/** @internal Exported for testing */
|
|
578
249
|
export async function routeRequest(method, pathname, searchParams, body) {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
uptime: Date.now() - (state?.startedAt || Date.now()),
|
|
585
|
-
activeSessions: sessionManager?.count() || 0,
|
|
586
|
-
cwd: state?.cwd || process.cwd(),
|
|
587
|
-
};
|
|
250
|
+
const routes = buildRoutes(createRouteContext());
|
|
251
|
+
const key = `${method} ${pathname}`;
|
|
252
|
+
const handler = routes[key];
|
|
253
|
+
if (!handler) {
|
|
254
|
+
throw new NotFoundError(`Unknown endpoint: ${method} ${pathname}`);
|
|
588
255
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
const { session_id, transcript_path, run_reflection } = body;
|
|
601
|
-
if (!session_id) {
|
|
602
|
-
throw new ValidationError('session_id required');
|
|
603
|
-
}
|
|
604
|
-
const session = sessionManager.get(session_id);
|
|
605
|
-
const transcriptFile = transcript_path || session?.transcriptPath || '';
|
|
606
|
-
// Flush session counters to learning_deltas before unregister
|
|
607
|
-
try {
|
|
608
|
-
const d = await getStorageDispatcher();
|
|
609
|
-
await d.flushSessionCounters('daemon-session');
|
|
610
|
-
}
|
|
611
|
-
catch (err) {
|
|
612
|
-
log(`[session] Failed to flush session counters: ${err}`);
|
|
613
|
-
}
|
|
614
|
-
// Unregister the session immediately (don't block on processing)
|
|
615
|
-
const removed = sessionManager.unregister(session_id);
|
|
616
|
-
clearBriefingCache(session_id); // Clean up any cached briefing
|
|
617
|
-
removeBudget(session_id); // Clean up token budget
|
|
618
|
-
removeObservations(session_id); // Clean up observation JSONL
|
|
619
|
-
flushBudgets();
|
|
620
|
-
log(`[session] Unregistered: ${session_id} (removed=${removed})`);
|
|
621
|
-
// Process session asynchronously (summarize transcript, extract learnings, save to memory)
|
|
622
|
-
if (run_reflection && transcriptFile) {
|
|
623
|
-
sessionManager.incrementPendingWork();
|
|
624
|
-
log(`[session] Queuing async processing for ${session_id}`);
|
|
625
|
-
// Fire-and-forget async processing
|
|
626
|
-
(async () => {
|
|
627
|
-
try {
|
|
628
|
-
const result = await processSessionEnd(transcriptFile, session_id, log);
|
|
629
|
-
log(`[session] Processing complete for ${session_id}: summary=${result.summary.length}chars, learnings=${result.learnings.length}, saved=${result.saved}`);
|
|
630
|
-
}
|
|
631
|
-
catch (err) {
|
|
632
|
-
log(`[session] Processing failed for ${session_id}: ${err}`);
|
|
633
|
-
}
|
|
634
|
-
finally {
|
|
635
|
-
sessionManager.decrementPendingWork();
|
|
636
|
-
// Check shutdown after work completes
|
|
637
|
-
checkShutdown();
|
|
638
|
-
}
|
|
639
|
-
})();
|
|
640
|
-
}
|
|
641
|
-
else {
|
|
642
|
-
// No processing needed, check shutdown immediately
|
|
643
|
-
checkShutdown();
|
|
644
|
-
}
|
|
645
|
-
return { success: removed, remaining_sessions: sessionManager.count() };
|
|
646
|
-
}
|
|
647
|
-
if (pathname === '/api/session/activity' && method === 'POST') {
|
|
648
|
-
const { session_id, type, transcript_path, is_service = false } = body;
|
|
649
|
-
if (!session_id || !type) {
|
|
650
|
-
throw new ValidationError('session_id and type required');
|
|
651
|
-
}
|
|
652
|
-
let session = sessionManager.activity(session_id, type);
|
|
653
|
-
if (!session) {
|
|
654
|
-
// Auto-register if session not found (with transcript_path if provided)
|
|
655
|
-
sessionManager.register(session_id, transcript_path || '', is_service);
|
|
656
|
-
session = sessionManager.activity(session_id, type);
|
|
657
|
-
log(`[session] Auto-registered and activity: ${session_id} (${type})${is_service ? ' (service)' : ''}`);
|
|
658
|
-
}
|
|
659
|
-
else if (transcript_path && !session.transcriptPath) {
|
|
660
|
-
// Update transcript path if not set
|
|
661
|
-
session.transcriptPath = transcript_path;
|
|
662
|
-
log(`[session] Activity: ${session_id} (${type}) + updated transcript`);
|
|
663
|
-
}
|
|
664
|
-
else {
|
|
665
|
-
log(`[session] Activity: ${session_id} (${type})`);
|
|
666
|
-
}
|
|
667
|
-
return { success: true };
|
|
668
|
-
}
|
|
669
|
-
if (pathname === '/api/sessions' && method === 'GET') {
|
|
670
|
-
const includeService = searchParams.get('includeService') === 'true';
|
|
671
|
-
const sessions = {};
|
|
672
|
-
for (const [id, session] of sessionManager.getAll(includeService)) {
|
|
673
|
-
sessions[id] = session;
|
|
674
|
-
}
|
|
675
|
-
return { sessions, count: sessionManager.count(includeService) };
|
|
676
|
-
}
|
|
677
|
-
// Search endpoints
|
|
678
|
-
if (pathname === '/api/search' && method === 'POST') {
|
|
679
|
-
const { query, limit = 5, threshold = 0.3 } = body;
|
|
680
|
-
if (!query) {
|
|
681
|
-
throw new ValidationError('query required');
|
|
682
|
-
}
|
|
683
|
-
const queryEmbedding = await getEmbedding(query);
|
|
684
|
-
const results = await hybridSearchDocs(query, queryEmbedding, limit, threshold);
|
|
685
|
-
// Track access for returned memories
|
|
686
|
-
const accesses = results
|
|
687
|
-
.filter((r) => r.memory_id)
|
|
688
|
-
.map((r) => ({ memoryId: r.memory_id, weight: 0.5 }));
|
|
689
|
-
if (accesses.length > 0) {
|
|
690
|
-
await incrementMemoryAccessBatch(accesses);
|
|
691
|
-
}
|
|
692
|
-
return { results };
|
|
693
|
-
}
|
|
694
|
-
if (pathname === '/api/search-code' && method === 'POST') {
|
|
695
|
-
const { query, limit = 5, threshold = 0.3 } = body;
|
|
696
|
-
if (!query) {
|
|
697
|
-
throw new ValidationError('query required');
|
|
698
|
-
}
|
|
699
|
-
const queryEmbedding = await getEmbedding(query);
|
|
700
|
-
const results = await hybridSearchCode(query, queryEmbedding, limit, threshold);
|
|
701
|
-
return { results };
|
|
702
|
-
}
|
|
703
|
-
if (pathname === '/api/recall' && method === 'POST') {
|
|
704
|
-
const { query, limit = 5 } = body;
|
|
705
|
-
// Empty query returns recent memories
|
|
706
|
-
if (!query) {
|
|
707
|
-
const memories = await getRecentMemories(limit);
|
|
708
|
-
return { results: memories };
|
|
709
|
-
}
|
|
710
|
-
// Generate embedding for semantic search
|
|
711
|
-
const queryEmbedding = await getEmbedding(query);
|
|
712
|
-
const results = await hybridSearchMemories(query, queryEmbedding, limit, 0.3);
|
|
713
|
-
// Track access for returned memories
|
|
714
|
-
const accesses = results
|
|
715
|
-
.filter((r) => r.id)
|
|
716
|
-
.map((r) => ({ memoryId: r.id, weight: 1.0 }));
|
|
717
|
-
if (accesses.length > 0) {
|
|
718
|
-
await incrementMemoryAccessBatch(accesses);
|
|
719
|
-
}
|
|
720
|
-
return { results };
|
|
721
|
-
}
|
|
722
|
-
if (pathname === '/api/pinned' && method === 'GET') {
|
|
723
|
-
const pinned = await getPinnedMemories();
|
|
724
|
-
return { results: pinned };
|
|
725
|
-
}
|
|
726
|
-
if (pathname === '/api/pinned/cleanup' && method === 'POST') {
|
|
727
|
-
// Remove false invariant flags from observation-type memories
|
|
728
|
-
const pinned = await getPinnedMemories();
|
|
729
|
-
let cleaned = 0;
|
|
730
|
-
for (const mem of pinned) {
|
|
731
|
-
if (mem.type === 'observation' && mem.is_invariant) {
|
|
732
|
-
await setMemoryInvariant(mem.id, false);
|
|
733
|
-
cleaned++;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
return { cleaned, total: pinned.length };
|
|
737
|
-
}
|
|
738
|
-
if (pathname === '/api/recall-by-tag' && method === 'POST') {
|
|
739
|
-
const { tag, limit = 5 } = body;
|
|
740
|
-
if (!tag) {
|
|
741
|
-
throw new ValidationError('tag required');
|
|
742
|
-
}
|
|
743
|
-
const results = await getMemoriesByTag(tag, limit);
|
|
744
|
-
return { results };
|
|
745
|
-
}
|
|
746
|
-
if (pathname === '/api/hook-rules' && method === 'POST') {
|
|
747
|
-
const { tool_name, tool_input: rawInput } = body;
|
|
748
|
-
if (!tool_name) {
|
|
749
|
-
throw new ValidationError('tool_name required');
|
|
750
|
-
}
|
|
751
|
-
const tool_input = rawInput && typeof rawInput === 'object' && !Array.isArray(rawInput)
|
|
752
|
-
? rawInput
|
|
753
|
-
: {};
|
|
754
|
-
// Check cache
|
|
755
|
-
const now = Date.now();
|
|
756
|
-
if (!hookRulesCache || now - hookRulesCache.timestamp > HOOK_RULES_CACHE_TTL) {
|
|
757
|
-
const memories = await getMemoriesByTag('hook-rule', 50);
|
|
758
|
-
hookRulesCache = { memories, timestamp: now };
|
|
759
|
-
}
|
|
760
|
-
const rules = matchRules(hookRulesCache.memories, tool_name, tool_input);
|
|
761
|
-
return { rules };
|
|
762
|
-
}
|
|
763
|
-
if (pathname === '/api/remember' && method === 'POST') {
|
|
764
|
-
const { content, tags = [], type = 'observation', source, global = false, valid_from, valid_until, } = body;
|
|
765
|
-
if (!content) {
|
|
766
|
-
throw new ValidationError('content required');
|
|
767
|
-
}
|
|
768
|
-
// In-flight dedup: if an identical request is already being processed, wait for it
|
|
769
|
-
// Prevents race condition when hooks fire twice for the same tool_use
|
|
770
|
-
const contentHash = content.slice(0, 200) + '|' + (tags || []).join(',');
|
|
771
|
-
const existing = rememberInFlight.get(contentHash);
|
|
772
|
-
if (existing) {
|
|
773
|
-
const result = await existing;
|
|
774
|
-
return { success: false, id: result.id, isDuplicate: true, reason: 'in-flight dedup' };
|
|
775
|
-
}
|
|
776
|
-
const processRemember = async () => {
|
|
777
|
-
// Check for sensitive content
|
|
778
|
-
const config = getConfig();
|
|
779
|
-
let finalContent = content;
|
|
780
|
-
if (config.sensitive_filter_enabled !== false) {
|
|
781
|
-
const scanResult = scanSensitive(content);
|
|
782
|
-
if (scanResult.hasSensitive) {
|
|
783
|
-
if (config.sensitive_auto_redact) {
|
|
784
|
-
finalContent = scanResult.redactedText;
|
|
785
|
-
}
|
|
786
|
-
else {
|
|
787
|
-
throw new ValidationError('Content contains sensitive information');
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
// Get embedding
|
|
792
|
-
const embedding = await getEmbedding(finalContent);
|
|
793
|
-
// Score quality
|
|
794
|
-
const qualityResult = await scoreMemory(finalContent);
|
|
795
|
-
if (!passesQualityThreshold(qualityResult)) {
|
|
796
|
-
return { success: false, reason: 'Below quality threshold', score: qualityResult.score };
|
|
797
|
-
}
|
|
798
|
-
// Save to appropriate DB
|
|
799
|
-
let result;
|
|
800
|
-
if (global) {
|
|
801
|
-
result = await saveGlobalMemory(finalContent, embedding, tags, type);
|
|
802
|
-
}
|
|
803
|
-
else {
|
|
804
|
-
result = await saveMemory(finalContent, embedding, tags, source ?? type, {
|
|
805
|
-
qualityScore: { score: qualityResult.score, factors: qualityResult.factors },
|
|
806
|
-
validFrom: valid_from,
|
|
807
|
-
validUntil: valid_until,
|
|
808
|
-
});
|
|
809
|
-
}
|
|
810
|
-
// Invalidate hook-rules cache if this memory is a hook rule
|
|
811
|
-
if (Array.isArray(tags) && tags.includes('hook-rule')) {
|
|
812
|
-
hookRulesCache = null;
|
|
813
|
-
}
|
|
814
|
-
return { success: !result.isDuplicate, id: result.id, isDuplicate: result.isDuplicate };
|
|
815
|
-
};
|
|
816
|
-
const promise = processRemember();
|
|
817
|
-
rememberInFlight.set(contentHash, promise);
|
|
818
|
-
setTimeout(() => rememberInFlight.delete(contentHash), REMEMBER_DEDUP_TTL_MS);
|
|
819
|
-
try {
|
|
820
|
-
return await promise;
|
|
821
|
-
}
|
|
822
|
-
finally {
|
|
823
|
-
rememberInFlight.delete(contentHash);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
// Reflection endpoint
|
|
827
|
-
if (pathname === '/api/reflect' && method === 'POST') {
|
|
828
|
-
const { session_id } = body;
|
|
829
|
-
const watcherConfig = getIdleWatcherConfig();
|
|
830
|
-
if (session_id) {
|
|
831
|
-
const session = sessionManager.get(session_id);
|
|
832
|
-
if (!session) {
|
|
833
|
-
throw new NotFoundError('Session not found');
|
|
834
|
-
}
|
|
835
|
-
await handleReflection(session_id, session);
|
|
836
|
-
sessionManager.markReflection(session_id);
|
|
837
|
-
return { success: true, session_id };
|
|
838
|
-
}
|
|
839
|
-
else {
|
|
840
|
-
// Run for all idle sessions
|
|
841
|
-
const idleSessions = sessionManager.getIdleSessions(watcherConfig.idle_minutes);
|
|
842
|
-
for (const { sessionId, session } of idleSessions) {
|
|
843
|
-
await handleReflection(sessionId, session);
|
|
844
|
-
sessionManager.markReflection(sessionId);
|
|
845
|
-
}
|
|
846
|
-
return { success: true, sessions_processed: idleSessions.length };
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
// Compact briefing endpoint (for /compact hook)
|
|
850
|
-
// Supports pre-generated cache for instant responses
|
|
851
|
-
if (pathname === '/api/briefing' && method === 'POST') {
|
|
852
|
-
const { transcript, transcript_path, session_id, format, include_learnings, include_memories, max_memories, use_cache, } = body;
|
|
853
|
-
// Try cached briefing first if session_id provided and use_cache not explicitly false
|
|
854
|
-
if (session_id && use_cache !== false) {
|
|
855
|
-
const cached = briefingCache.get(session_id);
|
|
856
|
-
if (cached) {
|
|
857
|
-
const age = Date.now() - cached.generatedAt;
|
|
858
|
-
if (age < BRIEFING_CACHE_MAX_AGE_MS) {
|
|
859
|
-
log(`[briefing] Serving cached briefing for ${session_id.slice(0, 8)} (age: ${Math.round(age / 1000)}s)`);
|
|
860
|
-
return { success: true, briefing: cached.briefing, cached: true };
|
|
861
|
-
}
|
|
256
|
+
return handler(body, searchParams);
|
|
257
|
+
}
|
|
258
|
+
let updateCheckTimer = null;
|
|
259
|
+
function scheduleUpdateCheck(logFn) {
|
|
260
|
+
// Run once at startup (after short delay to not block init)
|
|
261
|
+
const runCheck = () => {
|
|
262
|
+
import('../lib/version-check.js')
|
|
263
|
+
.then(({ checkForUpdate }) => checkForUpdate())
|
|
264
|
+
.then((result) => {
|
|
265
|
+
if (result?.update_available) {
|
|
266
|
+
logFn(`[update-check] Update available: ${result.current} → ${result.latest}`);
|
|
862
267
|
}
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
if (transcript) {
|
|
867
|
-
transcriptContent = transcript;
|
|
868
|
-
}
|
|
869
|
-
else if (transcript_path && fs.existsSync(transcript_path)) {
|
|
870
|
-
transcriptContent = fs.readFileSync(transcript_path, 'utf-8');
|
|
871
|
-
}
|
|
872
|
-
else {
|
|
873
|
-
throw new ValidationError('transcript or transcript_path required');
|
|
874
|
-
}
|
|
875
|
-
const result = await generateCompactBriefing(transcriptContent, {
|
|
876
|
-
format,
|
|
877
|
-
include_learnings,
|
|
878
|
-
include_memories,
|
|
879
|
-
max_memories,
|
|
268
|
+
})
|
|
269
|
+
.catch((err) => {
|
|
270
|
+
logFn(`[update-check] Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
880
271
|
});
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
generatedAt: Date.now(),
|
|
887
|
-
transcriptSize: stats?.size || 0,
|
|
888
|
-
});
|
|
889
|
-
}
|
|
890
|
-
return { ...result, cached: false };
|
|
891
|
-
}
|
|
892
|
-
// Status endpoints
|
|
893
|
-
if (pathname === '/api/status' && method === 'GET') {
|
|
894
|
-
const stats = await getStats();
|
|
895
|
-
const memStats = await getMemoryStats();
|
|
896
|
-
const watchStatus = getWatcherStatus();
|
|
897
|
-
const analyzeStatus = getAnalyzerStatus();
|
|
898
|
-
return {
|
|
899
|
-
daemon: {
|
|
900
|
-
pid: process.pid,
|
|
901
|
-
uptime: Date.now() - (state?.startedAt || Date.now()),
|
|
902
|
-
sessions: sessionManager.count(),
|
|
903
|
-
},
|
|
904
|
-
index: stats,
|
|
905
|
-
memories: memStats,
|
|
906
|
-
services: {
|
|
907
|
-
watch: watchStatus,
|
|
908
|
-
analyze: analyzeStatus,
|
|
909
|
-
},
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
// Watch service endpoints
|
|
913
|
-
if (pathname === '/api/watch/start' && method === 'POST') {
|
|
914
|
-
const { patterns, includeCode } = body;
|
|
915
|
-
const watchState = await startWatcher({ patterns, includeCode }, log);
|
|
916
|
-
return {
|
|
917
|
-
success: true,
|
|
918
|
-
active: watchState.active,
|
|
919
|
-
patterns: watchState.patterns,
|
|
920
|
-
includeCode: watchState.includeCode,
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
|
-
if (pathname === '/api/watch/stop' && method === 'POST') {
|
|
924
|
-
await stopWatcher(log);
|
|
925
|
-
return { success: true };
|
|
926
|
-
}
|
|
927
|
-
if (pathname === '/api/watch/status' && method === 'GET') {
|
|
928
|
-
return getWatcherStatus();
|
|
929
|
-
}
|
|
930
|
-
if (pathname === '/api/watch/index' && method === 'POST') {
|
|
931
|
-
const { file } = body;
|
|
932
|
-
if (!file) {
|
|
933
|
-
throw new ValidationError('file required');
|
|
934
|
-
}
|
|
935
|
-
await indexFileOnDemand(file, log);
|
|
936
|
-
return { success: true, file };
|
|
937
|
-
}
|
|
938
|
-
// Analyze service endpoints
|
|
939
|
-
if (pathname === '/api/analyze/start' && method === 'POST') {
|
|
940
|
-
const { intervalMinutes, mode } = body;
|
|
941
|
-
const analyzeState = startAnalyzer({ intervalMinutes, mode }, log);
|
|
942
|
-
return {
|
|
943
|
-
success: true,
|
|
944
|
-
active: analyzeState.active,
|
|
945
|
-
runsCompleted: analyzeState.runsCompleted,
|
|
946
|
-
};
|
|
947
|
-
}
|
|
948
|
-
if (pathname === '/api/analyze/stop' && method === 'POST') {
|
|
949
|
-
stopAnalyzer(log);
|
|
950
|
-
return { success: true };
|
|
951
|
-
}
|
|
952
|
-
if (pathname === '/api/analyze/status' && method === 'GET') {
|
|
953
|
-
return getAnalyzerStatus();
|
|
954
|
-
}
|
|
955
|
-
if (pathname === '/api/analyze' && method === 'POST') {
|
|
956
|
-
const { mode = 'claude' } = body;
|
|
957
|
-
await triggerAnalysis(mode, log);
|
|
958
|
-
return { success: true };
|
|
959
|
-
}
|
|
960
|
-
// Skills endpoints
|
|
961
|
-
if (pathname === '/api/skills/suggest' && method === 'POST') {
|
|
962
|
-
const { prompt, limit = 2 } = body;
|
|
963
|
-
if (!prompt) {
|
|
964
|
-
throw new ValidationError('prompt required');
|
|
965
|
-
}
|
|
966
|
-
const { suggestSkills, getSkillsConfig } = await import('../lib/skills.js');
|
|
967
|
-
const config = getSkillsConfig();
|
|
968
|
-
if (!config.enabled || !config.auto_suggest?.enabled) {
|
|
969
|
-
return { success: true, skills: [], disabled: true };
|
|
970
|
-
}
|
|
971
|
-
const suggestions = await suggestSkills(prompt, config);
|
|
972
|
-
return {
|
|
973
|
-
success: true,
|
|
974
|
-
skills: suggestions.slice(0, limit),
|
|
975
|
-
};
|
|
976
|
-
}
|
|
977
|
-
if (pathname === '/api/skills/index' && method === 'POST') {
|
|
978
|
-
const { indexLocalSkills } = await import('../lib/skills.js');
|
|
979
|
-
const cwd = state?.cwd || process.cwd();
|
|
980
|
-
const count = indexLocalSkills(cwd);
|
|
981
|
-
return { success: true, indexed: count };
|
|
982
|
-
}
|
|
983
|
-
if (pathname === '/api/skills/track' && method === 'POST') {
|
|
984
|
-
const { skill_name } = body;
|
|
985
|
-
if (!skill_name) {
|
|
986
|
-
throw new ValidationError('skill_name required');
|
|
987
|
-
}
|
|
988
|
-
const { trackSkillUsage } = await import('../lib/skills.js');
|
|
989
|
-
trackSkillUsage(skill_name);
|
|
990
|
-
return { success: true };
|
|
991
|
-
}
|
|
992
|
-
// Skyll status endpoint
|
|
993
|
-
if (pathname === '/api/skills/skyll' && method === 'GET') {
|
|
994
|
-
const { getSkyllStatus } = await import('../lib/skyll-client.js');
|
|
995
|
-
return getSkyllStatus();
|
|
996
|
-
}
|
|
997
|
-
// Services endpoint (list all services status)
|
|
998
|
-
if (pathname === '/api/services' && method === 'GET') {
|
|
999
|
-
return {
|
|
1000
|
-
watch: getWatcherStatus(),
|
|
1001
|
-
analyze: getAnalyzerStatus(),
|
|
1002
|
-
idle: {
|
|
1003
|
-
enabled: true,
|
|
1004
|
-
sessions: sessionManager.count(),
|
|
1005
|
-
},
|
|
1006
|
-
};
|
|
1007
|
-
}
|
|
1008
|
-
throw new NotFoundError(`Unknown endpoint: ${method} ${pathname}`);
|
|
272
|
+
};
|
|
273
|
+
setTimeout(runCheck, 5000); // 5s after startup
|
|
274
|
+
// Re-check every 12 hours (cache TTL is 24h, so this keeps it warm)
|
|
275
|
+
updateCheckTimer = setInterval(runCheck, 12 * 3600_000);
|
|
276
|
+
updateCheckTimer.unref(); // Don't prevent process exit
|
|
1009
277
|
}
|
|
1010
|
-
// ============================================================================
|
|
1011
|
-
// Daemon Lifecycle
|
|
1012
|
-
// ============================================================================
|
|
1013
278
|
export async function startDaemon() {
|
|
1014
279
|
if (state?.server) {
|
|
1015
280
|
return { port: state.port, pid: process.pid };
|
|
1016
281
|
}
|
|
1017
|
-
// Check if another daemon is already running (prevent duplicate processes)
|
|
1018
282
|
const existingPidFile = getDaemonPidFile();
|
|
1019
283
|
if (fs.existsSync(existingPidFile)) {
|
|
1020
284
|
try {
|
|
1021
285
|
const existingPid = parseInt(fs.readFileSync(existingPidFile, 'utf8').trim(), 10);
|
|
1022
286
|
if (existingPid && existingPid !== process.pid) {
|
|
1023
|
-
// Check if process is actually running
|
|
1024
287
|
try {
|
|
1025
|
-
process.kill(existingPid, 0);
|
|
1026
|
-
// Process exists, read port and return
|
|
288
|
+
process.kill(existingPid, 0);
|
|
1027
289
|
const portFile = getDaemonPortFile();
|
|
1028
290
|
if (fs.existsSync(portFile)) {
|
|
1029
291
|
const port = parseInt(fs.readFileSync(portFile, 'utf8').trim(), 10);
|
|
1030
292
|
log(`[daemon] Another daemon already running (pid=${existingPid}, port=${port})`);
|
|
1031
|
-
process.exit(0);
|
|
293
|
+
process.exit(0);
|
|
1032
294
|
}
|
|
1033
295
|
}
|
|
1034
|
-
catch {
|
|
1035
|
-
|
|
296
|
+
catch (error) {
|
|
297
|
+
logWarn('service', 'Failed to signal existing daemon process for liveness check', {
|
|
298
|
+
error: error instanceof Error ? error.message : String(error),
|
|
299
|
+
});
|
|
1036
300
|
log(`[daemon] Cleaning up stale PID file (pid=${existingPid} not running)`);
|
|
1037
301
|
fs.unlinkSync(existingPidFile);
|
|
1038
302
|
const portFile = getDaemonPortFile();
|
|
@@ -1051,15 +315,10 @@ export async function startDaemon() {
|
|
|
1051
315
|
const cwd = getProjectRoot();
|
|
1052
316
|
const watcherConfig = getIdleWatcherConfig();
|
|
1053
317
|
getIdleReflectionConfig();
|
|
1054
|
-
// Initialize storage dispatcher (routes to SQLite or PG based on config)
|
|
1055
318
|
await initStorageDispatcher();
|
|
1056
|
-
// Initialize session manager
|
|
1057
319
|
sessionManager = createSessionManager();
|
|
1058
|
-
// Load token budgets from previous daemon run
|
|
1059
320
|
loadBudgets();
|
|
1060
|
-
|
|
1061
|
-
cleanupStaleObservations();
|
|
1062
|
-
// Create HTTP server
|
|
321
|
+
initReflectionMaintenance();
|
|
1063
322
|
const server = http.createServer((req, res) => {
|
|
1064
323
|
handleRequest(req, res).catch((err) => {
|
|
1065
324
|
log(`[http] Unhandled error: ${err.message}`);
|
|
@@ -1067,14 +326,25 @@ export async function startDaemon() {
|
|
|
1067
326
|
res.end();
|
|
1068
327
|
});
|
|
1069
328
|
});
|
|
1070
|
-
//
|
|
1071
|
-
const
|
|
1072
|
-
|
|
1073
|
-
|
|
329
|
+
// Try stable port first (config or hash-based), then fall back to scan
|
|
330
|
+
const config = getConfig();
|
|
331
|
+
const stablePort = config.daemon?.port ?? getStablePort(cwd);
|
|
332
|
+
const fallbackStart = config.daemon?.port_range_start ?? DEFAULT_PORT_RANGE_START;
|
|
333
|
+
let port = stablePort;
|
|
334
|
+
let portAttempts = 0;
|
|
335
|
+
const maxAttempts = 1 + MAX_PORT_ATTEMPTS; // 1 stable + 100 fallback
|
|
336
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
1074
337
|
await new Promise((resolve, reject) => {
|
|
1075
338
|
server.once('error', (err) => {
|
|
1076
339
|
if (err.code === 'EADDRINUSE') {
|
|
1077
|
-
|
|
340
|
+
portAttempts++;
|
|
341
|
+
if (portAttempts === 1) {
|
|
342
|
+
// Stable port busy — switch to fallback range
|
|
343
|
+
port = fallbackStart;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
port++;
|
|
347
|
+
}
|
|
1078
348
|
resolve();
|
|
1079
349
|
}
|
|
1080
350
|
else {
|
|
@@ -1090,33 +360,30 @@ export async function startDaemon() {
|
|
|
1090
360
|
}
|
|
1091
361
|
}
|
|
1092
362
|
if (!server.listening) {
|
|
1093
|
-
throw new NetworkError(`Could not find available port
|
|
363
|
+
throw new NetworkError(`Could not find available port (tried ${stablePort}, then ${fallbackStart}-${fallbackStart + MAX_PORT_ATTEMPTS})`);
|
|
1094
364
|
}
|
|
1095
|
-
// Save state
|
|
1096
365
|
state = {
|
|
1097
366
|
cwd,
|
|
1098
367
|
startedAt: Date.now(),
|
|
1099
368
|
port,
|
|
1100
369
|
server,
|
|
1101
370
|
};
|
|
1102
|
-
// Write PID and port files
|
|
1103
371
|
fs.writeFileSync(getDaemonPidFile(), String(process.pid));
|
|
1104
372
|
fs.writeFileSync(getDaemonPortFile(), String(port));
|
|
1105
|
-
// Start idle watcher with briefing pre-generation
|
|
1106
373
|
idleWatcher = createIdleWatcher({
|
|
1107
374
|
sessionManager,
|
|
1108
|
-
onIdle:
|
|
1109
|
-
onPreGenerateBriefing: preGenerateBriefing,
|
|
375
|
+
onIdle: (sessionId, session) => performReflection(createRouteContext(), sessionId, session),
|
|
376
|
+
onPreGenerateBriefing: (sessionId, transcriptPath) => preGenerateBriefing(createRouteContext(), sessionId, transcriptPath),
|
|
1110
377
|
checkIntervalSeconds: watcherConfig.check_interval,
|
|
1111
378
|
idleMinutes: watcherConfig.idle_minutes,
|
|
1112
379
|
reflectionCooldownMinutes: watcherConfig.reflection_cooldown_minutes,
|
|
1113
|
-
preGenerateIdleSeconds: 120,
|
|
380
|
+
preGenerateIdleSeconds: 120,
|
|
1114
381
|
log,
|
|
1115
382
|
});
|
|
1116
383
|
idleWatcher.start();
|
|
1117
384
|
log(`[daemon] Started on port ${port} (pid=${process.pid})`);
|
|
1118
|
-
//
|
|
1119
|
-
|
|
385
|
+
// Background update check — keeps cache warm for MCP-only users
|
|
386
|
+
scheduleUpdateCheck(log);
|
|
1120
387
|
if (config.daemon?.watch?.auto_start) {
|
|
1121
388
|
const watchConfig = config.daemon.watch;
|
|
1122
389
|
await startWatcher({
|
|
@@ -1126,7 +393,6 @@ export async function startDaemon() {
|
|
|
1126
393
|
}, log);
|
|
1127
394
|
log(`[daemon] Auto-started watch service`);
|
|
1128
395
|
}
|
|
1129
|
-
// Auto-start analyze service if configured
|
|
1130
396
|
if (config.daemon?.analyze?.auto_start) {
|
|
1131
397
|
const analyzeConfig = config.daemon.analyze;
|
|
1132
398
|
startAnalyzer({
|
|
@@ -1135,7 +401,6 @@ export async function startDaemon() {
|
|
|
1135
401
|
}, log);
|
|
1136
402
|
log(`[daemon] Auto-started analyze service`);
|
|
1137
403
|
}
|
|
1138
|
-
// Setup graceful shutdown
|
|
1139
404
|
setupShutdownHandlers();
|
|
1140
405
|
return { port, pid: process.pid };
|
|
1141
406
|
}
|
|
@@ -1143,14 +408,18 @@ function setupShutdownHandlers() {
|
|
|
1143
408
|
const shutdown = () => shutdownDaemon();
|
|
1144
409
|
process.on('SIGTERM', shutdown);
|
|
1145
410
|
process.on('SIGINT', shutdown);
|
|
1146
|
-
// SIGHUP only exists on Unix
|
|
1147
411
|
if (process.platform !== 'win32') {
|
|
1148
412
|
process.on('SIGHUP', shutdown);
|
|
1149
413
|
}
|
|
414
|
+
// Prevent silent crashes — log and keep running
|
|
415
|
+
process.on('uncaughtException', (err) => {
|
|
416
|
+
log(`[daemon] UNCAUGHT EXCEPTION: ${err.message}\n${err.stack}`);
|
|
417
|
+
});
|
|
418
|
+
process.on('unhandledRejection', (reason) => {
|
|
419
|
+
const msg = reason instanceof Error ? `${reason.message}\n${reason.stack}` : String(reason);
|
|
420
|
+
log(`[daemon] UNHANDLED REJECTION: ${msg}`);
|
|
421
|
+
});
|
|
1150
422
|
}
|
|
1151
|
-
/**
|
|
1152
|
-
* Check if daemon should shutdown (no sessions and no pending work)
|
|
1153
|
-
*/
|
|
1154
423
|
function checkShutdown() {
|
|
1155
424
|
if (sessionManager?.canShutdown()) {
|
|
1156
425
|
log(`[daemon] No more sessions and no pending work, scheduling shutdown`);
|
|
@@ -1158,54 +427,56 @@ function checkShutdown() {
|
|
|
1158
427
|
if (sessionManager?.canShutdown()) {
|
|
1159
428
|
shutdownDaemon();
|
|
1160
429
|
}
|
|
1161
|
-
}, 5000);
|
|
430
|
+
}, 5000);
|
|
1162
431
|
}
|
|
1163
432
|
}
|
|
1164
|
-
export function shutdownDaemon() {
|
|
433
|
+
export async function shutdownDaemon() {
|
|
1165
434
|
log('[daemon] Shutting down...');
|
|
1166
|
-
|
|
435
|
+
if (updateCheckTimer) {
|
|
436
|
+
clearInterval(updateCheckTimer);
|
|
437
|
+
updateCheckTimer = null;
|
|
438
|
+
}
|
|
1167
439
|
if (idleWatcher) {
|
|
1168
440
|
idleWatcher.stop();
|
|
1169
441
|
idleWatcher = null;
|
|
1170
442
|
}
|
|
1171
|
-
|
|
1172
|
-
stopWatcher(log).catch((err) => log(`[shutdown] Watcher stop failed: ${err}`));
|
|
1173
|
-
// Stop analyze service
|
|
443
|
+
const watcherPromise = stopWatcher(log).catch((err) => log(`[shutdown] Watcher stop failed: ${err}`));
|
|
1174
444
|
stopAnalyzer(log);
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
445
|
+
const serverPromise = state?.server
|
|
446
|
+
? new Promise((resolve) => {
|
|
447
|
+
state.server.close(() => resolve());
|
|
448
|
+
state.server = null;
|
|
449
|
+
})
|
|
450
|
+
: Promise.resolve();
|
|
1181
451
|
processRegistry.killAll();
|
|
1182
|
-
//
|
|
1183
|
-
|
|
452
|
+
// Wait for watcher and HTTP server to finish before closing storage,
|
|
453
|
+
// so in-flight requests don't hit a closed DB.
|
|
454
|
+
await Promise.allSettled([watcherPromise, serverPromise]);
|
|
455
|
+
await closeStorageDispatcher().catch((err) => log(`[shutdown] Storage close failed: ${err}`));
|
|
1184
456
|
cleanupEmbeddings();
|
|
457
|
+
await cleanupReranker().catch((err) => log(`[shutdown] Reranker cleanup failed: ${err}`));
|
|
1185
458
|
cleanupQualityScoring();
|
|
1186
459
|
closeDb();
|
|
1187
460
|
closeGlobalDb();
|
|
1188
|
-
// Remove PID and port files
|
|
1189
461
|
try {
|
|
1190
462
|
fs.unlinkSync(getDaemonPidFile());
|
|
1191
463
|
}
|
|
1192
464
|
catch (err) {
|
|
1193
|
-
if (err.code !== 'ENOENT')
|
|
1194
|
-
log(`[shutdown] PID file removal failed: ${err}`);
|
|
465
|
+
if (!isErrnoException(err) || err.code !== 'ENOENT') {
|
|
466
|
+
log(`[shutdown] PID file removal failed: ${getErrorMessage(err)}`);
|
|
467
|
+
}
|
|
1195
468
|
}
|
|
1196
469
|
try {
|
|
1197
470
|
fs.unlinkSync(getDaemonPortFile());
|
|
1198
471
|
}
|
|
1199
472
|
catch (err) {
|
|
1200
|
-
if (err.code !== 'ENOENT')
|
|
1201
|
-
log(`[shutdown] Port file removal failed: ${err}`);
|
|
473
|
+
if (!isErrnoException(err) || err.code !== 'ENOENT') {
|
|
474
|
+
log(`[shutdown] Port file removal failed: ${getErrorMessage(err)}`);
|
|
475
|
+
}
|
|
1202
476
|
}
|
|
1203
477
|
log('[daemon] Shutdown complete');
|
|
1204
478
|
process.exit(0);
|
|
1205
479
|
}
|
|
1206
|
-
// ============================================================================
|
|
1207
|
-
// Test Helpers (exported for unit testing)
|
|
1208
|
-
// ============================================================================
|
|
1209
480
|
/** @internal Initialize module state for testing without starting HTTP server */
|
|
1210
481
|
export function _initTestState(cwd = process.cwd()) {
|
|
1211
482
|
sessionManager = createSessionManager();
|
|
@@ -1216,13 +487,10 @@ export function _resetTestState() {
|
|
|
1216
487
|
sessionManager = null;
|
|
1217
488
|
idleWatcher = null;
|
|
1218
489
|
state = null;
|
|
1219
|
-
|
|
1220
|
-
|
|
490
|
+
resetReflectionRoutesState();
|
|
491
|
+
resetMemoryRoutesState();
|
|
492
|
+
resetSearchRoutesState();
|
|
1221
493
|
}
|
|
1222
|
-
// ============================================================================
|
|
1223
|
-
// CLI Entry Point
|
|
1224
|
-
// ============================================================================
|
|
1225
|
-
// If run directly, start daemon
|
|
1226
494
|
if (process.argv[1]?.endsWith('service.js') || process.argv[1]?.endsWith('service.ts')) {
|
|
1227
495
|
startDaemon()
|
|
1228
496
|
.then(({ port, pid }) => {
|