gsd-pi 2.52.0 → 2.53.0-dev.a67436f
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 +55 -32
- package/dist/headless-query.js +1 -1
- package/dist/headless-ui.d.ts +2 -2
- package/dist/headless-ui.js +18 -15
- package/dist/headless.d.ts +11 -0
- package/dist/headless.js +178 -38
- package/dist/resources/extensions/get-secrets-from-user.js +7 -0
- package/dist/resources/extensions/gsd/auto/phases.js +28 -8
- package/dist/resources/extensions/gsd/auto-dispatch.js +5 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +70 -14
- package/dist/resources/extensions/gsd/auto.js +22 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +4 -10
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +3 -3
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -2
- package/dist/resources/extensions/gsd/git-service.js +4 -3
- package/dist/resources/extensions/gsd/guided-flow.js +4 -3
- package/dist/resources/extensions/gsd/markdown-renderer.js +5 -4
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +18 -2
- package/dist/resources/extensions/gsd/preferences-types.js +1 -1
- package/dist/resources/extensions/gsd/state.js +18 -29
- package/dist/resources/extensions/gsd/status-guards.js +12 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -3
- package/dist/resources/extensions/gsd/tools/complete-slice.js +4 -3
- package/dist/resources/extensions/gsd/tools/complete-task.js +4 -3
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +4 -14
- package/dist/resources/extensions/gsd/tools/plan-slice.js +4 -14
- package/dist/resources/extensions/gsd/tools/plan-task.js +4 -14
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +6 -7
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +4 -3
- package/dist/resources/extensions/gsd/tools/reopen-task.js +5 -4
- package/dist/resources/extensions/gsd/tools/replan-slice.js +5 -6
- package/dist/resources/extensions/gsd/validation.js +21 -0
- package/dist/resources/extensions/shared/rtk.js +14 -4
- package/dist/rtk.js +3 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +20 -20
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +20 -20
- package/dist/web/standalone/.next/server/chunks/2229.js +1 -1
- package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.87fd909ae0110f50.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-b950e4e384cc62b3.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-024d82be84800e52.js → webpack-bca0e732db0dcec3.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/README.md +6 -6
- package/packages/mcp-server/package.json +14 -4
- package/packages/mcp-server/src/cli.ts +1 -1
- package/packages/mcp-server/src/index.ts +1 -1
- package/packages/mcp-server/src/mcp-server.test.ts +2 -2
- package/packages/mcp-server/src/session-manager.ts +2 -2
- package/packages/mcp-server/src/types.ts +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/rpc-client/README.md +125 -0
- package/packages/rpc-client/examples/basic-usage.ts +13 -0
- package/packages/rpc-client/package.json +17 -3
- package/packages/rpc-client/src/index.ts +10 -0
- package/packages/rpc-client/src/jsonl.ts +64 -0
- package/packages/rpc-client/src/rpc-client.test.ts +568 -0
- package/packages/rpc-client/src/rpc-client.ts +666 -0
- package/packages/rpc-client/src/rpc-types.ts +399 -0
- package/packages/rpc-client/tsconfig.examples.json +17 -0
- package/packages/rpc-client/tsconfig.json +24 -0
- package/pkg/package.json +1 -1
- package/scripts/ensure-workspace-builds.cjs +36 -8
- package/src/resources/extensions/get-secrets-from-user.ts +8 -0
- package/src/resources/extensions/gsd/auto/phases.ts +38 -7
- package/src/resources/extensions/gsd/auto-dispatch.ts +6 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +73 -14
- package/src/resources/extensions/gsd/auto.ts +21 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +4 -11
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +3 -3
- package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -2
- package/src/resources/extensions/gsd/git-service.ts +4 -3
- package/src/resources/extensions/gsd/guided-flow.ts +4 -3
- package/src/resources/extensions/gsd/markdown-renderer.ts +5 -4
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +23 -1
- package/src/resources/extensions/gsd/preferences-types.ts +1 -1
- package/src/resources/extensions/gsd/state.ts +18 -29
- package/src/resources/extensions/gsd/status-guards.ts +13 -0
- package/src/resources/extensions/gsd/tests/active-milestone-id-guard.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/auto-stale-lock-self-kill.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +80 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +64 -30
- package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/parallel-orchestrator-zombie-cleanup.test.ts +277 -0
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/rate-limit-model-fallback.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +9 -8
- package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/status-guards.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/validation-gate-patterns.test.ts +124 -0
- package/src/resources/extensions/gsd/tests/validation.test.ts +72 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +4 -3
- package/src/resources/extensions/gsd/tools/complete-slice.ts +4 -3
- package/src/resources/extensions/gsd/tools/complete-task.ts +4 -3
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +4 -16
- package/src/resources/extensions/gsd/tools/plan-slice.ts +4 -16
- package/src/resources/extensions/gsd/tools/plan-task.ts +4 -16
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +6 -7
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +4 -3
- package/src/resources/extensions/gsd/tools/reopen-task.ts +5 -4
- package/src/resources/extensions/gsd/tools/replan-slice.ts +5 -7
- package/src/resources/extensions/gsd/validation.ts +23 -0
- package/src/resources/extensions/shared/rtk.ts +22 -4
- package/dist/web/standalone/.next/static/chunks/4024.21054f459af5cc78.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-fbecd1237e2d6d1f.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- /package/dist/web/standalone/.next/static/{vlgS2rkXjxeKhgXhdp4lh → YO-PWFRitlHM-L-dotlmm}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vlgS2rkXjxeKhgXhdp4lh → YO-PWFRitlHM-L-dotlmm}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -27,55 +27,78 @@ One command. Walk away. Come back to a built project with clean git history.
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
## What's New in v2.
|
|
30
|
+
## What's New in v2.52.0
|
|
31
31
|
|
|
32
|
-
###
|
|
32
|
+
### VS Code Extension & Web UI
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
- **VS Code integration** — status bar, file decorations, bash terminal, session tree, conversation history, and code lens. (#2651)
|
|
35
|
+
- **Dark mode contrast** — raised token floor and flattened opacity tier system for better readability. (#2734)
|
|
36
|
+
- **Auth token gate** — synthetic 401 on missing token, unauthenticated boot state, and recovery screen. (#2740)
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
- **v3 — state machine guards, actor identity, reversibility** — introduces formal state machine guards, tracks which actor (human vs agent) initiated each transition, and makes transitions reversible.
|
|
38
|
-
- **Hardened** — closes TOCTOU race conditions, intercepts bypass attempts, and resolves status inconsistencies.
|
|
38
|
+
### Capability Metadata & Model Routing
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
### v2.45.0 — New Commands and Capabilities
|
|
43
|
-
|
|
44
|
-
- **`/gsd rethink`** — conversational project reorganization. Rethink your milestone structure, slice decomposition, or overall approach through guided discussion. (#2459)
|
|
45
|
-
- **`/gsd mcp`** — MCP server status and connectivity. Check which MCP servers are configured, connected, and healthy. (#2362)
|
|
46
|
-
- **Complete offline mode** — GSD now works fully offline with local models. (#2429)
|
|
47
|
-
- **Global KNOWLEDGE.md injection** — `~/.gsd/agent/KNOWLEDGE.md` is injected into the system prompt, so cross-project knowledge persists globally. (#2331)
|
|
48
|
-
- **Mobile-responsive web UI** — the browser interface now works on phones and tablets. (#2354)
|
|
49
|
-
- **DB tool previews** — `renderCall`/`renderResult` previews on DB tools show what each tool call does before and after execution. (#2273)
|
|
50
|
-
- **Message timestamps** — user and assistant messages now include timestamps. (#2368)
|
|
40
|
+
- **Capability-based model selection** — replaced model-ID pattern matching with capability metadata, making custom provider integration more reliable. (#2548)
|
|
51
41
|
|
|
52
42
|
### Key Changes
|
|
53
43
|
|
|
54
|
-
-
|
|
55
|
-
- **
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
44
|
+
- **`--bare` mode** — wired across headless, pi-coding-agent, and resource-loader for minimal-output operation.
|
|
45
|
+
- **RPC protocol v2** — new types, init handshake with version detection, and runId generation on prompt/steer/follow_up commands.
|
|
46
|
+
- **PREFERENCES.md rename** — `preferences.md` renamed to `PREFERENCES.md` for consistency. (#2700, #2738)
|
|
47
|
+
- **Comprehensive SQLite audit** — indexes, caching, safety, and reconciliation fixes across gsd-db.
|
|
48
|
+
- **Unified error classifier** — three overlapping error classifiers consolidated into a single classify-decide-act pipeline.
|
|
58
49
|
|
|
59
50
|
### Key Fixes
|
|
60
51
|
|
|
61
|
-
- **Auto-mode
|
|
62
|
-
- **
|
|
63
|
-
-
|
|
64
|
-
- **
|
|
65
|
-
- **
|
|
66
|
-
- **
|
|
67
|
-
- **Windows
|
|
68
|
-
- **
|
|
52
|
+
- **Auto-mode stops on provider errors** — auto loop now halts after provider errors instead of retrying indefinitely. (#2762, #2764)
|
|
53
|
+
- **Transaction safety** — state machine guards moved inside transactions in 5 tool handlers (#2752), and `transaction()` made re-entrant.
|
|
54
|
+
- **Worktree seeding** — `preferences.md` seeded into auto-mode worktrees and included in worktree sync. (#2693)
|
|
55
|
+
- **Idle watchdog** — interactive tools exempted from stall detection (#2676), and filesystem activity no longer overrides stalled-tool detection. (#2697)
|
|
56
|
+
- **Milestone guards** — `allSlicesDone` guarded against vacuous truth on empty slice arrays (#2679), and `complete-milestone` dispatch blocked when validation is `needs-remediation`. (#2682)
|
|
57
|
+
- **Docker overhaul** — fragile setup replaced with proven container patterns. (#2716)
|
|
58
|
+
- **Windows** — EINVAL prevented by disabling detached process groups on Win32. (#2744)
|
|
59
|
+
- **Audit log** — `setLogBasePath` wired into engine init to resurrect audit logging. (#2745)
|
|
60
|
+
|
|
61
|
+
### v2.51.0 — Skills, RTK, and Verification
|
|
62
|
+
|
|
63
|
+
- **`/terminal` command** — direct shell execution from the slash command interface. (#2349)
|
|
64
|
+
- **Managed RTK integration** — RTK binary auto-provisioned with opt-in preference and web UI toggle. (#2620)
|
|
65
|
+
- **Verification classes** — compliance checked before milestone completion, with classes injected into validation prompts. (#2621, #2623)
|
|
66
|
+
- **Skills overhaul** — 30+ new skill packs covering major frameworks, databases, and cloud platforms; curated catalog with `~/.agents/skills/` as primary directory.
|
|
67
|
+
|
|
68
|
+
### v2.50.0 — Quality Gates
|
|
69
|
+
|
|
70
|
+
- **Quality gates** — 8-question quality gates added to planning and completion templates, with parallel evaluation via `evaluating-gates` phase.
|
|
71
|
+
- **Structured error propagation** — errors wired through `UnitResult` for better diagnostics.
|
|
72
|
+
|
|
73
|
+
### v2.49.0 — Git Trailers & Yolo Mode
|
|
74
|
+
|
|
75
|
+
- **`--yolo` flag** — `/gsd auto --yolo` for non-interactive project init.
|
|
76
|
+
- **Git trailers** — GSD metadata moved from commit subject scopes to git trailers.
|
|
77
|
+
|
|
78
|
+
### v2.48.0 — Forensics & Discussion
|
|
79
|
+
|
|
80
|
+
- **`/gsd discuss` for queued milestones** — target milestones still in the queue. (#2349)
|
|
81
|
+
- **Enhanced forensics** — journal and activity log awareness added to `/gsd forensics`.
|
|
82
|
+
|
|
83
|
+
### v2.47.0 — External Providers
|
|
84
|
+
|
|
85
|
+
- **External tool execution mode** — `externalToolExecution` mode for external providers in agent-core.
|
|
86
|
+
- **Claude Code CLI provider** — new provider extension for Claude Code CLI. (#2382)
|
|
69
87
|
|
|
70
|
-
### Previous highlights (v2.42–v2.
|
|
88
|
+
### Previous highlights (v2.42–v2.46)
|
|
71
89
|
|
|
90
|
+
- **Single-writer state engine** — disciplined state transitions with machine guards, actor identity, reversibility, and TOCTOU hardening. (#2494)
|
|
91
|
+
- **`/gsd rethink`** — conversational project reorganization. (#2459)
|
|
92
|
+
- **`/gsd mcp`** — MCP server status and connectivity. (#2362)
|
|
93
|
+
- **Complete offline mode** — fully offline with local models. (#2429)
|
|
94
|
+
- **Global KNOWLEDGE.md injection** — cross-project knowledge via `~/.gsd/agent/KNOWLEDGE.md`. (#2331)
|
|
95
|
+
- **Mobile-responsive web UI** — browser interface works on phones and tablets. (#2354)
|
|
96
|
+
- **Default isolation mode changed to `none`** — set `git.isolation: worktree` explicitly if needed. (#2481)
|
|
72
97
|
- **Non-API-key provider extensions** — support for Claude Code CLI and similar providers. (#2382)
|
|
73
98
|
- **Docker sandbox template** — official Docker template for isolated auto mode. (#2360)
|
|
74
99
|
- **DB-backed planning tools** — write-side state transitions use atomic SQLite tool calls. (#2141)
|
|
75
100
|
- **Declarative workflow engine** — YAML workflows through auto-loop. (#2024)
|
|
76
101
|
- **`/gsd fast`** — toggle service tier for prioritized API routing. (#1862)
|
|
77
|
-
- **Forensics dedup** — duplicate detection before issue creation. (#2105)
|
|
78
|
-
- **Startup optimizations** — pre-compiled extensions, compile cache, batch discovery. (#2125)
|
|
79
102
|
|
|
80
103
|
---
|
|
81
104
|
|
package/dist/headless-query.js
CHANGED
|
@@ -36,7 +36,7 @@ export async function handleQuery(basePath) {
|
|
|
36
36
|
const state = await deriveState(basePath);
|
|
37
37
|
// Derive next dispatch action
|
|
38
38
|
let next;
|
|
39
|
-
if (!state.activeMilestone) {
|
|
39
|
+
if (!state.activeMilestone?.id) {
|
|
40
40
|
next = {
|
|
41
41
|
action: 'stop',
|
|
42
42
|
reason: state.phase === 'complete' ? 'All milestones complete.' : state.nextAction,
|
package/dist/headless-ui.d.ts
CHANGED
|
@@ -18,6 +18,6 @@ interface ExtensionUIRequest {
|
|
|
18
18
|
[key: string]: unknown;
|
|
19
19
|
}
|
|
20
20
|
export type { ExtensionUIRequest };
|
|
21
|
-
export declare function handleExtensionUIRequest(event: ExtensionUIRequest,
|
|
21
|
+
export declare function handleExtensionUIRequest(event: ExtensionUIRequest, client: RpcClient): void;
|
|
22
22
|
export declare function formatProgress(event: Record<string, unknown>, verbose: boolean): string | null;
|
|
23
|
-
export declare function startSupervisedStdinReader(
|
|
23
|
+
export declare function startSupervisedStdinReader(client: RpcClient, onResponse: (id: string) => void): () => void;
|
package/dist/headless-ui.js
CHANGED
|
@@ -5,13 +5,12 @@
|
|
|
5
5
|
* formats progress events for stderr output, and reads orchestrator
|
|
6
6
|
* commands from stdin in supervised mode.
|
|
7
7
|
*/
|
|
8
|
-
import { attachJsonlLineReader
|
|
8
|
+
import { attachJsonlLineReader } from '@gsd/pi-coding-agent';
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
// Extension UI Auto-Responder
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
|
-
export function handleExtensionUIRequest(event,
|
|
12
|
+
export function handleExtensionUIRequest(event, client) {
|
|
13
13
|
const { id, method } = event;
|
|
14
|
-
let response;
|
|
15
14
|
switch (method) {
|
|
16
15
|
case 'select': {
|
|
17
16
|
// Lock-guard prompts list "View status" first, but headless needs "Force start"
|
|
@@ -23,31 +22,30 @@ export function handleExtensionUIRequest(event, writeToStdin) {
|
|
|
23
22
|
if (forceOption)
|
|
24
23
|
selected = forceOption;
|
|
25
24
|
}
|
|
26
|
-
|
|
25
|
+
client.sendUIResponse(id, { value: selected });
|
|
27
26
|
break;
|
|
28
27
|
}
|
|
29
28
|
case 'confirm':
|
|
30
|
-
|
|
29
|
+
client.sendUIResponse(id, { confirmed: true });
|
|
31
30
|
break;
|
|
32
31
|
case 'input':
|
|
33
|
-
|
|
32
|
+
client.sendUIResponse(id, { value: '' });
|
|
34
33
|
break;
|
|
35
34
|
case 'editor':
|
|
36
|
-
|
|
35
|
+
client.sendUIResponse(id, { value: event.prefill ?? '' });
|
|
37
36
|
break;
|
|
38
37
|
case 'notify':
|
|
39
38
|
case 'setStatus':
|
|
40
39
|
case 'setWidget':
|
|
41
40
|
case 'setTitle':
|
|
42
41
|
case 'set_editor_text':
|
|
43
|
-
|
|
42
|
+
client.sendUIResponse(id, { value: '' });
|
|
44
43
|
break;
|
|
45
44
|
default:
|
|
46
45
|
process.stderr.write(`[headless] Warning: unknown extension_ui_request method "${method}", cancelling\n`);
|
|
47
|
-
|
|
46
|
+
client.sendUIResponse(id, { cancelled: true });
|
|
48
47
|
break;
|
|
49
48
|
}
|
|
50
|
-
writeToStdin(serializeJsonLine(response));
|
|
51
49
|
}
|
|
52
50
|
// ---------------------------------------------------------------------------
|
|
53
51
|
// Progress Formatter
|
|
@@ -78,7 +76,7 @@ export function formatProgress(event, verbose) {
|
|
|
78
76
|
// ---------------------------------------------------------------------------
|
|
79
77
|
// Supervised Stdin Reader
|
|
80
78
|
// ---------------------------------------------------------------------------
|
|
81
|
-
export function startSupervisedStdinReader(
|
|
79
|
+
export function startSupervisedStdinReader(client, onResponse) {
|
|
82
80
|
return attachJsonlLineReader(process.stdin, (line) => {
|
|
83
81
|
let msg;
|
|
84
82
|
try {
|
|
@@ -90,12 +88,17 @@ export function startSupervisedStdinReader(stdinWriter, client, onResponse) {
|
|
|
90
88
|
}
|
|
91
89
|
const type = String(msg.type ?? '');
|
|
92
90
|
switch (type) {
|
|
93
|
-
case 'extension_ui_response':
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
case 'extension_ui_response': {
|
|
92
|
+
const id = String(msg.id ?? '');
|
|
93
|
+
const value = msg.value !== undefined ? String(msg.value) : undefined;
|
|
94
|
+
const confirmed = typeof msg.confirmed === 'boolean' ? msg.confirmed : undefined;
|
|
95
|
+
const cancelled = typeof msg.cancelled === 'boolean' ? msg.cancelled : undefined;
|
|
96
|
+
client.sendUIResponse(id, { value, confirmed, cancelled });
|
|
97
|
+
if (id) {
|
|
98
|
+
onResponse(id);
|
|
97
99
|
}
|
|
98
100
|
break;
|
|
101
|
+
}
|
|
99
102
|
case 'prompt':
|
|
100
103
|
client.prompt(String(msg.message ?? ''));
|
|
101
104
|
break;
|
package/dist/headless.d.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* 10 — blocked (command reported a blocker)
|
|
12
12
|
* 11 — cancelled (SIGINT/SIGTERM received)
|
|
13
13
|
*/
|
|
14
|
+
import type { SessionInfo } from '@gsd/pi-coding-agent';
|
|
14
15
|
import type { OutputFormat } from './headless-types.js';
|
|
15
16
|
export interface HeadlessOptions {
|
|
16
17
|
timeout: number;
|
|
@@ -31,5 +32,15 @@ export interface HeadlessOptions {
|
|
|
31
32
|
resumeSession?: string;
|
|
32
33
|
bare?: boolean;
|
|
33
34
|
}
|
|
35
|
+
export interface ResumeSessionResult {
|
|
36
|
+
session?: SessionInfo;
|
|
37
|
+
error?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve a session prefix to a single session.
|
|
41
|
+
* Exact id match is preferred over prefix match.
|
|
42
|
+
* Returns `{ session }` on unique match or `{ error }` on 0/ambiguous matches.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveResumeSession(sessions: SessionInfo[], prefix: string): ResumeSessionResult;
|
|
34
45
|
export declare function parseHeadlessArgs(argv: string[]): HeadlessOptions;
|
|
35
46
|
export declare function runHeadless(options: HeadlessOptions): Promise<void>;
|
package/dist/headless.js
CHANGED
|
@@ -14,12 +14,35 @@
|
|
|
14
14
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
15
15
|
import { join } from 'node:path';
|
|
16
16
|
import { resolve } from 'node:path';
|
|
17
|
-
import { RpcClient } from '@gsd/pi-coding-agent';
|
|
17
|
+
import { RpcClient, SessionManager } from '@gsd/pi-coding-agent';
|
|
18
|
+
import { getProjectSessionsDir } from './project-sessions.js';
|
|
18
19
|
import { loadAndValidateAnswerFile, AnswerInjector } from './headless-answers.js';
|
|
19
|
-
import { isTerminalNotification, isBlockedNotification, isMilestoneReadyNotification, isQuickCommand, FIRE_AND_FORGET_METHODS, IDLE_TIMEOUT_MS, NEW_MILESTONE_IDLE_TIMEOUT_MS, EXIT_SUCCESS, EXIT_ERROR, EXIT_BLOCKED, EXIT_CANCELLED, } from './headless-events.js';
|
|
20
|
+
import { isTerminalNotification, isBlockedNotification, isMilestoneReadyNotification, isQuickCommand, FIRE_AND_FORGET_METHODS, IDLE_TIMEOUT_MS, NEW_MILESTONE_IDLE_TIMEOUT_MS, EXIT_SUCCESS, EXIT_ERROR, EXIT_BLOCKED, EXIT_CANCELLED, mapStatusToExitCode, } from './headless-events.js';
|
|
20
21
|
import { VALID_OUTPUT_FORMATS } from './headless-types.js';
|
|
21
22
|
import { handleExtensionUIRequest, formatProgress, startSupervisedStdinReader, } from './headless-ui.js';
|
|
22
23
|
import { loadContext, bootstrapGsdProject, } from './headless-context.js';
|
|
24
|
+
/**
|
|
25
|
+
* Resolve a session prefix to a single session.
|
|
26
|
+
* Exact id match is preferred over prefix match.
|
|
27
|
+
* Returns `{ session }` on unique match or `{ error }` on 0/ambiguous matches.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveResumeSession(sessions, prefix) {
|
|
30
|
+
// Exact match takes priority
|
|
31
|
+
const exact = sessions.find(s => s.id === prefix);
|
|
32
|
+
if (exact) {
|
|
33
|
+
return { session: exact };
|
|
34
|
+
}
|
|
35
|
+
// Prefix match
|
|
36
|
+
const matches = sessions.filter(s => s.id.startsWith(prefix));
|
|
37
|
+
if (matches.length === 0) {
|
|
38
|
+
return { error: `No session matching '${prefix}' found` };
|
|
39
|
+
}
|
|
40
|
+
if (matches.length > 1) {
|
|
41
|
+
const list = matches.map(s => ` ${s.id}`).join('\n');
|
|
42
|
+
return { error: `Ambiguous session prefix '${prefix}' matches ${matches.length} sessions:\n${list}` };
|
|
43
|
+
}
|
|
44
|
+
return { session: matches[0] };
|
|
45
|
+
}
|
|
23
46
|
// ---------------------------------------------------------------------------
|
|
24
47
|
// CLI Argument Parser
|
|
25
48
|
// ---------------------------------------------------------------------------
|
|
@@ -256,6 +279,39 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
256
279
|
let exitCode = 0;
|
|
257
280
|
let milestoneReady = false; // tracks "Milestone X ready." for auto-chaining
|
|
258
281
|
const recentEvents = [];
|
|
282
|
+
// JSON batch mode: cost aggregation (cumulative-max pattern per K004)
|
|
283
|
+
let cumulativeCostUsd = 0;
|
|
284
|
+
let cumulativeInputTokens = 0;
|
|
285
|
+
let cumulativeOutputTokens = 0;
|
|
286
|
+
let cumulativeCacheReadTokens = 0;
|
|
287
|
+
let cumulativeCacheWriteTokens = 0;
|
|
288
|
+
let lastSessionId;
|
|
289
|
+
// Emit HeadlessJsonResult to stdout for --output-format json batch mode
|
|
290
|
+
function emitBatchJsonResult() {
|
|
291
|
+
if (options.outputFormat !== 'json')
|
|
292
|
+
return;
|
|
293
|
+
const duration = Date.now() - startTime;
|
|
294
|
+
const status = blocked ? 'blocked'
|
|
295
|
+
: exitCode === EXIT_CANCELLED ? 'cancelled'
|
|
296
|
+
: exitCode === EXIT_ERROR ? (totalEvents === 0 ? 'error' : 'timeout')
|
|
297
|
+
: 'success';
|
|
298
|
+
const result = {
|
|
299
|
+
status,
|
|
300
|
+
exitCode,
|
|
301
|
+
sessionId: lastSessionId,
|
|
302
|
+
duration,
|
|
303
|
+
cost: {
|
|
304
|
+
total: cumulativeCostUsd,
|
|
305
|
+
input_tokens: cumulativeInputTokens,
|
|
306
|
+
output_tokens: cumulativeOutputTokens,
|
|
307
|
+
cache_read_tokens: cumulativeCacheReadTokens,
|
|
308
|
+
cache_write_tokens: cumulativeCacheWriteTokens,
|
|
309
|
+
},
|
|
310
|
+
toolCalls: toolCallCount,
|
|
311
|
+
events: totalEvents,
|
|
312
|
+
};
|
|
313
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
314
|
+
}
|
|
259
315
|
function trackEvent(event) {
|
|
260
316
|
totalEvents++;
|
|
261
317
|
const type = String(event.type ?? 'unknown');
|
|
@@ -272,8 +328,11 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
272
328
|
if (recentEvents.length > 20)
|
|
273
329
|
recentEvents.shift();
|
|
274
330
|
}
|
|
275
|
-
//
|
|
276
|
-
let
|
|
331
|
+
// Client started flag — replaces old stdinWriter null-check
|
|
332
|
+
let clientStarted = false;
|
|
333
|
+
// Adapter for AnswerInjector — wraps client.sendUIResponse in a writeToStdin-compatible callback
|
|
334
|
+
// Initialized after client.start(); events won't fire before then
|
|
335
|
+
let injectorStdinAdapter = () => { };
|
|
277
336
|
// Supervised mode state
|
|
278
337
|
const pendingResponseTimers = new Map();
|
|
279
338
|
let supervisedFallback = false;
|
|
@@ -320,21 +379,54 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
320
379
|
resetIdleTimer();
|
|
321
380
|
// Answer injector: observe events for question metadata
|
|
322
381
|
injector?.observeEvent(eventObj);
|
|
323
|
-
// --json
|
|
324
|
-
|
|
382
|
+
// --json / --output-format stream-json: forward events as JSONL to stdout (filtered if --events)
|
|
383
|
+
// --output-format json (batch mode): suppress streaming, track cost for final result
|
|
384
|
+
if (options.json && options.outputFormat === 'stream-json') {
|
|
325
385
|
const eventType = String(eventObj.type ?? '');
|
|
326
386
|
if (!options.eventFilter || options.eventFilter.has(eventType)) {
|
|
327
387
|
process.stdout.write(JSON.stringify(eventObj) + '\n');
|
|
328
388
|
}
|
|
329
389
|
}
|
|
330
|
-
else {
|
|
390
|
+
else if (options.outputFormat === 'json') {
|
|
391
|
+
// Batch mode: silently track cost_update events (cumulative-max per K004)
|
|
392
|
+
const eventType = String(eventObj.type ?? '');
|
|
393
|
+
if (eventType === 'cost_update') {
|
|
394
|
+
const data = eventObj;
|
|
395
|
+
const cumCost = data.cumulativeCost;
|
|
396
|
+
if (cumCost) {
|
|
397
|
+
cumulativeCostUsd = Math.max(cumulativeCostUsd, Number(cumCost.costUsd ?? 0));
|
|
398
|
+
const tokens = data.tokens;
|
|
399
|
+
if (tokens) {
|
|
400
|
+
cumulativeInputTokens = Math.max(cumulativeInputTokens, tokens.input ?? 0);
|
|
401
|
+
cumulativeOutputTokens = Math.max(cumulativeOutputTokens, tokens.output ?? 0);
|
|
402
|
+
cumulativeCacheReadTokens = Math.max(cumulativeCacheReadTokens, tokens.cacheRead ?? 0);
|
|
403
|
+
cumulativeCacheWriteTokens = Math.max(cumulativeCacheWriteTokens, tokens.cacheWrite ?? 0);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
// Track sessionId from init_result
|
|
408
|
+
if (eventType === 'init_result') {
|
|
409
|
+
lastSessionId = String(eventObj.sessionId ?? '');
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
else if (!options.json) {
|
|
331
413
|
// Progress output to stderr
|
|
332
414
|
const line = formatProgress(eventObj, !!options.verbose);
|
|
333
415
|
if (line)
|
|
334
416
|
process.stderr.write(line + '\n');
|
|
335
417
|
}
|
|
418
|
+
// Handle execution_complete (v2 structured completion)
|
|
419
|
+
if (eventObj.type === 'execution_complete' && !completed) {
|
|
420
|
+
completed = true;
|
|
421
|
+
const status = String(eventObj.status ?? 'success');
|
|
422
|
+
exitCode = mapStatusToExitCode(status);
|
|
423
|
+
if (eventObj.status === 'blocked')
|
|
424
|
+
blocked = true;
|
|
425
|
+
resolveCompletion();
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
336
428
|
// Handle extension_ui_request
|
|
337
|
-
if (eventObj.type === 'extension_ui_request' &&
|
|
429
|
+
if (eventObj.type === 'extension_ui_request' && clientStarted) {
|
|
338
430
|
// Check for terminal notification before auto-responding
|
|
339
431
|
if (isBlockedNotification(eventObj)) {
|
|
340
432
|
blocked = true;
|
|
@@ -348,7 +440,7 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
348
440
|
}
|
|
349
441
|
// Answer injection: try to handle with pre-supplied answers before supervised/auto
|
|
350
442
|
if (injector && !FIRE_AND_FORGET_METHODS.has(String(eventObj.method ?? ''))) {
|
|
351
|
-
if (injector.tryHandle(eventObj,
|
|
443
|
+
if (injector.tryHandle(eventObj, injectorStdinAdapter)) {
|
|
352
444
|
if (completed) {
|
|
353
445
|
exitCode = blocked ? EXIT_BLOCKED : EXIT_SUCCESS;
|
|
354
446
|
resolveCompletion();
|
|
@@ -364,13 +456,13 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
364
456
|
const eventId = String(eventObj.id ?? '');
|
|
365
457
|
const timer = setTimeout(() => {
|
|
366
458
|
pendingResponseTimers.delete(eventId);
|
|
367
|
-
handleExtensionUIRequest(eventObj,
|
|
459
|
+
handleExtensionUIRequest(eventObj, client);
|
|
368
460
|
process.stdout.write(JSON.stringify({ type: 'supervised_timeout', id: eventId, method }) + '\n');
|
|
369
461
|
}, responseTimeout);
|
|
370
462
|
pendingResponseTimers.set(eventId, timer);
|
|
371
463
|
}
|
|
372
464
|
else {
|
|
373
|
-
handleExtensionUIRequest(eventObj,
|
|
465
|
+
handleExtensionUIRequest(eventObj, client);
|
|
374
466
|
}
|
|
375
467
|
// If we detected a terminal notification, resolve after responding
|
|
376
468
|
if (completed) {
|
|
@@ -393,13 +485,22 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
393
485
|
process.stderr.write('\n[headless] Interrupted, stopping child process...\n');
|
|
394
486
|
interrupted = true;
|
|
395
487
|
exitCode = EXIT_CANCELLED;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
488
|
+
// Kill child process — don't await, just fire and exit.
|
|
489
|
+
// The main flow may be awaiting a promise that resolves when the child dies,
|
|
490
|
+
// which would race with this handler. Exit synchronously to ensure correct exit code.
|
|
491
|
+
try {
|
|
492
|
+
client.stop().catch(() => { });
|
|
493
|
+
}
|
|
494
|
+
catch { }
|
|
495
|
+
if (timeoutTimer)
|
|
496
|
+
clearTimeout(timeoutTimer);
|
|
497
|
+
if (idleTimer)
|
|
498
|
+
clearTimeout(idleTimer);
|
|
499
|
+
// Emit batch JSON result if in json mode before exiting
|
|
500
|
+
if (options.outputFormat === 'json') {
|
|
501
|
+
emitBatchJsonResult();
|
|
502
|
+
}
|
|
503
|
+
process.exit(exitCode);
|
|
403
504
|
};
|
|
404
505
|
process.on('SIGINT', signalHandler);
|
|
405
506
|
process.on('SIGTERM', signalHandler);
|
|
@@ -413,21 +514,55 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
413
514
|
clearTimeout(timeoutTimer);
|
|
414
515
|
process.exit(1);
|
|
415
516
|
}
|
|
416
|
-
//
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
if (timeoutTimer)
|
|
422
|
-
clearTimeout(timeoutTimer);
|
|
423
|
-
process.exit(1);
|
|
517
|
+
// v2 protocol negotiation — attempt init for structured completion events
|
|
518
|
+
let v2Enabled = false;
|
|
519
|
+
try {
|
|
520
|
+
await client.init({ clientId: 'gsd-headless' });
|
|
521
|
+
v2Enabled = true;
|
|
424
522
|
}
|
|
425
|
-
|
|
426
|
-
|
|
523
|
+
catch {
|
|
524
|
+
process.stderr.write('[headless] Warning: v2 init failed, falling back to v1 string-matching\n');
|
|
525
|
+
}
|
|
526
|
+
clientStarted = true;
|
|
527
|
+
// --resume: resolve session ID and switch to it
|
|
528
|
+
if (options.resumeSession) {
|
|
529
|
+
const projectSessionsDir = getProjectSessionsDir(process.cwd());
|
|
530
|
+
const sessions = await SessionManager.list(process.cwd(), projectSessionsDir);
|
|
531
|
+
const result = resolveResumeSession(sessions, options.resumeSession);
|
|
532
|
+
if (result.error) {
|
|
533
|
+
process.stderr.write(`[headless] Error: ${result.error}\n`);
|
|
534
|
+
await client.stop();
|
|
535
|
+
if (timeoutTimer)
|
|
536
|
+
clearTimeout(timeoutTimer);
|
|
537
|
+
process.exit(1);
|
|
538
|
+
}
|
|
539
|
+
const matched = result.session;
|
|
540
|
+
const switchResult = await client.switchSession(matched.path);
|
|
541
|
+
if (switchResult.cancelled) {
|
|
542
|
+
process.stderr.write(`[headless] Error: Session switch to '${matched.id}' was cancelled by an extension\n`);
|
|
543
|
+
await client.stop();
|
|
544
|
+
if (timeoutTimer)
|
|
545
|
+
clearTimeout(timeoutTimer);
|
|
546
|
+
process.exit(1);
|
|
547
|
+
}
|
|
548
|
+
process.stderr.write(`[headless] Resuming session ${matched.id}\n`);
|
|
549
|
+
}
|
|
550
|
+
// Build injector adapter — wraps client.sendUIResponse for AnswerInjector's writeToStdin interface
|
|
551
|
+
injectorStdinAdapter = (data) => {
|
|
552
|
+
try {
|
|
553
|
+
const parsed = JSON.parse(data.trim());
|
|
554
|
+
if (parsed.type === 'extension_ui_response' && parsed.id) {
|
|
555
|
+
const { id, value, values, confirmed, cancelled } = parsed;
|
|
556
|
+
client.sendUIResponse(id, { value, values, confirmed, cancelled });
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
catch {
|
|
560
|
+
process.stderr.write('[headless] Warning: injector adapter received unparseable data\n');
|
|
561
|
+
}
|
|
427
562
|
};
|
|
428
563
|
// Start supervised stdin reader for orchestrator commands
|
|
429
564
|
if (options.supervised) {
|
|
430
|
-
stopSupervisedReader = startSupervisedStdinReader(
|
|
565
|
+
stopSupervisedReader = startSupervisedStdinReader(client, (id) => {
|
|
431
566
|
const timer = pendingResponseTimers.get(id);
|
|
432
567
|
if (timer) {
|
|
433
568
|
clearTimeout(timer);
|
|
@@ -437,15 +572,18 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
437
572
|
// Ensure stdin is in flowing mode for JSONL reading
|
|
438
573
|
process.stdin.resume();
|
|
439
574
|
}
|
|
440
|
-
// Detect child process crash
|
|
441
|
-
internalProcess
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
575
|
+
// Detect child process crash (read-only exit event subscription — not stdin access)
|
|
576
|
+
const internalProcess = client.process;
|
|
577
|
+
if (internalProcess) {
|
|
578
|
+
internalProcess.on('exit', (code) => {
|
|
579
|
+
if (!completed) {
|
|
580
|
+
const msg = `[headless] Child process exited unexpectedly with code ${code ?? 'null'}\n`;
|
|
581
|
+
process.stderr.write(msg);
|
|
582
|
+
exitCode = EXIT_ERROR;
|
|
583
|
+
resolveCompletion();
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
}
|
|
449
587
|
if (!options.json) {
|
|
450
588
|
process.stderr.write(`[headless] Running /gsd ${options.command}${options.commandArgs.length > 0 ? ' ' + options.commandArgs.join(' ') : ''}...\n`);
|
|
451
589
|
}
|
|
@@ -530,5 +668,7 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
530
668
|
}
|
|
531
669
|
}
|
|
532
670
|
}
|
|
671
|
+
// Emit structured JSON result in batch mode
|
|
672
|
+
emitBatchJsonResult();
|
|
533
673
|
return { exitCode, interrupted };
|
|
534
674
|
}
|
|
@@ -25,6 +25,11 @@ function maskPreview(value) {
|
|
|
25
25
|
function shellEscapeSingle(value) {
|
|
26
26
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
27
27
|
}
|
|
28
|
+
function hydrateProcessEnv(key, value) {
|
|
29
|
+
// Make newly collected secrets immediately visible to the current session.
|
|
30
|
+
// Some extensions read process.env directly and do not reload .env on every call.
|
|
31
|
+
process.env[key] = value;
|
|
32
|
+
}
|
|
28
33
|
async function writeEnvKey(filePath, key, value) {
|
|
29
34
|
let content = "";
|
|
30
35
|
try {
|
|
@@ -246,6 +251,7 @@ async function applySecrets(provided, destination, opts) {
|
|
|
246
251
|
try {
|
|
247
252
|
await writeEnvKey(opts.envFilePath, key, value);
|
|
248
253
|
applied.push(key);
|
|
254
|
+
hydrateProcessEnv(key, value);
|
|
249
255
|
}
|
|
250
256
|
catch (err) {
|
|
251
257
|
errors.push(`${key}: ${err.message}`);
|
|
@@ -265,6 +271,7 @@ async function applySecrets(provided, destination, opts) {
|
|
|
265
271
|
}
|
|
266
272
|
else {
|
|
267
273
|
applied.push(key);
|
|
274
|
+
hydrateProcessEnv(key, value);
|
|
268
275
|
}
|
|
269
276
|
}
|
|
270
277
|
catch (err) {
|