gsd-pi 2.53.0 → 2.54.0-dev.e1efc1a

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 (167) hide show
  1. package/dist/headless-ui.d.ts +2 -2
  2. package/dist/headless-ui.js +18 -15
  3. package/dist/headless.d.ts +11 -0
  4. package/dist/headless.js +178 -38
  5. package/dist/web/standalone/.next/BUILD_ID +1 -1
  6. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  7. package/dist/web/standalone/.next/build-manifest.json +3 -3
  8. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  9. package/dist/web/standalone/.next/required-server-files.json +4 -4
  10. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  11. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  13. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  21. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  24. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  25. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  27. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  31. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  32. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  33. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  34. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  35. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  36. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  37. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  38. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  39. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  40. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  41. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  42. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  43. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  44. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  45. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  46. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  47. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  48. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  49. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  50. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  51. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  52. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  53. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  60. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  77. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  83. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  97. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  99. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  101. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  103. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/index.html +1 -1
  113. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  114. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  115. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  116. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  118. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/page.js +2 -2
  120. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  122. package/dist/web/standalone/.next/server/chunks/2229.js +1 -1
  123. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  124. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/middleware.js +2 -2
  126. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  128. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  129. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  130. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  131. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  132. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  133. package/dist/web/standalone/.next/static/chunks/app/page-b950e4e384cc62b3.js +1 -0
  134. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  135. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  136. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  137. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  138. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  139. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  140. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  141. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  142. package/dist/web/standalone/server.js +1 -1
  143. package/package.json +1 -1
  144. package/packages/mcp-server/README.md +6 -6
  145. package/packages/mcp-server/package.json +14 -4
  146. package/packages/mcp-server/src/cli.ts +1 -1
  147. package/packages/mcp-server/src/index.ts +1 -1
  148. package/packages/mcp-server/src/mcp-server.test.ts +2 -2
  149. package/packages/mcp-server/src/session-manager.ts +2 -2
  150. package/packages/mcp-server/src/types.ts +1 -1
  151. package/packages/pi-coding-agent/package.json +1 -1
  152. package/packages/rpc-client/README.md +125 -0
  153. package/packages/rpc-client/examples/basic-usage.ts +13 -0
  154. package/packages/rpc-client/package.json +17 -3
  155. package/packages/rpc-client/src/index.ts +10 -0
  156. package/packages/rpc-client/src/jsonl.ts +64 -0
  157. package/packages/rpc-client/src/rpc-client.test.ts +568 -0
  158. package/packages/rpc-client/src/rpc-client.ts +666 -0
  159. package/packages/rpc-client/src/rpc-types.ts +399 -0
  160. package/packages/rpc-client/tsconfig.examples.json +17 -0
  161. package/packages/rpc-client/tsconfig.json +24 -0
  162. package/pkg/package.json +1 -1
  163. package/dist/web/standalone/.next/static/chunks/app/page-fbecd1237e2d6d1f.js +0 -1
  164. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  165. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  166. /package/dist/web/standalone/.next/static/{mWBOLPnUoeaTGiD-vhu5O → nISuDzAIpGYC-DVTvs4Po}/_buildManifest.js +0 -0
  167. /package/dist/web/standalone/.next/static/{mWBOLPnUoeaTGiD-vhu5O → nISuDzAIpGYC-DVTvs4Po}/_ssgManifest.js +0 -0
@@ -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, writeToStdin: (data: string) => void): void;
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(stdinWriter: (data: string) => void, client: RpcClient, onResponse: (id: string) => void): () => void;
23
+ export declare function startSupervisedStdinReader(client: RpcClient, onResponse: (id: string) => void): () => void;
@@ -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, serializeJsonLine } from '@gsd/pi-coding-agent';
8
+ import { attachJsonlLineReader } from '@gsd/pi-coding-agent';
9
9
  // ---------------------------------------------------------------------------
10
10
  // Extension UI Auto-Responder
11
11
  // ---------------------------------------------------------------------------
12
- export function handleExtensionUIRequest(event, writeToStdin) {
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
- response = { type: 'extension_ui_response', id, value: selected };
25
+ client.sendUIResponse(id, { value: selected });
27
26
  break;
28
27
  }
29
28
  case 'confirm':
30
- response = { type: 'extension_ui_response', id, confirmed: true };
29
+ client.sendUIResponse(id, { confirmed: true });
31
30
  break;
32
31
  case 'input':
33
- response = { type: 'extension_ui_response', id, value: '' };
32
+ client.sendUIResponse(id, { value: '' });
34
33
  break;
35
34
  case 'editor':
36
- response = { type: 'extension_ui_response', id, value: event.prefill ?? '' };
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
- response = { type: 'extension_ui_response', id, value: '' };
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
- response = { type: 'extension_ui_response', id, cancelled: true };
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(stdinWriter, client, onResponse) {
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
- stdinWriter(line + '\n');
95
- if (typeof msg.id === 'string') {
96
- onResponse(msg.id);
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;
@@ -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
- // Stdin writer for sending extension_ui_response to child
276
- let stdinWriter = null;
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 mode: forward events as JSONL to stdout (filtered if --events)
324
- if (options.json) {
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' && stdinWriter) {
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, stdinWriter)) {
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, stdinWriter);
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, stdinWriter);
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
- client.stop().finally(() => {
397
- if (timeoutTimer)
398
- clearTimeout(timeoutTimer);
399
- if (idleTimer)
400
- clearTimeout(idleTimer);
401
- process.exit(exitCode);
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
- // 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);
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
- stdinWriter = (data) => {
426
- internalProcess.stdin.write(data);
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(stdinWriter, client, (id) => {
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.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
- });
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
  }
@@ -1 +1 @@
1
- mWBOLPnUoeaTGiD-vhu5O
1
+ nISuDzAIpGYC-DVTvs4Po
@@ -1,46 +1,46 @@
1
1
  {
2
- "/_not-found/page": "/_not-found",
3
2
  "/_global-error/page": "/_global-error",
3
+ "/_not-found/page": "/_not-found",
4
+ "/api/bridge-terminal/input/route": "/api/bridge-terminal/input",
4
5
  "/api/bridge-terminal/resize/route": "/api/bridge-terminal/resize",
5
6
  "/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",
7
+ "/api/browse-directories/route": "/api/browse-directories",
8
8
  "/api/dev-mode/route": "/api/dev-mode",
9
+ "/api/bridge-terminal/stream/route": "/api/bridge-terminal/stream",
10
+ "/api/captures/route": "/api/captures",
11
+ "/api/export-data/route": "/api/export-data",
9
12
  "/api/cleanup/route": "/api/cleanup",
10
13
  "/api/doctor/route": "/api/doctor",
11
- "/api/browse-directories/route": "/api/browse-directories",
12
- "/api/export-data/route": "/api/export-data",
14
+ "/api/history/route": "/api/history",
13
15
  "/api/forensics/route": "/api/forensics",
14
- "/api/captures/route": "/api/captures",
15
16
  "/api/git/route": "/api/git",
16
- "/api/history/route": "/api/history",
17
- "/api/hooks/route": "/api/hooks",
18
17
  "/api/knowledge/route": "/api/knowledge",
19
- "/api/inspect/route": "/api/inspect",
18
+ "/api/hooks/route": "/api/hooks",
20
19
  "/api/experimental/route": "/api/experimental",
21
- "/api/live-state/route": "/api/live-state",
20
+ "/api/inspect/route": "/api/inspect",
22
21
  "/api/preferences/route": "/api/preferences",
23
- "/api/recovery/route": "/api/recovery",
22
+ "/api/live-state/route": "/api/live-state",
24
23
  "/api/onboarding/route": "/api/onboarding",
24
+ "/api/recovery/route": "/api/recovery",
25
25
  "/api/projects/route": "/api/projects",
26
26
  "/api/session/browser/route": "/api/session/browser",
27
27
  "/api/session/command/route": "/api/session/command",
28
28
  "/api/session/events/route": "/api/session/events",
29
- "/api/session/manage/route": "/api/session/manage",
30
29
  "/api/settings-data/route": "/api/settings-data",
31
- "/api/shutdown/route": "/api/shutdown",
32
- "/api/skill-health/route": "/api/skill-health",
33
30
  "/api/steer/route": "/api/steer",
31
+ "/api/session/manage/route": "/api/session/manage",
32
+ "/api/skill-health/route": "/api/skill-health",
33
+ "/api/shutdown/route": "/api/shutdown",
34
34
  "/api/terminal/input/route": "/api/terminal/input",
35
35
  "/api/terminal/resize/route": "/api/terminal/resize",
36
36
  "/api/switch-root/route": "/api/switch-root",
37
- "/api/terminal/sessions/route": "/api/terminal/sessions",
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/remote-questions/route": "/api/remote-questions",
41
40
  "/api/terminal/upload/route": "/api/terminal/upload",
42
- "/api/visualizer/route": "/api/visualizer",
41
+ "/api/undo/route": "/api/undo",
43
42
  "/api/update/route": "/api/update",
44
- "/api/remote-questions/route": "/api/remote-questions",
43
+ "/api/visualizer/route": "/api/visualizer",
44
+ "/api/files/route": "/api/files",
45
45
  "/page": "/"
46
46
  }
@@ -4,14 +4,14 @@
4
4
  ],
5
5
  "devFiles": [],
6
6
  "lowPriorityFiles": [
7
- "static/mWBOLPnUoeaTGiD-vhu5O/_buildManifest.js",
8
- "static/mWBOLPnUoeaTGiD-vhu5O/_ssgManifest.js"
7
+ "static/nISuDzAIpGYC-DVTvs4Po/_buildManifest.js",
8
+ "static/nISuDzAIpGYC-DVTvs4Po/_ssgManifest.js"
9
9
  ],
10
10
  "rootMainFiles": [
11
11
  "static/chunks/webpack-bca0e732db0dcec3.js",
12
12
  "static/chunks/4bd1b696-e5d7c65570c947b7.js",
13
13
  "static/chunks/3794-337d1ca25ad99a89.js",
14
- "static/chunks/main-app-d3d4c336195465f9.js"
14
+ "static/chunks/main-app-fdab67f7802d7832.js"
15
15
  ],
16
16
  "rootMainFilesTree": {},
17
17
  "pages": {
@@ -78,8 +78,8 @@
78
78
  "dynamicRoutes": {},
79
79
  "notFoundRoutes": [],
80
80
  "preview": {
81
- "previewModeId": "d295367633ac46f8d8b141bc50fa0ae0",
82
- "previewModeSigningKey": "544b70ab915f56609f2afe474d867f217ec3842f3ef6b58de09607bbafb0e432",
83
- "previewModeEncryptionKey": "389e809ac429411dec37ec0518d948521954643fc0f04066066ec0af454d99a5"
81
+ "previewModeId": "421c8ae4252416128c6285506cccf021",
82
+ "previewModeSigningKey": "55cb8a1dd17e322add989f48f333adbf1322c54b5b786db308294dc5891a464c",
83
+ "previewModeEncryptionKey": "48301e074f6610a318f4af6a7fb298f6ace2a45887285828301e9bba0ace5432"
84
84
  }
85
85
  }
@@ -100,7 +100,7 @@
100
100
  "transform": "lodash/{{member}}"
101
101
  }
102
102
  },
103
- "outputFileTracingRoot": "/home/runner/_work/gsd-2/gsd-2",
103
+ "outputFileTracingRoot": "/__w/gsd-2/gsd-2",
104
104
  "cacheComponents": false,
105
105
  "cacheLife": {
106
106
  "default": {
@@ -157,7 +157,7 @@
157
157
  "proxyPrefetch": "flexible",
158
158
  "optimisticClientCache": true,
159
159
  "manualClientBasePath": false,
160
- "cpus": 9,
160
+ "cpus": 5,
161
161
  "memoryBasedWorkersCount": false,
162
162
  "imgOptConcurrency": null,
163
163
  "imgOptTimeoutInSeconds": 7,
@@ -297,11 +297,11 @@
297
297
  "node-pty"
298
298
  ],
299
299
  "turbopack": {
300
- "root": "/home/runner/_work/gsd-2/gsd-2"
300
+ "root": "/__w/gsd-2/gsd-2"
301
301
  },
302
302
  "distDirRoot": ".next"
303
303
  },
304
- "appDir": "/home/runner/_work/gsd-2/gsd-2/web",
304
+ "appDir": "/__w/gsd-2/gsd-2/web",
305
305
  "relativeAppDir": "web",
306
306
  "files": [
307
307
  ".next/routes-manifest.json",