gsd-pi 2.53.0 → 2.54.0-dev.16631ca

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.
Files changed (200) hide show
  1. package/dist/cli.js +19 -19
  2. package/dist/headless-ui.d.ts +29 -3
  3. package/dist/headless-ui.js +221 -28
  4. package/dist/headless.d.ts +11 -0
  5. package/dist/headless.js +238 -41
  6. package/dist/resources/extensions/bg-shell/bg-shell-lifecycle.js +2 -2
  7. package/dist/resources/extensions/bg-shell/utilities.js +34 -5
  8. package/dist/resources/extensions/gsd/auto/phases.js +10 -1
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
  10. package/dist/resources/extensions/gsd/auto-model-selection.js +17 -1
  11. package/dist/resources/extensions/gsd/auto-prompts.js +9 -0
  12. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +18 -5
  13. package/dist/web/standalone/.next/BUILD_ID +1 -1
  14. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  15. package/dist/web/standalone/.next/build-manifest.json +4 -4
  16. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  17. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  18. package/dist/web/standalone/.next/required-server-files.json +4 -4
  19. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  20. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  21. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  22. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  30. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  33. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  36. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  40. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  41. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  42. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  43. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  44. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  45. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  46. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  47. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  48. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  49. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  50. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  51. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  52. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  58. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  60. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  86. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  92. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  106. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  108. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  110. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  112. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/index.html +1 -1
  122. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  123. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  124. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  125. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  127. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/page.js +2 -2
  129. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  131. package/dist/web/standalone/.next/server/chunks/2229.js +2 -2
  132. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  133. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/middleware.js +2 -2
  136. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  138. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  139. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  140. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  141. package/dist/web/standalone/.next/static/chunks/4024.82f2e2a838908338.js +9 -0
  142. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  143. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  144. package/dist/web/standalone/.next/static/chunks/app/page-b950e4e384cc62b3.js +1 -0
  145. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  146. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  147. package/dist/web/standalone/.next/static/chunks/{webpack-bca0e732db0dcec3.js → webpack-70adf6e3be5479ce.js} +1 -1
  148. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  149. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  150. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  151. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  152. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  153. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  154. package/dist/web/standalone/server.js +1 -1
  155. package/package.json +1 -1
  156. package/packages/mcp-server/README.md +6 -6
  157. package/packages/mcp-server/package.json +14 -4
  158. package/packages/mcp-server/src/cli.ts +1 -1
  159. package/packages/mcp-server/src/index.ts +1 -1
  160. package/packages/mcp-server/src/mcp-server.test.ts +2 -2
  161. package/packages/mcp-server/src/session-manager.ts +2 -2
  162. package/packages/mcp-server/src/types.ts +1 -1
  163. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +1 -1
  164. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  165. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -0
  167. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  168. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +14 -2
  169. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  170. package/packages/pi-coding-agent/package.json +1 -1
  171. package/packages/pi-coding-agent/src/core/model-registry.ts +1 -1
  172. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -2
  173. package/packages/rpc-client/README.md +125 -0
  174. package/packages/rpc-client/examples/basic-usage.ts +13 -0
  175. package/packages/rpc-client/package.json +17 -3
  176. package/packages/rpc-client/src/index.ts +10 -0
  177. package/packages/rpc-client/src/jsonl.ts +64 -0
  178. package/packages/rpc-client/src/rpc-client.test.ts +568 -0
  179. package/packages/rpc-client/src/rpc-client.ts +666 -0
  180. package/packages/rpc-client/src/rpc-types.ts +399 -0
  181. package/packages/rpc-client/tsconfig.examples.json +17 -0
  182. package/packages/rpc-client/tsconfig.json +24 -0
  183. package/pkg/package.json +1 -1
  184. package/src/resources/extensions/bg-shell/bg-shell-lifecycle.ts +2 -2
  185. package/src/resources/extensions/bg-shell/utilities.ts +39 -4
  186. package/src/resources/extensions/gsd/auto/phases.ts +14 -2
  187. package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
  188. package/src/resources/extensions/gsd/auto-model-selection.ts +21 -1
  189. package/src/resources/extensions/gsd/auto-prompts.ts +15 -0
  190. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +19 -6
  191. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +139 -0
  192. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +55 -0
  193. package/src/resources/extensions/gsd/tests/plan-milestone-queue-context.test.ts +48 -0
  194. package/src/resources/extensions/gsd/tests/register-extension-guard.test.ts +59 -0
  195. package/dist/web/standalone/.next/static/chunks/4024.87fd909ae0110f50.js +0 -9
  196. package/dist/web/standalone/.next/static/chunks/app/page-fbecd1237e2d6d1f.js +0 -1
  197. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  198. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  199. /package/dist/web/standalone/.next/static/{mWBOLPnUoeaTGiD-vhu5O → 8yiPxQ52ue_s6qdrrAxsH}/_buildManifest.js +0 -0
  200. /package/dist/web/standalone/.next/static/{mWBOLPnUoeaTGiD-vhu5O → 8yiPxQ52ue_s6qdrrAxsH}/_ssgManifest.js +0 -0
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
- import { handleExtensionUIRequest, formatProgress, startSupervisedStdinReader, } from './headless-ui.js';
22
+ import { handleExtensionUIRequest, formatProgress, formatThinkingLine, 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
  // ---------------------------------------------------------------------------
@@ -243,6 +266,8 @@ async function runHeadlessOnce(options, restartCount) {
243
266
  if (injector) {
244
267
  clientOptions.env = injector.getSecretEnvVars();
245
268
  }
269
+ // Signal headless mode to the GSD extension (skips UAT human pause, etc.)
270
+ clientOptions.env = { ...(clientOptions.env || {}), GSD_HEADLESS: '1' };
246
271
  // Propagate --bare to the child process
247
272
  if (options.bare) {
248
273
  clientOptions.args = [...(clientOptions.args || []), '--bare'];
@@ -256,6 +281,43 @@ async function runHeadlessOnce(options, restartCount) {
256
281
  let exitCode = 0;
257
282
  let milestoneReady = false; // tracks "Milestone X ready." for auto-chaining
258
283
  const recentEvents = [];
284
+ // JSON batch mode: cost aggregation (cumulative-max pattern per K004)
285
+ let cumulativeCostUsd = 0;
286
+ let cumulativeInputTokens = 0;
287
+ let cumulativeOutputTokens = 0;
288
+ let cumulativeCacheReadTokens = 0;
289
+ let cumulativeCacheWriteTokens = 0;
290
+ let lastSessionId;
291
+ // Verbose text-mode state
292
+ const toolStartTimes = new Map();
293
+ let lastCostData;
294
+ let thinkingBuffer = '';
295
+ // Emit HeadlessJsonResult to stdout for --output-format json batch mode
296
+ function emitBatchJsonResult() {
297
+ if (options.outputFormat !== 'json')
298
+ return;
299
+ const duration = Date.now() - startTime;
300
+ const status = blocked ? 'blocked'
301
+ : exitCode === EXIT_CANCELLED ? 'cancelled'
302
+ : exitCode === EXIT_ERROR ? (totalEvents === 0 ? 'error' : 'timeout')
303
+ : 'success';
304
+ const result = {
305
+ status,
306
+ exitCode,
307
+ sessionId: lastSessionId,
308
+ duration,
309
+ cost: {
310
+ total: cumulativeCostUsd,
311
+ input_tokens: cumulativeInputTokens,
312
+ output_tokens: cumulativeOutputTokens,
313
+ cache_read_tokens: cumulativeCacheReadTokens,
314
+ cache_write_tokens: cumulativeCacheWriteTokens,
315
+ },
316
+ toolCalls: toolCallCount,
317
+ events: totalEvents,
318
+ };
319
+ process.stdout.write(JSON.stringify(result) + '\n');
320
+ }
259
321
  function trackEvent(event) {
260
322
  totalEvents++;
261
323
  const type = String(event.type ?? 'unknown');
@@ -272,8 +334,11 @@ async function runHeadlessOnce(options, restartCount) {
272
334
  if (recentEvents.length > 20)
273
335
  recentEvents.shift();
274
336
  }
275
- // Stdin writer for sending extension_ui_response to child
276
- let stdinWriter = null;
337
+ // Client started flag replaces old stdinWriter null-check
338
+ let clientStarted = false;
339
+ // Adapter for AnswerInjector — wraps client.sendUIResponse in a writeToStdin-compatible callback
340
+ // Initialized after client.start(); events won't fire before then
341
+ let injectorStdinAdapter = () => { };
277
342
  // Supervised mode state
278
343
  const pendingResponseTimers = new Map();
279
344
  let supervisedFallback = false;
@@ -320,21 +385,105 @@ async function runHeadlessOnce(options, restartCount) {
320
385
  resetIdleTimer();
321
386
  // Answer injector: observe events for question metadata
322
387
  injector?.observeEvent(eventObj);
323
- // --json mode: forward events as JSONL to stdout (filtered if --events)
324
- if (options.json) {
388
+ // --json / --output-format stream-json: forward events as JSONL to stdout (filtered if --events)
389
+ // --output-format json (batch mode): suppress streaming, track cost for final result
390
+ if (options.json && options.outputFormat === 'stream-json') {
325
391
  const eventType = String(eventObj.type ?? '');
326
392
  if (!options.eventFilter || options.eventFilter.has(eventType)) {
327
393
  process.stdout.write(JSON.stringify(eventObj) + '\n');
328
394
  }
329
395
  }
330
- else {
331
- // Progress output to stderr
332
- const line = formatProgress(eventObj, !!options.verbose);
396
+ else if (options.outputFormat === 'json') {
397
+ // Batch mode: silently track cost_update events (cumulative-max per K004)
398
+ const eventType = String(eventObj.type ?? '');
399
+ if (eventType === 'cost_update') {
400
+ const data = eventObj;
401
+ const cumCost = data.cumulativeCost;
402
+ if (cumCost) {
403
+ cumulativeCostUsd = Math.max(cumulativeCostUsd, Number(cumCost.costUsd ?? 0));
404
+ const tokens = data.tokens;
405
+ if (tokens) {
406
+ cumulativeInputTokens = Math.max(cumulativeInputTokens, tokens.input ?? 0);
407
+ cumulativeOutputTokens = Math.max(cumulativeOutputTokens, tokens.output ?? 0);
408
+ cumulativeCacheReadTokens = Math.max(cumulativeCacheReadTokens, tokens.cacheRead ?? 0);
409
+ cumulativeCacheWriteTokens = Math.max(cumulativeCacheWriteTokens, tokens.cacheWrite ?? 0);
410
+ }
411
+ }
412
+ }
413
+ // Track sessionId from init_result
414
+ if (eventType === 'init_result') {
415
+ lastSessionId = String(eventObj.sessionId ?? '');
416
+ }
417
+ }
418
+ else if (!options.json) {
419
+ // Progress output to stderr with verbose state tracking
420
+ const eventType = String(eventObj.type ?? '');
421
+ // Track cost_update events for agent_end summary
422
+ if (eventType === 'cost_update') {
423
+ const data = eventObj;
424
+ const cumCost = data.cumulativeCost;
425
+ if (cumCost) {
426
+ const tokens = data.tokens;
427
+ lastCostData = {
428
+ costUsd: Number(cumCost.costUsd ?? 0),
429
+ inputTokens: tokens?.input ?? 0,
430
+ outputTokens: tokens?.output ?? 0,
431
+ };
432
+ }
433
+ }
434
+ // Accumulate thinking text from message_update text_delta events
435
+ if (eventType === 'message_update') {
436
+ const ame = eventObj.assistantMessageEvent;
437
+ if (ame?.type === 'text_delta') {
438
+ thinkingBuffer += String(ame.text ?? '');
439
+ }
440
+ }
441
+ // Track tool execution start timestamps
442
+ if (eventType === 'tool_execution_start') {
443
+ const toolCallId = String(eventObj.toolCallId ?? eventObj.id ?? '');
444
+ if (toolCallId)
445
+ toolStartTimes.set(toolCallId, Date.now());
446
+ }
447
+ // Flush thinking buffer before tool calls or message end
448
+ if (options.verbose && thinkingBuffer.trim() &&
449
+ (eventType === 'tool_execution_start' || eventType === 'message_end')) {
450
+ process.stderr.write(formatThinkingLine(thinkingBuffer) + '\n');
451
+ thinkingBuffer = '';
452
+ }
453
+ // Compute tool duration for tool_execution_end
454
+ let toolDuration;
455
+ let isToolError = false;
456
+ if (eventType === 'tool_execution_end') {
457
+ const toolCallId = String(eventObj.toolCallId ?? eventObj.id ?? '');
458
+ const startTime = toolStartTimes.get(toolCallId);
459
+ if (startTime) {
460
+ toolDuration = Date.now() - startTime;
461
+ toolStartTimes.delete(toolCallId);
462
+ }
463
+ isToolError = eventObj.isError === true || eventObj.error != null;
464
+ }
465
+ const ctx = {
466
+ verbose: !!options.verbose,
467
+ toolDuration,
468
+ isError: isToolError,
469
+ lastCost: eventType === 'agent_end' ? lastCostData : undefined,
470
+ };
471
+ const line = formatProgress(eventObj, ctx);
333
472
  if (line)
334
473
  process.stderr.write(line + '\n');
335
474
  }
475
+ // Handle execution_complete (v2 structured completion)
476
+ if (eventObj.type === 'execution_complete' && !completed) {
477
+ completed = true;
478
+ const status = String(eventObj.status ?? 'success');
479
+ exitCode = mapStatusToExitCode(status);
480
+ if (eventObj.status === 'blocked')
481
+ blocked = true;
482
+ resolveCompletion();
483
+ return;
484
+ }
336
485
  // Handle extension_ui_request
337
- if (eventObj.type === 'extension_ui_request' && stdinWriter) {
486
+ if (eventObj.type === 'extension_ui_request' && clientStarted) {
338
487
  // Check for terminal notification before auto-responding
339
488
  if (isBlockedNotification(eventObj)) {
340
489
  blocked = true;
@@ -348,7 +497,7 @@ async function runHeadlessOnce(options, restartCount) {
348
497
  }
349
498
  // Answer injection: try to handle with pre-supplied answers before supervised/auto
350
499
  if (injector && !FIRE_AND_FORGET_METHODS.has(String(eventObj.method ?? ''))) {
351
- if (injector.tryHandle(eventObj, stdinWriter)) {
500
+ if (injector.tryHandle(eventObj, injectorStdinAdapter)) {
352
501
  if (completed) {
353
502
  exitCode = blocked ? EXIT_BLOCKED : EXIT_SUCCESS;
354
503
  resolveCompletion();
@@ -364,13 +513,13 @@ async function runHeadlessOnce(options, restartCount) {
364
513
  const eventId = String(eventObj.id ?? '');
365
514
  const timer = setTimeout(() => {
366
515
  pendingResponseTimers.delete(eventId);
367
- handleExtensionUIRequest(eventObj, stdinWriter);
516
+ handleExtensionUIRequest(eventObj, client);
368
517
  process.stdout.write(JSON.stringify({ type: 'supervised_timeout', id: eventId, method }) + '\n');
369
518
  }, responseTimeout);
370
519
  pendingResponseTimers.set(eventId, timer);
371
520
  }
372
521
  else {
373
- handleExtensionUIRequest(eventObj, stdinWriter);
522
+ handleExtensionUIRequest(eventObj, client);
374
523
  }
375
524
  // If we detected a terminal notification, resolve after responding
376
525
  if (completed) {
@@ -393,13 +542,22 @@ async function runHeadlessOnce(options, restartCount) {
393
542
  process.stderr.write('\n[headless] Interrupted, stopping child process...\n');
394
543
  interrupted = true;
395
544
  exitCode = EXIT_CANCELLED;
396
- client.stop().finally(() => {
397
- if (timeoutTimer)
398
- clearTimeout(timeoutTimer);
399
- if (idleTimer)
400
- clearTimeout(idleTimer);
401
- process.exit(exitCode);
402
- });
545
+ // Kill child process — don't await, just fire and exit.
546
+ // The main flow may be awaiting a promise that resolves when the child dies,
547
+ // which would race with this handler. Exit synchronously to ensure correct exit code.
548
+ try {
549
+ client.stop().catch(() => { });
550
+ }
551
+ catch { }
552
+ if (timeoutTimer)
553
+ clearTimeout(timeoutTimer);
554
+ if (idleTimer)
555
+ clearTimeout(idleTimer);
556
+ // Emit batch JSON result if in json mode before exiting
557
+ if (options.outputFormat === 'json') {
558
+ emitBatchJsonResult();
559
+ }
560
+ process.exit(exitCode);
403
561
  };
404
562
  process.on('SIGINT', signalHandler);
405
563
  process.on('SIGTERM', signalHandler);
@@ -413,21 +571,55 @@ async function runHeadlessOnce(options, restartCount) {
413
571
  clearTimeout(timeoutTimer);
414
572
  process.exit(1);
415
573
  }
416
- // Access stdin writer from the internal process
417
- const internalProcess = client.process;
418
- if (!internalProcess?.stdin) {
419
- process.stderr.write('[headless] Error: Cannot access child process stdin\n');
420
- await client.stop();
421
- if (timeoutTimer)
422
- clearTimeout(timeoutTimer);
423
- process.exit(1);
574
+ // v2 protocol negotiation attempt init for structured completion events
575
+ let v2Enabled = false;
576
+ try {
577
+ await client.init({ clientId: 'gsd-headless' });
578
+ v2Enabled = true;
579
+ }
580
+ catch {
581
+ process.stderr.write('[headless] Warning: v2 init failed, falling back to v1 string-matching\n');
424
582
  }
425
- stdinWriter = (data) => {
426
- internalProcess.stdin.write(data);
583
+ clientStarted = true;
584
+ // --resume: resolve session ID and switch to it
585
+ if (options.resumeSession) {
586
+ const projectSessionsDir = getProjectSessionsDir(process.cwd());
587
+ const sessions = await SessionManager.list(process.cwd(), projectSessionsDir);
588
+ const result = resolveResumeSession(sessions, options.resumeSession);
589
+ if (result.error) {
590
+ process.stderr.write(`[headless] Error: ${result.error}\n`);
591
+ await client.stop();
592
+ if (timeoutTimer)
593
+ clearTimeout(timeoutTimer);
594
+ process.exit(1);
595
+ }
596
+ const matched = result.session;
597
+ const switchResult = await client.switchSession(matched.path);
598
+ if (switchResult.cancelled) {
599
+ process.stderr.write(`[headless] Error: Session switch to '${matched.id}' was cancelled by an extension\n`);
600
+ await client.stop();
601
+ if (timeoutTimer)
602
+ clearTimeout(timeoutTimer);
603
+ process.exit(1);
604
+ }
605
+ process.stderr.write(`[headless] Resuming session ${matched.id}\n`);
606
+ }
607
+ // Build injector adapter — wraps client.sendUIResponse for AnswerInjector's writeToStdin interface
608
+ injectorStdinAdapter = (data) => {
609
+ try {
610
+ const parsed = JSON.parse(data.trim());
611
+ if (parsed.type === 'extension_ui_response' && parsed.id) {
612
+ const { id, value, values, confirmed, cancelled } = parsed;
613
+ client.sendUIResponse(id, { value, values, confirmed, cancelled });
614
+ }
615
+ }
616
+ catch {
617
+ process.stderr.write('[headless] Warning: injector adapter received unparseable data\n');
618
+ }
427
619
  };
428
620
  // Start supervised stdin reader for orchestrator commands
429
621
  if (options.supervised) {
430
- stopSupervisedReader = startSupervisedStdinReader(stdinWriter, client, (id) => {
622
+ stopSupervisedReader = startSupervisedStdinReader(client, (id) => {
431
623
  const timer = pendingResponseTimers.get(id);
432
624
  if (timer) {
433
625
  clearTimeout(timer);
@@ -437,15 +629,18 @@ async function runHeadlessOnce(options, restartCount) {
437
629
  // Ensure stdin is in flowing mode for JSONL reading
438
630
  process.stdin.resume();
439
631
  }
440
- // Detect child process crash
441
- internalProcess.on('exit', (code) => {
442
- if (!completed) {
443
- const msg = `[headless] Child process exited unexpectedly with code ${code ?? 'null'}\n`;
444
- process.stderr.write(msg);
445
- exitCode = EXIT_ERROR;
446
- resolveCompletion();
447
- }
448
- });
632
+ // Detect child process crash (read-only exit event subscription — not stdin access)
633
+ const internalProcess = client.process;
634
+ if (internalProcess) {
635
+ internalProcess.on('exit', (code) => {
636
+ if (!completed) {
637
+ const msg = `[headless] Child process exited unexpectedly with code ${code ?? 'null'}\n`;
638
+ process.stderr.write(msg);
639
+ exitCode = EXIT_ERROR;
640
+ resolveCompletion();
641
+ }
642
+ });
643
+ }
449
644
  if (!options.json) {
450
645
  process.stderr.write(`[headless] Running /gsd ${options.command}${options.commandArgs.length > 0 ? ' ' + options.commandArgs.join(' ') : ''}...\n`);
451
646
  }
@@ -530,5 +725,7 @@ async function runHeadlessOnce(options, restartCount) {
530
725
  }
531
726
  }
532
727
  }
728
+ // Emit structured JSON result in batch mode
729
+ emitBatchJsonResult();
533
730
  return { exitCode, interrupted };
534
731
  }
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { truncateToWidth, visibleWidth, } from "@gsd/pi-tui";
6
6
  import { processes, pendingAlerts, cleanupAll, cleanupSessionProcesses, persistManifest, loadManifest, pruneDeadProcesses, } from "./process-manager.js";
7
- import { formatUptime, resolveBgShellPersistenceCwd } from "./utilities.js";
7
+ import { formatUptime, getBgShellLiveCwd, resolveBgShellPersistenceCwd } from "./utilities.js";
8
8
  import { formatTokenCount } from "../shared/format-utils.js";
9
9
  export function registerBgShellLifecycle(pi, state) {
10
10
  function syncLatestCtxCwd() {
@@ -161,7 +161,7 @@ export function registerBgShellLifecycle(pi, state) {
161
161
  return {
162
162
  render(width) {
163
163
  // ── Line 1: pwd (branch) [session] ... bg status ──
164
- let pwd = process.cwd();
164
+ let pwd = getBgShellLiveCwd(state.latestCtx?.cwd);
165
165
  const home = process.env.HOME || process.env.USERPROFILE;
166
166
  if (home && pwd.startsWith(home)) {
167
167
  pwd = `~${pwd.slice(home.length)}`;
@@ -36,15 +36,44 @@ export const formatUptime = formatDuration;
36
36
  export function formatTimeAgo(timestamp) {
37
37
  return formatDuration(Date.now() - timestamp) + " ago";
38
38
  }
39
- export function resolveBgShellPersistenceCwd(cachedCwd, liveCwd = process.cwd(), pathExists = existsSync) {
39
+ function deriveProjectRootFromAutoWorktree(cachedCwd) {
40
+ if (!cachedCwd)
41
+ return undefined;
42
+ const match = cachedCwd.match(/^(.*?)[\\/]\.gsd[\\/]worktrees[\\/][^\\/]+(?:[\\/].*)?$/);
43
+ return match?.[1];
44
+ }
45
+ export function getBgShellLiveCwd(cachedCwd, pathExists = existsSync, getCwd = () => process.cwd(), chdir = (path) => process.chdir(path)) {
46
+ try {
47
+ return getCwd();
48
+ }
49
+ catch {
50
+ const projectRoot = deriveProjectRootFromAutoWorktree(cachedCwd);
51
+ const home = process.env.HOME || process.env.USERPROFILE;
52
+ const fallbacks = [projectRoot, cachedCwd, home, "/"].filter((candidate) => Boolean(candidate));
53
+ for (const candidate of fallbacks) {
54
+ if (candidate !== "/" && !pathExists(candidate))
55
+ continue;
56
+ try {
57
+ chdir(candidate);
58
+ }
59
+ catch {
60
+ // Best-effort only. Returning a known-good fallback is enough to avoid crashes.
61
+ }
62
+ return candidate;
63
+ }
64
+ return "/";
65
+ }
66
+ }
67
+ export function resolveBgShellPersistenceCwd(cachedCwd, liveCwd = undefined, pathExists = existsSync) {
68
+ const resolvedLiveCwd = liveCwd ?? getBgShellLiveCwd(cachedCwd, pathExists);
40
69
  const cachedIsAutoWorktree = /(?:^|[\\/])\.gsd[\\/]worktrees[\\/]/.test(cachedCwd);
41
70
  if (!cachedIsAutoWorktree)
42
71
  return cachedCwd;
43
- if (cachedCwd === liveCwd && pathExists(cachedCwd))
72
+ if (cachedCwd === resolvedLiveCwd && pathExists(cachedCwd))
44
73
  return cachedCwd;
45
74
  if (!pathExists(cachedCwd))
46
- return liveCwd;
47
- if (liveCwd !== cachedCwd)
48
- return liveCwd;
75
+ return resolvedLiveCwd;
76
+ if (resolvedLiveCwd !== cachedCwd)
77
+ return resolvedLiveCwd;
49
78
  return cachedCwd;
50
79
  }
@@ -29,6 +29,14 @@ import { writeUnitRuntimeRecord } from "../unit-runtime.js";
29
29
  export function _resolveReportBasePath(s) {
30
30
  return s.originalBasePath || s.basePath;
31
31
  }
32
+ /**
33
+ * Resolve the authoritative project base for dispatch guards.
34
+ * Prior-milestone completion lives at the project root, even when the active
35
+ * unit is running inside an auto worktree.
36
+ */
37
+ export function _resolveDispatchGuardBasePath(s) {
38
+ return s.originalBasePath || s.basePath;
39
+ }
32
40
  /**
33
41
  * Generate and write an HTML milestone report snapshot.
34
42
  * Extracted from the milestone-transition block in autoLoop.
@@ -459,7 +467,8 @@ export async function runDispatch(ic, preData, loopState) {
459
467
  else if (preDispatchResult.prompt) {
460
468
  prompt = preDispatchResult.prompt;
461
469
  }
462
- const priorSliceBlocker = deps.getPriorSliceCompletionBlocker(s.basePath, deps.getMainBranch(s.basePath), unitType, unitId);
470
+ const guardBasePath = _resolveDispatchGuardBasePath(s);
471
+ const priorSliceBlocker = deps.getPriorSliceCompletionBlocker(guardBasePath, deps.getMainBranch(guardBasePath), unitType, unitId);
463
472
  if (priorSliceBlocker) {
464
473
  await deps.stopAuto(ctx, pi, priorSliceBlocker);
465
474
  debugLog("autoLoop", { phase: "exit", reason: "prior-slice-blocker" });
@@ -115,7 +115,7 @@ export const DISPATCH_RULES = [
115
115
  unitType: "run-uat",
116
116
  unitId: `${mid}/${sliceId}`,
117
117
  prompt: await buildRunUatPrompt(mid, sliceId, relSliceFile(basePath, mid, sliceId, "UAT"), uatContent ?? "", basePath),
118
- pauseAfterDispatch: uatType !== "artifact-driven" && uatType !== "browser-executable" && uatType !== "runtime-executable",
118
+ pauseAfterDispatch: !process.env.GSD_HEADLESS && uatType !== "artifact-driven" && uatType !== "browser-executable" && uatType !== "runtime-executable",
119
119
  };
120
120
  },
121
121
  },
@@ -8,6 +8,22 @@ import { classifyUnitComplexity, tierLabel } from "./complexity-classifier.js";
8
8
  import { resolveModelForComplexity, escalateTier } from "./model-router.js";
9
9
  import { getLedger, getProjectTotals } from "./metrics.js";
10
10
  import { unitPhaseLabel } from "./auto-dashboard.js";
11
+ export function resolvePreferredModelConfig(unitType, autoModeStartModel) {
12
+ const explicitConfig = resolveModelWithFallbacksForUnit(unitType);
13
+ if (explicitConfig)
14
+ return explicitConfig;
15
+ const routingConfig = resolveDynamicRoutingConfig();
16
+ if (!routingConfig.enabled || !routingConfig.tier_models)
17
+ return undefined;
18
+ const ceilingModel = routingConfig.tier_models.heavy
19
+ ?? (autoModeStartModel ? `${autoModeStartModel.provider}/${autoModeStartModel.id}` : undefined);
20
+ if (!ceilingModel)
21
+ return undefined;
22
+ return {
23
+ primary: ceilingModel,
24
+ fallbacks: [],
25
+ };
26
+ }
11
27
  /**
12
28
  * Select and apply the appropriate model for a unit dispatch.
13
29
  * Handles: per-unit-type model preferences, dynamic complexity routing,
@@ -16,7 +32,7 @@ import { unitPhaseLabel } from "./auto-dashboard.js";
16
32
  * Returns routing metadata for metrics tracking.
17
33
  */
18
34
  export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, prefs, verbose, autoModeStartModel, retryContext) {
19
- const modelConfig = resolveModelWithFallbacksForUnit(unitType);
35
+ const modelConfig = resolvePreferredModelConfig(unitType, autoModeStartModel);
20
36
  let routing = null;
21
37
  if (modelConfig) {
22
38
  const availableModels = ctx.modelRegistry.getAvailable();
@@ -66,6 +66,10 @@ function buildSourceFilePaths(base, mid, sid) {
66
66
  if (existsSync(decisionsPath)) {
67
67
  paths.push(`- **Decisions**: \`${relGsdRootFile("DECISIONS")}\``);
68
68
  }
69
+ const queuePath = resolveGsdRootFile(base, "QUEUE");
70
+ if (existsSync(queuePath)) {
71
+ paths.push(`- **Queue**: \`${relGsdRootFile("QUEUE")}\``);
72
+ }
69
73
  const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
70
74
  if (contextPath) {
71
75
  paths.push(`- **Milestone Context**: \`${relMilestoneFile(base, mid, "CONTEXT")}\``);
@@ -812,6 +816,11 @@ export async function buildPlanMilestonePrompt(mid, midTitle, base, level) {
812
816
  if (decisionsInline)
813
817
  inlined.push(decisionsInline);
814
818
  }
819
+ const queuePath = resolveGsdRootFile(base, "QUEUE");
820
+ if (existsSync(queuePath)) {
821
+ const queueInline = await inlineFileSmart(queuePath, relGsdRootFile("QUEUE"), "Project Queue", `${mid} ${midTitle}`);
822
+ inlined.push(queueInline);
823
+ }
815
824
  const knowledgeInlinePM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
816
825
  if (knowledgeInlinePM)
817
826
  inlined.push(knowledgeInlinePM);
@@ -6,14 +6,27 @@ import { registerDynamicTools } from "./dynamic-tools.js";
6
6
  import { registerJournalTools } from "./journal-tools.js";
7
7
  import { registerHooks } from "./register-hooks.js";
8
8
  import { registerShortcuts } from "./register-shortcuts.js";
9
+ export function handleRecoverableExtensionProcessError(err) {
10
+ if (err.code === "EPIPE") {
11
+ process.exit(0);
12
+ }
13
+ if (err.code === "ENOENT") {
14
+ const syscall = err.syscall;
15
+ if (syscall?.startsWith("spawn")) {
16
+ process.stderr.write(`[gsd] spawn ENOENT: ${err.path ?? "unknown"} — command not found\n`);
17
+ return true;
18
+ }
19
+ if (syscall === "uv_cwd") {
20
+ process.stderr.write(`[gsd] ENOENT (${syscall}): ${err.message}\n`);
21
+ return true;
22
+ }
23
+ }
24
+ return false;
25
+ }
9
26
  function installEpipeGuard() {
10
27
  if (!process.listeners("uncaughtException").some((listener) => listener.name === "_gsdEpipeGuard")) {
11
28
  const _gsdEpipeGuard = (err) => {
12
- if (err.code === "EPIPE") {
13
- process.exit(0);
14
- }
15
- if (err.code === "ENOENT" && err.syscall?.startsWith("spawn")) {
16
- process.stderr.write(`[gsd] spawn ENOENT: ${err.path ?? "unknown"} — command not found\n`);
29
+ if (handleRecoverableExtensionProcessError(err)) {
17
30
  return;
18
31
  }
19
32
  throw err;
@@ -1 +1 @@
1
- mWBOLPnUoeaTGiD-vhu5O
1
+ 8yiPxQ52ue_s6qdrrAxsH
@@ -3,44 +3,44 @@
3
3
  "/_global-error/page": "/_global-error",
4
4
  "/api/bridge-terminal/resize/route": "/api/bridge-terminal/resize",
5
5
  "/api/boot/route": "/api/boot",
6
- "/api/bridge-terminal/input/route": "/api/bridge-terminal/input",
7
- "/api/bridge-terminal/stream/route": "/api/bridge-terminal/stream",
8
- "/api/dev-mode/route": "/api/dev-mode",
6
+ "/api/browse-directories/route": "/api/browse-directories",
9
7
  "/api/cleanup/route": "/api/cleanup",
8
+ "/api/bridge-terminal/input/route": "/api/bridge-terminal/input",
10
9
  "/api/doctor/route": "/api/doctor",
11
- "/api/browse-directories/route": "/api/browse-directories",
10
+ "/api/captures/route": "/api/captures",
11
+ "/api/dev-mode/route": "/api/dev-mode",
12
12
  "/api/export-data/route": "/api/export-data",
13
+ "/api/bridge-terminal/stream/route": "/api/bridge-terminal/stream",
13
14
  "/api/forensics/route": "/api/forensics",
14
- "/api/captures/route": "/api/captures",
15
- "/api/git/route": "/api/git",
16
15
  "/api/history/route": "/api/history",
17
- "/api/hooks/route": "/api/hooks",
18
- "/api/knowledge/route": "/api/knowledge",
16
+ "/api/git/route": "/api/git",
19
17
  "/api/inspect/route": "/api/inspect",
20
18
  "/api/experimental/route": "/api/experimental",
19
+ "/api/hooks/route": "/api/hooks",
20
+ "/api/knowledge/route": "/api/knowledge",
21
21
  "/api/live-state/route": "/api/live-state",
22
+ "/api/projects/route": "/api/projects",
23
+ "/api/onboarding/route": "/api/onboarding",
22
24
  "/api/preferences/route": "/api/preferences",
23
25
  "/api/recovery/route": "/api/recovery",
24
- "/api/onboarding/route": "/api/onboarding",
25
- "/api/projects/route": "/api/projects",
26
26
  "/api/session/browser/route": "/api/session/browser",
27
- "/api/session/command/route": "/api/session/command",
28
27
  "/api/session/events/route": "/api/session/events",
29
- "/api/session/manage/route": "/api/session/manage",
28
+ "/api/session/command/route": "/api/session/command",
30
29
  "/api/settings-data/route": "/api/settings-data",
31
- "/api/shutdown/route": "/api/shutdown",
32
30
  "/api/skill-health/route": "/api/skill-health",
31
+ "/api/shutdown/route": "/api/shutdown",
33
32
  "/api/steer/route": "/api/steer",
34
- "/api/terminal/input/route": "/api/terminal/input",
35
- "/api/terminal/resize/route": "/api/terminal/resize",
33
+ "/api/session/manage/route": "/api/session/manage",
34
+ "/api/files/route": "/api/files",
36
35
  "/api/switch-root/route": "/api/switch-root",
37
- "/api/terminal/sessions/route": "/api/terminal/sessions",
36
+ "/api/terminal/resize/route": "/api/terminal/resize",
38
37
  "/api/terminal/stream/route": "/api/terminal/stream",
39
- "/api/undo/route": "/api/undo",
40
- "/api/files/route": "/api/files",
38
+ "/api/terminal/sessions/route": "/api/terminal/sessions",
39
+ "/api/terminal/input/route": "/api/terminal/input",
40
+ "/api/update/route": "/api/update",
41
41
  "/api/terminal/upload/route": "/api/terminal/upload",
42
42
  "/api/visualizer/route": "/api/visualizer",
43
- "/api/update/route": "/api/update",
44
43
  "/api/remote-questions/route": "/api/remote-questions",
44
+ "/api/undo/route": "/api/undo",
45
45
  "/page": "/"
46
46
  }