gsd-pi 2.80.0-dev.c5c38454b → 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/gsd/auto/loop.js +32 -1
- 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 +13 -1
- 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 +43 -1
- 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 +21 -2
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +27 -20
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +21 -0
- 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/context-budget.js +37 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
- package/dist/resources/extensions/gsd/db-base-schema.js +4 -2
- package/dist/resources/extensions/gsd/db-migration-steps.js +6 -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/gsd-db.js +46 -13
- package/dist/resources/extensions/gsd/guided-flow.js +80 -14
- 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/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/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 +7 -7
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- 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/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 +7 -7
- package/dist/web/standalone/.next/server/middleware-build-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/package.json +3 -3
- 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-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 +32 -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 +1 -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 +3 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +2 -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/settings-manager.d.ts +20 -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 +25 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -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 +3 -0
- 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 +15 -5
- 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 +53 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
- 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 +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- 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 +38 -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 +2 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +3 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +39 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +4 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +56 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +24 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +3 -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/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +18 -8
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/tui.ts +20 -8
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/src/resources/GSD-WORKFLOW.md +2 -2
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/loop.ts +50 -8
- 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 +18 -1
- 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 +54 -0
- 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 +21 -1
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +27 -19
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
- 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/context-budget.ts +44 -2
- package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
- package/src/resources/extensions/gsd/db-base-schema.ts +4 -2
- package/src/resources/extensions/gsd/db-migration-steps.ts +8 -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/gsd-db.ts +50 -13
- package/src/resources/extensions/gsd/guided-flow.ts +96 -14
- 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/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-loop.test.ts +72 -1
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +14 -1
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
- 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/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/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/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/workflow-tool-executors.test.ts +3 -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/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/worktree-manager.ts +15 -4
- package/src/resources/extensions/gsd/worktree-resolver.ts +36 -15
- /package/dist/web/standalone/.next/static/{TCSim36ZpcPu2WgeoC45g → -5nHJWzSdG-WkPMul_khA}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{TCSim36ZpcPu2WgeoC45g → -5nHJWzSdG-WkPMul_khA}/_ssgManifest.js +0 -0
|
@@ -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
|
});
|
|
@@ -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
|
|
@@ -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
|
}
|
|
@@ -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),
|
|
@@ -38,6 +38,7 @@ import { _getAdapter, isDbAvailable } from "./gsd-db.js";
|
|
|
38
38
|
import { gsdRoot, normalizeRealPath } from "./paths.js";
|
|
39
39
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
40
40
|
import { effectiveLockFile } from "./session-lock.js";
|
|
41
|
+
import { isInFlightRuntimePhase, listUnitRuntimeRecords, type AutoUnitRuntimeRecord } from "./unit-runtime.js";
|
|
41
42
|
|
|
42
43
|
export interface LockData {
|
|
43
44
|
pid: number;
|
|
@@ -103,10 +104,40 @@ function getLatestDispatchForWorker(workerId: string):
|
|
|
103
104
|
return row ?? null;
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
function
|
|
107
|
+
function latestInFlightRuntimeRecord(basePath: string): AutoUnitRuntimeRecord | null {
|
|
108
|
+
const records = listUnitRuntimeRecords(basePath).filter((record) =>
|
|
109
|
+
isInFlightRuntimePhase(record.phase),
|
|
110
|
+
);
|
|
111
|
+
if (records.length === 0) return null;
|
|
112
|
+
return records.sort((a, b) => {
|
|
113
|
+
const bTime = b.updatedAt || b.startedAt || 0;
|
|
114
|
+
const aTime = a.updatedAt || a.startedAt || 0;
|
|
115
|
+
return bTime - aTime;
|
|
116
|
+
})[0] ?? null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function runtimeRecordToLockData(worker: AutoWorkerRow, record: AutoUnitRuntimeRecord, sessionFile?: string): LockData {
|
|
120
|
+
const startedAt = Number.isFinite(record.startedAt)
|
|
121
|
+
? new Date(record.startedAt).toISOString()
|
|
122
|
+
: worker.started_at;
|
|
123
|
+
return {
|
|
124
|
+
pid: worker.pid,
|
|
125
|
+
startedAt: worker.started_at,
|
|
126
|
+
unitType: record.unitType,
|
|
127
|
+
unitId: record.unitId,
|
|
128
|
+
unitStartedAt: startedAt,
|
|
129
|
+
sessionFile,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function workerToLockData(basePath: string, worker: AutoWorkerRow): LockData {
|
|
107
134
|
const dispatch = getLatestDispatchForWorker(worker.worker_id);
|
|
108
135
|
const sessionFile =
|
|
109
136
|
getRuntimeKv<string>("worker", worker.worker_id, SESSION_FILE_KV_KEY) ?? undefined;
|
|
137
|
+
if (!dispatch) {
|
|
138
|
+
const runtimeRecord = latestInFlightRuntimeRecord(basePath);
|
|
139
|
+
if (runtimeRecord) return runtimeRecordToLockData(worker, runtimeRecord, sessionFile);
|
|
140
|
+
}
|
|
110
141
|
return {
|
|
111
142
|
pid: worker.pid,
|
|
112
143
|
startedAt: worker.started_at,
|
|
@@ -204,7 +235,7 @@ export function readCrashLock(basePath: string): LockData | null {
|
|
|
204
235
|
try {
|
|
205
236
|
const projectRoot = normalizeRealPath(basePath);
|
|
206
237
|
const stale = findStaleWorkerForProject(projectRoot);
|
|
207
|
-
if (stale) return workerToLockData(stale);
|
|
238
|
+
if (stale) return workerToLockData(basePath, stale);
|
|
208
239
|
} catch {
|
|
209
240
|
// Fall through to the legacy lock-file compatibility path.
|
|
210
241
|
}
|
|
@@ -260,25 +291,48 @@ export function formatCrashInfo(lock: LockData): string {
|
|
|
260
291
|
*/
|
|
261
292
|
export function emitCrashRecoveredUnitEnd(basePath: string, lock: LockData): void {
|
|
262
293
|
if (!lock.unitType || !lock.unitId || lock.unitType === "starting") return;
|
|
294
|
+
emitOpenUnitEndForUnit(basePath, lock.unitType, lock.unitId, "crash-recovered");
|
|
295
|
+
}
|
|
263
296
|
|
|
297
|
+
export function emitOpenUnitEndForUnit(
|
|
298
|
+
basePath: string,
|
|
299
|
+
unitType: string,
|
|
300
|
+
unitId: string,
|
|
301
|
+
status: string,
|
|
302
|
+
errorContext?: { message: string; category: string; stopReason?: string; isTransient?: boolean; retryAfterMs?: number },
|
|
303
|
+
): boolean {
|
|
264
304
|
try {
|
|
265
305
|
const all = queryJournal(basePath);
|
|
266
306
|
|
|
267
307
|
const starts = all.filter(
|
|
268
|
-
(e) =>
|
|
308
|
+
(e) =>
|
|
309
|
+
e.eventType === "unit-start" &&
|
|
310
|
+
e.data?.unitType === unitType &&
|
|
311
|
+
e.data?.unitId === unitId,
|
|
269
312
|
);
|
|
270
|
-
if (starts.length === 0) return;
|
|
313
|
+
if (starts.length === 0) return false;
|
|
271
314
|
|
|
272
|
-
const lastStart =
|
|
315
|
+
const lastStart = [...starts].reverse().find((start) => {
|
|
316
|
+
return !all.some(
|
|
317
|
+
(e) =>
|
|
318
|
+
e.eventType === "unit-end" &&
|
|
319
|
+
e.data?.unitType === unitType &&
|
|
320
|
+
e.data?.unitId === unitId &&
|
|
321
|
+
e.causedBy?.flowId === start.flowId &&
|
|
322
|
+
e.causedBy?.seq === start.seq,
|
|
323
|
+
);
|
|
324
|
+
});
|
|
325
|
+
if (!lastStart) return false;
|
|
273
326
|
|
|
274
327
|
const alreadyClosed = all.some(
|
|
275
328
|
(e) =>
|
|
276
329
|
e.eventType === "unit-end" &&
|
|
277
|
-
e.data?.
|
|
330
|
+
e.data?.unitType === unitType &&
|
|
331
|
+
e.data?.unitId === unitId &&
|
|
278
332
|
e.causedBy?.flowId === lastStart.flowId &&
|
|
279
333
|
e.causedBy?.seq === lastStart.seq,
|
|
280
334
|
);
|
|
281
|
-
if (alreadyClosed) return;
|
|
335
|
+
if (alreadyClosed) return false;
|
|
282
336
|
|
|
283
337
|
const maxSeq = all
|
|
284
338
|
.filter((e) => e.flowId === lastStart.flowId)
|
|
@@ -290,15 +344,18 @@ export function emitCrashRecoveredUnitEnd(basePath: string, lock: LockData): voi
|
|
|
290
344
|
seq: maxSeq + 1,
|
|
291
345
|
eventType: "unit-end",
|
|
292
346
|
data: {
|
|
293
|
-
unitType
|
|
294
|
-
unitId
|
|
295
|
-
status
|
|
347
|
+
unitType,
|
|
348
|
+
unitId,
|
|
349
|
+
status,
|
|
296
350
|
artifactVerified: false,
|
|
351
|
+
...(errorContext ? { errorContext } : {}),
|
|
297
352
|
},
|
|
298
353
|
causedBy: { flowId: lastStart.flowId, seq: lastStart.seq },
|
|
299
354
|
});
|
|
355
|
+
return true;
|
|
300
356
|
} catch {
|
|
301
357
|
// Never throw from crash recovery path.
|
|
358
|
+
return false;
|
|
302
359
|
}
|
|
303
360
|
}
|
|
304
361
|
|
|
@@ -359,6 +359,47 @@ export function markCanceled(dispatchId: number, reason: string): void {
|
|
|
359
359
|
).run({ ":id": dispatchId, ":ended_at": now, ":reason": reason });
|
|
360
360
|
}
|
|
361
361
|
|
|
362
|
+
/**
|
|
363
|
+
* Best-effort signal/crash cleanup: cancel the latest active dispatch owned by
|
|
364
|
+
* a worker when the process is exiting before the normal loop can settle it.
|
|
365
|
+
*/
|
|
366
|
+
export function markLatestActiveForWorkerCanceled(workerId: string, reason: string): boolean {
|
|
367
|
+
if (!isDbAvailable()) return false;
|
|
368
|
+
const now = new Date().toISOString();
|
|
369
|
+
const db = _getAdapter()!;
|
|
370
|
+
const result = transaction(() => {
|
|
371
|
+
return db.prepare(
|
|
372
|
+
`UPDATE unit_dispatches
|
|
373
|
+
SET status = 'canceled', ended_at = :ended_at, exit_reason = :reason
|
|
374
|
+
WHERE id = (
|
|
375
|
+
SELECT id FROM unit_dispatches
|
|
376
|
+
WHERE worker_id = :worker_id
|
|
377
|
+
AND status IN ('pending','claimed','running')
|
|
378
|
+
ORDER BY id DESC
|
|
379
|
+
LIMIT 1
|
|
380
|
+
)`,
|
|
381
|
+
).run({
|
|
382
|
+
":ended_at": now,
|
|
383
|
+
":reason": reason,
|
|
384
|
+
":worker_id": workerId,
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
const changes =
|
|
388
|
+
typeof (result as { changes?: unknown }).changes === "number"
|
|
389
|
+
? (result as { changes: number }).changes
|
|
390
|
+
: 0;
|
|
391
|
+
if (changes <= 0) return false;
|
|
392
|
+
insertAuditEvent({
|
|
393
|
+
eventId: randomUUID(),
|
|
394
|
+
traceId: workerId,
|
|
395
|
+
category: "orchestration",
|
|
396
|
+
type: "dispatch-canceled",
|
|
397
|
+
ts: now,
|
|
398
|
+
payload: { workerId, reason },
|
|
399
|
+
});
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
|
|
362
403
|
/**
|
|
363
404
|
* Fetch the most recent N dispatches for a unit. Used by recordDispatchClaim
|
|
364
405
|
* callers to compute attempt_n and by detect-stuck.ts (B3) to consult
|
|
@@ -57,7 +57,8 @@ export function createBaseSchemaObjects(db: DbAdapter, hooks: BaseSchemaHooks):
|
|
|
57
57
|
slice_id TEXT DEFAULT NULL,
|
|
58
58
|
task_id TEXT DEFAULT NULL,
|
|
59
59
|
full_content TEXT NOT NULL DEFAULT '',
|
|
60
|
-
imported_at TEXT NOT NULL DEFAULT ''
|
|
60
|
+
imported_at TEXT NOT NULL DEFAULT '',
|
|
61
|
+
content_hash TEXT DEFAULT NULL
|
|
61
62
|
)
|
|
62
63
|
`);
|
|
63
64
|
|
|
@@ -76,7 +77,8 @@ export function createBaseSchemaObjects(db: DbAdapter, hooks: BaseSchemaHooks):
|
|
|
76
77
|
hit_count INTEGER NOT NULL DEFAULT 0,
|
|
77
78
|
scope TEXT NOT NULL DEFAULT 'project',
|
|
78
79
|
tags TEXT NOT NULL DEFAULT '[]',
|
|
79
|
-
structured_fields TEXT DEFAULT NULL
|
|
80
|
+
structured_fields TEXT DEFAULT NULL,
|
|
81
|
+
last_hit_at TEXT DEFAULT NULL
|
|
80
82
|
)
|
|
81
83
|
`);
|
|
82
84
|
|
|
@@ -416,6 +416,14 @@ export function applyMigrationV26MilestoneCommitAttributions(db: DbAdapter): voi
|
|
|
416
416
|
db.exec("CREATE INDEX IF NOT EXISTS idx_milestone_commit_attr_milestone ON milestone_commit_attributions(milestone_id)");
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
+
export function applyMigrationV27ArtifactHash(db: DbAdapter): void {
|
|
420
|
+
ensureColumn(db, "artifacts", "content_hash", "ALTER TABLE artifacts ADD COLUMN content_hash TEXT DEFAULT NULL");
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export function applyMigrationV28MemoryLastHitAt(db: DbAdapter): void {
|
|
424
|
+
ensureColumn(db, "memories", "last_hit_at", "ALTER TABLE memories ADD COLUMN last_hit_at TEXT DEFAULT NULL");
|
|
425
|
+
}
|
|
426
|
+
|
|
419
427
|
export interface MigrationV22Hooks {
|
|
420
428
|
copyQualityGateRowsToRepairedTable(db: DbAdapter): void;
|
|
421
429
|
}
|