gsd-pi 2.80.0-dev.2be7caf18 → 2.80.0-dev.3065f7bfa
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 +1 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/phases.js +178 -108
- package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +20 -30
- package/dist/resources/extensions/gsd/auto/session.js +8 -0
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +5 -32
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +17 -4
- package/dist/resources/extensions/gsd/auto-prompts.js +90 -15
- package/dist/resources/extensions/gsd/auto-recovery.js +43 -1
- package/dist/resources/extensions/gsd/auto-start.js +197 -6
- 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-worktree.js +111 -1
- package/dist/resources/extensions/gsd/auto.js +79 -24
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +42 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +49 -36
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +15 -5
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +9 -3
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +7 -1
- package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +9 -3
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +8 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +238 -28
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +82 -23
- package/dist/resources/extensions/gsd/clean-root-preflight.js +24 -6
- package/dist/resources/extensions/gsd/commands-handlers.js +23 -9
- package/dist/resources/extensions/gsd/context-budget.js +37 -2
- 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/ecosystem/gsd-extension-api.js +2 -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 +66 -32
- package/dist/resources/extensions/gsd/memory-store.js +69 -12
- package/dist/resources/extensions/gsd/native-git-bridge.js +32 -8
- package/dist/resources/extensions/gsd/orphan-stash-audit.js +101 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +13 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +22 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +2 -2
- package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
- package/dist/resources/extensions/gsd/unit-runtime.js +11 -0
- package/dist/resources/extensions/gsd/workflow-protocol.js +131 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +68 -21
- 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 +15 -15
- 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 +15 -15
- 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/src/workflow-tools.test.ts +9 -2
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +4 -1
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +5 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/index.d.ts +1 -0
- package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/index.js +2 -0
- package/packages/pi-agent-core/dist/index.js.map +1 -1
- package/packages/pi-agent-core/dist/token-audit.d.ts +47 -0
- package/packages/pi-agent-core/dist/token-audit.d.ts.map +1 -0
- package/packages/pi-agent-core/dist/token-audit.js +221 -0
- package/packages/pi-agent-core/dist/token-audit.js.map +1 -0
- package/packages/pi-agent-core/dist/types.d.ts +9 -0
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +128 -0
- package/packages/pi-agent-core/src/agent-loop.ts +4 -1
- package/packages/pi-agent-core/src/agent.ts +8 -0
- package/packages/pi-agent-core/src/index.ts +2 -0
- package/packages/pi-agent-core/src/token-audit.test.ts +189 -0
- package/packages/pi-agent-core/src/token-audit.ts +287 -0
- package/packages/pi-agent-core/src/types.ts +14 -0
- package/packages/pi-agent-core/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-tool-refresh.test.js +18 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +41 -7
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +8 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -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 -6
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +3 -3
- 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 +32 -1
- 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/hooks-runner.test.js +2 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -2
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +74 -2
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js +22 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -7
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -3
- package/packages/pi-coding-agent/dist/core/system-prompt.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-tool-refresh.test.ts +25 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +45 -7
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +10 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +3 -3
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +5 -5
- package/packages/pi-coding-agent/src/core/extensions/types.ts +35 -1
- package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +2 -0
- package/packages/pi-coding-agent/src/core/sdk-tool-filter.test.ts +60 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +85 -3
- package/packages/pi-coding-agent/src/core/skill-tool.test.ts +28 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +8 -10
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -2
- package/src/resources/extensions/gsd/auto/phases.ts +251 -172
- package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
- package/src/resources/extensions/gsd/auto/run-unit.ts +25 -30
- package/src/resources/extensions/gsd/auto/session.ts +8 -0
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -34
- package/src/resources/extensions/gsd/auto-dispatch.ts +16 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +18 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +95 -14
- package/src/resources/extensions/gsd/auto-recovery.ts +54 -0
- package/src/resources/extensions/gsd/auto-start.ts +230 -9
- 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-worktree.ts +123 -0
- package/src/resources/extensions/gsd/auto.ts +87 -19
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +49 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +50 -36
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -5
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +10 -3
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +8 -1
- package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +10 -3
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +9 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +283 -28
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +90 -22
- package/src/resources/extensions/gsd/clean-root-preflight.ts +32 -7
- package/src/resources/extensions/gsd/commands-handlers.ts +34 -15
- package/src/resources/extensions/gsd/context-budget.ts +44 -2
- 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/ecosystem/gsd-extension-api.ts +3 -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 +84 -39
- package/src/resources/extensions/gsd/memory-store.ts +77 -12
- package/src/resources/extensions/gsd/native-git-bridge.ts +39 -6
- package/src/resources/extensions/gsd/orphan-stash-audit.ts +117 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +13 -3
- package/src/resources/extensions/gsd/pre-execution-checks.ts +23 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +417 -10
- 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/auto-wrapup-inflight-guard.test.ts +168 -6
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +15 -6
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +5 -1
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +5 -2
- package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +219 -0
- package/src/resources/extensions/gsd/tests/finalize-survivor-branch.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +239 -1
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +242 -0
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +34 -2
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/parallel-orchestrator-fast-forward.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +45 -5
- package/src/resources/extensions/gsd/tests/prompt-duplication-cuts.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +38 -17
- package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/select-resumable-milestone.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -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 +38 -0
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/system-context-memory.test.ts +112 -0
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +7 -9
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +291 -0
- package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/workflow-protocol-excerpt.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +22 -19
- package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +167 -4
- package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
- package/src/resources/extensions/gsd/unit-runtime.ts +11 -0
- package/src/resources/extensions/gsd/workflow-protocol.ts +160 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +85 -19
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +0 -97
- /package/dist/web/standalone/.next/static/{3EDDd9ULaybSupPoA_vf- → O2WvnXROue6l7nAp_4dhC}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{3EDDd9ULaybSupPoA_vf- → O2WvnXROue6l7nAp_4dhC}/_ssgManifest.js +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: System prompt and hidden context bootstrap for GSD sessions.
|
|
1
3
|
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
2
4
|
import { join } from "node:path";
|
|
3
5
|
|
|
@@ -21,6 +23,11 @@ import { toPosixPath } from "../../shared/mod.js";
|
|
|
21
23
|
import { autoEnableCmuxPreferences } from "../commands-cmux.js";
|
|
22
24
|
import { gsdHome } from "../gsd-home.js";
|
|
23
25
|
|
|
26
|
+
const DEFAULT_CONTEXT_MESSAGE_MAX_CHARS = 4_000;
|
|
27
|
+
const DEFAULT_KNOWLEDGE_MAX_CHARS = 12_000;
|
|
28
|
+
const DEFAULT_CODEBASE_MAX_CHARS = 8_000;
|
|
29
|
+
const MIN_CONTEXT_MESSAGE_MAX_CHARS = 1_000;
|
|
30
|
+
const MIN_KNOWLEDGE_MAX_CHARS = 1_000;
|
|
24
31
|
|
|
25
32
|
/**
|
|
26
33
|
* Bundled skill triggers — resolved dynamically at runtime instead of
|
|
@@ -158,8 +165,6 @@ export async function buildBeforeAgentStartResult(
|
|
|
158
165
|
logWarning("bootstrap", `decisions backfill failed: ${(e as Error).message}`);
|
|
159
166
|
}
|
|
160
167
|
|
|
161
|
-
const memoryBlock = await loadMemoryBlock(event.prompt ?? "");
|
|
162
|
-
|
|
163
168
|
let newSkillsBlock = "";
|
|
164
169
|
if (hasSkillSnapshot()) {
|
|
165
170
|
const newSkills = detectNewSkills();
|
|
@@ -190,11 +195,10 @@ export async function buildBeforeAgentStartResult(
|
|
|
190
195
|
if (rawContent) {
|
|
191
196
|
// Cap injection size to ~2 000 tokens to avoid bloating every request.
|
|
192
197
|
// Full map is always available at .gsd/CODEBASE.md.
|
|
193
|
-
const MAX_CODEBASE_CHARS = 8_000;
|
|
194
198
|
const generatedMatch = rawContent.match(/Generated: (\S+)/);
|
|
195
199
|
const generatedAt = generatedMatch?.[1] ?? "unknown";
|
|
196
|
-
const content = rawContent.length >
|
|
197
|
-
? rawContent.slice(0,
|
|
200
|
+
const content = rawContent.length > DEFAULT_CODEBASE_MAX_CHARS
|
|
201
|
+
? rawContent.slice(0, DEFAULT_CODEBASE_MAX_CHARS) + "\n\n*(truncated — see .gsd/CODEBASE.md for full map)*"
|
|
198
202
|
: rawContent;
|
|
199
203
|
codebaseBlock = `\n\n[PROJECT CODEBASE — File structure and descriptions (generated ${generatedAt}, auto-refreshed when GSD detects tracked file changes; use /gsd codebase stats for status)]\n\n${content}`;
|
|
200
204
|
}
|
|
@@ -206,6 +210,9 @@ export async function buildBeforeAgentStartResult(
|
|
|
206
210
|
warnDeprecatedAgentInstructions();
|
|
207
211
|
|
|
208
212
|
const injection = await buildGuidedExecuteContextInjection(event.prompt, process.cwd());
|
|
213
|
+
const memoryBlock = await loadMemoryBlock(event.prompt ?? "", {
|
|
214
|
+
includePromptRelevant: !(injection && isLowEntropyResumePrompt(event.prompt ?? "")),
|
|
215
|
+
});
|
|
209
216
|
|
|
210
217
|
// Re-inject forensics context on follow-up turns (#2941)
|
|
211
218
|
const forensicsInjection = !injection ? buildForensicsContextInjection(process.cwd(), event.prompt) : null;
|
|
@@ -218,12 +225,9 @@ export async function buildBeforeAgentStartResult(
|
|
|
218
225
|
: "";
|
|
219
226
|
|
|
220
227
|
// memoryBlock is FTS-queried against the user prompt and changes per call.
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
// memoryBlock injected via the context message may itself be cached up to
|
|
225
|
-
// that boundary; that's orthogonal and unchanged from prior behavior. The
|
|
226
|
-
// load-bearing win here is preserving the system+tools cache hit. (#5019)
|
|
228
|
+
// Keeping it out of `fullSystem` preserves provider prompt-cache stability
|
|
229
|
+
// for the static system/tool prefix. The dynamic memory block rides the
|
|
230
|
+
// volatile context message instead. (#5019)
|
|
227
231
|
const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${codebaseBlock}${newSkillsBlock}${worktreeBlock}${subagentModelBlock}`;
|
|
228
232
|
|
|
229
233
|
stopContextTimer({
|
|
@@ -255,21 +259,55 @@ export function buildContextMessage(opts: {
|
|
|
255
259
|
injection: string | null;
|
|
256
260
|
forensicsInjection: string | null;
|
|
257
261
|
}): { customType: string; content: string; display: false } | null {
|
|
258
|
-
const
|
|
262
|
+
const contextCharLimit = getContextMessageCharLimit();
|
|
263
|
+
const memoryContent = markMemoryContextSupplied(opts.memoryBlock.trim());
|
|
259
264
|
if (opts.injection) {
|
|
260
|
-
const content =
|
|
265
|
+
const content = limitContextMessageContent(
|
|
266
|
+
memoryContent ? `${memoryContent}\n\n${opts.injection}` : opts.injection,
|
|
267
|
+
contextCharLimit,
|
|
268
|
+
);
|
|
261
269
|
return { customType: "gsd-guided-context", content, display: false as const };
|
|
262
270
|
}
|
|
263
271
|
if (opts.forensicsInjection) {
|
|
264
|
-
const content =
|
|
272
|
+
const content = limitContextMessageContent(
|
|
273
|
+
memoryContent ? `${memoryContent}\n\n${opts.forensicsInjection}` : opts.forensicsInjection,
|
|
274
|
+
contextCharLimit,
|
|
275
|
+
);
|
|
265
276
|
return { customType: "gsd-forensics", content, display: false as const };
|
|
266
277
|
}
|
|
267
278
|
if (memoryContent) {
|
|
268
|
-
return {
|
|
279
|
+
return {
|
|
280
|
+
customType: "gsd-memory",
|
|
281
|
+
content: limitContextMessageContent(memoryContent, contextCharLimit),
|
|
282
|
+
display: false as const,
|
|
283
|
+
};
|
|
269
284
|
}
|
|
270
285
|
return null;
|
|
271
286
|
}
|
|
272
287
|
|
|
288
|
+
function getContextMessageCharLimit(): number | null {
|
|
289
|
+
const raw = process.env.PI_GSD_CONTEXT_MAX_CHARS;
|
|
290
|
+
if (!raw) return DEFAULT_CONTEXT_MESSAGE_MAX_CHARS;
|
|
291
|
+
if (raw === "0") return null;
|
|
292
|
+
const parsed = Number(raw);
|
|
293
|
+
if (!Number.isFinite(parsed) || parsed < MIN_CONTEXT_MESSAGE_MAX_CHARS) {
|
|
294
|
+
return DEFAULT_CONTEXT_MESSAGE_MAX_CHARS;
|
|
295
|
+
}
|
|
296
|
+
return Math.floor(parsed);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function limitContextMessageContent(content: string, limit: number | null): string {
|
|
300
|
+
if (!limit || content.length <= limit) return content;
|
|
301
|
+
const suffix = "\n\n[GSD Context Truncated]\nFull context is available from the referenced .gsd files and tools; read on demand only if this excerpt lacks required evidence.";
|
|
302
|
+
const headBudget = Math.max(0, limit - suffix.length);
|
|
303
|
+
return `${content.slice(0, headBudget).trimEnd()}${suffix}`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function markMemoryContextSupplied(memoryContent: string): string {
|
|
307
|
+
if (!memoryContent) return "";
|
|
308
|
+
return `[GSD Context Metadata]\n- Memory supplied: yes\n\n${memoryContent}`;
|
|
309
|
+
}
|
|
310
|
+
|
|
273
311
|
/**
|
|
274
312
|
* ADR-013 step 4 — auto-injection parity for the memories table.
|
|
275
313
|
*
|
|
@@ -288,7 +326,10 @@ export function buildContextMessage(opts: {
|
|
|
288
326
|
* with a token-budget cap. Failures degrade gracefully — the function never
|
|
289
327
|
* throws and returns "" so the system prompt construction continues.
|
|
290
328
|
*/
|
|
291
|
-
export async function loadMemoryBlock(
|
|
329
|
+
export async function loadMemoryBlock(
|
|
330
|
+
userPrompt: string,
|
|
331
|
+
opts: { includePromptRelevant?: boolean } = {},
|
|
332
|
+
): Promise<string> {
|
|
292
333
|
try {
|
|
293
334
|
const { formatMemoriesForPrompt, getActiveMemoriesRanked, queryMemoriesRanked } = await import("../memory-store.js");
|
|
294
335
|
|
|
@@ -310,7 +351,7 @@ export async function loadMemoryBlock(userPrompt: string): Promise<string> {
|
|
|
310
351
|
|
|
311
352
|
let relevant: typeof allRanked = [];
|
|
312
353
|
const trimmed = userPrompt.trim();
|
|
313
|
-
if (trimmed) {
|
|
354
|
+
if (trimmed && opts.includePromptRelevant !== false) {
|
|
314
355
|
const hits = queryMemoriesRanked({ query: trimmed, k: QUERY_K });
|
|
315
356
|
relevant = hits.map((h) => h.memory).filter((m) => !criticalIds.has(m.id));
|
|
316
357
|
}
|
|
@@ -362,14 +403,37 @@ export function loadKnowledgeBlock(gsdHomeDir: string, cwd: string): { block: st
|
|
|
362
403
|
}
|
|
363
404
|
|
|
364
405
|
const parts: string[] = [];
|
|
365
|
-
if (globalKnowledge)
|
|
366
|
-
|
|
406
|
+
if (globalKnowledge) {
|
|
407
|
+
parts.push(`## Global Knowledge\nSource: \`${globalKnowledgePath}\`\n\n${globalKnowledge}`);
|
|
408
|
+
}
|
|
409
|
+
if (projectKnowledge) {
|
|
410
|
+
parts.push(`## Project Knowledge\nSource: \`${knowledgePath}\`\n\n${projectKnowledge}`);
|
|
411
|
+
}
|
|
412
|
+
const body = limitKnowledgeBlock(parts.join("\n\n"), getKnowledgeCharLimit());
|
|
367
413
|
return {
|
|
368
|
-
block: `\n\n[KNOWLEDGE — Rules, patterns, and lessons learned]\n\n${
|
|
414
|
+
block: `\n\n[KNOWLEDGE — Rules, patterns, and lessons learned]\n\n${body}`,
|
|
369
415
|
globalSizeKb,
|
|
370
416
|
};
|
|
371
417
|
}
|
|
372
418
|
|
|
419
|
+
function getKnowledgeCharLimit(): number | null {
|
|
420
|
+
const raw = process.env.PI_GSD_KNOWLEDGE_MAX_CHARS;
|
|
421
|
+
if (!raw) return DEFAULT_KNOWLEDGE_MAX_CHARS;
|
|
422
|
+
if (raw === "0") return null;
|
|
423
|
+
const parsed = Number(raw);
|
|
424
|
+
if (!Number.isFinite(parsed) || parsed < MIN_KNOWLEDGE_MAX_CHARS) {
|
|
425
|
+
return DEFAULT_KNOWLEDGE_MAX_CHARS;
|
|
426
|
+
}
|
|
427
|
+
return Math.floor(parsed);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function limitKnowledgeBlock(content: string, limit: number | null): string {
|
|
431
|
+
if (!limit || content.length <= limit) return content;
|
|
432
|
+
const suffix = "\n\n[Knowledge Truncated]\nFull KNOWLEDGE.md content remains available at the source path(s) above; read on demand only if this excerpt lacks a required rule.";
|
|
433
|
+
const headBudget = Math.max(0, limit - suffix.length);
|
|
434
|
+
return `${content.slice(0, headBudget).trimEnd()}${suffix}`;
|
|
435
|
+
}
|
|
436
|
+
|
|
373
437
|
function buildWorktreeContextBlock(): string {
|
|
374
438
|
const worktreeName = getActiveWorktreeName();
|
|
375
439
|
const worktreeMainCwd = getWorktreeOriginalCwd();
|
|
@@ -423,6 +487,11 @@ function buildWorktreeContextBlock(): string {
|
|
|
423
487
|
*/
|
|
424
488
|
const RESUME_INTENT_PATTERNS = /^(continue|resume|ok|go|go ahead|proceed|keep going|carry on|next|yes|yeah|yep|sure|do it|let's go|pick up where you left off)$/;
|
|
425
489
|
|
|
490
|
+
export function isLowEntropyResumePrompt(prompt: string): boolean {
|
|
491
|
+
const trimmed = prompt.trim().toLowerCase().replace(/[.!?,]+$/g, "");
|
|
492
|
+
return RESUME_INTENT_PATTERNS.test(trimmed);
|
|
493
|
+
}
|
|
494
|
+
|
|
426
495
|
async function buildGuidedExecuteContextInjection(prompt: string, basePath: string): Promise<string | null> {
|
|
427
496
|
const ensureStateDbOpen = async () => {
|
|
428
497
|
const { ensureDbOpen } = await import("./dynamic-tools.js");
|
|
@@ -452,8 +521,7 @@ async function buildGuidedExecuteContextInjection(prompt: string, basePath: stri
|
|
|
452
521
|
// control/help/diagnostic prompts with unrelated execution context.
|
|
453
522
|
// Phase-gated: only fire during "executing" to avoid misrouting during
|
|
454
523
|
// replanning, gate evaluation, or other non-execution phases.
|
|
455
|
-
|
|
456
|
-
if (RESUME_INTENT_PATTERNS.test(trimmed)) {
|
|
524
|
+
if (isLowEntropyResumePrompt(prompt)) {
|
|
457
525
|
await ensureStateDbOpen();
|
|
458
526
|
const state = await deriveState(basePath);
|
|
459
527
|
if (state.phase === "executing" && state.activeTask && state.activeMilestone && state.activeSlice) {
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* #2909: Adds a fast-path git status check before milestone completion merges.
|
|
5
5
|
* When the working tree is dirty the user is warned and changes are auto-stashed
|
|
6
|
-
* so the merge can proceed cleanly.
|
|
7
|
-
* restores the stashed changes.
|
|
6
|
+
* so the merge can proceed cleanly. After the merge completes, postflightPopStash
|
|
7
|
+
* restores the stashed changes and reports whether manual recovery is needed.
|
|
8
8
|
*
|
|
9
9
|
* Design constraints (from Trek-e approval):
|
|
10
10
|
* - Warn the user before stashing (no silent surprises)
|
|
11
11
|
* - git stash push / git stash pop only — no custom stash management layer
|
|
12
|
-
* - Stash/pop errors are logged but MUST NOT block the merge
|
|
12
|
+
* - Stash/pop errors are logged but MUST NOT block the merge itself
|
|
13
13
|
* - Fast-path status check — clean trees pay no extra cost
|
|
14
14
|
*/
|
|
15
15
|
|
|
@@ -27,6 +27,13 @@ export interface PreflightResult {
|
|
|
27
27
|
summary: string;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export interface PostflightResult {
|
|
31
|
+
restored: boolean;
|
|
32
|
+
needsManualRecovery: boolean;
|
|
33
|
+
message: string;
|
|
34
|
+
stashRef?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
30
37
|
function findPreflightStashRef(basePath: string, milestoneId: string, stashMarker?: string): string | null {
|
|
31
38
|
const markerPrefix = `gsd-preflight-stash:${milestoneId}:`;
|
|
32
39
|
let fallbackRef: string | null = null;
|
|
@@ -112,14 +119,15 @@ export function preflightCleanRoot(
|
|
|
112
119
|
*
|
|
113
120
|
* Only called when preflightCleanRoot returned stashPushed=true.
|
|
114
121
|
* Any pop error (e.g. conflict) is logged and notified but does NOT throw —
|
|
115
|
-
* the merge already completed successfully.
|
|
122
|
+
* the merge already completed successfully. Callers must treat
|
|
123
|
+
* needsManualRecovery=true as a dirty workspace stop, not a clean completion.
|
|
116
124
|
*/
|
|
117
125
|
export function postflightPopStash(
|
|
118
126
|
basePath: string,
|
|
119
127
|
milestoneId: string,
|
|
120
128
|
stashMarker: string | undefined,
|
|
121
129
|
notify: (message: string, level: "info" | "warning" | "error") => void,
|
|
122
|
-
):
|
|
130
|
+
): PostflightResult {
|
|
123
131
|
let stashRef: string | null = null;
|
|
124
132
|
try {
|
|
125
133
|
stashRef = findPreflightStashRef(basePath, milestoneId, stashMarker);
|
|
@@ -127,7 +135,11 @@ export function postflightPopStash(
|
|
|
127
135
|
const msg = `No matching GSD preflight stash found for milestone ${milestoneId}; leaving stash list untouched.`;
|
|
128
136
|
logWarning("preflight", msg);
|
|
129
137
|
notify(msg, "warning");
|
|
130
|
-
return
|
|
138
|
+
return {
|
|
139
|
+
restored: false,
|
|
140
|
+
needsManualRecovery: true,
|
|
141
|
+
message: msg,
|
|
142
|
+
};
|
|
131
143
|
}
|
|
132
144
|
execFileSync("git", ["stash", "pop", stashRef], {
|
|
133
145
|
cwd: basePath,
|
|
@@ -135,7 +147,14 @@ export function postflightPopStash(
|
|
|
135
147
|
encoding: "utf-8",
|
|
136
148
|
env: GIT_NO_PROMPT_ENV,
|
|
137
149
|
});
|
|
138
|
-
|
|
150
|
+
const msg = `Restored stashed changes after milestone ${milestoneId} merge.`;
|
|
151
|
+
notify(msg, "info");
|
|
152
|
+
return {
|
|
153
|
+
restored: true,
|
|
154
|
+
needsManualRecovery: false,
|
|
155
|
+
message: msg,
|
|
156
|
+
stashRef,
|
|
157
|
+
};
|
|
139
158
|
} catch (err) {
|
|
140
159
|
// Pop conflicts mean the merged code collides with the stashed changes.
|
|
141
160
|
// Log a warning — the user needs to resolve manually, but the merge succeeded.
|
|
@@ -145,5 +164,11 @@ export function postflightPopStash(
|
|
|
145
164
|
const msg = `git stash pop ${stashRef ?? ""}`.trim() + ` failed after merge of milestone ${milestoneId}: ${err instanceof Error ? err.message : String(err)}. ${restoreHint}`;
|
|
146
165
|
logWarning("preflight", msg);
|
|
147
166
|
notify(msg, "warning");
|
|
167
|
+
return {
|
|
168
|
+
restored: false,
|
|
169
|
+
needsManualRecovery: true,
|
|
170
|
+
message: msg,
|
|
171
|
+
...(stashRef ? { stashRef } : {}),
|
|
172
|
+
};
|
|
148
173
|
}
|
|
149
174
|
}
|
|
@@ -26,6 +26,15 @@ import { isAutoActive, checkRemoteAutoSession } from "./auto.js";
|
|
|
26
26
|
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
27
27
|
import { currentDirectoryRoot, projectRoot } from "./commands/context.js";
|
|
28
28
|
import { loadPrompt } from "./prompt-loader.js";
|
|
29
|
+
import {
|
|
30
|
+
buildDoctorHealIssuePayload,
|
|
31
|
+
buildDoctorHealSummary,
|
|
32
|
+
buildWorkflowDispatchContent,
|
|
33
|
+
} from "./workflow-protocol.js";
|
|
34
|
+
import {
|
|
35
|
+
restoreGsdWorkflowTools,
|
|
36
|
+
scopeGsdWorkflowToolsForDispatch,
|
|
37
|
+
} from "./bootstrap/register-hooks.js";
|
|
29
38
|
|
|
30
39
|
const UPDATE_REGISTRY_URL = "https://registry.npmjs.org/gsd-pi/latest";
|
|
31
40
|
const UPDATE_FETCH_TIMEOUT_MS = 5000;
|
|
@@ -72,18 +81,23 @@ export function dispatchDoctorHeal(pi: ExtensionAPI, scope: string | undefined,
|
|
|
72
81
|
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(gsdHome(), "agent", "GSD-WORKFLOW.md");
|
|
73
82
|
const workflow = readFileSync(workflowPath, "utf-8");
|
|
74
83
|
const prompt = loadPrompt("doctor-heal", {
|
|
75
|
-
doctorSummary: reportText,
|
|
76
|
-
structuredIssues,
|
|
84
|
+
doctorSummary: buildDoctorHealSummary(reportText),
|
|
85
|
+
structuredIssues: buildDoctorHealIssuePayload(structuredIssues),
|
|
77
86
|
scopeLabel: scope ?? "active milestone / blocking scope",
|
|
78
87
|
doctorCommandSuffix: scope ? ` ${scope}` : "",
|
|
79
88
|
});
|
|
80
89
|
|
|
81
|
-
const content =
|
|
90
|
+
const content = buildWorkflowDispatchContent({ workflow, workflowPath, task: prompt });
|
|
91
|
+
const savedTools = scopeGsdWorkflowToolsForDispatch(pi);
|
|
82
92
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
93
|
+
try {
|
|
94
|
+
pi.sendMessage(
|
|
95
|
+
{ customType: "gsd-doctor-heal", content, display: false },
|
|
96
|
+
{ triggerTurn: true },
|
|
97
|
+
);
|
|
98
|
+
} finally {
|
|
99
|
+
restoreGsdWorkflowTools(pi, savedTools);
|
|
100
|
+
}
|
|
87
101
|
}
|
|
88
102
|
|
|
89
103
|
/** Parse doctor command args into structured flags and positionals (pure, no I/O). */
|
|
@@ -258,15 +272,20 @@ export async function handleTriage(ctx: ExtensionCommandContext, pi: ExtensionAP
|
|
|
258
272
|
|
|
259
273
|
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(gsdHome(), "agent", "GSD-WORKFLOW.md");
|
|
260
274
|
const workflow = readFileSync(workflowPath, "utf-8");
|
|
275
|
+
const savedTools = scopeGsdWorkflowToolsForDispatch(pi);
|
|
261
276
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
277
|
+
try {
|
|
278
|
+
pi.sendMessage(
|
|
279
|
+
{
|
|
280
|
+
customType: "gsd-triage",
|
|
281
|
+
content: buildWorkflowDispatchContent({ workflow, workflowPath, task: prompt }),
|
|
282
|
+
display: false,
|
|
283
|
+
},
|
|
284
|
+
{ triggerTurn: true },
|
|
285
|
+
);
|
|
286
|
+
} finally {
|
|
287
|
+
restoreGsdWorkflowTools(pi, savedTools);
|
|
288
|
+
}
|
|
270
289
|
}
|
|
271
290
|
|
|
272
291
|
export async function handleSteer(change: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
|
|
@@ -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),
|
|
@@ -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
|
}
|
|
@@ -204,6 +204,9 @@ export function createGSDExtensionAPI(
|
|
|
204
204
|
getAllTools: () => pi.getAllTools(),
|
|
205
205
|
setActiveTools: (...args: Parameters<ExtensionAPI["setActiveTools"]>) =>
|
|
206
206
|
pi.setActiveTools(...args),
|
|
207
|
+
getVisibleSkills: () => pi.getVisibleSkills(),
|
|
208
|
+
setVisibleSkills: (...args: Parameters<ExtensionAPI["setVisibleSkills"]>) =>
|
|
209
|
+
pi.setVisibleSkills(...args),
|
|
207
210
|
getCommands: () => pi.getCommands(),
|
|
208
211
|
|
|
209
212
|
// ── Model & thinking ───────────────────────────────────────────────
|
|
@@ -14,6 +14,7 @@ import { isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
|
|
|
14
14
|
import { gsdRoot } from "./paths.js";
|
|
15
15
|
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
16
16
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
17
|
+
import { logWarning } from "./workflow-logger.js";
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
import {
|
|
@@ -722,16 +723,53 @@ export class GitServiceImpl {
|
|
|
722
723
|
if (keyFiles.length === 0) return false;
|
|
723
724
|
|
|
724
725
|
const allExclusions = [...RUNTIME_EXCLUSION_PATHS, ...extraExclusions];
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
726
|
+
const normalized = keyFiles
|
|
727
|
+
.map(file => normalizeRepoRelativePath(this.basePath, file))
|
|
728
|
+
.filter((file): file is string => file !== null)
|
|
729
|
+
.filter(file => !isExcludedScopedPath(file, allExclusions));
|
|
730
|
+
|
|
731
|
+
// Drop entries that don't exist on disk. The LLM occasionally lists files
|
|
732
|
+
// it intended to write but didn't (or names them with wrong casing/path).
|
|
733
|
+
// Pre-`b304f738b` `git add -A` swallowed these silently; the scoped
|
|
734
|
+
// pathspec form passes each path explicitly, so a single bad entry made
|
|
735
|
+
// the whole commit fail (see #5500). Filter so valid paths still commit.
|
|
736
|
+
const missing: string[] = [];
|
|
737
|
+
const existing: string[] = [];
|
|
738
|
+
for (const path of normalized) {
|
|
739
|
+
if (existsSync(join(this.basePath, path))) {
|
|
740
|
+
existing.push(path);
|
|
741
|
+
} else {
|
|
742
|
+
missing.push(path);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
if (missing.length > 0) {
|
|
746
|
+
logWarning(
|
|
747
|
+
"engine",
|
|
748
|
+
`scoped stage: dropping ${missing.length} non-existent keyFile(s) from task commit: ${missing.join(", ")}`,
|
|
749
|
+
{ file: "git-service.ts" },
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const paths = Array.from(new Set(existing));
|
|
731
754
|
if (paths.length === 0) return false;
|
|
732
755
|
|
|
733
|
-
|
|
734
|
-
|
|
756
|
+
try {
|
|
757
|
+
nativeAddPaths(this.basePath, paths);
|
|
758
|
+
return true;
|
|
759
|
+
} catch (err) {
|
|
760
|
+
// Defense-in-depth: even after existence filtering, libgit2/git can
|
|
761
|
+
// still reject paths (gitignore matches, case-only differences on
|
|
762
|
+
// case-insensitive FS, submodule boundaries). Returning false lets
|
|
763
|
+
// autoCommit fall through to smartStage so the commit still goes out
|
|
764
|
+
// — restoring the resilience the unscoped path used to provide.
|
|
765
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
766
|
+
logWarning(
|
|
767
|
+
"engine",
|
|
768
|
+
`scoped stage failed (${msg}); falling back to smartStage`,
|
|
769
|
+
{ file: "git-service.ts" },
|
|
770
|
+
);
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
735
773
|
}
|
|
736
774
|
|
|
737
775
|
/** Tracks whether runtime file cleanup has run this session. */
|