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: Registers GSD extension runtime hooks and token-saving tool policies.
|
|
1
3
|
import { join } from "node:path";
|
|
2
4
|
import { isToolCallEventType } from "@gsd/pi-coding-agent";
|
|
3
5
|
import { updateSnapshot } from "../ecosystem/gsd-extension-api.js";
|
|
@@ -19,10 +21,149 @@ import { initNotificationWidget } from "../notification-widget.js";
|
|
|
19
21
|
import { resolveWorktreeProjectRoot } from "../worktree-root.js";
|
|
20
22
|
import { extractSubagentAgentClasses } from "./subagent-input.js";
|
|
21
23
|
import { approvalGateIdForUnit, isExplicitApprovalResponse, shouldPauseForUserApprovalQuestion } from "../user-input-boundary.js";
|
|
24
|
+
import { resolveSkillManifest } from "../skill-manifest.js";
|
|
22
25
|
// Skip the welcome screen on the very first session_start — cli.ts already
|
|
23
26
|
// printed it before the TUI launched. Only re-print on /clear (subsequent sessions).
|
|
24
27
|
let isFirstSession = true;
|
|
25
28
|
let approvalQuestionAbortInFlight = false;
|
|
29
|
+
let deferredApprovalGate = null;
|
|
30
|
+
export const MINIMAL_GSD_TOOL_NAMES = [
|
|
31
|
+
"gsd_exec",
|
|
32
|
+
"gsd_exec_search",
|
|
33
|
+
"gsd_resume",
|
|
34
|
+
"gsd_milestone_status",
|
|
35
|
+
"gsd_checkpoint_db",
|
|
36
|
+
"memory_query",
|
|
37
|
+
"capture_thought",
|
|
38
|
+
];
|
|
39
|
+
export const MINIMAL_AUTO_BASE_TOOL_NAMES = [
|
|
40
|
+
"ask_user_questions",
|
|
41
|
+
"bash",
|
|
42
|
+
"bg_shell",
|
|
43
|
+
"edit",
|
|
44
|
+
"glob",
|
|
45
|
+
"grep",
|
|
46
|
+
"ls",
|
|
47
|
+
"read",
|
|
48
|
+
"write",
|
|
49
|
+
];
|
|
50
|
+
const AUTO_UNIT_SCOPED_TOOLS = {
|
|
51
|
+
"research-milestone": ["gsd_summary_save", "gsd_decision_save"],
|
|
52
|
+
"plan-milestone": ["gsd_plan_milestone", "gsd_decision_save", "gsd_requirement_update"],
|
|
53
|
+
"discuss-milestone": ["gsd_summary_save", "gsd_decision_save", "gsd_requirement_save"],
|
|
54
|
+
"validate-milestone": ["gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
|
|
55
|
+
"complete-milestone": ["gsd_complete_milestone", "subagent"],
|
|
56
|
+
"research-slice": ["gsd_summary_save", "gsd_decision_save"],
|
|
57
|
+
"plan-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
|
|
58
|
+
"refine-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
|
|
59
|
+
"replan-slice": ["gsd_replan_slice", "gsd_plan_task", "gsd_decision_save"],
|
|
60
|
+
"complete-slice": ["gsd_slice_complete", "gsd_decision_save", "gsd_requirement_update", "subagent"],
|
|
61
|
+
"reassess-roadmap": ["gsd_reassess_roadmap"],
|
|
62
|
+
"execute-task": ["gsd_task_complete", "gsd_decision_save"],
|
|
63
|
+
"execute-task-simple": ["gsd_task_complete", "gsd_decision_save"],
|
|
64
|
+
"reactive-execute": ["gsd_task_complete", "gsd_decision_save"],
|
|
65
|
+
"run-uat": ["gsd_summary_save"],
|
|
66
|
+
"gate-evaluate": ["gsd_save_gate_result"],
|
|
67
|
+
"rewrite-docs": ["gsd_summary_save", "gsd_decision_save"],
|
|
68
|
+
"workflow-preferences": ["gsd_summary_save"],
|
|
69
|
+
"discuss-project": ["gsd_summary_save", "gsd_decision_save", "gsd_requirement_save"],
|
|
70
|
+
"discuss-requirements": ["gsd_requirement_save", "gsd_summary_save"],
|
|
71
|
+
"research-decision": ["gsd_summary_save"],
|
|
72
|
+
"research-project": ["gsd_summary_save", "gsd_decision_save"],
|
|
73
|
+
};
|
|
74
|
+
const WORKFLOW_GSD_TOOL_NAMES = [
|
|
75
|
+
...MINIMAL_GSD_TOOL_NAMES,
|
|
76
|
+
...Object.values(AUTO_UNIT_SCOPED_TOOLS).flat(),
|
|
77
|
+
].filter(isGsdManagedTool);
|
|
78
|
+
function isGsdManagedTool(name) {
|
|
79
|
+
return name.startsWith("gsd_") || name === "memory_query" || name === "capture_thought" || name === "gsd_graph";
|
|
80
|
+
}
|
|
81
|
+
export function buildMinimalGsdToolSet(activeToolNames) {
|
|
82
|
+
const active = new Set(activeToolNames);
|
|
83
|
+
const preserved = activeToolNames.filter((name) => !isGsdManagedTool(name));
|
|
84
|
+
const minimal = MINIMAL_GSD_TOOL_NAMES.filter((name) => active.has(name));
|
|
85
|
+
return [...new Set([...preserved, ...minimal])];
|
|
86
|
+
}
|
|
87
|
+
export function buildMinimalAutoGsdToolSet(activeToolNames, unitType) {
|
|
88
|
+
const active = new Set(activeToolNames);
|
|
89
|
+
const unitTools = unitType ? AUTO_UNIT_SCOPED_TOOLS[unitType] ?? [] : [];
|
|
90
|
+
const autoBaseTools = new Set(MINIMAL_AUTO_BASE_TOOL_NAMES);
|
|
91
|
+
const preserved = activeToolNames.filter((name) => autoBaseTools.has(name));
|
|
92
|
+
const scoped = [...MINIMAL_GSD_TOOL_NAMES, ...unitTools].filter((name) => active.has(name));
|
|
93
|
+
return [...new Set([...preserved, ...scoped])];
|
|
94
|
+
}
|
|
95
|
+
export function buildMinimalGsdWorkflowToolSet(activeToolNames) {
|
|
96
|
+
const active = new Set(activeToolNames);
|
|
97
|
+
const autoBaseTools = new Set(MINIMAL_AUTO_BASE_TOOL_NAMES);
|
|
98
|
+
const preserved = activeToolNames.filter((name) => autoBaseTools.has(name));
|
|
99
|
+
const scoped = WORKFLOW_GSD_TOOL_NAMES.filter((name) => active.has(name));
|
|
100
|
+
return [...new Set([...preserved, ...scoped])];
|
|
101
|
+
}
|
|
102
|
+
export function buildRequestScopedGsdToolSet(activeToolNames, requestCustomMessages) {
|
|
103
|
+
for (let index = (requestCustomMessages?.length ?? 0) - 1; index >= 0; index--) {
|
|
104
|
+
const currentCustomType = requestCustomMessages?.[index]?.customType;
|
|
105
|
+
if (currentCustomType === "gsd-run" ||
|
|
106
|
+
currentCustomType === "gsd-discuss" ||
|
|
107
|
+
currentCustomType === "gsd-doctor-heal" ||
|
|
108
|
+
currentCustomType === "gsd-triage") {
|
|
109
|
+
return buildMinimalGsdWorkflowToolSet(activeToolNames);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
export function isFullGsdToolSurfaceRequested() {
|
|
115
|
+
return process.env.PI_GSD_FULL_TOOLS === "1";
|
|
116
|
+
}
|
|
117
|
+
function isGeneralGsdToolScopingRequested() {
|
|
118
|
+
return process.env.PI_GSD_MINIMAL_TOOLS === "1";
|
|
119
|
+
}
|
|
120
|
+
function applyMinimalGsdToolSurface(pi) {
|
|
121
|
+
if (isFullGsdToolSurfaceRequested())
|
|
122
|
+
return;
|
|
123
|
+
const dash = getAutoRuntimeSnapshot();
|
|
124
|
+
if (dash.active && dash.currentUnit) {
|
|
125
|
+
pi.setActiveTools(buildMinimalAutoGsdToolSet(pi.getActiveTools(), dash.currentUnit.type));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!isGeneralGsdToolScopingRequested())
|
|
129
|
+
return;
|
|
130
|
+
pi.setActiveTools(buildMinimalGsdToolSet(pi.getActiveTools()));
|
|
131
|
+
}
|
|
132
|
+
export function scopeGsdWorkflowToolsForDispatch(pi, unitType) {
|
|
133
|
+
if (isFullGsdToolSurfaceRequested())
|
|
134
|
+
return null;
|
|
135
|
+
const current = pi.getActiveTools();
|
|
136
|
+
const scoped = unitType
|
|
137
|
+
? buildMinimalAutoGsdToolSet(current, unitType)
|
|
138
|
+
: buildMinimalGsdWorkflowToolSet(current);
|
|
139
|
+
const toolsChanged = !(scoped.length === current.length && scoped.every((name, index) => name === current[index]));
|
|
140
|
+
const skillManifest = resolveSkillManifest(unitType);
|
|
141
|
+
const canScopeSkills = skillManifest !== null && pi.getVisibleSkills && pi.setVisibleSkills;
|
|
142
|
+
if (!toolsChanged && !canScopeSkills) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
if (toolsChanged) {
|
|
146
|
+
pi.setActiveTools(scoped);
|
|
147
|
+
}
|
|
148
|
+
const visibleSkills = canScopeSkills ? pi.getVisibleSkills() : undefined;
|
|
149
|
+
if (canScopeSkills) {
|
|
150
|
+
pi.setVisibleSkills(skillManifest);
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
tools: toolsChanged ? current : null,
|
|
154
|
+
visibleSkills,
|
|
155
|
+
restoreVisibleSkills: Boolean(canScopeSkills),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export function restoreGsdWorkflowTools(pi, savedState) {
|
|
159
|
+
if (!savedState)
|
|
160
|
+
return;
|
|
161
|
+
if (savedState.tools)
|
|
162
|
+
pi.setActiveTools(savedState.tools);
|
|
163
|
+
if (savedState.restoreVisibleSkills && pi.setVisibleSkills) {
|
|
164
|
+
pi.setVisibleSkills(savedState.visibleSkills);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
26
167
|
async function deriveGsdState(basePath) {
|
|
27
168
|
const { deriveState } = await import("../state.js");
|
|
28
169
|
return deriveState(basePath);
|
|
@@ -71,11 +212,51 @@ async function applyCompactionThresholdOverride(ctx) {
|
|
|
71
212
|
// Non-fatal: leave any existing override in place.
|
|
72
213
|
}
|
|
73
214
|
}
|
|
74
|
-
|
|
75
|
-
|
|
215
|
+
function clearDeferredApprovalGate(basePath) {
|
|
216
|
+
if (!basePath || deferredApprovalGate?.basePath === basePath) {
|
|
217
|
+
deferredApprovalGate = null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function deferApprovalGate(gateId, basePath) {
|
|
221
|
+
deferredApprovalGate = { gateId, basePath };
|
|
222
|
+
}
|
|
223
|
+
function contextBasePath(ctx) {
|
|
224
|
+
return typeof ctx?.cwd === "string" ? ctx.cwd : process.cwd();
|
|
225
|
+
}
|
|
226
|
+
function activateDeferredApprovalGate(basePath) {
|
|
227
|
+
if (deferredApprovalGate?.basePath !== basePath)
|
|
228
|
+
return;
|
|
229
|
+
setPendingGate(deferredApprovalGate.gateId, basePath);
|
|
230
|
+
deferredApprovalGate = null;
|
|
231
|
+
}
|
|
232
|
+
function isContextDraftSummarySave(toolName, input) {
|
|
233
|
+
if (toolName !== "gsd_summary_save" && toolName !== "summary_save")
|
|
234
|
+
return false;
|
|
235
|
+
if (!input || typeof input !== "object")
|
|
236
|
+
return false;
|
|
237
|
+
return input.artifact_type === "CONTEXT-DRAFT";
|
|
238
|
+
}
|
|
239
|
+
function shouldBlockDeferredApprovalTool(toolName, input, basePath) {
|
|
240
|
+
if (deferredApprovalGate?.basePath !== basePath)
|
|
241
|
+
return { block: false };
|
|
242
|
+
if (toolName === "ask_user_questions")
|
|
243
|
+
return { block: false };
|
|
244
|
+
if (isContextDraftSummarySave(toolName, input))
|
|
245
|
+
return { block: false };
|
|
246
|
+
return {
|
|
247
|
+
block: true,
|
|
248
|
+
reason: [
|
|
249
|
+
`HARD BLOCK: Approval question "${deferredApprovalGate.gateId}" has been shown to the user.`,
|
|
250
|
+
`Only CONTEXT-DRAFT persistence may finish in this same assistant turn.`,
|
|
251
|
+
`Wait for the user's answer before calling additional tools.`,
|
|
252
|
+
].join(" "),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
export function resolveNotificationStoreBasePath(basePath) {
|
|
256
|
+
return resolveWorktreeProjectRoot(basePath);
|
|
76
257
|
}
|
|
77
258
|
function initSessionNotifications(ctx) {
|
|
78
|
-
initNotificationStore(resolveNotificationStoreBasePath());
|
|
259
|
+
initNotificationStore(resolveNotificationStoreBasePath(contextBasePath(ctx)));
|
|
79
260
|
installNotifyInterceptor(ctx);
|
|
80
261
|
initNotificationWidget(ctx);
|
|
81
262
|
}
|
|
@@ -109,28 +290,30 @@ async function writeContextModeCompactionSnapshot(basePath) {
|
|
|
109
290
|
}
|
|
110
291
|
export function registerHooks(pi, ecosystemHandlers) {
|
|
111
292
|
pi.on("session_start", async (_event, ctx) => {
|
|
293
|
+
const basePath = contextBasePath(ctx);
|
|
112
294
|
initSessionNotifications(ctx);
|
|
113
295
|
if (!isAutoActive()) {
|
|
114
296
|
const { initHealthWidget } = await import("../health-widget.js");
|
|
115
297
|
initHealthWidget(ctx);
|
|
116
298
|
}
|
|
117
|
-
resetWriteGateState(
|
|
299
|
+
resetWriteGateState(basePath);
|
|
118
300
|
resetToolCallLoopGuard();
|
|
119
301
|
approvalQuestionAbortInFlight = false;
|
|
302
|
+
clearDeferredApprovalGate();
|
|
120
303
|
await resetAskUserQuestionsTurnCache();
|
|
121
304
|
await syncServiceTierStatus(ctx);
|
|
122
305
|
await applyDisabledModelProviderPolicy(ctx);
|
|
123
306
|
await applyCompactionThresholdOverride(ctx);
|
|
124
307
|
// Skip MCP auto-prep when running inside an auto-worktree (see session_switch below).
|
|
125
308
|
const { isInAutoWorktree } = await import("../auto-worktree.js");
|
|
126
|
-
if (!isInAutoWorktree(
|
|
309
|
+
if (!isInAutoWorktree(basePath)) {
|
|
127
310
|
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
128
|
-
prepareWorkflowMcpForProject(ctx,
|
|
311
|
+
prepareWorkflowMcpForProject(ctx, basePath);
|
|
129
312
|
}
|
|
130
313
|
// Apply show_token_cost preference (#1515)
|
|
131
314
|
try {
|
|
132
315
|
const { loadEffectiveGSDPreferences } = await import("../preferences.js");
|
|
133
|
-
const prefs = loadEffectiveGSDPreferences();
|
|
316
|
+
const prefs = loadEffectiveGSDPreferences(basePath);
|
|
134
317
|
process.env.GSD_SHOW_TOKEN_COST = prefs?.preferences.show_token_cost ? "1" : "";
|
|
135
318
|
}
|
|
136
319
|
catch { /* non-fatal */ }
|
|
@@ -162,11 +345,13 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
162
345
|
}
|
|
163
346
|
});
|
|
164
347
|
pi.on("session_switch", async (_event, ctx) => {
|
|
348
|
+
const basePath = contextBasePath(ctx);
|
|
165
349
|
initSessionNotifications(ctx);
|
|
166
|
-
resetWriteGateState(
|
|
350
|
+
resetWriteGateState(basePath);
|
|
167
351
|
resetToolCallLoopGuard();
|
|
352
|
+
clearDeferredApprovalGate();
|
|
168
353
|
await resetAskUserQuestionsTurnCache();
|
|
169
|
-
clearDiscussionFlowState(
|
|
354
|
+
clearDiscussionFlowState(basePath);
|
|
170
355
|
await syncServiceTierStatus(ctx);
|
|
171
356
|
await applyDisabledModelProviderPolicy(ctx);
|
|
172
357
|
await applyCompactionThresholdOverride(ctx);
|
|
@@ -175,9 +360,9 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
175
360
|
// post-chdir rewrites the file mid-run (non-idempotent due to cwd-relative
|
|
176
361
|
// CLI path resolution), dirtying the tree and breaking the milestone merge.
|
|
177
362
|
const { isInAutoWorktree } = await import("../auto-worktree.js");
|
|
178
|
-
if (!isInAutoWorktree(
|
|
363
|
+
if (!isInAutoWorktree(basePath)) {
|
|
179
364
|
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
180
|
-
prepareWorkflowMcpForProject(ctx,
|
|
365
|
+
prepareWorkflowMcpForProject(ctx, basePath);
|
|
181
366
|
}
|
|
182
367
|
await loadToolApiKeysForSession();
|
|
183
368
|
if (!isAutoActive()) {
|
|
@@ -189,10 +374,11 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
189
374
|
}
|
|
190
375
|
});
|
|
191
376
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
377
|
+
applyMinimalGsdToolSurface(pi);
|
|
192
378
|
// Wait for ecosystem loader to finish (no-op after first turn).
|
|
193
379
|
const { getEcosystemReadyPromise } = await import("../ecosystem/loader.js");
|
|
194
380
|
await getEcosystemReadyPromise();
|
|
195
|
-
const beforeAgentBasePath =
|
|
381
|
+
const beforeAgentBasePath = contextBasePath(ctx);
|
|
196
382
|
const pendingApprovalGate = getPendingGate(beforeAgentBasePath);
|
|
197
383
|
if (pendingApprovalGate && isExplicitApprovalResponse(event.prompt, pendingApprovalGate)) {
|
|
198
384
|
markApprovalGateVerified(pendingApprovalGate, beforeAgentBasePath);
|
|
@@ -201,13 +387,14 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
201
387
|
markDepthVerified(milestoneId, beforeAgentBasePath);
|
|
202
388
|
clearPendingGate(beforeAgentBasePath);
|
|
203
389
|
}
|
|
390
|
+
clearDeferredApprovalGate(beforeAgentBasePath);
|
|
204
391
|
// GSD's own context injection (existing behavior — unchanged).
|
|
205
392
|
const { buildBeforeAgentStartResult } = await import("./system-context.js");
|
|
206
393
|
const gsdResult = await buildBeforeAgentStartResult(event, ctx);
|
|
207
394
|
// Refresh the snapshot used by ecosystem getPhase()/getActiveUnit().
|
|
208
395
|
// deriveState has its own ~100ms cache so this is cheap on repeat calls.
|
|
209
396
|
try {
|
|
210
|
-
const state = await deriveGsdState(
|
|
397
|
+
const state = await deriveGsdState(beforeAgentBasePath);
|
|
211
398
|
updateSnapshot(state);
|
|
212
399
|
}
|
|
213
400
|
catch {
|
|
@@ -244,7 +431,12 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
244
431
|
resetToolCallLoopGuard();
|
|
245
432
|
await resetAskUserQuestionsTurnCache();
|
|
246
433
|
const { handleAgentEnd } = await import("./agent-end-recovery.js");
|
|
247
|
-
|
|
434
|
+
try {
|
|
435
|
+
await handleAgentEnd(pi, event, ctx);
|
|
436
|
+
}
|
|
437
|
+
finally {
|
|
438
|
+
activateDeferredApprovalGate(contextBasePath(ctx));
|
|
439
|
+
}
|
|
248
440
|
});
|
|
249
441
|
// Squash-merge quick-task branch back to the original branch after the
|
|
250
442
|
// agent turn completes (#2668). cleanupQuickBranch is a no-op when no
|
|
@@ -258,8 +450,8 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
258
450
|
// Best-effort: don't break the turn lifecycle if cleanup fails.
|
|
259
451
|
}
|
|
260
452
|
});
|
|
261
|
-
pi.on("session_before_compact", async () => {
|
|
262
|
-
const basePath =
|
|
453
|
+
pi.on("session_before_compact", async (_event, ctx) => {
|
|
454
|
+
const basePath = contextBasePath(ctx);
|
|
263
455
|
// Context Mode is default-on. Write the resumable snapshot before any
|
|
264
456
|
// active-auto cancel return so auto sessions still leave re-entry context.
|
|
265
457
|
await writeContextModeCompactionSnapshot(basePath);
|
|
@@ -323,7 +515,7 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
323
515
|
if (!unitType) {
|
|
324
516
|
try {
|
|
325
517
|
const { getPendingDeepProjectSetupUnitForContext } = await import("../guided-flow.js");
|
|
326
|
-
const pending = getPendingDeepProjectSetupUnitForContext(ctx,
|
|
518
|
+
const pending = getPendingDeepProjectSetupUnitForContext(ctx, contextBasePath(ctx));
|
|
327
519
|
unitType = pending?.unitType;
|
|
328
520
|
unitId = pending?.unitId;
|
|
329
521
|
}
|
|
@@ -332,7 +524,7 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
332
524
|
}
|
|
333
525
|
}
|
|
334
526
|
if (!unitType) {
|
|
335
|
-
const milestoneId = await getDiscussionMilestoneIdFor(
|
|
527
|
+
const milestoneId = await getDiscussionMilestoneIdFor(contextBasePath(ctx));
|
|
336
528
|
if (milestoneId) {
|
|
337
529
|
unitType = "discuss-milestone";
|
|
338
530
|
unitId = milestoneId;
|
|
@@ -342,11 +534,12 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
342
534
|
return;
|
|
343
535
|
const gateId = approvalGateIdForUnit(unitType, unitId);
|
|
344
536
|
if (gateId)
|
|
345
|
-
|
|
537
|
+
deferApprovalGate(gateId, contextBasePath(ctx));
|
|
346
538
|
approvalQuestionAbortInFlight = true;
|
|
347
539
|
ctx.ui.notify(`${unitType}${unitId ? ` ${unitId}` : ""} is waiting for your approval - pausing before more tool calls run.`, "info");
|
|
348
|
-
// The pending gate
|
|
349
|
-
//
|
|
540
|
+
// The durable pending gate is activated at agent_end so same-turn
|
|
541
|
+
// CONTEXT-DRAFT persistence can finish after the text boundary streams.
|
|
542
|
+
// The tool_call hook below still blocks non-draft tools in this turn.
|
|
350
543
|
// Aborting mid-stream eats the model's question text on external CLI
|
|
351
544
|
// providers (Claude Code SDK) because lastTextContent isn't populated
|
|
352
545
|
// from in-flight builder state — the user only ever sees "Claude Code
|
|
@@ -356,7 +549,7 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
356
549
|
const { isParallelActive, shutdownParallel } = await import("../parallel-orchestrator.js");
|
|
357
550
|
if (isParallelActive()) {
|
|
358
551
|
try {
|
|
359
|
-
await shutdownParallel(
|
|
552
|
+
await shutdownParallel(contextBasePath(ctx));
|
|
360
553
|
}
|
|
361
554
|
catch {
|
|
362
555
|
// best-effort
|
|
@@ -369,14 +562,17 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
369
562
|
saveActivityLog(ctx, dash.basePath, dash.currentUnit.type, dash.currentUnit.id);
|
|
370
563
|
}
|
|
371
564
|
});
|
|
372
|
-
pi.on("tool_call", async (event) => {
|
|
373
|
-
const discussionBasePath =
|
|
565
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
566
|
+
const discussionBasePath = contextBasePath(ctx);
|
|
374
567
|
const toolName = canonicalToolName(event.toolName);
|
|
375
568
|
// ── Loop guard: block repeated identical tool calls ──
|
|
376
569
|
const loopCheck = checkToolCallLoop(toolName, event.input);
|
|
377
570
|
if (loopCheck.block) {
|
|
378
571
|
return { block: true, reason: loopCheck.reason };
|
|
379
572
|
}
|
|
573
|
+
const deferredGateGuard = shouldBlockDeferredApprovalTool(toolName, event.input, discussionBasePath);
|
|
574
|
+
if (deferredGateGuard.block)
|
|
575
|
+
return deferredGateGuard;
|
|
380
576
|
// ── Discussion gate enforcement: track pending gate questions ─────────
|
|
381
577
|
// Only gate-shaped ask_user_questions calls should block execution.
|
|
382
578
|
// The gate stays pending until the user selects the approval option.
|
|
@@ -515,7 +711,7 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
515
711
|
}
|
|
516
712
|
}
|
|
517
713
|
});
|
|
518
|
-
pi.on("tool_result", async (event) => {
|
|
714
|
+
pi.on("tool_result", async (event, ctx) => {
|
|
519
715
|
if (isAutoActive() && typeof event.toolCallId === "string") {
|
|
520
716
|
markToolEnd(event.toolCallId);
|
|
521
717
|
}
|
|
@@ -535,7 +731,7 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
535
731
|
const toolName = canonicalToolName(event.toolName);
|
|
536
732
|
if (toolName !== "ask_user_questions")
|
|
537
733
|
return;
|
|
538
|
-
const basePath =
|
|
734
|
+
const basePath = contextBasePath(ctx);
|
|
539
735
|
const milestoneId = await getDiscussionMilestoneIdFor(basePath);
|
|
540
736
|
const queueActive = isQueuePhaseActive(basePath);
|
|
541
737
|
const details = event.details;
|
|
@@ -737,8 +933,22 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
737
933
|
// Tool set adaptation hook (ADR-005 Phase 4)
|
|
738
934
|
// Extensions can override tool set after model selection by returning { toolNames: [...] }
|
|
739
935
|
// Return undefined to let the built-in provider compatibility filtering proceed.
|
|
740
|
-
pi.on("adjust_tool_set", async (
|
|
741
|
-
|
|
936
|
+
pi.on("adjust_tool_set", async (event) => {
|
|
937
|
+
if (isFullGsdToolSurfaceRequested())
|
|
938
|
+
return undefined;
|
|
939
|
+
const removed = new Set(event.filteredTools);
|
|
940
|
+
const providerCompatible = event.activeToolNames.filter((name) => !removed.has(name));
|
|
941
|
+
const requestScoped = buildRequestScopedGsdToolSet(providerCompatible, event.requestCustomMessages);
|
|
942
|
+
if (requestScoped) {
|
|
943
|
+
return { toolNames: requestScoped };
|
|
944
|
+
}
|
|
945
|
+
const dash = getAutoRuntimeSnapshot();
|
|
946
|
+
if (dash.active && dash.currentUnit) {
|
|
947
|
+
return { toolNames: buildMinimalAutoGsdToolSet(providerCompatible, dash.currentUnit.type) };
|
|
948
|
+
}
|
|
949
|
+
if (isGeneralGsdToolScopingRequested()) {
|
|
950
|
+
return { toolNames: buildMinimalGsdToolSet(providerCompatible) };
|
|
951
|
+
}
|
|
742
952
|
return undefined;
|
|
743
953
|
});
|
|
744
954
|
}
|
|
@@ -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
|
import { logWarning } from "../workflow-logger.js";
|
|
@@ -17,6 +19,11 @@ import { formatOverridesSection, formatShortcut, loadActiveOverrides, loadFile,
|
|
|
17
19
|
import { toPosixPath } from "../../shared/mod.js";
|
|
18
20
|
import { autoEnableCmuxPreferences } from "../commands-cmux.js";
|
|
19
21
|
import { gsdHome } from "../gsd-home.js";
|
|
22
|
+
const DEFAULT_CONTEXT_MESSAGE_MAX_CHARS = 4_000;
|
|
23
|
+
const DEFAULT_KNOWLEDGE_MAX_CHARS = 12_000;
|
|
24
|
+
const DEFAULT_CODEBASE_MAX_CHARS = 8_000;
|
|
25
|
+
const MIN_CONTEXT_MESSAGE_MAX_CHARS = 1_000;
|
|
26
|
+
const MIN_KNOWLEDGE_MAX_CHARS = 1_000;
|
|
20
27
|
/**
|
|
21
28
|
* Bundled skill triggers — resolved dynamically at runtime instead of
|
|
22
29
|
* hardcoding absolute paths in the system prompt template. Only skills
|
|
@@ -135,7 +142,6 @@ export async function buildBeforeAgentStartResult(event, ctx) {
|
|
|
135
142
|
catch (e) {
|
|
136
143
|
logWarning("bootstrap", `decisions backfill failed: ${e.message}`);
|
|
137
144
|
}
|
|
138
|
-
const memoryBlock = await loadMemoryBlock(event.prompt ?? "");
|
|
139
145
|
let newSkillsBlock = "";
|
|
140
146
|
if (hasSkillSnapshot()) {
|
|
141
147
|
const newSkills = detectNewSkills();
|
|
@@ -165,11 +171,10 @@ export async function buildBeforeAgentStartResult(event, ctx) {
|
|
|
165
171
|
if (rawContent) {
|
|
166
172
|
// Cap injection size to ~2 000 tokens to avoid bloating every request.
|
|
167
173
|
// Full map is always available at .gsd/CODEBASE.md.
|
|
168
|
-
const MAX_CODEBASE_CHARS = 8_000;
|
|
169
174
|
const generatedMatch = rawContent.match(/Generated: (\S+)/);
|
|
170
175
|
const generatedAt = generatedMatch?.[1] ?? "unknown";
|
|
171
|
-
const content = rawContent.length >
|
|
172
|
-
? rawContent.slice(0,
|
|
176
|
+
const content = rawContent.length > DEFAULT_CODEBASE_MAX_CHARS
|
|
177
|
+
? rawContent.slice(0, DEFAULT_CODEBASE_MAX_CHARS) + "\n\n*(truncated — see .gsd/CODEBASE.md for full map)*"
|
|
173
178
|
: rawContent;
|
|
174
179
|
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}`;
|
|
175
180
|
}
|
|
@@ -180,6 +185,9 @@ export async function buildBeforeAgentStartResult(event, ctx) {
|
|
|
180
185
|
}
|
|
181
186
|
warnDeprecatedAgentInstructions();
|
|
182
187
|
const injection = await buildGuidedExecuteContextInjection(event.prompt, process.cwd());
|
|
188
|
+
const memoryBlock = await loadMemoryBlock(event.prompt ?? "", {
|
|
189
|
+
includePromptRelevant: !(injection && isLowEntropyResumePrompt(event.prompt ?? "")),
|
|
190
|
+
});
|
|
183
191
|
// Re-inject forensics context on follow-up turns (#2941)
|
|
184
192
|
const forensicsInjection = !injection ? buildForensicsContextInjection(process.cwd(), event.prompt) : null;
|
|
185
193
|
const worktreeBlock = buildWorktreeContextBlock();
|
|
@@ -188,12 +196,9 @@ export async function buildBeforeAgentStartResult(event, ctx) {
|
|
|
188
196
|
? `\n\n## Subagent Model\n\nWhen spawning subagents via the \`subagent\` tool, always pass \`model: "${subagentModelConfig.primary}"\` in the tool call parameters. Never omit this — always specify it explicitly.`
|
|
189
197
|
: "";
|
|
190
198
|
// memoryBlock is FTS-queried against the user prompt and changes per call.
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
// memoryBlock injected via the context message may itself be cached up to
|
|
195
|
-
// that boundary; that's orthogonal and unchanged from prior behavior. The
|
|
196
|
-
// load-bearing win here is preserving the system+tools cache hit. (#5019)
|
|
199
|
+
// Keeping it out of `fullSystem` preserves provider prompt-cache stability
|
|
200
|
+
// for the static system/tool prefix. The dynamic memory block rides the
|
|
201
|
+
// volatile context message instead. (#5019)
|
|
197
202
|
const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${codebaseBlock}${newSkillsBlock}${worktreeBlock}${subagentModelBlock}`;
|
|
198
203
|
stopContextTimer({
|
|
199
204
|
systemPromptSize: fullSystem.length,
|
|
@@ -217,20 +222,49 @@ export async function buildBeforeAgentStartResult(event, ctx) {
|
|
|
217
222
|
* filesystem and DB dependencies to exercise this routing logic in-place.
|
|
218
223
|
*/
|
|
219
224
|
export function buildContextMessage(opts) {
|
|
220
|
-
const
|
|
225
|
+
const contextCharLimit = getContextMessageCharLimit();
|
|
226
|
+
const memoryContent = markMemoryContextSupplied(opts.memoryBlock.trim());
|
|
221
227
|
if (opts.injection) {
|
|
222
|
-
const content = memoryContent ? `${memoryContent}\n\n${opts.injection}` : opts.injection;
|
|
228
|
+
const content = limitContextMessageContent(memoryContent ? `${memoryContent}\n\n${opts.injection}` : opts.injection, contextCharLimit);
|
|
223
229
|
return { customType: "gsd-guided-context", content, display: false };
|
|
224
230
|
}
|
|
225
231
|
if (opts.forensicsInjection) {
|
|
226
|
-
const content = memoryContent ? `${memoryContent}\n\n${opts.forensicsInjection}` : opts.forensicsInjection;
|
|
232
|
+
const content = limitContextMessageContent(memoryContent ? `${memoryContent}\n\n${opts.forensicsInjection}` : opts.forensicsInjection, contextCharLimit);
|
|
227
233
|
return { customType: "gsd-forensics", content, display: false };
|
|
228
234
|
}
|
|
229
235
|
if (memoryContent) {
|
|
230
|
-
return {
|
|
236
|
+
return {
|
|
237
|
+
customType: "gsd-memory",
|
|
238
|
+
content: limitContextMessageContent(memoryContent, contextCharLimit),
|
|
239
|
+
display: false,
|
|
240
|
+
};
|
|
231
241
|
}
|
|
232
242
|
return null;
|
|
233
243
|
}
|
|
244
|
+
function getContextMessageCharLimit() {
|
|
245
|
+
const raw = process.env.PI_GSD_CONTEXT_MAX_CHARS;
|
|
246
|
+
if (!raw)
|
|
247
|
+
return DEFAULT_CONTEXT_MESSAGE_MAX_CHARS;
|
|
248
|
+
if (raw === "0")
|
|
249
|
+
return null;
|
|
250
|
+
const parsed = Number(raw);
|
|
251
|
+
if (!Number.isFinite(parsed) || parsed < MIN_CONTEXT_MESSAGE_MAX_CHARS) {
|
|
252
|
+
return DEFAULT_CONTEXT_MESSAGE_MAX_CHARS;
|
|
253
|
+
}
|
|
254
|
+
return Math.floor(parsed);
|
|
255
|
+
}
|
|
256
|
+
function limitContextMessageContent(content, limit) {
|
|
257
|
+
if (!limit || content.length <= limit)
|
|
258
|
+
return content;
|
|
259
|
+
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.";
|
|
260
|
+
const headBudget = Math.max(0, limit - suffix.length);
|
|
261
|
+
return `${content.slice(0, headBudget).trimEnd()}${suffix}`;
|
|
262
|
+
}
|
|
263
|
+
function markMemoryContextSupplied(memoryContent) {
|
|
264
|
+
if (!memoryContent)
|
|
265
|
+
return "";
|
|
266
|
+
return `[GSD Context Metadata]\n- Memory supplied: yes\n\n${memoryContent}`;
|
|
267
|
+
}
|
|
234
268
|
/**
|
|
235
269
|
* ADR-013 step 4 — auto-injection parity for the memories table.
|
|
236
270
|
*
|
|
@@ -249,7 +283,7 @@ export function buildContextMessage(opts) {
|
|
|
249
283
|
* with a token-budget cap. Failures degrade gracefully — the function never
|
|
250
284
|
* throws and returns "" so the system prompt construction continues.
|
|
251
285
|
*/
|
|
252
|
-
export async function loadMemoryBlock(userPrompt) {
|
|
286
|
+
export async function loadMemoryBlock(userPrompt, opts = {}) {
|
|
253
287
|
try {
|
|
254
288
|
const { formatMemoriesForPrompt, getActiveMemoriesRanked, queryMemoriesRanked } = await import("../memory-store.js");
|
|
255
289
|
// Categories that belong in every turn. Pre-ADR-013 this was just
|
|
@@ -268,7 +302,7 @@ export async function loadMemoryBlock(userPrompt) {
|
|
|
268
302
|
const criticalIds = new Set(critical.map((m) => m.id));
|
|
269
303
|
let relevant = [];
|
|
270
304
|
const trimmed = userPrompt.trim();
|
|
271
|
-
if (trimmed) {
|
|
305
|
+
if (trimmed && opts.includePromptRelevant !== false) {
|
|
272
306
|
const hits = queryMemoriesRanked({ query: trimmed, k: QUERY_K });
|
|
273
307
|
relevant = hits.map((h) => h.memory).filter((m) => !criticalIds.has(m.id));
|
|
274
308
|
}
|
|
@@ -319,15 +353,37 @@ export function loadKnowledgeBlock(gsdHomeDir, cwd) {
|
|
|
319
353
|
return { block: "", globalSizeKb: 0 };
|
|
320
354
|
}
|
|
321
355
|
const parts = [];
|
|
322
|
-
if (globalKnowledge)
|
|
323
|
-
parts.push(`## Global Knowledge\n\n${globalKnowledge}`);
|
|
324
|
-
|
|
325
|
-
|
|
356
|
+
if (globalKnowledge) {
|
|
357
|
+
parts.push(`## Global Knowledge\nSource: \`${globalKnowledgePath}\`\n\n${globalKnowledge}`);
|
|
358
|
+
}
|
|
359
|
+
if (projectKnowledge) {
|
|
360
|
+
parts.push(`## Project Knowledge\nSource: \`${knowledgePath}\`\n\n${projectKnowledge}`);
|
|
361
|
+
}
|
|
362
|
+
const body = limitKnowledgeBlock(parts.join("\n\n"), getKnowledgeCharLimit());
|
|
326
363
|
return {
|
|
327
|
-
block: `\n\n[KNOWLEDGE — Rules, patterns, and lessons learned]\n\n${
|
|
364
|
+
block: `\n\n[KNOWLEDGE — Rules, patterns, and lessons learned]\n\n${body}`,
|
|
328
365
|
globalSizeKb,
|
|
329
366
|
};
|
|
330
367
|
}
|
|
368
|
+
function getKnowledgeCharLimit() {
|
|
369
|
+
const raw = process.env.PI_GSD_KNOWLEDGE_MAX_CHARS;
|
|
370
|
+
if (!raw)
|
|
371
|
+
return DEFAULT_KNOWLEDGE_MAX_CHARS;
|
|
372
|
+
if (raw === "0")
|
|
373
|
+
return null;
|
|
374
|
+
const parsed = Number(raw);
|
|
375
|
+
if (!Number.isFinite(parsed) || parsed < MIN_KNOWLEDGE_MAX_CHARS) {
|
|
376
|
+
return DEFAULT_KNOWLEDGE_MAX_CHARS;
|
|
377
|
+
}
|
|
378
|
+
return Math.floor(parsed);
|
|
379
|
+
}
|
|
380
|
+
function limitKnowledgeBlock(content, limit) {
|
|
381
|
+
if (!limit || content.length <= limit)
|
|
382
|
+
return content;
|
|
383
|
+
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.";
|
|
384
|
+
const headBudget = Math.max(0, limit - suffix.length);
|
|
385
|
+
return `${content.slice(0, headBudget).trimEnd()}${suffix}`;
|
|
386
|
+
}
|
|
331
387
|
function buildWorktreeContextBlock() {
|
|
332
388
|
const worktreeName = getActiveWorktreeName();
|
|
333
389
|
const worktreeMainCwd = getWorktreeOriginalCwd();
|
|
@@ -376,6 +432,10 @@ function buildWorktreeContextBlock() {
|
|
|
376
432
|
* Tested against the trimmed, lowercased prompt with trailing punctuation stripped.
|
|
377
433
|
*/
|
|
378
434
|
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)$/;
|
|
435
|
+
export function isLowEntropyResumePrompt(prompt) {
|
|
436
|
+
const trimmed = prompt.trim().toLowerCase().replace(/[.!?,]+$/g, "");
|
|
437
|
+
return RESUME_INTENT_PATTERNS.test(trimmed);
|
|
438
|
+
}
|
|
379
439
|
async function buildGuidedExecuteContextInjection(prompt, basePath) {
|
|
380
440
|
const ensureStateDbOpen = async () => {
|
|
381
441
|
const { ensureDbOpen } = await import("./dynamic-tools.js");
|
|
@@ -402,8 +462,7 @@ async function buildGuidedExecuteContextInjection(prompt, basePath) {
|
|
|
402
462
|
// control/help/diagnostic prompts with unrelated execution context.
|
|
403
463
|
// Phase-gated: only fire during "executing" to avoid misrouting during
|
|
404
464
|
// replanning, gate evaluation, or other non-execution phases.
|
|
405
|
-
|
|
406
|
-
if (RESUME_INTENT_PATTERNS.test(trimmed)) {
|
|
465
|
+
if (isLowEntropyResumePrompt(prompt)) {
|
|
407
466
|
await ensureStateDbOpen();
|
|
408
467
|
const state = await deriveState(basePath);
|
|
409
468
|
if (state.phase === "executing" && state.activeTask && state.activeMilestone && state.activeSlice) {
|