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.
- package/dist/cli.js +19 -19
- package/dist/headless-ui.d.ts +29 -3
- package/dist/headless-ui.js +221 -28
- package/dist/headless.d.ts +11 -0
- package/dist/headless.js +238 -41
- package/dist/resources/extensions/bg-shell/bg-shell-lifecycle.js +2 -2
- package/dist/resources/extensions/bg-shell/utilities.js +34 -5
- package/dist/resources/extensions/gsd/auto/phases.js +10 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
- package/dist/resources/extensions/gsd/auto-model-selection.js +17 -1
- package/dist/resources/extensions/gsd/auto-prompts.js +9 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +18 -5
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
- 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 +4 -4
- 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 +19 -19
- package/dist/web/standalone/.next/server/chunks/2229.js +2 -2
- 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.82f2e2a838908338.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-bca0e732db0dcec3.js → webpack-70adf6e3be5479ce.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/dist/core/model-registry.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +14 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/model-registry.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -2
- 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/src/resources/extensions/bg-shell/bg-shell-lifecycle.ts +2 -2
- package/src/resources/extensions/bg-shell/utilities.ts +39 -4
- package/src/resources/extensions/gsd/auto/phases.ts +14 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
- package/src/resources/extensions/gsd/auto-model-selection.ts +21 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +15 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +19 -6
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/plan-milestone-queue-context.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/register-extension-guard.test.ts +59 -0
- package/dist/web/standalone/.next/static/chunks/4024.87fd909ae0110f50.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/{mWBOLPnUoeaTGiD-vhu5O → 8yiPxQ52ue_s6qdrrAxsH}/_buildManifest.js +0 -0
- /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
|
-
//
|
|
276
|
-
let
|
|
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
|
|
324
|
-
|
|
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
|
-
//
|
|
332
|
-
const
|
|
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' &&
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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
|
-
//
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
process.
|
|
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
|
-
|
|
426
|
-
|
|
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(
|
|
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
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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 =
|
|
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
|
-
|
|
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 ===
|
|
72
|
+
if (cachedCwd === resolvedLiveCwd && pathExists(cachedCwd))
|
|
44
73
|
return cachedCwd;
|
|
45
74
|
if (!pathExists(cachedCwd))
|
|
46
|
-
return
|
|
47
|
-
if (
|
|
48
|
-
return
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
35
|
-
"/api/
|
|
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/
|
|
36
|
+
"/api/terminal/resize/route": "/api/terminal/resize",
|
|
38
37
|
"/api/terminal/stream/route": "/api/terminal/stream",
|
|
39
|
-
"/api/
|
|
40
|
-
"/api/
|
|
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
|
}
|