@selesai/code 0.13.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.js +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/extensions/ascii.txt +9 -0
- package/dist/extensions/package.json +1 -2
- package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
- package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
- package/dist/extensions/pi-hermes-memory/README.md +643 -0
- package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
- package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
- package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
- package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
- package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
- package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
- package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
- package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
- package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
- package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
- package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
- package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
- package/dist/extensions/pi-hermes-memory/package.json +66 -0
- package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
- package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
- package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
- package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
- package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
- package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
- package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
- package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
- package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
- package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
- package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
- package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
- package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
- package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
- package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
- package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
- package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
- package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
- package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
- package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
- package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
- package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
- package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
- package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
- package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
- package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
- package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
- package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
- package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
- package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
- package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
- package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
- package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
- package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
- package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
- package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
- package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
- package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
- package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
- package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
- package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
- package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
- package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
- package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
- package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
- package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
- package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
- package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
- package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
- package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
- package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
- package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
- package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
- package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
- package/dist/extensions/pi-subagents/.oxlintrc.json +43 -0
- package/dist/extensions/pi-subagents/AGENTS.md +5 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +157 -0
- package/dist/extensions/pi-subagents/README.md +12 -6
- package/dist/extensions/pi-subagents/VISION.md +81 -0
- package/dist/extensions/pi-subagents/agents/claude-code-writer.md +15 -0
- package/dist/extensions/pi-subagents/agents/claude-code.md +15 -0
- package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +15 -0
- package/dist/extensions/pi-subagents/agents/codex-exec.md +15 -0
- package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +14 -0
- package/dist/extensions/pi-subagents/agents/cursor-agent.md +14 -0
- package/dist/extensions/pi-subagents/agents/reviewer.md +1 -3
- package/dist/extensions/pi-subagents/docs/agents.md +488 -0
- package/dist/extensions/pi-subagents/docs/configuration.md +46 -9
- package/dist/extensions/pi-subagents/docs/extension-api.md +42 -3
- package/dist/extensions/pi-subagents/docs/models.md +27 -4
- package/dist/extensions/pi-subagents/docs/observability.md +10 -9
- package/dist/extensions/pi-subagents/docs/tool-reference.md +102 -6
- package/dist/extensions/pi-subagents/docs/workflows.md +135 -0
- package/dist/extensions/pi-subagents/package-lock.json +389 -2
- package/dist/extensions/pi-subagents/package.json +3 -1
- package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
- package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +3 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +7 -3
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +26 -5
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -22
- package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +4 -4
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +9 -2
- package/dist/extensions/pi-subagents/src/agents/agents.ts +322 -75
- package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +6 -0
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-events.ts +70 -0
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +27 -20
- package/dist/extensions/pi-subagents/src/api/agents.ts +10 -5
- package/dist/extensions/pi-subagents/src/api/background-work.ts +5 -1
- package/dist/extensions/pi-subagents/src/api/delegation.ts +0 -7
- package/dist/extensions/pi-subagents/src/api/preflight.ts +34 -12
- package/dist/extensions/pi-subagents/src/extension/config.ts +70 -0
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -3
- package/dist/extensions/pi-subagents/src/extension/index.ts +83 -30
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +44 -15
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +55 -19
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +41 -15
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -31
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -1
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -10
- package/dist/extensions/pi-subagents/src/inspectors/herdr/session-roots-codec.ts +42 -0
- package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +51 -3
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +2 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +5 -6
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +77 -10
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +176 -53
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +19 -12
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +29 -27
- package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +20 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +23 -261
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +73 -7
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +8 -3
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +74 -8
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +37 -21
- package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +8 -8
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +184 -9
- package/dist/extensions/pi-subagents/src/runs/background/result-delivery-ownership.ts +45 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +28 -14
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +38 -15
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +50 -13
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +93 -8
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +10 -4
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -14
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +549 -377
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +58 -10
- package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +26 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +23 -9
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +9 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +204 -129
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +9 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +23 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +773 -311
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +140 -115
- package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +119 -0
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +463 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +19 -4
- package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +151 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +21 -7
- package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +129 -0
- package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +89 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +5 -4
- package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +167 -0
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +122 -0
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +348 -55
- package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +5 -5
- package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +230 -0
- package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +105 -0
- package/dist/extensions/pi-subagents/src/runs/shared/launch-cwd.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +422 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +69 -7
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +47 -6
- package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +7 -2
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +9 -3
- package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +419 -7
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +15 -1
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +81 -8
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +13 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +21 -1
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +44 -4
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +98 -13
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +847 -0
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +18 -0
- package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +46 -0
- package/dist/extensions/pi-subagents/src/shared/extension-context.ts +24 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +21 -0
- package/dist/extensions/pi-subagents/src/shared/formatters.ts +18 -3
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +5 -1
- package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +450 -0
- package/dist/extensions/pi-subagents/src/shared/session-file-trust.ts +19 -0
- package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +14 -3
- package/dist/extensions/pi-subagents/src/shared/settings.ts +19 -105
- package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +17 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +359 -40
- package/dist/extensions/pi-subagents/src/shared/utils.ts +41 -84
- package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +116 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -9
- package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +0 -4
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -2
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +378 -88
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +22 -11
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +3 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +147 -79
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +11 -5
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +39 -18
- package/dist/extensions/pi-subagents/src/tui/render.ts +369 -46
- package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +1 -1
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +6 -3
- package/dist/extensions/pi-subagents/src/workflows/host-command.ts +230 -0
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +740 -58
- package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +121 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +270 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +195 -6
- package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +246 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1114 -260
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +150 -3
- package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +55 -0
- package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +57 -0
- package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +59 -0
- package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +57 -1
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +4 -3
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +54 -4
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +482 -4
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +157 -9
- package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +5 -5
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1508 -291
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +423 -1
- package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/support/cursor-smoke-inputs.ts +51 -0
- package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +5 -5
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +6 -6
- package/dist/extensions/pi-subagents/test/unit/abort-recovery.test.ts +152 -0
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +184 -6
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +66 -6
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/anti-slop-oxlint.test.ts +86 -0
- package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +101 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +121 -0
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +14 -7
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +190 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +42 -0
- package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +8 -4
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +78 -1
- package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/child-session-name.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +245 -0
- package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +192 -0
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +150 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +259 -0
- package/dist/extensions/pi-subagents/test/unit/cursor-smoke-inputs.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +1 -4
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/extension-context.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +223 -0
- package/dist/extensions/pi-subagents/test/unit/fast-mode-extension.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +208 -2
- package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +29 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +110 -1
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
- package/dist/extensions/pi-subagents/test/unit/formatters.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +18 -9
- package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +63 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +132 -0
- package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +156 -0
- package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +98 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +178 -26
- package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +2 -1
- package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +138 -0
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +91 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +72 -7
- package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +20 -2
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +129 -1
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +4 -0
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +358 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +574 -21
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +109 -1
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +71 -1
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +214 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +29 -8
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/unit/result-delivery-ownership.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +101 -3
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +10 -8
- package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +71 -1
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +41 -7
- package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +136 -2
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +137 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +33 -12
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +504 -1
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +12 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +3 -5
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +5 -6
- package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +0 -8
- package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +5 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +157 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +63 -4
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +27 -23
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +7 -5
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +106 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +484 -9
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +38 -4
- package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +155 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +318 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +365 -0
- package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +349 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +2 -7
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/index.ts +13 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/rules/no-service-constructor-imports.ts +52 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/index.ts +41 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-chained-type-assertions.ts +77 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts +49 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-known-value-widening.ts +247 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-module-mocking.ts +91 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-object-parameters.ts +126 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-apply.ts +28 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-get.ts +28 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-runtime-typeof.ts +67 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-shape-in-symbol-names.ts +39 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-parameters.ts +83 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-returns.ts +115 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts +72 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts +134 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-widen-then-assert.ts +366 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/require-safety-comment-for-type-assertion.ts +62 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/dictionary-types.ts +502 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/lexical-type-parameters.ts +60 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/reflect-method.ts +35 -0
- package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +1 -1
- package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +2 -2
- package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -7
- package/dist/skills/workflow/SKILL.md +65 -50
- package/package.json +112 -110
- package/dist/extensions/enable-readonly-tools.test.ts +0 -63
- package/dist/extensions/enable-readonly-tools.ts +0 -24
- package/dist/extensions/model-prompt-injector/config.json +0 -16
- package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
- package/dist/extensions/model-prompt-injector/index.ts +0 -148
- package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-consolidation — when memory hits capacity, trigger automatic
|
|
3
|
+
* consolidation instead of returning an error.
|
|
4
|
+
*
|
|
5
|
+
* Default transport: in-process direct completion (same mechanism as
|
|
6
|
+
* background review — see review-memory-ops.ts), used only when a caller
|
|
7
|
+
* supplies model/modelRegistry access (the manual `/memory-consolidate`
|
|
8
|
+
* command has it; the automatic over-capacity consolidator registered on
|
|
9
|
+
* MemoryStore does not, since MemoryStore itself has no extension-runtime
|
|
10
|
+
* access, so that path stays subprocess-only). Falls back to a `pi -p`
|
|
11
|
+
* subprocess when direct mode is unavailable, declines, or fails.
|
|
12
|
+
*
|
|
13
|
+
* The subprocess child process modifies files on disk, so the parent MUST
|
|
14
|
+
* reload from disk after a subprocess-based consolidation completes.
|
|
15
|
+
*/
|
|
16
|
+
import { resolveProjectName, resolveProjectStore, type ProjectNameRef, type ProjectStoreRef } from "../project-context.js";
|
|
17
|
+
|
|
18
|
+
import * as fs from "node:fs/promises";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { createHash } from "node:crypto";
|
|
21
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
22
|
+
import { MemoryStore } from "../store/memory-store.js";
|
|
23
|
+
import { DatabaseManager } from "../store/db.js";
|
|
24
|
+
import {
|
|
25
|
+
CONSOLIDATION_PROMPT,
|
|
26
|
+
DEFAULT_CONSOLIDATION_TIMEOUT_MS,
|
|
27
|
+
DIRECT_CONSOLIDATION_SYSTEM_PROMPT,
|
|
28
|
+
ENTRY_DELIMITER,
|
|
29
|
+
} from "../constants.js";
|
|
30
|
+
import type { ConsolidationResult, MemoryConfig } from "../types.js";
|
|
31
|
+
import { AGENT_ROOT } from "../paths.js";
|
|
32
|
+
import { execChildPrompt } from "./pi-child-process.js";
|
|
33
|
+
import { runDirectMemoryCompletion, usesDirectTransport } from "./review-memory-ops.js";
|
|
34
|
+
import { AtomicLockCoordinator } from "../store/atomic-lock-coordinator.js";
|
|
35
|
+
|
|
36
|
+
type MemoryTarget = "memory" | "user" | "failure";
|
|
37
|
+
type ToolMemoryTarget = MemoryTarget | "project";
|
|
38
|
+
type ConsolidationLlmConfig = Pick<MemoryConfig, "llmModelOverride" | "llmThinkingOverride" | "reviewTransport">;
|
|
39
|
+
|
|
40
|
+
// staleMs is deliberately decoupled from the consolidation timeout. The holder
|
|
41
|
+
// beats every CONSOLIDATION_LOCK_HEARTBEAT_MS while its child runs, so a
|
|
42
|
+
// legitimately slow consolidation (up to 2x timeoutMs once retryWithoutOverrides
|
|
43
|
+
// fires) never loses its lease, while a holder that stops making progress is
|
|
44
|
+
// reclaimable after seconds instead of after its worst-case runtime (#144).
|
|
45
|
+
const CONSOLIDATION_LOCK_STALE_MS = 45_000;
|
|
46
|
+
const CONSOLIDATION_LOCK_HEARTBEAT_MS = 10_000;
|
|
47
|
+
// Contention is usually transient. Poll for the lock the way
|
|
48
|
+
// acquireMarkdownMutationLock does instead of hard-failing the memory write
|
|
49
|
+
// that triggered auto-consolidation on the very first collision.
|
|
50
|
+
const CONSOLIDATION_LOCK_WAIT_MS = 5_000;
|
|
51
|
+
const CONSOLIDATION_LOCK_POLL_MS = 50;
|
|
52
|
+
const CONSOLIDATION_LOCK_ENV = "PI_HERMES_CONSOLIDATION_LOCK_DIR";
|
|
53
|
+
const CONSOLIDATION_LOCK_WAIT_ENV = "PI_HERMES_CONSOLIDATION_LOCK_WAIT_MS";
|
|
54
|
+
|
|
55
|
+
interface ConsolidationLock {
|
|
56
|
+
release: () => Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface ConsolidationLockAttempt {
|
|
60
|
+
lock: ConsolidationLock | null;
|
|
61
|
+
/** True when the lock was held by someone else on the first attempt. */
|
|
62
|
+
contended: boolean;
|
|
63
|
+
waitedMs: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function consolidationLockRoot(): string {
|
|
67
|
+
return process.env[CONSOLIDATION_LOCK_ENV]?.trim()
|
|
68
|
+
|| path.join(AGENT_ROOT, "pi-hermes-memory", ".consolidation-locks");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function sanitizeLockPart(value: string): string {
|
|
72
|
+
return value.replace(/[^a-z0-9._-]+/gi, "_").slice(0, 80) || "unknown";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function consolidationLockKey(target: MemoryTarget, toolTarget: ToolMemoryTarget, storageIdentity: string): string {
|
|
76
|
+
const storageHash = createHash("sha256").update(storageIdentity).digest("hex");
|
|
77
|
+
return `${sanitizeLockPart(toolTarget)}:${sanitizeLockPart(target)}:${storageHash}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function consolidationLockWaitMs(): number {
|
|
81
|
+
const configured = Number(process.env[CONSOLIDATION_LOCK_WAIT_ENV]);
|
|
82
|
+
return Number.isFinite(configured) && configured >= 0 ? configured : CONSOLIDATION_LOCK_WAIT_MS;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function acquireConsolidationLock(
|
|
86
|
+
store: MemoryStore,
|
|
87
|
+
target: MemoryTarget,
|
|
88
|
+
toolTarget: ToolMemoryTarget,
|
|
89
|
+
): Promise<ConsolidationLockAttempt> {
|
|
90
|
+
const storageIdentity = await store.getStorageIdentity(target);
|
|
91
|
+
const root = consolidationLockRoot();
|
|
92
|
+
await fs.mkdir(root, { recursive: true });
|
|
93
|
+
const coordinator = AtomicLockCoordinator.shared(path.join(root, "locks.sqlite"));
|
|
94
|
+
const key = consolidationLockKey(target, toolTarget, storageIdentity);
|
|
95
|
+
const lockOptions = { staleMs: CONSOLIDATION_LOCK_STALE_MS };
|
|
96
|
+
|
|
97
|
+
const startedAt = Date.now();
|
|
98
|
+
let lease = coordinator.tryAcquire(key, lockOptions);
|
|
99
|
+
const contended = !lease;
|
|
100
|
+
if (contended) {
|
|
101
|
+
const deadline = startedAt + consolidationLockWaitMs();
|
|
102
|
+
while (!lease && Date.now() < deadline) {
|
|
103
|
+
// Same shape as acquireMarkdownMutationLock; Promise.withResolvers would
|
|
104
|
+
// need an ES2024 lib this project does not target.
|
|
105
|
+
await new Promise((resolve) => setTimeout(resolve, CONSOLIDATION_LOCK_POLL_MS));
|
|
106
|
+
lease = coordinator.tryAcquire(key, lockOptions);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const waitedMs = Date.now() - startedAt;
|
|
111
|
+
if (!lease) return { lock: null, contended, waitedMs };
|
|
112
|
+
|
|
113
|
+
const held = lease;
|
|
114
|
+
const heartbeat = setInterval(() => {
|
|
115
|
+
try {
|
|
116
|
+
held.renew();
|
|
117
|
+
} catch {
|
|
118
|
+
// A missed beat only moves the lease closer to staleMs; the next beat
|
|
119
|
+
// recovers, and a permanently broken lock DB should not crash the run.
|
|
120
|
+
}
|
|
121
|
+
}, CONSOLIDATION_LOCK_HEARTBEAT_MS);
|
|
122
|
+
heartbeat.unref?.();
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
lock: {
|
|
126
|
+
release: async () => {
|
|
127
|
+
clearInterval(heartbeat);
|
|
128
|
+
held.release();
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
contended,
|
|
132
|
+
waitedMs,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function entriesForTarget(store: MemoryStore, target: MemoryTarget): string[] {
|
|
137
|
+
if (target === "user") return store.getUserEntries();
|
|
138
|
+
if (target === "failure") return store.getAllFailureEntries();
|
|
139
|
+
return store.getMemoryEntries();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function labelForTarget(target: MemoryTarget, toolTarget: ToolMemoryTarget): string {
|
|
143
|
+
if (toolTarget === "project") return "Project Memory";
|
|
144
|
+
if (target === "user") return "User Profile";
|
|
145
|
+
if (target === "failure") return "Failure Memory";
|
|
146
|
+
return "Memory";
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function describeConsolidationFailure(
|
|
150
|
+
result: { code: number; stderr?: string; killed?: boolean },
|
|
151
|
+
timeoutMs: number,
|
|
152
|
+
): string {
|
|
153
|
+
const stderr = result.stderr?.trim();
|
|
154
|
+
const terminated = result.killed || result.code === 124 || result.code === 143;
|
|
155
|
+
|
|
156
|
+
if (terminated) {
|
|
157
|
+
return `Consolidation subprocess was terminated (likely timeout or cancellation). Timeout: ${timeoutMs}ms. Raise consolidationTimeoutMs if consolidation legitimately needs longer.`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return `Consolidation process exited with code ${result.code}: ${stderr?.slice(0, 200) || "unknown error"}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function buildConsolidationPrompt(
|
|
164
|
+
target: MemoryTarget,
|
|
165
|
+
toolTarget: ToolMemoryTarget,
|
|
166
|
+
entries: string[],
|
|
167
|
+
): string {
|
|
168
|
+
return [
|
|
169
|
+
CONSOLIDATION_PROMPT,
|
|
170
|
+
"",
|
|
171
|
+
`--- Current ${labelForTarget(target, toolTarget)} Entries ---`,
|
|
172
|
+
entries.join(ENTRY_DELIMITER) || "(empty)",
|
|
173
|
+
"",
|
|
174
|
+
`Use memory_add, memory_replace, or memory_remove to consolidate. Target: '${toolTarget}'`,
|
|
175
|
+
].join("\n");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function triggerConsolidation(
|
|
179
|
+
pi: ExtensionAPI,
|
|
180
|
+
store: MemoryStore,
|
|
181
|
+
target: MemoryTarget,
|
|
182
|
+
signal?: AbortSignal,
|
|
183
|
+
timeoutMs: number = DEFAULT_CONSOLIDATION_TIMEOUT_MS,
|
|
184
|
+
toolTarget: ToolMemoryTarget = target,
|
|
185
|
+
llmConfig: ConsolidationLlmConfig = {},
|
|
186
|
+
directCtx: Pick<ExtensionContext, "model" | "modelRegistry"> | null = null,
|
|
187
|
+
dbManager: DatabaseManager | null = null,
|
|
188
|
+
projectName?: string | null,
|
|
189
|
+
deps: { runDirectMemoryCompletion?: typeof runDirectMemoryCompletion } = {},
|
|
190
|
+
): Promise<ConsolidationResult> {
|
|
191
|
+
const entries = entriesForTarget(store, target);
|
|
192
|
+
const currentContent = entries.join(ENTRY_DELIMITER);
|
|
193
|
+
const runDirect = deps.runDirectMemoryCompletion ?? runDirectMemoryCompletion;
|
|
194
|
+
|
|
195
|
+
if (directCtx && usesDirectTransport(llmConfig)) {
|
|
196
|
+
try {
|
|
197
|
+
const directResult = await runDirect(
|
|
198
|
+
directCtx,
|
|
199
|
+
store,
|
|
200
|
+
toolTarget === "project" ? store : null,
|
|
201
|
+
{
|
|
202
|
+
systemPrompt: DIRECT_CONSOLIDATION_SYSTEM_PROMPT,
|
|
203
|
+
userPrompt: [
|
|
204
|
+
`--- Current ${labelForTarget(target, toolTarget)} Entries (target: '${toolTarget}') ---`,
|
|
205
|
+
currentContent || "(empty)",
|
|
206
|
+
"",
|
|
207
|
+
`Only emit operations with "target": "${toolTarget}".`,
|
|
208
|
+
].join("\n"),
|
|
209
|
+
config: llmConfig,
|
|
210
|
+
timeoutMs,
|
|
211
|
+
signal,
|
|
212
|
+
requireAtomicShrink: true,
|
|
213
|
+
expectedTarget: toolTarget,
|
|
214
|
+
},
|
|
215
|
+
dbManager,
|
|
216
|
+
projectName,
|
|
217
|
+
);
|
|
218
|
+
// Consolidation only did its job if it actually freed space — unlike
|
|
219
|
+
// review/flush/correction, an empty or fully-skipped result here is a
|
|
220
|
+
// failure worth falling back to subprocess for, not a normal outcome.
|
|
221
|
+
if (directResult.ok && directResult.appliedCount > 0) {
|
|
222
|
+
return { consolidated: true };
|
|
223
|
+
}
|
|
224
|
+
} catch {
|
|
225
|
+
// Fall through to subprocess below.
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let lock: ConsolidationLock | null = null;
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
const attempt = await acquireConsolidationLock(store, target, toolTarget);
|
|
233
|
+
lock = attempt.lock;
|
|
234
|
+
if (!lock) {
|
|
235
|
+
// Not a failure: the work is already running in another session. Say so
|
|
236
|
+
// plainly so the memory-write path can ask for a retry instead of
|
|
237
|
+
// reporting a broken consolidation mid-task (#144).
|
|
238
|
+
return {
|
|
239
|
+
consolidated: false,
|
|
240
|
+
deferred: true,
|
|
241
|
+
error: `Consolidation already in progress for target '${toolTarget}' in another session`
|
|
242
|
+
+ ` (waited ${attempt.waitedMs}ms). Nothing was consolidated here — retry shortly.`,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
let promptEntries = entries;
|
|
247
|
+
if (attempt.contended) {
|
|
248
|
+
// We queued behind another session's consolidation and it has now
|
|
249
|
+
// finished. If it already freed space, running a second LLM pass here
|
|
250
|
+
// costs a child turn and over-compresses memory for nothing — hand the
|
|
251
|
+
// caller a reload-and-retry instead.
|
|
252
|
+
try {
|
|
253
|
+
await store.loadFromDisk();
|
|
254
|
+
const refreshed = entriesForTarget(store, target);
|
|
255
|
+
if (refreshed.join(ENTRY_DELIMITER).length < currentContent.length) {
|
|
256
|
+
return { consolidated: true };
|
|
257
|
+
}
|
|
258
|
+
promptEntries = refreshed;
|
|
259
|
+
} catch {
|
|
260
|
+
// Reload failed — consolidate the entries we already read instead.
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const result = await execChildPrompt(pi, buildConsolidationPrompt(target, toolTarget, promptEntries), llmConfig, {
|
|
265
|
+
signal,
|
|
266
|
+
timeoutMs,
|
|
267
|
+
retryWithoutOverrides: true,
|
|
268
|
+
}) as { code: number; stdout?: string; stderr?: string; killed?: boolean };
|
|
269
|
+
|
|
270
|
+
if (result.code === 0) {
|
|
271
|
+
return { consolidated: true };
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
consolidated: false,
|
|
275
|
+
error: describeConsolidationFailure(result, timeoutMs),
|
|
276
|
+
};
|
|
277
|
+
} catch (err) {
|
|
278
|
+
const message = String(err);
|
|
279
|
+
if (message.includes("extension ctx is stale")) {
|
|
280
|
+
// Session replaced/reloaded while consolidation was running. The new
|
|
281
|
+
// session re-initializes the store and will consolidate on its own next
|
|
282
|
+
// write, so this is a skip, not a failure — report it as deferred so the
|
|
283
|
+
// caller asks for a retry instead of surfacing a stale-ctx error.
|
|
284
|
+
return {
|
|
285
|
+
consolidated: false,
|
|
286
|
+
deferred: true,
|
|
287
|
+
error: "session replaced or reloaded during consolidation — will consolidate on next write",
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
consolidated: false,
|
|
292
|
+
error: `Consolidation failed: ${message.slice(0, 200)}`,
|
|
293
|
+
};
|
|
294
|
+
} finally {
|
|
295
|
+
if (lock) {
|
|
296
|
+
try { await lock.release(); } catch { /* best-effort cleanup */ }
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Register the /memory-consolidate command for manual consolidation.
|
|
303
|
+
*/
|
|
304
|
+
export function registerConsolidateCommand(
|
|
305
|
+
pi: ExtensionAPI,
|
|
306
|
+
store: MemoryStore,
|
|
307
|
+
timeoutMs: number = DEFAULT_CONSOLIDATION_TIMEOUT_MS,
|
|
308
|
+
projectStore: ProjectStoreRef = null,
|
|
309
|
+
projectName: ProjectNameRef = null,
|
|
310
|
+
llmConfig: ConsolidationLlmConfig = {},
|
|
311
|
+
dbManager: DatabaseManager | null = null,
|
|
312
|
+
deps: { runDirectMemoryCompletion?: typeof runDirectMemoryCompletion } = {},
|
|
313
|
+
): void {
|
|
314
|
+
pi.registerCommand("memory-consolidate", {
|
|
315
|
+
description: "Manually trigger memory consolidation to free up space",
|
|
316
|
+
handler: async (_args, ctx) => {
|
|
317
|
+
const results: string[] = [];
|
|
318
|
+
const activeProjectStore = resolveProjectStore(projectStore);
|
|
319
|
+
const activeProjectName = resolveProjectName(projectName);
|
|
320
|
+
const targets: Array<{
|
|
321
|
+
label: string;
|
|
322
|
+
store: MemoryStore;
|
|
323
|
+
target: MemoryTarget;
|
|
324
|
+
toolTarget: ToolMemoryTarget;
|
|
325
|
+
}> = [
|
|
326
|
+
{ label: "memory", store, target: "memory", toolTarget: "memory" },
|
|
327
|
+
{ label: "user", store, target: "user", toolTarget: "user" },
|
|
328
|
+
{ label: "failure", store, target: "failure", toolTarget: "failure" },
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
if (activeProjectStore) {
|
|
332
|
+
targets.push({
|
|
333
|
+
label: activeProjectName ? `project:${activeProjectName}` : "project",
|
|
334
|
+
store: activeProjectStore,
|
|
335
|
+
target: "memory",
|
|
336
|
+
toolTarget: "project",
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
ctx.ui.notify(
|
|
342
|
+
`🔄 Starting memory consolidation for ${targets.length} target${targets.length === 1 ? "" : "s"}...`,
|
|
343
|
+
"info",
|
|
344
|
+
);
|
|
345
|
+
} catch {
|
|
346
|
+
// Best-effort only. If the command context is already stale, continue
|
|
347
|
+
// with the consolidation work rather than failing before it starts.
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
for (const item of targets) {
|
|
351
|
+
const entries = entriesForTarget(item.store, item.target);
|
|
352
|
+
|
|
353
|
+
if (entries.length === 0) {
|
|
354
|
+
results.push(`${item.label}: (empty, nothing to consolidate)`);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
try {
|
|
359
|
+
ctx.ui.notify(
|
|
360
|
+
`⏳ Consolidating ${item.label}...`,
|
|
361
|
+
"info",
|
|
362
|
+
);
|
|
363
|
+
} catch {
|
|
364
|
+
// Best-effort progress feedback only.
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const result = await triggerConsolidation(
|
|
368
|
+
pi,
|
|
369
|
+
item.store,
|
|
370
|
+
item.target,
|
|
371
|
+
ctx.signal,
|
|
372
|
+
timeoutMs,
|
|
373
|
+
item.toolTarget,
|
|
374
|
+
llmConfig,
|
|
375
|
+
ctx,
|
|
376
|
+
dbManager,
|
|
377
|
+
activeProjectName,
|
|
378
|
+
deps,
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
if (result.consolidated) {
|
|
382
|
+
await item.store.loadFromDisk();
|
|
383
|
+
results.push(`${item.label}: ✅ consolidated`);
|
|
384
|
+
} else {
|
|
385
|
+
results.push(`${item.label}: ❌ ${result.error}`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const summary = `\n 🔄 Memory Consolidation\n ${"─".repeat(30)}\n${results.map((r) => ` ${r}`).join("\n")}`;
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
ctx.ui.notify(summary, "info");
|
|
393
|
+
} catch {
|
|
394
|
+
// Child consolidation can indirectly trigger a runtime reload/session
|
|
395
|
+
// replacement. If that happens, the original command ctx is stale by
|
|
396
|
+
// the time we reach the final summary, so the command should exit
|
|
397
|
+
// quietly instead of surfacing a stale-ctx error.
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background review — learning loop that auto-saves memory every N turns.
|
|
3
|
+
* Ported from hermes-agent/run_agent.py (_spawn_background_review, _memory_nudge_interval).
|
|
4
|
+
* See PLAN.md → "Hermes Source File Reference Map" for source lines.
|
|
5
|
+
*
|
|
6
|
+
* Default transport: in-process complete() side-channel (preserves parent LLM cache).
|
|
7
|
+
* Fallback: pi.exec("pi", ["-p", ...]) subprocess when direct path is unavailable.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
11
|
+
import {
|
|
12
|
+
buildMemoryTargetRoutingGuidance,
|
|
13
|
+
COMBINED_REVIEW_PROMPT,
|
|
14
|
+
DIRECT_REVIEW_SYSTEM_PROMPT,
|
|
15
|
+
} from "../constants.js";
|
|
16
|
+
import { MemoryStore } from "../store/memory-store.js";
|
|
17
|
+
import { DatabaseManager } from "../store/db.js";
|
|
18
|
+
import type { MemoryConfig } from "../types.js";
|
|
19
|
+
import { applyRecentMessageLimit, collectMessageParts } from "./message-parts.js";
|
|
20
|
+
import { execChildPrompt, resolveChildPiModel } from "./pi-child-process.js";
|
|
21
|
+
import { runDirectMemoryCompletion, usesDirectTransport, type DirectReviewResult } from "./review-memory-ops.js";
|
|
22
|
+
|
|
23
|
+
import { resolveProjectName, resolveProjectStore, type ProjectNameRef, type ProjectStoreRef } from "../project-context.js";
|
|
24
|
+
export interface BackgroundReviewOptions {
|
|
25
|
+
dbManager?: DatabaseManager | null;
|
|
26
|
+
projectName?: ProjectNameRef;
|
|
27
|
+
deps?: BackgroundReviewDeps;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface BackgroundReviewDeps {
|
|
31
|
+
runDirectReview?: typeof runDirectMemoryCompletion;
|
|
32
|
+
execChildPrompt?: typeof execChildPrompt;
|
|
33
|
+
/** Test-only hook: called once runReview() has fully settled (after the
|
|
34
|
+
* fire-and-forget review work completes and reviewInProgress resets),
|
|
35
|
+
* since production callers never await runReview() directly. */
|
|
36
|
+
onReviewSettled?: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ReviewPromptInput {
|
|
40
|
+
parts: string[];
|
|
41
|
+
currentMemory: string;
|
|
42
|
+
currentUser: string;
|
|
43
|
+
currentProject: string | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function buildSubprocessReviewPrompt(input: ReviewPromptInput): string {
|
|
47
|
+
const reviewPrompt = [
|
|
48
|
+
COMBINED_REVIEW_PROMPT,
|
|
49
|
+
"",
|
|
50
|
+
buildMemoryTargetRoutingGuidance(input.currentProject !== null),
|
|
51
|
+
"",
|
|
52
|
+
"--- Current Memory ---",
|
|
53
|
+
input.currentMemory || "(empty)",
|
|
54
|
+
"",
|
|
55
|
+
"--- Current User Profile ---",
|
|
56
|
+
input.currentUser || "(empty)",
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
if (input.currentProject !== null) {
|
|
60
|
+
reviewPrompt.push(
|
|
61
|
+
"",
|
|
62
|
+
"--- Current Project Memory ---",
|
|
63
|
+
input.currentProject || "(empty)",
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
reviewPrompt.push(
|
|
68
|
+
"",
|
|
69
|
+
"--- Conversation to Review ---",
|
|
70
|
+
input.parts.join("\n\n"),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return reviewPrompt.join("\n");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function buildDirectReviewUserPrompt(input: ReviewPromptInput): string {
|
|
77
|
+
const sections = [
|
|
78
|
+
"--- Current Memory ---",
|
|
79
|
+
input.currentMemory || "(empty)",
|
|
80
|
+
"",
|
|
81
|
+
"--- Current User Profile ---",
|
|
82
|
+
input.currentUser || "(empty)",
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
if (input.currentProject !== null) {
|
|
86
|
+
sections.push(
|
|
87
|
+
"",
|
|
88
|
+
"--- Current Project Memory ---",
|
|
89
|
+
input.currentProject || "(empty)",
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
sections.push(
|
|
94
|
+
"",
|
|
95
|
+
"--- Conversation to Review ---",
|
|
96
|
+
input.parts.join("\n\n"),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return sections.join("\n");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function shouldNotifyDirect(result: DirectReviewResult): boolean {
|
|
103
|
+
return result.ok && result.appliedCount > 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function shouldNotifySubprocess(stdout: string | undefined): boolean {
|
|
107
|
+
const output = stdout?.trim();
|
|
108
|
+
return !!output && !output.toLowerCase().includes("nothing to save");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function diagnosticDetail(value: unknown): string {
|
|
112
|
+
const detail = value instanceof Error ? value.message : String(value ?? "").trim();
|
|
113
|
+
return detail.replace(/\s+/g, " ").slice(0, 300);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function runSubprocessReview(
|
|
117
|
+
pi: ExtensionAPI,
|
|
118
|
+
prompt: string,
|
|
119
|
+
config: MemoryConfig,
|
|
120
|
+
execChild: typeof execChildPrompt,
|
|
121
|
+
ctx: Pick<ExtensionContext, "cwd" | "model" | "signal">,
|
|
122
|
+
): Promise<{ code: number; stdout?: string; stderr?: string }> {
|
|
123
|
+
return execChild(pi, prompt, config, {
|
|
124
|
+
cwd: ctx.cwd,
|
|
125
|
+
model: resolveChildPiModel(ctx.model),
|
|
126
|
+
signal: ctx.signal,
|
|
127
|
+
timeoutMs: 120000,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function setupBackgroundReview(
|
|
132
|
+
pi: ExtensionAPI,
|
|
133
|
+
store: MemoryStore,
|
|
134
|
+
projectStore: ProjectStoreRef,
|
|
135
|
+
config: MemoryConfig,
|
|
136
|
+
options: BackgroundReviewOptions = {},
|
|
137
|
+
): void {
|
|
138
|
+
const dbManager = options.dbManager ?? null;
|
|
139
|
+
const projectName = options.projectName ?? null;
|
|
140
|
+
const runDirectReview = options.deps?.runDirectReview ?? runDirectMemoryCompletion;
|
|
141
|
+
const execChild = options.deps?.execChildPrompt ?? execChildPrompt;
|
|
142
|
+
const onReviewSettled = options.deps?.onReviewSettled;
|
|
143
|
+
|
|
144
|
+
let turnsSinceReview = 0;
|
|
145
|
+
let toolCallsSinceReview = 0;
|
|
146
|
+
let userTurnCount = 0;
|
|
147
|
+
let reviewInProgress = false;
|
|
148
|
+
|
|
149
|
+
pi.on("message_end", async (event, _ctx) => {
|
|
150
|
+
if (event.message.role === "user") {
|
|
151
|
+
userTurnCount++;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
pi.on("turn_end", async (event, ctx) => {
|
|
156
|
+
turnsSinceReview++;
|
|
157
|
+
|
|
158
|
+
if (!config.reviewEnabled) return;
|
|
159
|
+
if (reviewInProgress) return;
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
const msg = event.message;
|
|
163
|
+
if (msg?.role === "assistant") {
|
|
164
|
+
const content = msg?.content;
|
|
165
|
+
if (Array.isArray(content)) {
|
|
166
|
+
for (const block of content) {
|
|
167
|
+
if (block && typeof block === "object" && block.type === "toolCall") {
|
|
168
|
+
toolCallsSinceReview++;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch {
|
|
174
|
+
// If we can't count tool calls, fall back to turn-based only
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const turnThresholdMet = turnsSinceReview >= config.nudgeInterval;
|
|
178
|
+
const toolCallThresholdMet = toolCallsSinceReview >= config.nudgeToolCalls;
|
|
179
|
+
|
|
180
|
+
if (!turnThresholdMet && !toolCallThresholdMet) return;
|
|
181
|
+
if (userTurnCount < 3) return;
|
|
182
|
+
|
|
183
|
+
turnsSinceReview = 0;
|
|
184
|
+
toolCallsSinceReview = 0;
|
|
185
|
+
reviewInProgress = true;
|
|
186
|
+
|
|
187
|
+
let allParts: string[] = [];
|
|
188
|
+
try {
|
|
189
|
+
const entries = ctx.sessionManager.getBranch();
|
|
190
|
+
allParts = collectMessageParts(entries);
|
|
191
|
+
} catch {
|
|
192
|
+
reviewInProgress = false;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (allParts.length < 4) {
|
|
196
|
+
reviewInProgress = false;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const parts = applyRecentMessageLimit(allParts, config.reviewRecentMessages);
|
|
201
|
+
const activeProjectStore = resolveProjectStore(projectStore);
|
|
202
|
+
const activeProjectName = resolveProjectName(projectName);
|
|
203
|
+
const promptInput: ReviewPromptInput = {
|
|
204
|
+
parts,
|
|
205
|
+
currentMemory: store.getMemoryEntries().join("\n§\n"),
|
|
206
|
+
currentUser: store.getUserEntries().join("\n§\n"),
|
|
207
|
+
currentProject: activeProjectStore ? activeProjectStore.getMemoryEntries().join("\n§\n") : null,
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const subprocessPrompt = buildSubprocessReviewPrompt(promptInput);
|
|
211
|
+
const directPrompt = buildDirectReviewUserPrompt(promptInput);
|
|
212
|
+
|
|
213
|
+
const finishReview = () => {
|
|
214
|
+
reviewInProgress = false;
|
|
215
|
+
onReviewSettled?.();
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const notifyIfSaved = (saved: boolean) => {
|
|
219
|
+
if (saved) {
|
|
220
|
+
ctx.ui.notify("💾 Memory auto-reviewed and updated", "info");
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const runReview = async (): Promise<void> => {
|
|
225
|
+
let directFailure: string | undefined;
|
|
226
|
+
|
|
227
|
+
if (usesDirectTransport(config)) {
|
|
228
|
+
try {
|
|
229
|
+
const directResult = await runDirectReview(
|
|
230
|
+
ctx as Pick<ExtensionContext, "model" | "modelRegistry">,
|
|
231
|
+
store,
|
|
232
|
+
activeProjectStore,
|
|
233
|
+
{
|
|
234
|
+
userPrompt: directPrompt,
|
|
235
|
+
systemPrompt: [
|
|
236
|
+
DIRECT_REVIEW_SYSTEM_PROMPT,
|
|
237
|
+
"",
|
|
238
|
+
buildMemoryTargetRoutingGuidance(activeProjectStore !== null),
|
|
239
|
+
].join("\n"),
|
|
240
|
+
config,
|
|
241
|
+
timeoutMs: 120000,
|
|
242
|
+
},
|
|
243
|
+
dbManager,
|
|
244
|
+
activeProjectName,
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
if (directResult.ok) {
|
|
248
|
+
notifyIfSaved(shouldNotifyDirect(directResult));
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (directResult.fallbackReason === "empty") {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
directFailure = [
|
|
256
|
+
directResult.fallbackReason ?? "failed",
|
|
257
|
+
directResult.error,
|
|
258
|
+
].filter(Boolean).join(": ");
|
|
259
|
+
} catch (error) {
|
|
260
|
+
directFailure = diagnosticDetail(error);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
let subprocessResult: { code: number; stdout?: string; stderr?: string };
|
|
265
|
+
try {
|
|
266
|
+
subprocessResult = await runSubprocessReview(pi, subprocessPrompt, config, execChild, ctx);
|
|
267
|
+
} catch (error) {
|
|
268
|
+
if (directFailure) {
|
|
269
|
+
ctx.ui.notify(
|
|
270
|
+
`Memory auto-review failed in both transports. Direct: ${diagnosticDetail(directFailure)}. `
|
|
271
|
+
+ `Subprocess: ${diagnosticDetail(error)}. Check the active model/provider or set llmModelOverride.`,
|
|
272
|
+
"warning",
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (subprocessResult.code === 0) {
|
|
279
|
+
notifyIfSaved(shouldNotifySubprocess(subprocessResult.stdout));
|
|
280
|
+
} else if (directFailure) {
|
|
281
|
+
const subprocessDetail = subprocessResult.stderr?.trim() || subprocessResult.stdout?.trim()
|
|
282
|
+
|| `exit code ${subprocessResult.code}`;
|
|
283
|
+
ctx.ui.notify(
|
|
284
|
+
`Memory auto-review failed in both transports. Direct: ${diagnosticDetail(directFailure)}. `
|
|
285
|
+
+ `Subprocess: ${diagnosticDetail(subprocessDetail)}. Check the active model/provider or set llmModelOverride.`,
|
|
286
|
+
"warning",
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
runReview()
|
|
292
|
+
.catch(() => {
|
|
293
|
+
// Best-effort only; transport failures are diagnosed after both paths settle.
|
|
294
|
+
})
|
|
295
|
+
.finally(finishReview);
|
|
296
|
+
});
|
|
297
|
+
}
|