gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.e51d2c88c
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 +75 -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/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/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/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
- 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 +11 -11
- 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 +90 -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/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/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/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/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-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/packages/contracts/tsconfig.tsbuildinfo +0 -1
- /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → 8F5YpnZNBaooIWGF4GBV3}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → 8F5YpnZNBaooIWGF4GBV3}/_ssgManifest.js +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// GSD-2 + src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts - Handles provider and agent-end recovery for GSD auto-mode.
|
|
2
|
+
|
|
1
3
|
import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
|
|
2
4
|
|
|
3
5
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -12,7 +14,7 @@ import { clearPathCache } from "../paths.js";
|
|
|
12
14
|
import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, pauseAuto, setCurrentDispatchedModelId } from "../auto.js";
|
|
13
15
|
import { getNextFallbackModel, resolveModelWithFallbacksForUnit } from "../preferences.js";
|
|
14
16
|
import { pauseAutoForProviderError } from "../provider-error-pause.js";
|
|
15
|
-
import { isSessionSwitchInFlight, resolveAgentEnd } from "../auto/resolve.js";
|
|
17
|
+
import { isSessionSwitchInFlight, resolveAgentEnd, resolveAgentEndCancelled } from "../auto/resolve.js";
|
|
16
18
|
import { resolveModelId } from "../auto-model-selection.js";
|
|
17
19
|
import { resolveProjectRoot } from "../worktree.js";
|
|
18
20
|
import { clearDiscussionFlowState } from "./write-gate.js";
|
|
@@ -69,6 +71,11 @@ export function _buildAbortedPauseContext(lastMsg: { errorMessage?: unknown }):
|
|
|
69
71
|
};
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
export function isUserInitiatedAbortMessage(message: string | undefined | null): boolean {
|
|
75
|
+
if (!message) return false;
|
|
76
|
+
return /\b(?:claude code process aborted by user|request aborted by user|process aborted by user)\b/i.test(message);
|
|
77
|
+
}
|
|
78
|
+
|
|
72
79
|
async function pauseTransientWithBackoff(
|
|
73
80
|
cls: ErrorClass,
|
|
74
81
|
pi: ExtensionAPI,
|
|
@@ -146,9 +153,29 @@ export async function handleAgentEnd(
|
|
|
146
153
|
if (maybeHandleEmptyIntentTurn(event, isAutoActive())) return;
|
|
147
154
|
|
|
148
155
|
if (!isAutoActive()) return;
|
|
149
|
-
if (isSessionSwitchInFlight()) return;
|
|
150
156
|
|
|
151
157
|
const lastMsg = event.messages[event.messages.length - 1];
|
|
158
|
+
if (isSessionSwitchInFlight()) {
|
|
159
|
+
if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "error") {
|
|
160
|
+
const rawErrorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
|
|
161
|
+
if (isUserInitiatedAbortMessage(rawErrorMsg)) {
|
|
162
|
+
resolveAgentEndCancelled({
|
|
163
|
+
message: rawErrorMsg,
|
|
164
|
+
category: "aborted",
|
|
165
|
+
isTransient: false,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
} else if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "aborted") {
|
|
169
|
+
const content = "content" in lastMsg ? lastMsg.content : undefined;
|
|
170
|
+
const hasEmptyContent = Array.isArray(content) && content.length === 0;
|
|
171
|
+
const hasErrorMessage = "errorMessage" in lastMsg && !!lastMsg.errorMessage;
|
|
172
|
+
if (!hasEmptyContent || hasErrorMessage) {
|
|
173
|
+
resolveAgentEndCancelled(_buildAbortedPauseContext(lastMsg as { errorMessage?: unknown }));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
152
179
|
if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "aborted") {
|
|
153
180
|
// Empty content with aborted stopReason is a non-fatal agent stop (the LLM
|
|
154
181
|
// chose to end without producing output). Only pause on genuine fatal aborts
|
|
@@ -180,6 +207,14 @@ export async function handleAgentEnd(
|
|
|
180
207
|
// is in the assistant message text content. Fall back to content when
|
|
181
208
|
// errorMessage looks uninformative.
|
|
182
209
|
const rawErrorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
|
|
210
|
+
if (isUserInitiatedAbortMessage(rawErrorMsg)) {
|
|
211
|
+
resolveAgentEndCancelled({
|
|
212
|
+
message: rawErrorMsg,
|
|
213
|
+
category: "aborted",
|
|
214
|
+
isTransient: false,
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
183
218
|
const isUseless = !rawErrorMsg || /^(success|ok|true|error|unknown)$/i.test(rawErrorMsg.trim());
|
|
184
219
|
// #3588: When errorMessage is uninformative, extract the real error from
|
|
185
220
|
// the assistant message text content for display purposes only.
|
|
@@ -6,23 +6,36 @@
|
|
|
6
6
|
import { Type } from "@sinclair/typebox";
|
|
7
7
|
import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
8
8
|
|
|
9
|
+
async function loadContextModePreferences(baseDir: string) {
|
|
10
|
+
const [{ loadEffectiveGSDPreferences }, { logWarning }] = await Promise.all([
|
|
11
|
+
import("../preferences.js"),
|
|
12
|
+
import("../workflow-logger.js"),
|
|
13
|
+
]);
|
|
14
|
+
try {
|
|
15
|
+
return loadEffectiveGSDPreferences(baseDir)?.preferences ?? null;
|
|
16
|
+
} catch (err) {
|
|
17
|
+
logWarning("tool", `Context Mode tool could not load preferences: ${err instanceof Error ? err.message : String(err)}`);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
9
22
|
export function registerExecTools(pi: ExtensionAPI): void {
|
|
10
23
|
pi.registerTool({
|
|
11
24
|
name: "gsd_exec",
|
|
12
25
|
label: "Exec (Sandboxed)",
|
|
13
26
|
description:
|
|
14
|
-
"Run a short script (bash/node/python) in a subprocess.
|
|
27
|
+
"Run a short script (bash/node/python) in a subprocess. Capped stdout/stderr and metadata persist to " +
|
|
15
28
|
".gsd/exec/<id>.{stdout,stderr,meta.json}; only a short digest returns in context. Use " +
|
|
16
29
|
"this instead of reading many files or emitting large tool outputs — e.g. have the script " +
|
|
17
30
|
"count/grep/summarize and log the finding. Enabled by default; opt out via " +
|
|
18
31
|
"preferences.context_mode.enabled=false.",
|
|
19
32
|
promptSnippet:
|
|
20
|
-
"Run a bash/node/python script in a sandbox;
|
|
33
|
+
"Run a bash/node/python script in a sandbox; capped output is saved to disk and only a digest returns",
|
|
21
34
|
promptGuidelines: [
|
|
22
35
|
"Prefer gsd_exec for analyses that would otherwise read >3 files or produce large tool output.",
|
|
23
36
|
"Write scripts that log the finding (counts, matches, summaries) rather than raw dumps.",
|
|
24
37
|
"The digest is the last ~300 chars of stdout — size your log output accordingly.",
|
|
25
|
-
"Need
|
|
38
|
+
"Need persisted output? Read the stdout_path returned in details (file on local disk).",
|
|
26
39
|
],
|
|
27
40
|
parameters: Type.Object({
|
|
28
41
|
runtime: Type.Union(
|
|
@@ -40,20 +53,11 @@ export function registerExecTools(pi: ExtensionAPI): void {
|
|
|
40
53
|
),
|
|
41
54
|
}),
|
|
42
55
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
import("../preferences.js"),
|
|
46
|
-
import("../workflow-logger.js"),
|
|
47
|
-
]);
|
|
48
|
-
let prefs: ReturnType<typeof loadEffectiveGSDPreferences> | null = null;
|
|
49
|
-
try {
|
|
50
|
-
prefs = loadEffectiveGSDPreferences();
|
|
51
|
-
} catch (err) {
|
|
52
|
-
logWarning("tool", `gsd_exec could not load preferences: ${err instanceof Error ? err.message : String(err)}`);
|
|
53
|
-
}
|
|
56
|
+
const { executeGsdExec } = await import("../tools/exec-tool.js");
|
|
57
|
+
const baseDir = process.cwd();
|
|
54
58
|
return executeGsdExec(params as Parameters<typeof executeGsdExec>[0], {
|
|
55
|
-
baseDir
|
|
56
|
-
preferences:
|
|
59
|
+
baseDir,
|
|
60
|
+
preferences: await loadContextModePreferences(baseDir),
|
|
57
61
|
});
|
|
58
62
|
},
|
|
59
63
|
});
|
|
@@ -67,7 +71,7 @@ export function registerExecTools(pi: ExtensionAPI): void {
|
|
|
67
71
|
promptSnippet: "Search prior gsd_exec runs by substring, runtime, or failing-only filter",
|
|
68
72
|
promptGuidelines: [
|
|
69
73
|
"Use this before re-running an expensive analysis — the prior run's stdout file may still answer.",
|
|
70
|
-
"The preview shows the trailing ~300 chars of stdout; read stdout_path for
|
|
74
|
+
"The preview shows the trailing ~300 chars of stdout; read stdout_path for persisted output.",
|
|
71
75
|
],
|
|
72
76
|
parameters: Type.Object({
|
|
73
77
|
query: Type.Optional(Type.String({ description: "Substring matched against id and purpose (case-insensitive)." })),
|
|
@@ -81,8 +85,10 @@ export function registerExecTools(pi: ExtensionAPI): void {
|
|
|
81
85
|
}),
|
|
82
86
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
83
87
|
const { executeExecSearch } = await import("../tools/exec-search-tool.js");
|
|
88
|
+
const baseDir = process.cwd();
|
|
84
89
|
return executeExecSearch(params as Parameters<typeof executeExecSearch>[0], {
|
|
85
|
-
baseDir
|
|
90
|
+
baseDir,
|
|
91
|
+
preferences: await loadContextModePreferences(baseDir),
|
|
86
92
|
});
|
|
87
93
|
},
|
|
88
94
|
});
|
|
@@ -102,8 +108,10 @@ export function registerExecTools(pi: ExtensionAPI): void {
|
|
|
102
108
|
parameters: Type.Object({}),
|
|
103
109
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
104
110
|
const { executeResume } = await import("../tools/resume-tool.js");
|
|
111
|
+
const baseDir = process.cwd();
|
|
105
112
|
return executeResume(params as Parameters<typeof executeResume>[0], {
|
|
106
|
-
baseDir
|
|
113
|
+
baseDir,
|
|
114
|
+
preferences: await loadContextModePreferences(baseDir),
|
|
107
115
|
});
|
|
108
116
|
},
|
|
109
117
|
});
|
|
@@ -7,7 +7,7 @@ import type { GSDEcosystemBeforeAgentStartHandler } from "../ecosystem/gsd-exten
|
|
|
7
7
|
import { updateSnapshot } from "../ecosystem/gsd-extension-api.js";
|
|
8
8
|
|
|
9
9
|
import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
|
|
10
|
-
import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer, isQueuePhaseActive, markApprovalGateVerified, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockPlanningUnit, shouldBlockQueueExecution, isGateQuestionId, setPendingGate, clearPendingGate, getPendingGate, shouldBlockPendingGate, shouldBlockPendingGateBash, extractDepthVerificationMilestoneId } from "./write-gate.js";
|
|
10
|
+
import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer, isQueuePhaseActive, markApprovalGateVerified, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockPlanningUnit, shouldBlockQueueExecution, shouldBlockWorktreeWrite, isGateQuestionId, setPendingGate, clearPendingGate, getPendingGate, shouldBlockPendingGate, shouldBlockPendingGateBash, extractDepthVerificationMilestoneId } from "./write-gate.js";
|
|
11
11
|
import { resolveManifest } from "../unit-context-manifest.js";
|
|
12
12
|
import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
|
|
13
13
|
import { loadFile, saveFile, formatContinue } from "../files.js";
|
|
@@ -65,6 +65,26 @@ async function applyDisabledModelProviderPolicy(ctx: ExtensionContext): Promise<
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Bridge `context_management.compaction_threshold_percent` from GSD preferences
|
|
70
|
+
* into the agent's runtime compaction settings (#5475). The preference is
|
|
71
|
+
* validated to (0.5, 0.95) at load time, but defense-in-depth normalization
|
|
72
|
+
* here protects against a stale or hand-edited prefs file. Calling with
|
|
73
|
+
* `undefined` clears any prior override so a removed preference does not leak.
|
|
74
|
+
*/
|
|
75
|
+
async function applyCompactionThresholdOverride(ctx: ExtensionContext): Promise<void> {
|
|
76
|
+
try {
|
|
77
|
+
const { loadEffectiveGSDPreferences } = await import("../preferences.js");
|
|
78
|
+
const prefs = loadEffectiveGSDPreferences();
|
|
79
|
+
const raw = prefs?.preferences.context_management?.compaction_threshold_percent;
|
|
80
|
+
const value =
|
|
81
|
+
typeof raw === "number" && Number.isFinite(raw) && raw > 0 && raw < 1 ? raw : undefined;
|
|
82
|
+
ctx.setCompactionThresholdOverride(value);
|
|
83
|
+
} catch {
|
|
84
|
+
// Non-fatal: leave any existing override in place.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
68
88
|
export function resolveNotificationStoreBasePath(cwd: string = process.cwd()): string {
|
|
69
89
|
return resolveWorktreeProjectRoot(cwd);
|
|
70
90
|
}
|
|
@@ -123,6 +143,7 @@ export function registerHooks(
|
|
|
123
143
|
await resetAskUserQuestionsTurnCache();
|
|
124
144
|
await syncServiceTierStatus(ctx);
|
|
125
145
|
await applyDisabledModelProviderPolicy(ctx);
|
|
146
|
+
await applyCompactionThresholdOverride(ctx);
|
|
126
147
|
// Skip MCP auto-prep when running inside an auto-worktree (see session_switch below).
|
|
127
148
|
const { isInAutoWorktree } = await import("../auto-worktree.js");
|
|
128
149
|
if (!isInAutoWorktree(process.cwd())) {
|
|
@@ -172,6 +193,7 @@ export function registerHooks(
|
|
|
172
193
|
clearDiscussionFlowState(process.cwd());
|
|
173
194
|
await syncServiceTierStatus(ctx);
|
|
174
195
|
await applyDisabledModelProviderPolicy(ctx);
|
|
196
|
+
await applyCompactionThresholdOverride(ctx);
|
|
175
197
|
// Skip MCP auto-prep when running inside an auto-worktree. The worktree
|
|
176
198
|
// already has .mcp.json from createAutoWorktree, and re-running the writer
|
|
177
199
|
// post-chdir rewrites the file mid-run (non-idempotent due to cwd-relative
|
|
@@ -482,6 +504,22 @@ export function registerHooks(
|
|
|
482
504
|
}
|
|
483
505
|
}
|
|
484
506
|
|
|
507
|
+
// ── Worktree-isolation write gate (#5199) ────────────────────────────
|
|
508
|
+
// Block planning-write tools from landing code at the project root when
|
|
509
|
+
// git.isolation=worktree but auto-mode hasn't created the milestone
|
|
510
|
+
// worktree yet. Without this, writes silently orphan outside git history.
|
|
511
|
+
if (isToolCallEventType("write", event) || isToolCallEventType("edit", event)) {
|
|
512
|
+
const wtBasePath = resolveWorktreeProjectRoot(dash.basePath ?? discussionBasePath);
|
|
513
|
+
const wtGuard = shouldBlockWorktreeWrite(
|
|
514
|
+
event.toolName,
|
|
515
|
+
event.input.path,
|
|
516
|
+
wtBasePath,
|
|
517
|
+
isAutoActive(),
|
|
518
|
+
dash.currentUnit?.type,
|
|
519
|
+
);
|
|
520
|
+
if (wtGuard.block) return wtGuard;
|
|
521
|
+
}
|
|
522
|
+
|
|
485
523
|
// ── Single-writer engine: block direct writes to STATE.md ──────────
|
|
486
524
|
// Covers write, edit, and bash tools to prevent bypass vectors.
|
|
487
525
|
if (isToolCallEventType("write", event)) {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { minimatch } from "minimatch";
|
|
5
5
|
|
|
6
|
+
import { getIsolationMode } from "../preferences.js";
|
|
6
7
|
import type { ToolsPolicy } from "../unit-context-manifest.js";
|
|
7
8
|
import { logWarning } from "../workflow-logger.js";
|
|
9
|
+
import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "../worktree-root.js";
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Regex matching milestone CONTEXT.md file names in both legacy M001
|
|
@@ -882,3 +884,135 @@ export function shouldBlockPlanningUnit(
|
|
|
882
884
|
// avoids breaking gsd_* MCP tools or future safe additions.
|
|
883
885
|
return { block: false };
|
|
884
886
|
}
|
|
887
|
+
|
|
888
|
+
// ─── Worktree isolation write gate (#5199) ────────────────────────────────
|
|
889
|
+
//
|
|
890
|
+
// When `git.isolation: worktree` is configured, the per-unit commit pipeline
|
|
891
|
+
// only runs inside the auto-mode loop (`auto-post-unit.ts`). If the LLM
|
|
892
|
+
// authors code at the project root before auto-mode is started, those writes
|
|
893
|
+
// land in the working tree but never reach a commit — they're silently
|
|
894
|
+
// orphaned outside git history. This guard blocks those writes at the
|
|
895
|
+
// tool_call seam so the agent receives a clear error instead.
|
|
896
|
+
|
|
897
|
+
const WORKTREE_GATE_BOOTSTRAP_UNITS = new Set([
|
|
898
|
+
"discuss-milestone",
|
|
899
|
+
"plan-milestone",
|
|
900
|
+
"init",
|
|
901
|
+
]);
|
|
902
|
+
|
|
903
|
+
function realpathOrResolve(p: string): string {
|
|
904
|
+
const abs = resolve(p);
|
|
905
|
+
try {
|
|
906
|
+
return realpathSync(abs);
|
|
907
|
+
} catch {
|
|
908
|
+
// Path doesn't exist (yet) — realpath the deepest existing ancestor so
|
|
909
|
+
// platforms where /tmp -> /private/tmp don't break containment checks.
|
|
910
|
+
let dir = abs;
|
|
911
|
+
const tail: string[] = [];
|
|
912
|
+
while (dir && dir !== resolve(dir, "..")) {
|
|
913
|
+
try {
|
|
914
|
+
const real = realpathSync(dir);
|
|
915
|
+
return tail.length ? join(real, ...tail.reverse()) : real;
|
|
916
|
+
} catch {
|
|
917
|
+
const idx = dir.lastIndexOf(sep);
|
|
918
|
+
if (idx <= 0) break;
|
|
919
|
+
tail.push(dir.slice(idx + 1));
|
|
920
|
+
dir = dir.slice(0, idx) || sep;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return abs;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
function isPathContained(target: string, container: string): boolean {
|
|
928
|
+
if (target === container) return true;
|
|
929
|
+
return target.startsWith(container.endsWith(sep) ? container : container + sep);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Block planning-write tool calls that would land code at the project root
|
|
934
|
+
* while `git.isolation: worktree` is in effect and auto-mode hasn't created
|
|
935
|
+
* (or flipped cwd into) the milestone worktree.
|
|
936
|
+
*
|
|
937
|
+
* Pure / unit-testable. Callers in `register-hooks.ts` supply the resolved
|
|
938
|
+
* project root and current auto liveness; this function does no I/O beyond
|
|
939
|
+
* realpath resolution.
|
|
940
|
+
*
|
|
941
|
+
* Allow rules (in order):
|
|
942
|
+
* 1. Tool isn't a planning-write (write/edit/multi_edit/notebook_edit).
|
|
943
|
+
* 2. `GSD_DISABLE_WORKTREE_WRITE_GUARD=1` self-hosting bypass.
|
|
944
|
+
* 3. Isolation mode is not "worktree".
|
|
945
|
+
* 4. Active unit is a bootstrap unit (discuss-milestone/plan-milestone/init).
|
|
946
|
+
* 5. Target is inside `<projectRoot>/.gsd/worktrees/` (a real worktree).
|
|
947
|
+
* 6. Target is inside `<projectRoot>/.gsd/` and isn't masquerading as a
|
|
948
|
+
* worktrees sibling (rejects the `.gsd/worktrees-extra/…` prefix trick).
|
|
949
|
+
* 7. Auto is live AND `effectiveBasePath` is itself a `.gsd/worktrees/…` path.
|
|
950
|
+
*
|
|
951
|
+
* Otherwise: block with a message that points the agent at `/gsd` to start
|
|
952
|
+
* auto-mode.
|
|
953
|
+
*/
|
|
954
|
+
export function shouldBlockWorktreeWrite(
|
|
955
|
+
toolName: string,
|
|
956
|
+
targetPath: string,
|
|
957
|
+
effectiveBasePath: string,
|
|
958
|
+
isAutoLive: boolean,
|
|
959
|
+
currentUnitType?: string | null,
|
|
960
|
+
): { block: boolean; reason?: string } {
|
|
961
|
+
const tool = canonicalToolName(toolName);
|
|
962
|
+
if (!PLANNING_WRITE_TOOLS.has(tool)) return { block: false };
|
|
963
|
+
if (process.env.GSD_DISABLE_WORKTREE_WRITE_GUARD === "1") return { block: false };
|
|
964
|
+
if (getIsolationMode(effectiveBasePath) !== "worktree") return { block: false };
|
|
965
|
+
if (currentUnitType && WORKTREE_GATE_BOOTSTRAP_UNITS.has(currentUnitType)) return { block: false };
|
|
966
|
+
|
|
967
|
+
if (!targetPath) {
|
|
968
|
+
return {
|
|
969
|
+
block: true,
|
|
970
|
+
reason: [
|
|
971
|
+
`HARD BLOCK: ${tool} called with empty path while \`git.isolation: worktree\` is configured`,
|
|
972
|
+
`and auto-mode is not active. Refusing to allow writes that cannot be located.`,
|
|
973
|
+
].join(" "),
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// Resolve the target relative to the project root, then realpath to defeat
|
|
978
|
+
// symlink-based escapes and prefix tricks (e.g. .gsd/worktrees-extra/).
|
|
979
|
+
const projectRoot = resolveWorktreeProjectRoot(effectiveBasePath);
|
|
980
|
+
const absTarget = isAbsolute(targetPath) ? targetPath : resolve(projectRoot, targetPath);
|
|
981
|
+
const realTarget = realpathOrResolve(absTarget);
|
|
982
|
+
const realRoot = realpathOrResolve(projectRoot);
|
|
983
|
+
const realGsd = realpathOrResolve(join(projectRoot, ".gsd"));
|
|
984
|
+
const realWorktreesDir = realpathOrResolve(join(projectRoot, ".gsd", "worktrees"));
|
|
985
|
+
|
|
986
|
+
// Allow writes inside the legitimate worktrees subtree.
|
|
987
|
+
if (isPathContained(realTarget, realWorktreesDir)) return { block: false };
|
|
988
|
+
|
|
989
|
+
// Allow writes to .gsd/ planning artifacts, but reject siblings whose name
|
|
990
|
+
// starts with "worktrees" (the worktrees-extra prefix trick — case 4).
|
|
991
|
+
if (isPathContained(realTarget, realGsd)) {
|
|
992
|
+
const rel = relative(realGsd, realTarget);
|
|
993
|
+
const firstSeg = rel.split(/[\/\\]/)[0] ?? "";
|
|
994
|
+
if (!firstSeg.startsWith("worktrees")) return { block: false };
|
|
995
|
+
// fall through: looks like worktrees<something> sibling — block
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// Auto is live and the caller is operating inside a worktree path —
|
|
999
|
+
// host tool's write happens in worktree context; let it through.
|
|
1000
|
+
if (isAutoLive && isGsdWorktreePath(effectiveBasePath)) return { block: false };
|
|
1001
|
+
|
|
1002
|
+
// Block. Provide enough context that the agent can self-correct.
|
|
1003
|
+
const displayTarget = isPathContained(realTarget, realRoot)
|
|
1004
|
+
? relative(realRoot, realTarget) || "."
|
|
1005
|
+
: realTarget;
|
|
1006
|
+
return {
|
|
1007
|
+
block: true,
|
|
1008
|
+
reason: [
|
|
1009
|
+
`HARD BLOCK: Worktree isolation is configured (\`git.isolation: worktree\`) but auto-mode is`,
|
|
1010
|
+
`not running and the target "${displayTarget}" is not inside \`.gsd/worktrees/<MID>/\`.`,
|
|
1011
|
+
`Code edits at the project root would be lost — only the auto-mode commit pipeline`,
|
|
1012
|
+
`(auto-post-unit) commits work, and it never runs outside the loop.`,
|
|
1013
|
+
`Required action: start auto-mode with \`/gsd\` so the milestone worktree is created,`,
|
|
1014
|
+
`then write inside it. To disable this guard for self-hosting development, set`,
|
|
1015
|
+
`GSD_DISABLE_WORKTREE_WRITE_GUARD=1.`,
|
|
1016
|
+
].join(" "),
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
@@ -21,10 +21,34 @@ import { nativeHasChanges } from "./native-git-bridge.js";
|
|
|
21
21
|
export interface PreflightResult {
|
|
22
22
|
/** true when a stash was pushed and postflightPopStash should be called */
|
|
23
23
|
stashPushed: boolean;
|
|
24
|
+
/** Unique marker embedded in the stash message for targeted restoration */
|
|
25
|
+
stashMarker?: string;
|
|
24
26
|
/** human-readable summary of what happened (empty string for clean trees) */
|
|
25
27
|
summary: string;
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
function findPreflightStashRef(basePath: string, milestoneId: string, stashMarker?: string): string | null {
|
|
31
|
+
const markerPrefix = `gsd-preflight-stash:${milestoneId}:`;
|
|
32
|
+
let fallbackRef: string | null = null;
|
|
33
|
+
try {
|
|
34
|
+
const list = execFileSync("git", ["stash", "list", "--format=%gd%x00%s"], {
|
|
35
|
+
cwd: basePath,
|
|
36
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
37
|
+
encoding: "utf-8",
|
|
38
|
+
env: GIT_NO_PROMPT_ENV,
|
|
39
|
+
});
|
|
40
|
+
for (const line of list.split("\n")) {
|
|
41
|
+
const [ref, subject] = line.split("\x00");
|
|
42
|
+
if (!ref || !subject) continue;
|
|
43
|
+
if (stashMarker && subject.includes(stashMarker)) return ref;
|
|
44
|
+
if (!fallbackRef && subject.includes(markerPrefix)) fallbackRef = ref;
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
logWarning("preflight", `stash list failed before restore: ${err instanceof Error ? err.message : String(err)}`);
|
|
48
|
+
}
|
|
49
|
+
return fallbackRef;
|
|
50
|
+
}
|
|
51
|
+
|
|
28
52
|
/**
|
|
29
53
|
* Check the working tree for dirty files before a milestone merge.
|
|
30
54
|
*
|
|
@@ -62,7 +86,8 @@ export function preflightCleanRoot(
|
|
|
62
86
|
|
|
63
87
|
// Push the stash
|
|
64
88
|
try {
|
|
65
|
-
|
|
89
|
+
const stashMarker = `gsd-preflight-stash:${milestoneId}:${process.pid}:${Date.now()}:${process.hrtime.bigint().toString(36)}`;
|
|
90
|
+
execFileSync("git", ["stash", "push", "--include-untracked", "-m", `gsd-preflight-stash [${stashMarker}]`], {
|
|
66
91
|
cwd: basePath,
|
|
67
92
|
stdio: ["ignore", "pipe", "pipe"],
|
|
68
93
|
encoding: "utf-8",
|
|
@@ -70,6 +95,7 @@ export function preflightCleanRoot(
|
|
|
70
95
|
});
|
|
71
96
|
return {
|
|
72
97
|
stashPushed: true,
|
|
98
|
+
stashMarker,
|
|
73
99
|
summary: `Stashed uncommitted changes before merge (milestone ${milestoneId}).`,
|
|
74
100
|
};
|
|
75
101
|
} catch (err) {
|
|
@@ -91,10 +117,19 @@ export function preflightCleanRoot(
|
|
|
91
117
|
export function postflightPopStash(
|
|
92
118
|
basePath: string,
|
|
93
119
|
milestoneId: string,
|
|
120
|
+
stashMarker: string | undefined,
|
|
94
121
|
notify: (message: string, level: "info" | "warning" | "error") => void,
|
|
95
122
|
): void {
|
|
123
|
+
let stashRef: string | null = null;
|
|
96
124
|
try {
|
|
97
|
-
|
|
125
|
+
stashRef = findPreflightStashRef(basePath, milestoneId, stashMarker);
|
|
126
|
+
if (!stashRef) {
|
|
127
|
+
const msg = `No matching GSD preflight stash found for milestone ${milestoneId}; leaving stash list untouched.`;
|
|
128
|
+
logWarning("preflight", msg);
|
|
129
|
+
notify(msg, "warning");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
execFileSync("git", ["stash", "pop", stashRef], {
|
|
98
133
|
cwd: basePath,
|
|
99
134
|
stdio: ["ignore", "pipe", "pipe"],
|
|
100
135
|
encoding: "utf-8",
|
|
@@ -104,7 +139,10 @@ export function postflightPopStash(
|
|
|
104
139
|
} catch (err) {
|
|
105
140
|
// Pop conflicts mean the merged code collides with the stashed changes.
|
|
106
141
|
// Log a warning — the user needs to resolve manually, but the merge succeeded.
|
|
107
|
-
const
|
|
142
|
+
const restoreHint = stashRef
|
|
143
|
+
? `Run "git stash pop ${stashRef}" or "git stash apply ${stashRef}" manually to restore the correct stash.`
|
|
144
|
+
: `Run "git stash list" to find the matching GSD preflight stash before restoring manually.`;
|
|
145
|
+
const msg = `git stash pop ${stashRef ?? ""}`.trim() + ` failed after merge of milestone ${milestoneId}: ${err instanceof Error ? err.message : String(err)}. ${restoreHint}`;
|
|
108
146
|
logWarning("preflight", msg);
|
|
109
147
|
notify(msg, "warning");
|
|
110
148
|
}
|
|
@@ -45,5 +45,11 @@ export async function handleGSDCommand(
|
|
|
45
45
|
|
|
46
46
|
if (handled) return;
|
|
47
47
|
|
|
48
|
+
if (trimmed.includes(" ")) {
|
|
49
|
+
const { handleDo } = await import("../commands-do.js");
|
|
50
|
+
await handleDo(trimmed, ctx, pi);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
48
54
|
ctx.ui.notify(`Unknown: /gsd ${trimmed}. Run /gsd help for available commands.`, "warning");
|
|
49
55
|
}
|
|
@@ -222,14 +222,19 @@ Using the \`write\` tool, persist the full structured report to
|
|
|
222
222
|
LEARNINGS.md is the full, cited audit trail. Write it first — subsequent steps
|
|
223
223
|
feed from its content.
|
|
224
224
|
|
|
225
|
-
### Step 3 —
|
|
225
|
+
### Step 3 — Run one bounded duplicate check
|
|
226
226
|
|
|
227
|
-
Before persisting
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
227
|
+
Before persisting extracted items in Steps 4–6, do at most one duplicate-check
|
|
228
|
+
pass for the durable Decisions, Lessons, and Patterns from this milestone. Use
|
|
229
|
+
the already-written LEARNINGS.md content and call \`memory_query\` once with a
|
|
230
|
+
compact keyword summary for the whole batch. If that result clearly shows a
|
|
231
|
+
semantically equivalent high-confidence memory for an item, mark only that item
|
|
232
|
+
as already captured and skip it in its respective persistence step.
|
|
233
|
+
|
|
234
|
+
Do not re-read milestone artefacts or repeat memory queries category-by-category
|
|
235
|
+
after this point. The memory store is the single source of truth for
|
|
236
|
+
cross-session durable knowledge — no other persistence call is part of this
|
|
237
|
+
flow.
|
|
233
238
|
|
|
234
239
|
### Step 4 — Persist Patterns via \`capture_thought\`
|
|
235
240
|
|
|
@@ -268,11 +273,11 @@ later projection back to a human-visible decisions register stays lossless
|
|
|
268
273
|
|
|
269
274
|
### Step 7 — Deduplication rule (applies to Steps 4, 5, 6)
|
|
270
275
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
over creating a second slightly-different row
|
|
275
|
-
signal.
|
|
276
|
+
Use only the duplicate-check result from Step 3. If that bounded check returned
|
|
277
|
+
a semantically equivalent memory at high confidence for an extracted item, skip
|
|
278
|
+
the capture entirely. Otherwise, persist the item once via \`capture_thought\`.
|
|
279
|
+
Prefer skipping a near-duplicate over creating a second slightly-different row
|
|
280
|
+
— redundancy degrades the signal.
|
|
276
281
|
|
|
277
282
|
### Step 8 — Surprises stay only in LEARNINGS.md
|
|
278
283
|
|
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
* @see D001 (module location), D002 (200K fallback), D003 (section-boundary truncation)
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
type TokenProvider,
|
|
13
|
+
getCharsPerToken,
|
|
14
|
+
isAccurateCountingAvailable,
|
|
15
|
+
countTokensSync,
|
|
16
|
+
} from "./token-counter.js";
|
|
12
17
|
|
|
13
18
|
// ─── Budget ratio constants ──────────────────────────────────────────────────
|
|
14
19
|
// Percentages of total context window allocated to each budget category.
|
|
@@ -32,6 +37,24 @@ const DEFAULT_CONTEXT_WINDOW = 200_000;
|
|
|
32
37
|
/** Conservative effective context for Claude Code subscription routing (#4676) */
|
|
33
38
|
const CLAUDE_CODE_EFFECTIVE_CONTEXT_WINDOW = 200_000;
|
|
34
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Cached empirical chars-per-token from a tiktoken probe, keyed by provider.
|
|
42
|
+
* countTokensSync's fallback path is provider-aware, so we cache per-provider
|
|
43
|
+
* to preserve that distinction once the encoder warms. The cl100k_base encoder
|
|
44
|
+
* itself gives a stable ratio for ASCII English so a single probe per provider
|
|
45
|
+
* key is sufficient. Empty map means "not yet probed" or "encoder unavailable".
|
|
46
|
+
*/
|
|
47
|
+
const _empiricalCharsPerTokenByProvider = new Map<string, number>();
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Test hook — clears the empirical chars-per-token cache so test cases that
|
|
51
|
+
* assert against the static char-ratio fallback aren't polluted by a prior
|
|
52
|
+
* tiktoken-warmed run in the same process. Production code must not call this.
|
|
53
|
+
*/
|
|
54
|
+
export function _resetEmpiricalCacheForTest(): void {
|
|
55
|
+
_empiricalCharsPerTokenByProvider.clear();
|
|
56
|
+
}
|
|
57
|
+
|
|
35
58
|
/** Percentage of context consumed before suggesting a continue-here checkpoint */
|
|
36
59
|
const CONTINUE_THRESHOLD_PERCENT = 70;
|
|
37
60
|
|
|
@@ -101,7 +124,26 @@ export interface MinimalPreferences {
|
|
|
101
124
|
export function computeBudgets(contextWindow: number, provider?: TokenProvider): BudgetAllocation {
|
|
102
125
|
const effectiveWindow = contextWindow > 0 ? contextWindow : DEFAULT_CONTEXT_WINDOW;
|
|
103
126
|
const charsPerToken = provider ? getCharsPerToken(provider) : CHARS_PER_TOKEN;
|
|
104
|
-
|
|
127
|
+
|
|
128
|
+
// Prefer the tiktoken encoder for total-char estimation when it has been
|
|
129
|
+
// warmed (initTokenCounter resolved). The cl100k_base ratio is stable for
|
|
130
|
+
// ASCII English, so probe once per provider and cache — computeBudgets is
|
|
131
|
+
// called multiple times per prompt build and the probe encode is otherwise
|
|
132
|
+
// wasted work.
|
|
133
|
+
let totalChars: number;
|
|
134
|
+
if (isAccurateCountingAvailable()) {
|
|
135
|
+
const providerKey = provider ?? "__default__";
|
|
136
|
+
let empirical = _empiricalCharsPerTokenByProvider.get(providerKey);
|
|
137
|
+
if (empirical === undefined) {
|
|
138
|
+
const probe = "the quick brown fox jumps over the lazy dog ".repeat(64);
|
|
139
|
+
const probeTokens = countTokensSync(probe, provider);
|
|
140
|
+
empirical = probeTokens > 0 ? probe.length / probeTokens : charsPerToken;
|
|
141
|
+
_empiricalCharsPerTokenByProvider.set(providerKey, empirical);
|
|
142
|
+
}
|
|
143
|
+
totalChars = effectiveWindow * empirical;
|
|
144
|
+
} else {
|
|
145
|
+
totalChars = effectiveWindow * charsPerToken;
|
|
146
|
+
}
|
|
105
147
|
|
|
106
148
|
return {
|
|
107
149
|
summaryBudgetChars: Math.floor(totalChars * SUMMARY_RATIO),
|