gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.cf9433f56
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 +4 -2
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +2 -2
- package/dist/resources/extensions/github-sync/templates.js +39 -8
- package/dist/resources/extensions/gsd/auto/loop.js +48 -10
- package/dist/resources/extensions/gsd/auto/phases.js +66 -45
- package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +32 -16
- package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
- package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
- package/dist/resources/extensions/gsd/auto-prompts.js +124 -2
- package/dist/resources/extensions/gsd/auto-recovery.js +197 -9
- package/dist/resources/extensions/gsd/auto-start.js +2 -3
- package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
- package/dist/resources/extensions/gsd/auto.js +77 -5
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +36 -3
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +27 -20
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
- package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
- package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
- package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
- package/dist/resources/extensions/gsd/context-budget.js +37 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
- package/dist/resources/extensions/gsd/db-base-schema.js +18 -2
- package/dist/resources/extensions/gsd/db-migration-steps.js +22 -0
- package/dist/resources/extensions/gsd/detection.js +106 -0
- package/dist/resources/extensions/gsd/git-service.js +36 -4
- package/dist/resources/extensions/gsd/graph.js +9 -3
- package/dist/resources/extensions/gsd/gsd-db.js +146 -13
- package/dist/resources/extensions/gsd/guided-flow.js +82 -16
- package/dist/resources/extensions/gsd/memory-store.js +69 -12
- package/dist/resources/extensions/gsd/migrate/command.js +40 -1
- package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
- package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
- package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
- package/dist/resources/extensions/gsd/pre-execution-checks.js +7 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
- package/dist/resources/extensions/gsd/quick.js +34 -2
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
- package/dist/resources/extensions/gsd/tools/context-mode-tool-result.js +15 -0
- package/dist/resources/extensions/gsd/tools/exec-search-tool.js +5 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +3 -15
- package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
- package/dist/resources/extensions/gsd/tools/resume-tool.js +5 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
- package/dist/resources/extensions/gsd/unit-context-composer.js +12 -3
- package/dist/resources/extensions/gsd/unit-runtime.js +22 -0
- package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
- package/dist/resources/extensions/gsd/worktree-resolver.js +33 -17
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
- package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
- package/package.json +12 -8
- package/packages/contracts/package.json +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +22 -17
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +75 -2
- package/packages/mcp-server/src/workflow-tools.ts +30 -16
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
- package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/fake-model.js +27 -0
- package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
- package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/index.js +8 -0
- package/packages/pi-ai/dist/models/index.js.map +1 -1
- package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
- package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/fake.js +319 -0
- package/packages/pi-ai/dist/providers/fake.js.map +1 -0
- package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
- package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
- package/packages/pi-ai/src/models/fake-model.ts +30 -0
- package/packages/pi-ai/src/models/index.ts +9 -0
- package/packages/pi-ai/src/providers/fake.ts +376 -0
- package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +8 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +76 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +17 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +99 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +24 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +33 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +109 -17
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +69 -2
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +93 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +26 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +8 -0
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +89 -0
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
- package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
- package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
- package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +110 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -1
- package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +51 -1
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +122 -17
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +99 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +92 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +28 -0
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
- package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
- package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
- package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
- package/packages/pi-tui/dist/index.d.ts +1 -0
- package/packages/pi-tui/dist/index.d.ts.map +1 -1
- package/packages/pi-tui/dist/index.js +2 -0
- package/packages/pi-tui/dist/index.js.map +1 -1
- package/packages/pi-tui/dist/style.d.ts +41 -0
- package/packages/pi-tui/dist/style.d.ts.map +1 -0
- package/packages/pi-tui/dist/style.js +158 -0
- package/packages/pi-tui/dist/style.js.map +1 -0
- package/packages/pi-tui/dist/tui.d.ts +0 -1
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +21 -16
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
- package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
- package/packages/pi-tui/src/index.ts +9 -0
- package/packages/pi-tui/src/style.ts +225 -0
- package/packages/pi-tui/src/tui.ts +23 -16
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +18 -1
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js +36 -27
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/src/resources/GSD-WORKFLOW.md +2 -2
- package/src/resources/extensions/github-sync/templates.ts +38 -8
- package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/loop.ts +67 -18
- package/src/resources/extensions/gsd/auto/phases.ts +77 -48
- package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
- package/src/resources/extensions/gsd/auto/run-unit.ts +42 -15
- package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
- package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
- package/src/resources/extensions/gsd/auto-prompts.ts +133 -2
- package/src/resources/extensions/gsd/auto-recovery.ts +207 -7
- package/src/resources/extensions/gsd/auto-start.ts +7 -6
- package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
- package/src/resources/extensions/gsd/auto.ts +92 -4
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +37 -2
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +27 -19
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +39 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
- package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
- package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
- package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
- package/src/resources/extensions/gsd/context-budget.ts +44 -2
- package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
- package/src/resources/extensions/gsd/db-base-schema.ts +19 -2
- package/src/resources/extensions/gsd/db-migration-steps.ts +25 -0
- package/src/resources/extensions/gsd/detection.ts +128 -0
- package/src/resources/extensions/gsd/git-service.ts +46 -8
- package/src/resources/extensions/gsd/graph.ts +12 -5
- package/src/resources/extensions/gsd/gsd-db.ts +168 -13
- package/src/resources/extensions/gsd/guided-flow.ts +98 -16
- package/src/resources/extensions/gsd/memory-store.ts +77 -12
- package/src/resources/extensions/gsd/migrate/command.ts +47 -1
- package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
- package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
- package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
- package/src/resources/extensions/gsd/pre-execution-checks.ts +7 -0
- package/src/resources/extensions/gsd/preferences-types.ts +1 -1
- package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
- package/src/resources/extensions/gsd/quick.ts +37 -2
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
- package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +155 -5
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +184 -2
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +14 -1
- package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
- package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
- package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/exec-history.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +234 -0
- package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +101 -2
- package/src/resources/extensions/gsd/tests/status-db-open.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +136 -4
- package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +63 -1
- package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
- package/src/resources/extensions/gsd/tools/context-mode-tool-result.ts +25 -0
- package/src/resources/extensions/gsd/tools/exec-search-tool.ts +7 -7
- package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -23
- package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
- package/src/resources/extensions/gsd/tools/resume-tool.ts +7 -7
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
- package/src/resources/extensions/gsd/unit-context-composer.ts +19 -4
- package/src/resources/extensions/gsd/unit-runtime.ts +25 -0
- package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
- package/src/resources/extensions/gsd/worktree-resolver.ts +36 -15
- package/packages/contracts/tsconfig.tsbuildinfo +0 -1
- /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → -5nHJWzSdG-WkPMul_khA}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → -5nHJWzSdG-WkPMul_khA}/_ssgManifest.js +0 -0
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
// excluded from this invariant.
|
|
24
24
|
|
|
25
25
|
import { createRequire } from "node:module";
|
|
26
|
+
import { createHash } from "node:crypto";
|
|
26
27
|
import { existsSync, copyFileSync, mkdirSync, realpathSync } from "node:fs";
|
|
27
28
|
import { dirname } from "node:path";
|
|
28
29
|
import type { Decision, Requirement, GateRow, GateId, GateScope, GateStatus, GateVerdict } from "./types.js";
|
|
@@ -77,6 +78,9 @@ import {
|
|
|
77
78
|
applyMigrationV21StructuredMemories,
|
|
78
79
|
applyMigrationV22QualityGateRepair,
|
|
79
80
|
applyMigrationV23MilestoneQueue,
|
|
81
|
+
applyMigrationV26MilestoneCommitAttributions,
|
|
82
|
+
applyMigrationV27ArtifactHash,
|
|
83
|
+
applyMigrationV28MemoryLastHitAt,
|
|
80
84
|
} from "./db-migration-steps.js";
|
|
81
85
|
import { isMemoriesFtsAvailableSchema, tryCreateMemoriesFtsSchema } from "./db-memory-fts-schema.js";
|
|
82
86
|
import { createDbOpenState, type DbOpenPhase } from "./db-open-state.js";
|
|
@@ -105,7 +109,7 @@ const providerLoader = createSqliteProviderLoader({
|
|
|
105
109
|
writeStderr: (message: string) => process.stderr.write(message),
|
|
106
110
|
});
|
|
107
111
|
|
|
108
|
-
export const SCHEMA_VERSION =
|
|
112
|
+
export const SCHEMA_VERSION = 28;
|
|
109
113
|
|
|
110
114
|
function initSchema(db: DbAdapter, fileBacked: boolean): void {
|
|
111
115
|
if (fileBacked) db.exec("PRAGMA journal_mode=WAL");
|
|
@@ -329,6 +333,21 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
329
333
|
recordSchemaVersion(db, 25);
|
|
330
334
|
}
|
|
331
335
|
|
|
336
|
+
if (currentVersion < 26) {
|
|
337
|
+
applyMigrationV26MilestoneCommitAttributions(db);
|
|
338
|
+
recordSchemaVersion(db, 26);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (currentVersion < 27) {
|
|
342
|
+
applyMigrationV27ArtifactHash(db);
|
|
343
|
+
recordSchemaVersion(db, 27);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (currentVersion < 28) {
|
|
347
|
+
applyMigrationV28MemoryLastHitAt(db);
|
|
348
|
+
recordSchemaVersion(db, 28);
|
|
349
|
+
}
|
|
350
|
+
|
|
332
351
|
db.exec("COMMIT");
|
|
333
352
|
} catch (err) {
|
|
334
353
|
db.exec("ROLLBACK");
|
|
@@ -907,9 +926,10 @@ export function insertArtifact(a: {
|
|
|
907
926
|
full_content: string;
|
|
908
927
|
}): void {
|
|
909
928
|
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
929
|
+
const contentHash = createHash("sha256").update(a.full_content).digest("hex");
|
|
910
930
|
currentDb.prepare(
|
|
911
|
-
`INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at)
|
|
912
|
-
VALUES (:path, :artifact_type, :milestone_id, :slice_id, :task_id, :full_content, :imported_at)`,
|
|
931
|
+
`INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash)
|
|
932
|
+
VALUES (:path, :artifact_type, :milestone_id, :slice_id, :task_id, :full_content, :imported_at, :content_hash)`,
|
|
913
933
|
).run({
|
|
914
934
|
":path": a.path,
|
|
915
935
|
":artifact_type": a.artifact_type,
|
|
@@ -918,6 +938,7 @@ export function insertArtifact(a: {
|
|
|
918
938
|
":task_id": a.task_id,
|
|
919
939
|
":full_content": a.full_content,
|
|
920
940
|
":imported_at": new Date().toISOString(),
|
|
941
|
+
":content_hash": contentHash,
|
|
921
942
|
});
|
|
922
943
|
}
|
|
923
944
|
|
|
@@ -1349,6 +1370,45 @@ export function getSliceTasks(milestoneId: string, sliceId: string): TaskRow[] {
|
|
|
1349
1370
|
return rows.map(rowToTask);
|
|
1350
1371
|
}
|
|
1351
1372
|
|
|
1373
|
+
export function getCompletedMilestoneTaskFileHints(milestoneId: string): string[] {
|
|
1374
|
+
if (!currentDb) return [];
|
|
1375
|
+
const rows = currentDb.prepare(
|
|
1376
|
+
`SELECT files, key_files
|
|
1377
|
+
FROM tasks
|
|
1378
|
+
WHERE milestone_id = :mid AND status IN ('complete', 'done')`,
|
|
1379
|
+
).all({ ":mid": milestoneId }) as Array<Record<string, unknown>>;
|
|
1380
|
+
|
|
1381
|
+
const hints = new Set<string>();
|
|
1382
|
+
for (const row of rows) {
|
|
1383
|
+
for (const raw of [row["files"], row["key_files"]]) {
|
|
1384
|
+
for (const file of parseStringArrayColumn(raw)) {
|
|
1385
|
+
const normalized = normalizeRepoPath(file);
|
|
1386
|
+
if (normalized) hints.add(normalized);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
return [...hints];
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
function parseStringArrayColumn(raw: unknown): string[] {
|
|
1394
|
+
if (Array.isArray(raw)) return raw.filter((entry): entry is string => typeof entry === "string");
|
|
1395
|
+
if (typeof raw !== "string") return [];
|
|
1396
|
+
const trimmed = raw.trim();
|
|
1397
|
+
if (!trimmed) return [];
|
|
1398
|
+
try {
|
|
1399
|
+
const parsed = JSON.parse(trimmed);
|
|
1400
|
+
if (Array.isArray(parsed)) return parsed.filter((entry): entry is string => typeof entry === "string");
|
|
1401
|
+
if (typeof parsed === "string") return [parsed];
|
|
1402
|
+
} catch {
|
|
1403
|
+
return trimmed.split(",");
|
|
1404
|
+
}
|
|
1405
|
+
return [];
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
function normalizeRepoPath(file: string): string {
|
|
1409
|
+
return file.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1352
1412
|
// ─── ADR-011 Phase 2 escalation helpers ──────────────────────────────────
|
|
1353
1413
|
|
|
1354
1414
|
/** Set pause-on-escalation state on a completed task. Mutually exclusive with awaiting_review. */
|
|
@@ -1750,6 +1810,13 @@ export function reconcileWorktreeDb(
|
|
|
1750
1810
|
const hasEscalationAwaiting = wtTaskInfo.some((col) => col["name"] === "escalation_awaiting_review");
|
|
1751
1811
|
const hasEscalationArtifact = wtTaskInfo.some((col) => col["name"] === "escalation_artifact_path");
|
|
1752
1812
|
const hasEscalationOverride = wtTaskInfo.some((col) => col["name"] === "escalation_override_applied_at");
|
|
1813
|
+
const wtArtifactInfo = adapter.prepare("PRAGMA wt.table_info('artifacts')").all();
|
|
1814
|
+
const hasArtifactContentHash = wtArtifactInfo.some((col) => col["name"] === "content_hash");
|
|
1815
|
+
const wtMemoryInfo = adapter.prepare("PRAGMA wt.table_info('memories')").all();
|
|
1816
|
+
const hasMemoryScope = wtMemoryInfo.some((col) => col["name"] === "scope");
|
|
1817
|
+
const hasMemoryTags = wtMemoryInfo.some((col) => col["name"] === "tags");
|
|
1818
|
+
const hasMemoryStructuredFields = wtMemoryInfo.some((col) => col["name"] === "structured_fields");
|
|
1819
|
+
const hasMemoryLastHitAt = wtMemoryInfo.some((col) => col["name"] === "last_hit_at");
|
|
1753
1820
|
|
|
1754
1821
|
const decConf = adapter.prepare(
|
|
1755
1822
|
`SELECT m.id FROM decisions m INNER JOIN wt.decisions w ON m.id = w.id WHERE m.decision != w.decision OR m.choice != w.choice OR m.rationale != w.rationale OR ${
|
|
@@ -1797,12 +1864,17 @@ export function reconcileWorktreeDb(
|
|
|
1797
1864
|
FROM wt.requirements
|
|
1798
1865
|
`).run());
|
|
1799
1866
|
|
|
1867
|
+
// V27: preserve content_hash. If the worktree predates V27 (no column),
|
|
1868
|
+
// fall back to the main DB's existing hash so reconcile doesn't null
|
|
1869
|
+
// out integrity fingerprints on artifacts that were unchanged in wt.
|
|
1800
1870
|
merged.artifacts = countChanges(adapter.prepare(`
|
|
1801
1871
|
INSERT OR REPLACE INTO artifacts (
|
|
1802
|
-
path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
|
|
1872
|
+
path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash
|
|
1803
1873
|
)
|
|
1804
|
-
SELECT path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
|
|
1805
|
-
|
|
1874
|
+
SELECT w.path, w.artifact_type, w.milestone_id, w.slice_id, w.task_id, w.full_content, w.imported_at,
|
|
1875
|
+
${hasArtifactContentHash ? "w.content_hash" : "m.content_hash"}
|
|
1876
|
+
FROM wt.artifacts w
|
|
1877
|
+
LEFT JOIN artifacts m ON m.path = w.path
|
|
1806
1878
|
`).run());
|
|
1807
1879
|
|
|
1808
1880
|
// Merge milestones — worktree may have updated status/planning fields.
|
|
@@ -1904,15 +1976,25 @@ export function reconcileWorktreeDb(
|
|
|
1904
1976
|
LEFT JOIN tasks m ON m.milestone_id = w.milestone_id AND m.slice_id = w.slice_id AND m.id = w.id
|
|
1905
1977
|
`).run());
|
|
1906
1978
|
|
|
1907
|
-
// Merge memories — keep worktree-learned insights
|
|
1979
|
+
// Merge memories — keep worktree-learned insights.
|
|
1980
|
+
// V18 (scope, tags), V21 (structured_fields), V28 (last_hit_at): for each
|
|
1981
|
+
// column the wt may not yet have (older worktree DB), fall back to the
|
|
1982
|
+
// main DB's existing value via LEFT JOIN so reconcile never silently
|
|
1983
|
+
// resets these fields to defaults on rows that already had them.
|
|
1908
1984
|
merged.memories = countChanges(adapter.prepare(`
|
|
1909
1985
|
INSERT OR REPLACE INTO memories (
|
|
1910
1986
|
seq, id, category, content, confidence, source_unit_type, source_unit_id,
|
|
1911
|
-
created_at, updated_at, superseded_by, hit_count
|
|
1987
|
+
created_at, updated_at, superseded_by, hit_count,
|
|
1988
|
+
scope, tags, structured_fields, last_hit_at
|
|
1912
1989
|
)
|
|
1913
|
-
SELECT seq, id, category, content, confidence, source_unit_type, source_unit_id,
|
|
1914
|
-
created_at, updated_at, superseded_by, hit_count
|
|
1915
|
-
|
|
1990
|
+
SELECT w.seq, w.id, w.category, w.content, w.confidence, w.source_unit_type, w.source_unit_id,
|
|
1991
|
+
w.created_at, w.updated_at, w.superseded_by, w.hit_count,
|
|
1992
|
+
${hasMemoryScope ? "w.scope" : "COALESCE(m.scope, 'project')"},
|
|
1993
|
+
${hasMemoryTags ? "w.tags" : "COALESCE(m.tags, '[]')"},
|
|
1994
|
+
${hasMemoryStructuredFields ? "w.structured_fields" : "m.structured_fields"},
|
|
1995
|
+
${hasMemoryLastHitAt ? "w.last_hit_at" : "m.last_hit_at"}
|
|
1996
|
+
FROM wt.memories w
|
|
1997
|
+
LEFT JOIN memories m ON m.id = w.id
|
|
1916
1998
|
`).run());
|
|
1917
1999
|
|
|
1918
2000
|
// Merge verification evidence — append-only, use INSERT OR IGNORE to avoid duplicates
|
|
@@ -2073,6 +2155,9 @@ export function deleteMilestone(milestoneId: string): void {
|
|
|
2073
2155
|
currentDb!.prepare(
|
|
2074
2156
|
`DELETE FROM artifacts WHERE milestone_id = :mid`,
|
|
2075
2157
|
).run({ ":mid": milestoneId });
|
|
2158
|
+
currentDb!.prepare(
|
|
2159
|
+
`DELETE FROM milestone_commit_attributions WHERE milestone_id = :mid`,
|
|
2160
|
+
).run({ ":mid": milestoneId });
|
|
2076
2161
|
currentDb!.prepare(
|
|
2077
2162
|
`DELETE FROM milestone_leases WHERE milestone_id = :mid`,
|
|
2078
2163
|
).run({ ":mid": milestoneId });
|
|
@@ -2391,6 +2476,75 @@ export function upsertTurnGitTransaction(entry: {
|
|
|
2391
2476
|
});
|
|
2392
2477
|
}
|
|
2393
2478
|
|
|
2479
|
+
export function getMilestoneCommitAttributionShas(milestoneId: string): string[] {
|
|
2480
|
+
if (!currentDb) return [];
|
|
2481
|
+
const rows = currentDb.prepare(
|
|
2482
|
+
`SELECT commit_sha
|
|
2483
|
+
FROM milestone_commit_attributions
|
|
2484
|
+
WHERE milestone_id = :mid
|
|
2485
|
+
ORDER BY created_at, commit_sha`,
|
|
2486
|
+
).all({ ":mid": milestoneId }) as Array<Record<string, unknown>>;
|
|
2487
|
+
return rows
|
|
2488
|
+
.map((row) => typeof row["commit_sha"] === "string" ? row["commit_sha"] : "")
|
|
2489
|
+
.filter(Boolean);
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
export function recordMilestoneCommitAttribution(entry: {
|
|
2493
|
+
commitSha: string;
|
|
2494
|
+
milestoneId: string;
|
|
2495
|
+
sliceId?: string;
|
|
2496
|
+
taskId?: string;
|
|
2497
|
+
source: "recorded" | "backfill";
|
|
2498
|
+
confidence: number;
|
|
2499
|
+
files: string[];
|
|
2500
|
+
createdAt: string;
|
|
2501
|
+
}): void {
|
|
2502
|
+
if (!currentDb) return;
|
|
2503
|
+
transaction(() => {
|
|
2504
|
+
currentDb!.prepare(
|
|
2505
|
+
`INSERT OR REPLACE INTO milestone_commit_attributions (
|
|
2506
|
+
commit_sha, milestone_id, slice_id, task_id, source, confidence, files_json, created_at
|
|
2507
|
+
) VALUES (
|
|
2508
|
+
:commit_sha, :milestone_id, :slice_id, :task_id, :source, :confidence, :files_json, :created_at
|
|
2509
|
+
)`,
|
|
2510
|
+
).run({
|
|
2511
|
+
":commit_sha": entry.commitSha,
|
|
2512
|
+
":milestone_id": entry.milestoneId,
|
|
2513
|
+
":slice_id": entry.sliceId ?? null,
|
|
2514
|
+
":task_id": entry.taskId ?? null,
|
|
2515
|
+
":source": entry.source,
|
|
2516
|
+
":confidence": entry.confidence,
|
|
2517
|
+
":files_json": JSON.stringify(entry.files),
|
|
2518
|
+
":created_at": entry.createdAt,
|
|
2519
|
+
});
|
|
2520
|
+
|
|
2521
|
+
currentDb!.prepare(
|
|
2522
|
+
`INSERT OR IGNORE INTO audit_events (
|
|
2523
|
+
event_id, trace_id, turn_id, caused_by, category, type, ts, payload_json
|
|
2524
|
+
) VALUES (
|
|
2525
|
+
:event_id, :trace_id, :turn_id, :caused_by, :category, :type, :ts, :payload_json
|
|
2526
|
+
)`,
|
|
2527
|
+
).run({
|
|
2528
|
+
":event_id": `milestone-commit-attribution:${entry.milestoneId}:${entry.commitSha}`,
|
|
2529
|
+
":trace_id": "milestone-commit-attribution",
|
|
2530
|
+
":turn_id": null,
|
|
2531
|
+
":caused_by": null,
|
|
2532
|
+
":category": "git",
|
|
2533
|
+
":type": "milestone-commit-attribution-recorded",
|
|
2534
|
+
":ts": entry.createdAt,
|
|
2535
|
+
":payload_json": JSON.stringify({
|
|
2536
|
+
commitSha: entry.commitSha,
|
|
2537
|
+
milestoneId: entry.milestoneId,
|
|
2538
|
+
sliceId: entry.sliceId ?? null,
|
|
2539
|
+
taskId: entry.taskId ?? null,
|
|
2540
|
+
source: entry.source,
|
|
2541
|
+
confidence: entry.confidence,
|
|
2542
|
+
files: entry.files,
|
|
2543
|
+
}),
|
|
2544
|
+
});
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2394
2548
|
export function insertAuditEvent(entry: {
|
|
2395
2549
|
eventId: string;
|
|
2396
2550
|
traceId: string;
|
|
@@ -2494,6 +2648,7 @@ export function clearEngineHierarchy(): void {
|
|
|
2494
2648
|
currentDb!.exec("DELETE FROM slice_dependencies");
|
|
2495
2649
|
currentDb!.exec("DELETE FROM assessments");
|
|
2496
2650
|
currentDb!.exec("DELETE FROM replan_history");
|
|
2651
|
+
currentDb!.exec("DELETE FROM milestone_commit_attributions");
|
|
2497
2652
|
currentDb!.exec("DELETE FROM tasks");
|
|
2498
2653
|
currentDb!.exec("DELETE FROM slices");
|
|
2499
2654
|
currentDb!.exec("DELETE FROM milestone_leases");
|
|
@@ -2944,8 +3099,8 @@ export function updateMemoryContentRow(
|
|
|
2944
3099
|
export function incrementMemoryHitCount(id: string, updatedAt: string): void {
|
|
2945
3100
|
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2946
3101
|
currentDb.prepare(
|
|
2947
|
-
"UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at WHERE id = :id",
|
|
2948
|
-
).run({ ":updated_at": updatedAt, ":id": id });
|
|
3102
|
+
"UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at, last_hit_at = :last_hit_at WHERE id = :id",
|
|
3103
|
+
).run({ ":updated_at": updatedAt, ":last_hit_at": updatedAt, ":id": id });
|
|
2949
3104
|
}
|
|
2950
3105
|
|
|
2951
3106
|
export function supersedeMemoryRow(oldId: string, newId: string, updatedAt: string): void {
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
formatInterruptedSessionRunningMessage,
|
|
31
31
|
formatInterruptedSessionSummary,
|
|
32
32
|
} from "./interrupted-session.js";
|
|
33
|
-
import { listUnitRuntimeRecords, clearUnitRuntimeRecord } from "./unit-runtime.js";
|
|
33
|
+
import { listUnitRuntimeRecords, clearUnitRuntimeRecord, isInFlightRuntimePhase } from "./unit-runtime.js";
|
|
34
34
|
import { resolveExpectedArtifactPath } from "./auto.js";
|
|
35
35
|
import { gsdHome } from "./gsd-home.js";
|
|
36
36
|
import {
|
|
@@ -87,6 +87,34 @@ import { logWarning } from "./workflow-logger.js";
|
|
|
87
87
|
import { deleteRuntimeKv } from "./db/runtime-kv.js";
|
|
88
88
|
import { PAUSED_SESSION_KV_KEY } from "./interrupted-session.js";
|
|
89
89
|
|
|
90
|
+
type AutoStartOptions = Parameters<typeof startAutoDetached>[4];
|
|
91
|
+
type AutoStartLauncher = typeof startAutoDetached;
|
|
92
|
+
|
|
93
|
+
function scheduleAutoStartAfterIdle(
|
|
94
|
+
ctx: ExtensionCommandContext,
|
|
95
|
+
pi: ExtensionAPI,
|
|
96
|
+
basePath: string,
|
|
97
|
+
verboseMode: boolean,
|
|
98
|
+
options?: AutoStartOptions,
|
|
99
|
+
launch: AutoStartLauncher = startAutoDetached,
|
|
100
|
+
): void {
|
|
101
|
+
const waitForIdle =
|
|
102
|
+
typeof (ctx as { waitForIdle?: unknown }).waitForIdle === "function"
|
|
103
|
+
? ctx.waitForIdle.bind(ctx)
|
|
104
|
+
: async () => {};
|
|
105
|
+
void waitForIdle()
|
|
106
|
+
.then(() => {
|
|
107
|
+
setTimeout(() => launch(ctx, pi, basePath, verboseMode, options), 0);
|
|
108
|
+
})
|
|
109
|
+
.catch((err) => {
|
|
110
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
111
|
+
ctx.ui.notify(`Auto-start failed while waiting for the prior turn to settle: ${message}`, "error");
|
|
112
|
+
logWarning("guided", `auto-start idle wait failed: ${message}`);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const _scheduleAutoStartAfterIdleForTest = scheduleAutoStartAfterIdle;
|
|
117
|
+
|
|
90
118
|
// ─── Scope-based validator wrappers ──────────────────────────────────────────
|
|
91
119
|
// These thin wrappers accept a MilestoneScope so callers that already hold a
|
|
92
120
|
// pinned scope never have to re-derive (basePath, milestoneId) separately.
|
|
@@ -118,6 +146,22 @@ export function resolveExpectedArtifactPathForScope(
|
|
|
118
146
|
return resolveExpectedArtifactPath(unitType, unitId, scope.workspace.projectRoot);
|
|
119
147
|
}
|
|
120
148
|
|
|
149
|
+
async function runQuickTaskChoice(ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
|
|
150
|
+
if (!ctx.hasUI) {
|
|
151
|
+
ctx.ui.notify("Run /gsd quick <task> for small bounded work, or /gsd do <task> for natural-language routing.", "info");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const task = (await ctx.ui.input("Quick task", "Describe the small task to run with /gsd quick"))?.trim();
|
|
156
|
+
if (!task) {
|
|
157
|
+
ctx.ui.notify("Quick task cancelled.", "info");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const { handleQuick } = await import("./quick.js");
|
|
162
|
+
await handleQuick(task, ctx, pi);
|
|
163
|
+
}
|
|
164
|
+
|
|
121
165
|
/**
|
|
122
166
|
* Scope-based overload of isGhostMilestone.
|
|
123
167
|
* Binds basePath and milestoneId from the scope, ensuring path resolution
|
|
@@ -430,7 +474,7 @@ async function dispatchNextDeepProjectSetupStage(entry: PendingDeepProjectSetupE
|
|
|
430
474
|
|
|
431
475
|
if (!hasPendingDeepStage(prefs, entry.basePath)) {
|
|
432
476
|
pendingDeepProjectSetupMap.delete(entry.basePath);
|
|
433
|
-
|
|
477
|
+
scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
|
|
434
478
|
return true;
|
|
435
479
|
}
|
|
436
480
|
|
|
@@ -463,7 +507,7 @@ async function dispatchNextDeepProjectSetupStage(entry: PendingDeepProjectSetupE
|
|
|
463
507
|
entry.ctx.ui.notify(result.reason, result.level);
|
|
464
508
|
} else if (hasPendingDeepStage(prefs, entry.basePath)) {
|
|
465
509
|
pendingDeepProjectSetupMap.delete(entry.basePath);
|
|
466
|
-
|
|
510
|
+
scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
|
|
467
511
|
return true;
|
|
468
512
|
}
|
|
469
513
|
return false;
|
|
@@ -471,7 +515,7 @@ async function dispatchNextDeepProjectSetupStage(entry: PendingDeepProjectSetupE
|
|
|
471
515
|
|
|
472
516
|
if (!USER_DRIVEN_DEEP_SETUP_UNITS.has(result.unitType)) {
|
|
473
517
|
pendingDeepProjectSetupMap.delete(entry.basePath);
|
|
474
|
-
|
|
518
|
+
scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
|
|
475
519
|
return true;
|
|
476
520
|
}
|
|
477
521
|
|
|
@@ -677,7 +721,7 @@ export function checkAutoStartAfterDiscuss(): boolean {
|
|
|
677
721
|
|
|
678
722
|
pendingAutoStartMap.delete(basePath);
|
|
679
723
|
ctx.ui.notify(`Milestone ${milestoneId} ready.`, "success");
|
|
680
|
-
|
|
724
|
+
scheduleAutoStartAfterIdle(ctx, pi, basePath, false, { step });
|
|
681
725
|
return true;
|
|
682
726
|
}
|
|
683
727
|
|
|
@@ -1786,8 +1830,8 @@ function selfHealRuntimeRecords(basePath: string, ctx: ExtensionContext): { clea
|
|
|
1786
1830
|
cleared++;
|
|
1787
1831
|
continue;
|
|
1788
1832
|
}
|
|
1789
|
-
// Clear records stuck in
|
|
1790
|
-
if (phase
|
|
1833
|
+
// Clear records stuck in an in-flight phase (process died mid-unit).
|
|
1834
|
+
if (isInFlightRuntimePhase(phase)) {
|
|
1791
1835
|
clearUnitRuntimeRecord(basePath, unitType, unitId);
|
|
1792
1836
|
cleared++;
|
|
1793
1837
|
}
|
|
@@ -2042,6 +2086,23 @@ export async function showSmartEntry(
|
|
|
2042
2086
|
}
|
|
2043
2087
|
}
|
|
2044
2088
|
|
|
2089
|
+
if (interrupted.classification !== "recoverable") {
|
|
2090
|
+
try {
|
|
2091
|
+
const { autoImportMarkdownHierarchyIfDbMismatch } = await import("./migration-auto-check.js");
|
|
2092
|
+
const result = await autoImportMarkdownHierarchyIfDbMismatch(basePath);
|
|
2093
|
+
if (result.action === "imported") {
|
|
2094
|
+
ctx.ui.notify(
|
|
2095
|
+
`Recovered migrated planning state into gsd.db (${result.reason}): ${result.afterDb.milestones} milestone(s), ${result.afterDb.slices} slice(s), ${result.afterDb.tasks} task(s).`,
|
|
2096
|
+
"info",
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2099
|
+
} catch (err) {
|
|
2100
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2101
|
+
ctx.ui.notify(`GSD could not auto-import existing planning state into gsd.db: ${message}`, "warning");
|
|
2102
|
+
logWarning("guided", `planning state auto-import failed: ${message}`, { file: "guided-flow.ts" });
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2045
2106
|
// Always derive from the project root — the assessment may have derived
|
|
2046
2107
|
// state from a worktree path that was cleaned up in the stale branch above.
|
|
2047
2108
|
const state = await deriveState(basePath);
|
|
@@ -2064,8 +2125,8 @@ export async function showSmartEntry(
|
|
|
2064
2125
|
// standard wizard below.
|
|
2065
2126
|
{
|
|
2066
2127
|
const prefs = loadEffectiveGSDPreferences(basePath)?.preferences;
|
|
2067
|
-
const {
|
|
2068
|
-
if (
|
|
2128
|
+
const { shouldRunDeepProjectSetup } = await import("./auto-dispatch.js");
|
|
2129
|
+
if (shouldRunDeepProjectSetup(state, prefs, basePath)) {
|
|
2069
2130
|
await startDeepProjectSetupForeground(ctx, pi, basePath, stepMode);
|
|
2070
2131
|
return;
|
|
2071
2132
|
}
|
|
@@ -2134,17 +2195,24 @@ export async function showSmartEntry(
|
|
|
2134
2195
|
title: "GSD — Get Shit Done",
|
|
2135
2196
|
summary: ["No active milestone."],
|
|
2136
2197
|
actions: [
|
|
2198
|
+
{
|
|
2199
|
+
id: "quick_task",
|
|
2200
|
+
label: "Quick task",
|
|
2201
|
+
description: "For small bounded work, run /gsd quick <task> or /gsd do <task>.",
|
|
2202
|
+
recommended: true,
|
|
2203
|
+
},
|
|
2137
2204
|
{
|
|
2138
2205
|
id: "new_milestone",
|
|
2139
2206
|
label: "Create next milestone",
|
|
2140
|
-
description: "Define
|
|
2141
|
-
recommended: true,
|
|
2207
|
+
description: "Define a larger body of work with planning artifacts.",
|
|
2142
2208
|
},
|
|
2143
2209
|
],
|
|
2144
2210
|
notYetMessage: "Run /gsd when ready.",
|
|
2145
2211
|
});
|
|
2146
2212
|
|
|
2147
|
-
if (choice === "
|
|
2213
|
+
if (choice === "quick_task") {
|
|
2214
|
+
await runQuickTaskChoice(ctx, pi);
|
|
2215
|
+
} else if (choice === "new_milestone") {
|
|
2148
2216
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
2149
2217
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId,
|
|
2150
2218
|
`New milestone ${nextId}.`,
|
|
@@ -2181,11 +2249,16 @@ export async function showSmartEntry(
|
|
|
2181
2249
|
title: `GSD — ${milestoneId}: ${milestoneTitle}`,
|
|
2182
2250
|
summary: ["All milestones complete."],
|
|
2183
2251
|
actions: [
|
|
2252
|
+
{
|
|
2253
|
+
id: "quick_task",
|
|
2254
|
+
label: "Quick task",
|
|
2255
|
+
description: "Do a small bounded task without opening a milestone.",
|
|
2256
|
+
recommended: true,
|
|
2257
|
+
},
|
|
2184
2258
|
{
|
|
2185
2259
|
id: "new_milestone",
|
|
2186
2260
|
label: "Start new milestone",
|
|
2187
2261
|
description: "Define and plan the next milestone.",
|
|
2188
|
-
recommended: true,
|
|
2189
2262
|
},
|
|
2190
2263
|
{
|
|
2191
2264
|
id: "status",
|
|
@@ -2196,7 +2269,9 @@ export async function showSmartEntry(
|
|
|
2196
2269
|
notYetMessage: "Run /gsd when ready.",
|
|
2197
2270
|
});
|
|
2198
2271
|
|
|
2199
|
-
if (choice === "
|
|
2272
|
+
if (choice === "quick_task") {
|
|
2273
|
+
await runQuickTaskChoice(ctx, pi);
|
|
2274
|
+
} else if (choice === "new_milestone") {
|
|
2200
2275
|
const milestoneIds = findMilestoneIds(basePath);
|
|
2201
2276
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
2202
2277
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
@@ -2300,13 +2375,18 @@ export async function showSmartEntry(
|
|
|
2300
2375
|
const hasContext = !!(contextFile && await loadFile(contextFile));
|
|
2301
2376
|
|
|
2302
2377
|
const actions = [
|
|
2378
|
+
{
|
|
2379
|
+
id: "quick_task",
|
|
2380
|
+
label: "Quick task instead",
|
|
2381
|
+
description: "Use this when the work is small and should not become a milestone.",
|
|
2382
|
+
recommended: true,
|
|
2383
|
+
},
|
|
2303
2384
|
{
|
|
2304
2385
|
id: "plan",
|
|
2305
2386
|
label: "Create roadmap",
|
|
2306
2387
|
description: hasContext
|
|
2307
2388
|
? "Context captured. Decompose into slices with a boundary map."
|
|
2308
2389
|
: "Decompose the milestone into slices with a boundary map.",
|
|
2309
|
-
recommended: true,
|
|
2310
2390
|
},
|
|
2311
2391
|
...(!hasContext ? [{
|
|
2312
2392
|
id: "discuss",
|
|
@@ -2332,7 +2412,9 @@ export async function showSmartEntry(
|
|
|
2332
2412
|
notYetMessage: "Run /gsd when ready.",
|
|
2333
2413
|
});
|
|
2334
2414
|
|
|
2335
|
-
if (choice === "
|
|
2415
|
+
if (choice === "quick_task") {
|
|
2416
|
+
await runQuickTaskChoice(ctx, pi);
|
|
2417
|
+
} else if (choice === "plan") {
|
|
2336
2418
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
2337
2419
|
await dispatchWorkflow(
|
|
2338
2420
|
pi,
|
|
@@ -44,6 +44,8 @@ export interface Memory {
|
|
|
44
44
|
* decisions table (Step 5) with the original scope/decision/choice/etc.
|
|
45
45
|
*/
|
|
46
46
|
structured_fields: Record<string, unknown> | null;
|
|
47
|
+
/** ISO timestamp of the most recent memory_query hit. NULL until first hit. */
|
|
48
|
+
last_hit_at: string | null;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
export type MemoryActionCreate = {
|
|
@@ -100,6 +102,27 @@ const CATEGORY_PRIORITY: Record<string, number> = {
|
|
|
100
102
|
preference: 5,
|
|
101
103
|
};
|
|
102
104
|
|
|
105
|
+
// ─── Scoring Helpers ─────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Time-decay factor for memory relevance scoring.
|
|
109
|
+
* Returns 1.0 for never-hit or recently-hit memories, decaying linearly to
|
|
110
|
+
* 0.7 for memories not accessed in 90+ days. Floor at 0.7 keeps old-but-valid
|
|
111
|
+
* knowledge from being fully suppressed.
|
|
112
|
+
*
|
|
113
|
+
* Defensive parsing: invalid timestamp strings (NaN from Date.parse) are
|
|
114
|
+
* treated as "no decay" rather than propagating NaN into score arithmetic.
|
|
115
|
+
* Future timestamps (clock skew, manual DB edits) clamp to daysAgo=0 so the
|
|
116
|
+
* factor stays in the documented [0.7, 1.0] contract.
|
|
117
|
+
*/
|
|
118
|
+
export function memoryDecayFactor(lastHitAt: string | null): number {
|
|
119
|
+
if (!lastHitAt) return 1.0;
|
|
120
|
+
const ts = Date.parse(lastHitAt);
|
|
121
|
+
if (!Number.isFinite(ts)) return 1.0;
|
|
122
|
+
const daysAgo = Math.max(0, (Date.now() - ts) / 86_400_000);
|
|
123
|
+
return Math.max(0.7, 1.0 - 0.3 * Math.min(1.0, daysAgo / 90));
|
|
124
|
+
}
|
|
125
|
+
|
|
103
126
|
// ─── Row Mapping ────────────────────────────────────────────────────────────
|
|
104
127
|
|
|
105
128
|
function rowToMemory(row: Record<string, unknown>): Memory {
|
|
@@ -118,6 +141,7 @@ function rowToMemory(row: Record<string, unknown>): Memory {
|
|
|
118
141
|
scope: (row['scope'] as string) ?? 'project',
|
|
119
142
|
tags: parseTags(row['tags']),
|
|
120
143
|
structured_fields: parseStructuredFields(row['structured_fields']),
|
|
144
|
+
last_hit_at: (row['last_hit_at'] as string | null) ?? null,
|
|
121
145
|
};
|
|
122
146
|
}
|
|
123
147
|
|
|
@@ -233,15 +257,39 @@ export function queryMemoriesRanked(opts: QueryMemoriesOptions): RankedMemory[]
|
|
|
233
257
|
: [];
|
|
234
258
|
|
|
235
259
|
if (keywordHits.length === 0 && semanticHits.length === 0 && !trimmedQuery) {
|
|
236
|
-
// No query at all —
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
260
|
+
// No query at all — return top-k by decay-aware ranked score.
|
|
261
|
+
//
|
|
262
|
+
// Build the candidate pool from a direct SQL query that honors the
|
|
263
|
+
// request's activeClause (i.e. include_superseded). Using
|
|
264
|
+
// getActiveMemoriesRanked here would silently drop superseded rows even
|
|
265
|
+
// when the caller explicitly opted in, and would slice by raw score
|
|
266
|
+
// before decay/filters had a chance to reorder.
|
|
267
|
+
const candidatePool = Math.min(Math.max(k * 5, 50), 500);
|
|
268
|
+
const rows = adapter
|
|
269
|
+
.prepare(
|
|
270
|
+
`SELECT * FROM memories ${activeClause}
|
|
271
|
+
ORDER BY (confidence * (1.0 + hit_count * 0.1)) DESC
|
|
272
|
+
LIMIT :limit`,
|
|
273
|
+
)
|
|
274
|
+
.all({ ':limit': candidatePool });
|
|
275
|
+
|
|
276
|
+
const ranked: RankedMemory[] = [];
|
|
277
|
+
for (const row of rows) {
|
|
278
|
+
const memory = rowToMemory(row);
|
|
279
|
+
if (!passesFilters(memory, opts)) continue;
|
|
280
|
+
const decay = memoryDecayFactor(memory.last_hit_at);
|
|
281
|
+
const score = memory.confidence * (1 + memory.hit_count * 0.1) * decay;
|
|
282
|
+
ranked.push({
|
|
283
|
+
memory,
|
|
284
|
+
score,
|
|
285
|
+
keywordRank: null,
|
|
286
|
+
semanticRank: null,
|
|
287
|
+
confidenceBoost: score,
|
|
288
|
+
reason: 'ranked' as const,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
ranked.sort((a, b) => b.score - a.score);
|
|
292
|
+
return ranked.slice(0, k);
|
|
245
293
|
}
|
|
246
294
|
|
|
247
295
|
// 3) Reciprocal rank fusion — each hit contributes 1/(rrfK + rank).
|
|
@@ -275,7 +323,7 @@ export function queryMemoriesRanked(opts: QueryMemoriesOptions): RankedMemory[]
|
|
|
275
323
|
const ranked: RankedMemory[] = [];
|
|
276
324
|
for (const entry of fused.values()) {
|
|
277
325
|
if (!passesFilters(entry.memory, opts)) continue;
|
|
278
|
-
const boost = entry.memory.confidence * (1 + entry.memory.hit_count * 0.1);
|
|
326
|
+
const boost = entry.memory.confidence * (1 + entry.memory.hit_count * 0.1) * memoryDecayFactor(entry.memory.last_hit_at);
|
|
279
327
|
const reason: RankedMemory['reason'] =
|
|
280
328
|
entry.kwRank != null && entry.semRank != null
|
|
281
329
|
? 'both'
|
|
@@ -313,6 +361,8 @@ function passesFilters(memory: Memory, filters: QueryMemoriesFilters): boolean {
|
|
|
313
361
|
return true;
|
|
314
362
|
}
|
|
315
363
|
|
|
364
|
+
let ftsWarningEmitted = false;
|
|
365
|
+
|
|
316
366
|
function keywordSearch(
|
|
317
367
|
adapter: NonNullable<ReturnType<typeof _getAdapter>>,
|
|
318
368
|
rawQuery: string,
|
|
@@ -340,14 +390,29 @@ function keywordSearch(
|
|
|
340
390
|
}
|
|
341
391
|
}
|
|
342
392
|
|
|
343
|
-
// LIKE fallback — scans
|
|
393
|
+
// LIKE fallback — scans a capped candidate pool.
|
|
394
|
+
if (!ftsWarningEmitted) {
|
|
395
|
+
ftsWarningEmitted = true;
|
|
396
|
+
logWarning('memory-store', 'FTS5 unavailable — using LIKE fallback scan (consider enabling FTS5)');
|
|
397
|
+
}
|
|
398
|
+
|
|
344
399
|
const terms = rawQuery
|
|
345
400
|
.toLowerCase()
|
|
346
401
|
.split(/[^a-z0-9_]+/)
|
|
347
402
|
.filter((t) => t.length >= 2);
|
|
348
403
|
if (terms.length === 0) return [];
|
|
349
404
|
|
|
350
|
-
const
|
|
405
|
+
const preScanCap = Math.min(limit * 20, 2000);
|
|
406
|
+
// ORDER BY confidence-weighted hit_count DESC so the cap keeps the most
|
|
407
|
+
// valuable candidates instead of the oldest-by-rowid (which would silently
|
|
408
|
+
// exclude recently-stored memories on tables larger than preScanCap).
|
|
409
|
+
const rows = adapter
|
|
410
|
+
.prepare(
|
|
411
|
+
`SELECT * FROM memories ${activeClause}
|
|
412
|
+
ORDER BY (confidence * (1.0 + hit_count * 0.1)) DESC
|
|
413
|
+
LIMIT :preScanCap`,
|
|
414
|
+
)
|
|
415
|
+
.all({ ':preScanCap': preScanCap });
|
|
351
416
|
const scored: Array<{ memory: Memory; score: number }> = [];
|
|
352
417
|
for (const row of rows) {
|
|
353
418
|
const memory = rowToMemory(row);
|