commandmate 0.25.0 → 0.26.1
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/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +141 -141
- package/.next/app-path-routes-manifest.json +28 -28
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +12 -12
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +28 -28
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/4455.js +1 -1
- package/.next/server/chunks/5959.js +4 -4
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6274.js +12 -9
- package/.next/server/chunks/7458.js +6 -6
- package/.next/server/chunks/9373.js +4 -3
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages-manifest.json +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{6381-51f9c4a128aadc0b.js → 6381-0e097c0286155883.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/{page-207c69540fd856ef.js → page-fdfca039b7f5837e.js} +1 -1
- package/.next/trace +12 -12
- package/dist/cli/commands/task.d.ts.map +1 -1
- package/dist/cli/commands/task.js +2 -1
- package/dist/cli/commands/verify.d.ts +14 -0
- package/dist/cli/commands/verify.d.ts.map +1 -1
- package/dist/cli/commands/verify.js +109 -3
- package/dist/cli/commands/wait.d.ts +2 -0
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +184 -4
- package/dist/cli/types/api-responses.d.ts +172 -0
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/types/index.d.ts +31 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/types/index.js +26 -0
- package/dist/cli/utils/verify-runner.d.ts +36 -1
- package/dist/cli/utils/verify-runner.d.ts.map +1 -1
- package/dist/cli/utils/verify-runner.js +114 -6
- package/dist/server/src/lib/auto-yes-state.js +108 -3
- package/dist/server/src/lib/cli-tools/antigravity.js +8 -4
- package/dist/server/src/lib/cli-tools/codex.js +5 -1
- package/dist/server/src/lib/cli-tools/copilot.js +5 -1
- package/dist/server/src/lib/cli-tools/gemini.js +15 -15
- package/dist/server/src/lib/cli-tools/opencode.js +9 -3
- package/dist/server/src/lib/detection/upstream-faults.js +120 -0
- package/dist/server/src/lib/hooks/permission-decision-service.js +13 -5
- package/dist/server/src/lib/hooks/sources/antigravity/hooks-config.js +18 -11
- package/dist/server/src/lib/hooks/sources/antigravity/source.js +10 -9
- package/dist/server/src/lib/hooks/sources/claude/source.js +5 -2
- package/dist/server/src/lib/hooks/sources/codex/hooks-config.js +26 -18
- package/dist/server/src/lib/hooks/sources/codex/source.js +6 -9
- package/dist/server/src/lib/hooks/sources/copilot/hook-settings.js +22 -12
- package/dist/server/src/lib/hooks/sources/copilot/source.js +1 -1
- package/dist/server/src/lib/hooks/sources/define-source.js +6 -4
- package/dist/server/src/lib/hooks/sources/gemini/settings-generator.js +14 -8
- package/dist/server/src/lib/hooks/sources/gemini/source.js +29 -29
- package/dist/server/src/lib/hooks/sources/index.js +4 -1
- package/dist/server/src/lib/hooks/sources/launch-command.js +61 -0
- package/dist/server/src/lib/hooks/sources/legacy-relay.js +2 -1
- package/dist/server/src/lib/hooks/sources/opencode/source.js +10 -7
- package/dist/server/src/lib/polling/prompt-dedup-state.js +95 -0
- package/dist/server/src/lib/polling/response-checker.js +12 -0
- package/dist/server/src/lib/polling/response-poller-core.js +11 -1
- package/dist/server/src/lib/session/agent-session-lifecycle.js +28 -7
- package/dist/server/src/lib/session/claude-session.js +5 -1
- package/dist/server/src/lib/session/current-output-builder.js +34 -0
- package/dist/server/src/lib/verification/env-snapshot.js +21 -3
- package/dist/server/src/lib/verification/gate-runner.js +258 -7
- package/dist/server/src/lib/verification/machine-lock.js +204 -0
- package/dist/server/src/lib/verification/scope-gate.js +126 -13
- package/dist/server/src/lib/verification/verify-config.js +85 -3
- package/dist/server/src/lib/verification/worktree-index.js +140 -0
- package/package.json +1 -1
- /package/.next/static/{ehkNc5xDMDkXlua08Bwq0 → wyFLOpmKpr45BHZ4svYpE}/_buildManifest.js +0 -0
- /package/.next/static/{ehkNc5xDMDkXlua08Bwq0 → wyFLOpmKpr45BHZ4svYpE}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/task.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6BpC,wBAAgB,iBAAiB,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/task.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6BpC,wBAAgB,iBAAiB,IAAI,OAAO,CA4H3C"}
|
|
@@ -108,7 +108,8 @@ function createTaskCommand() {
|
|
|
108
108
|
// — verify.yaml can be opened, this cannot. Printing the id alone would
|
|
109
109
|
// leave a reader unable to tell which criterion the run was judged by.
|
|
110
110
|
for (const gate of task.contract.verify.gateDefinitions ?? []) {
|
|
111
|
-
|
|
111
|
+
const mutex = gate.mutex ? `, mutex=${gate.mutex}` : '';
|
|
112
|
+
console.log(`GATE-DEF: ${gate.id} ${gate.command} (timeoutSec=${gate.timeoutSec}${mutex})`);
|
|
112
113
|
}
|
|
113
114
|
console.log(`AUTO-YES: ${task.contract.autoYes.mode ?? 'unset'}`);
|
|
114
115
|
console.log(`CREATED: ${task.createdAt}`);
|
|
@@ -15,5 +15,19 @@
|
|
|
15
15
|
* succeeds exits 0 even when it finds nothing.
|
|
16
16
|
*/
|
|
17
17
|
import { Command } from 'commander';
|
|
18
|
+
import type { VerificationScopeDetail } from '../types/api-responses';
|
|
19
|
+
/**
|
|
20
|
+
* Read the scope gate's report back into structured evidence.
|
|
21
|
+
*
|
|
22
|
+
* Parsing the gate's own prose is not the shape anyone would choose, but it is
|
|
23
|
+
* the only one available: `verification_gate_results` has no column for
|
|
24
|
+
* structured detail, and the CLI's `formatDetail` already reads work-evidence's
|
|
25
|
+
* counts out of `logTail` the same way (src/cli/utils/verify-runner.ts).
|
|
26
|
+
*
|
|
27
|
+
* Returns null when the tail is not a report — a skip, an error, or a gate that
|
|
28
|
+
* was never entered all write a sentence — because an empty `admitted` on those
|
|
29
|
+
* would claim the gate looked and found nothing.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseScopeEvidence(logTail: string | null | undefined): VerificationScopeDetail | null;
|
|
18
32
|
export declare function createVerifyCommand(): Command;
|
|
19
33
|
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,KAAK,EAMV,uBAAuB,EAExB,MAAM,wBAAwB,CAAC;AAmChC;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,uBAAuB,GAAG,IAAI,CA8BrG;AAyED,wBAAgB,mBAAmB,IAAI,OAAO,CA0D7C"}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* succeeds exits 0 even when it finds nothing.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.parseScopeEvidence = parseScopeEvidence;
|
|
19
20
|
exports.createVerifyCommand = createVerifyCommand;
|
|
20
21
|
const commander_1 = require("commander");
|
|
21
22
|
const types_1 = require("../types");
|
|
@@ -27,6 +28,103 @@ const MAX_HISTORY_DAYS = 90;
|
|
|
27
28
|
const MAX_HISTORY_LIMIT = 500;
|
|
28
29
|
/** Gate statuses that mean the gate did not pass. `skipped` is not a failure. */
|
|
29
30
|
const FAILED_GATE_STATUSES = new Set(['failed', 'timeout', 'error']);
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// Scope-gate evidence (Issue #1841)
|
|
33
|
+
// =============================================================================
|
|
34
|
+
/** Built-in gate id; mirrors SCOPE_GATE_ID in lib/verification/verify-config.ts. */
|
|
35
|
+
const SCOPE_GATE_ID = 'scope';
|
|
36
|
+
/**
|
|
37
|
+
* Separator the scope gate puts between a path and the rule that decided it.
|
|
38
|
+
*
|
|
39
|
+
* Mirrors SCOPE_PATTERN_ARROW in `src/lib/verification/scope-gate.ts`. The two
|
|
40
|
+
* cannot import each other — tsconfig.cli.json compiles `src/cli/**` alone,
|
|
41
|
+
* with no path aliases, so the CLI bundle never pulls in the server's
|
|
42
|
+
* dependency graph — so a round-trip test feeds a real gate report through this
|
|
43
|
+
* parser instead of trusting the constants to be edited together.
|
|
44
|
+
*/
|
|
45
|
+
const SCOPE_PATTERN_ARROW = ' \u2190 ';
|
|
46
|
+
/**
|
|
47
|
+
* Read the scope gate's report back into structured evidence.
|
|
48
|
+
*
|
|
49
|
+
* Parsing the gate's own prose is not the shape anyone would choose, but it is
|
|
50
|
+
* the only one available: `verification_gate_results` has no column for
|
|
51
|
+
* structured detail, and the CLI's `formatDetail` already reads work-evidence's
|
|
52
|
+
* counts out of `logTail` the same way (src/cli/utils/verify-runner.ts).
|
|
53
|
+
*
|
|
54
|
+
* Returns null when the tail is not a report — a skip, an error, or a gate that
|
|
55
|
+
* was never entered all write a sentence — because an empty `admitted` on those
|
|
56
|
+
* would claim the gate looked and found nothing.
|
|
57
|
+
*/
|
|
58
|
+
function parseScopeEvidence(logTail) {
|
|
59
|
+
if (!logTail)
|
|
60
|
+
return null;
|
|
61
|
+
const summary = /^scope: baseRef=.* changed=(\d+) violations=(\d+)$/m.exec(logTail);
|
|
62
|
+
if (!summary)
|
|
63
|
+
return null;
|
|
64
|
+
const changed = Number(summary[1]);
|
|
65
|
+
const violationTotal = Number(summary[2]);
|
|
66
|
+
const admitted = [];
|
|
67
|
+
const violations = [];
|
|
68
|
+
for (const line of logTail.split('\n')) {
|
|
69
|
+
if (line.startsWith(' + ')) {
|
|
70
|
+
const [path, pattern] = splitAtArrow(line.slice(4));
|
|
71
|
+
// The gate always names the admitting rule; a `+` line without one is
|
|
72
|
+
// output this parser does not understand, and inventing an empty pattern
|
|
73
|
+
// for it would put a claim in the JSON that nothing made.
|
|
74
|
+
if (pattern !== null)
|
|
75
|
+
admitted.push({ path, pattern });
|
|
76
|
+
}
|
|
77
|
+
else if (line.startsWith(' - ')) {
|
|
78
|
+
violations.push(splitAtArrow(line.slice(4))[0]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
admitted,
|
|
83
|
+
violations,
|
|
84
|
+
// From the summary line, not from the lists: both are capped at 100 by the
|
|
85
|
+
// report, and `changed` counts every path either way. Admitted is the
|
|
86
|
+
// remainder because the gate classifies each changed path exactly once.
|
|
87
|
+
totals: { changed, admitted: changed - violationTotal, violations: violationTotal },
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Split `<path> \u2190 <pattern>`, keeping the whole line as the path when no
|
|
92
|
+
* arrow is present (an allow-miss violation has no rule to name).
|
|
93
|
+
*
|
|
94
|
+
* Split at the *last* arrow so a path containing the sequence still yields the
|
|
95
|
+
* pattern the gate wrote.
|
|
96
|
+
*/
|
|
97
|
+
function splitAtArrow(text) {
|
|
98
|
+
const at = text.lastIndexOf(SCOPE_PATTERN_ARROW);
|
|
99
|
+
if (at === -1)
|
|
100
|
+
return [text, null];
|
|
101
|
+
return [text.slice(0, at), text.slice(at + SCOPE_PATTERN_ARROW.length)];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Attach the evidence the CLI parses out of gate logs before a run is printed
|
|
105
|
+
* as JSON: {@link parseScopeEvidence} on the scope gate (Issue #1841), and the
|
|
106
|
+
* retry record on any gate that was re-run (Issue #1772).
|
|
107
|
+
*
|
|
108
|
+
* Copies rather than mutates: the run object is also what the polling loop
|
|
109
|
+
* reported from, and a JSON-only field has no business appearing in it.
|
|
110
|
+
*/
|
|
111
|
+
function withGateEvidence(run) {
|
|
112
|
+
const gates = run.gates ?? [];
|
|
113
|
+
return {
|
|
114
|
+
...run,
|
|
115
|
+
gates: gates.map((gate) => {
|
|
116
|
+
const scope = gate.gateId === SCOPE_GATE_ID ? parseScopeEvidence(gate.logTail) : null;
|
|
117
|
+
const flaky = (0, verify_runner_1.parseFlakyMarker)(gate.logTail);
|
|
118
|
+
if (!scope && !flaky)
|
|
119
|
+
return gate;
|
|
120
|
+
return {
|
|
121
|
+
...gate,
|
|
122
|
+
...(scope ? { scope } : {}),
|
|
123
|
+
...(flaky ? { flaky } : {}),
|
|
124
|
+
};
|
|
125
|
+
}),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
30
128
|
function formatSeconds(durationMs) {
|
|
31
129
|
return durationMs === null ? 'n/a' : `${(durationMs / 1000).toFixed(1)}s`;
|
|
32
130
|
}
|
|
@@ -96,7 +194,7 @@ function createVerifyCommand() {
|
|
|
96
194
|
suppressResultLine: options.json,
|
|
97
195
|
});
|
|
98
196
|
if (options.json && outcome.run) {
|
|
99
|
-
console.log(JSON.stringify(outcome.run));
|
|
197
|
+
console.log(JSON.stringify(withGateEvidence(outcome.run)));
|
|
100
198
|
}
|
|
101
199
|
process.exit(outcome.exitCode);
|
|
102
200
|
}
|
|
@@ -206,7 +304,11 @@ function addShowCommand(parent) {
|
|
|
206
304
|
}
|
|
207
305
|
const run = data.run;
|
|
208
306
|
if (json) {
|
|
209
|
-
|
|
307
|
+
// `show` is the forensic surface — the one a reader reaches for to
|
|
308
|
+
// reconstruct a run days later — so the scope evidence and the retry
|
|
309
|
+
// record have to be readable here too, not only on the run that
|
|
310
|
+
// produced them.
|
|
311
|
+
console.log(JSON.stringify(withGateEvidence(run), null, 2));
|
|
210
312
|
return;
|
|
211
313
|
}
|
|
212
314
|
console.log(`run #${run.id} ${run.status} worktree=${run.worktreeId} trigger=${run.trigger}`);
|
|
@@ -219,7 +321,11 @@ function addShowCommand(parent) {
|
|
|
219
321
|
// judged, so "which of these was the repository's own definition of
|
|
220
322
|
// passing" should not have to be inferred from an absent marker.
|
|
221
323
|
const source = gate.source ? ` src=${gate.source}` : '';
|
|
222
|
-
|
|
324
|
+
// Appended rather than replacing `status`, which is the stored column
|
|
325
|
+
// this view exists to show verbatim (#1772). The marker in the log
|
|
326
|
+
// tail below carries both runs' numbers.
|
|
327
|
+
const flaky = (0, verify_runner_1.parseFlakyMarker)(gate.logTail)?.outcome === 'flaky' ? ` ${verify_runner_1.FLAKY_GATE_LABEL}` : '';
|
|
328
|
+
console.log(` ${gate.gateId} ${gate.status} exit=${exit} ${formatSeconds(gate.durationMs)}${source}${flaky}`);
|
|
223
329
|
if (gate.logTail) {
|
|
224
330
|
for (const line of gate.logTail.split('\n')) {
|
|
225
331
|
console.log(` | ${line}`);
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* layer could not classify at all — Issue #1708. Both are reported as
|
|
10
10
|
* exit 10 with a distinguishing `type` rather than a new exit code, so
|
|
11
11
|
* callers that already branch on 10 keep working.)
|
|
12
|
+
* - 11: UPSTREAM_FAULT (--fail-on-upstream-fault only, Issue #1839: the agent
|
|
13
|
+
* came back to its composer with an upstream API failure on the frame)
|
|
12
14
|
* - 124: TIMEOUT (--timeout exceeded)
|
|
13
15
|
* Issue #1544 adds --verify / --require-work, which can turn a detected
|
|
14
16
|
* completion into 20 (VERIFY_FAILED) or 21 (NOT_STARTED).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wait.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/wait.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"wait.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/wait.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsqBpC,wBAAgB,iBAAiB,IAAI,OAAO,CA8G3C"}
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* layer could not classify at all — Issue #1708. Both are reported as
|
|
11
11
|
* exit 10 with a distinguishing `type` rather than a new exit code, so
|
|
12
12
|
* callers that already branch on 10 keep working.)
|
|
13
|
+
* - 11: UPSTREAM_FAULT (--fail-on-upstream-fault only, Issue #1839: the agent
|
|
14
|
+
* came back to its composer with an upstream API failure on the frame)
|
|
13
15
|
* - 124: TIMEOUT (--timeout exceeded)
|
|
14
16
|
* Issue #1544 adds --verify / --require-work, which can turn a detected
|
|
15
17
|
* completion into 20 (VERIFY_FAILED) or 21 (NOT_STARTED).
|
|
@@ -47,6 +49,65 @@ const SELECTION_LIST_PROMPT_TYPE = 'selection_list';
|
|
|
47
49
|
* whereas a new exit code reads as an infrastructure error to all of them.
|
|
48
50
|
*/
|
|
49
51
|
const UNCLASSIFIED_PROMPT_TYPE = 'unclassified';
|
|
52
|
+
/**
|
|
53
|
+
* Why `wait` decided the agent was done, printed on the completion line
|
|
54
|
+
* (Issue #1839).
|
|
55
|
+
*
|
|
56
|
+
* `Completed: <id>` used to be the whole story, which made two very different
|
|
57
|
+
* situations indistinguishable in a log: the agent's own `Stop` hook arrived, or
|
|
58
|
+
* nothing but the screen said so. The measurement behind this Issue is exactly
|
|
59
|
+
* that difference — a 529 storm returns Claude to its composer in ~3 s with the
|
|
60
|
+
* scraper reporting `ready`/`input_prompt` and no `Stop` ever sent — so the
|
|
61
|
+
* basis belongs next to the verdict rather than in a triage session afterwards.
|
|
62
|
+
*/
|
|
63
|
+
const COMPLETION_BASIS = {
|
|
64
|
+
/** The agent reported the turn ended, and the report postdates this wait's turn. */
|
|
65
|
+
HOOK_STOP: 'hook_stop',
|
|
66
|
+
/** The tmux session went away after we had seen it alive. */
|
|
67
|
+
SESSION_GONE: 'session_gone',
|
|
68
|
+
/** The terminal frame says the agent is back at its composer. Nothing corroborated it. */
|
|
69
|
+
SCRAPER_READY: 'scraper_ready',
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Structured event types that mean "a turn is under way" (Issue #1839).
|
|
73
|
+
*
|
|
74
|
+
* Mirrors AGENT_EVENT_TYPES in src/lib/hooks/agent-event-types.ts; duplicated
|
|
75
|
+
* rather than imported because the CLI bundle keeps its own copy of the API
|
|
76
|
+
* shapes (see api-responses.ts).
|
|
77
|
+
*
|
|
78
|
+
* `notification` is deliberately absent, and that is the one measured deviation
|
|
79
|
+
* from the design sketched in Issue #1839. The Issue proposed accepting
|
|
80
|
+
* `stop` **or** `idle_prompt` as the end of a turn; the live capture of
|
|
81
|
+
* 2026-08-20 (see docs/design/upstream-fault-turn-boundary-1839.md) shows
|
|
82
|
+
* Claude 2.1.236 emitting `Notification(idle_prompt)` 62 s into a turn that
|
|
83
|
+
* ran nothing at all, because the composer had been idle for a minute. Reading
|
|
84
|
+
* it as a turn boundary would have re-created the false completion this gate
|
|
85
|
+
* exists to stop, one minute later. Only `Stop` ends a turn.
|
|
86
|
+
*/
|
|
87
|
+
const TURN_OPENING_EVENT_TYPES = new Set([
|
|
88
|
+
'user_prompt_submit',
|
|
89
|
+
'pre_tool_use',
|
|
90
|
+
'post_tool_use',
|
|
91
|
+
]);
|
|
92
|
+
/**
|
|
93
|
+
* How far *before* this wait started a turn-opening event may sit and still be
|
|
94
|
+
* read as the turn this wait is about (Issue #1839).
|
|
95
|
+
*
|
|
96
|
+
* `wait` does not know when `send` ran, and the honest proxy is its own start:
|
|
97
|
+
* an orchestrator sends and then waits, so the turn's `UserPromptSubmit` lands
|
|
98
|
+
* after `wait` is already polling (measured at +0.6 s from the send). One poll
|
|
99
|
+
* interval of slack covers a `wait` that starts a moment late.
|
|
100
|
+
*
|
|
101
|
+
* The bound is what keeps this from being a new way to hang. Structured events
|
|
102
|
+
* live in a server-side Map keyed by (worktree, tool, instance) and are NOT
|
|
103
|
+
* fenced by generation on the way into `structuredEvents` — a stale
|
|
104
|
+
* `user_prompt_submit` from a previous agent process can still be the last event
|
|
105
|
+
* on the wire. Refusing to adopt anything older than this wait means such a
|
|
106
|
+
* record cannot gate it: a `wait` on an already-idle session sees no fresh
|
|
107
|
+
* turn-opening event, keeps `turnStartedAt` null, and behaves exactly as it did
|
|
108
|
+
* before this Issue.
|
|
109
|
+
*/
|
|
110
|
+
const TURN_ADOPTION_GRACE_MS = POLL_INTERVAL_MS;
|
|
50
111
|
/**
|
|
51
112
|
* How long `isUnclassifiedActive` must hold before `wait` treats it as a stop
|
|
52
113
|
* reason (Issue #1708).
|
|
@@ -88,13 +149,57 @@ function activeSuppression(data, now) {
|
|
|
88
149
|
return null;
|
|
89
150
|
return { suppression, ageSeconds: Math.max(0, Math.round(ageMs / 1000)) };
|
|
90
151
|
}
|
|
152
|
+
/** The wording for the four reasons a contract's `autoYes` block really authors. */
|
|
153
|
+
const CONTRACT_POLICY_CAUSE = 'by contract policy';
|
|
91
154
|
/**
|
|
92
|
-
*
|
|
155
|
+
* What withheld the answer, in the words `wait` puts on stderr (Issue #1843).
|
|
156
|
+
*
|
|
157
|
+
* A Record over the whole reason union rather than one fixed phrase: adding a
|
|
158
|
+
* reason to {@link AutoYesSuppressionReason} without deciding what `wait` should
|
|
159
|
+
* say about it is a compile error here, which is the point. Until #1843 the
|
|
160
|
+
* notice hard-coded "by contract policy" for every reason, so #1829's
|
|
161
|
+
* `agent-launch-dialog` — a product-side decision the poller makes with no
|
|
162
|
+
* contract in sight (`src/lib/auto-yes-poller.ts`, the `getCodexLifecycleDialog`
|
|
163
|
+
* branch) — sent operators of contract-less worktrees hunting for a
|
|
164
|
+
* `denyPatterns` entry that did not exist.
|
|
165
|
+
*
|
|
166
|
+
* The three sites that record a suppression are covered: the poller's launch-dialog
|
|
167
|
+
* branch (`agent-launch-dialog`), the poller's policy branch and
|
|
168
|
+
* `lib/hooks/permission-decision-service.ts` (both the four `evaluatePolicyAgainstTexts`
|
|
169
|
+
* verdicts, which is why those four keep the pre-#1843 wording exactly).
|
|
170
|
+
*/
|
|
171
|
+
const SUPPRESSION_CAUSE = {
|
|
172
|
+
'mode-off': CONTRACT_POLICY_CAUSE,
|
|
173
|
+
'deny-pattern': CONTRACT_POLICY_CAUSE,
|
|
174
|
+
'deny-pattern-unusable': CONTRACT_POLICY_CAUSE,
|
|
175
|
+
'type-not-allowed': CONTRACT_POLICY_CAUSE,
|
|
176
|
+
'agent-launch-dialog': "while the agent's launch dialog was on screen",
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* {@link SUPPRESSION_CAUSE} for a reason that arrived over the wire.
|
|
180
|
+
*
|
|
181
|
+
* `reason` is a server-supplied string, so a server newer than this CLI can name
|
|
182
|
+
* a reason the union does not have. Naming it verbatim is the only honest answer:
|
|
183
|
+
* quietly folding an unknown reason into "by contract policy" is the same
|
|
184
|
+
* misattribution #1843 exists to remove, just for a future reason instead of
|
|
185
|
+
* `agent-launch-dialog`.
|
|
186
|
+
*/
|
|
187
|
+
function suppressionCause(reason) {
|
|
188
|
+
return Object.prototype.hasOwnProperty.call(SUPPRESSION_CAUSE, reason)
|
|
189
|
+
? SUPPRESSION_CAUSE[reason]
|
|
190
|
+
: `for a reason this CLI does not recognise (${reason})`;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* One stderr line naming what is holding this prompt (Issue #1699).
|
|
93
194
|
*
|
|
94
195
|
* `wait` used to print only "Waiting for human response...", which reads the
|
|
95
196
|
* same whether a human was always going to answer or whether Auto-Yes silently
|
|
96
197
|
* refused to. That ambiguity is what let a deny pattern matching a finished turn
|
|
97
198
|
* stall two workers unnoticed; the pattern and the reason code belong on screen.
|
|
199
|
+
*
|
|
200
|
+
* Issue #1843: the cause is chosen per reason rather than asserted. `reason=` is
|
|
201
|
+
* in the payload either way, so the prefix is the only part that can lie — and it
|
|
202
|
+
* did, for every non-contract suppression.
|
|
98
203
|
*/
|
|
99
204
|
function formatSuppressionNotice(suppression, ageSeconds) {
|
|
100
205
|
const parts = [
|
|
@@ -105,7 +210,38 @@ function formatSuppressionNotice(suppression, ageSeconds) {
|
|
|
105
210
|
if (suppression.pattern !== undefined) {
|
|
106
211
|
parts.push(`pattern=${JSON.stringify(suppression.pattern)}`);
|
|
107
212
|
}
|
|
108
|
-
|
|
213
|
+
const cause = suppressionCause(suppression.reason);
|
|
214
|
+
return ` auto-yes suppressed this prompt ${cause}: ${parts.join(' ')} (${ageSeconds}s ago)`;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Adopt the turn this wait is about, if this poll shows one opening.
|
|
218
|
+
*
|
|
219
|
+
* @param previous - the turn start adopted so far, or null
|
|
220
|
+
* @returns the newest adopted turn start, or `previous` when nothing qualified
|
|
221
|
+
*/
|
|
222
|
+
function adoptTurnStart(data, waitStartedAt, previous) {
|
|
223
|
+
const events = data.structuredEvents;
|
|
224
|
+
if (!events || events.lastEventAt == null || events.lastEventType == null)
|
|
225
|
+
return previous;
|
|
226
|
+
if (!TURN_OPENING_EVENT_TYPES.has(events.lastEventType))
|
|
227
|
+
return previous;
|
|
228
|
+
if (events.lastEventAt < waitStartedAt - TURN_ADOPTION_GRACE_MS)
|
|
229
|
+
return previous;
|
|
230
|
+
return previous === null || events.lastEventAt > previous ? events.lastEventAt : previous;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Whether the agent has reported the end of the turn this wait adopted
|
|
234
|
+
* (Issue #1839).
|
|
235
|
+
*
|
|
236
|
+
* True when no turn was adopted at all, which is the whole of the unchanged
|
|
237
|
+
* path: a session whose agent posts no hooks never adopts one, so every caller
|
|
238
|
+
* on a machine without hook injection sees precisely the pre-#1839 behaviour.
|
|
239
|
+
*/
|
|
240
|
+
function turnSettled(data, turnStartedAt) {
|
|
241
|
+
if (turnStartedAt === null)
|
|
242
|
+
return true;
|
|
243
|
+
const stoppedAt = data.lastStopEventAt;
|
|
244
|
+
return stoppedAt != null && stoppedAt >= turnStartedAt;
|
|
109
245
|
}
|
|
110
246
|
/**
|
|
111
247
|
* Poll a single worktree until completion, prompt, or timeout.
|
|
@@ -128,6 +264,12 @@ async function pollWorktree(client, worktreeId, options) {
|
|
|
128
264
|
* (Issue #1708).
|
|
129
265
|
*/
|
|
130
266
|
let unclassifiedSince = null;
|
|
267
|
+
/**
|
|
268
|
+
* Epoch ms of the `user_prompt_submit` / `pre_tool_use` / `post_tool_use` this
|
|
269
|
+
* wait adopted as "the turn I am waiting on", or null when the agent has
|
|
270
|
+
* reported none (Issue #1839). See {@link adoptTurnStart}.
|
|
271
|
+
*/
|
|
272
|
+
let turnStartedAt = null;
|
|
131
273
|
while (true) {
|
|
132
274
|
// Check timeout
|
|
133
275
|
if (options.timeout) {
|
|
@@ -159,6 +301,10 @@ async function pollWorktree(client, worktreeId, options) {
|
|
|
159
301
|
if (data.isRunning) {
|
|
160
302
|
everRunning = true;
|
|
161
303
|
}
|
|
304
|
+
// Issue #1839: done before any exit path so the turn is adopted even on a
|
|
305
|
+
// poll that ends in a prompt — the same turn is still open when the human
|
|
306
|
+
// answers and `--on-prompt human` resumes polling.
|
|
307
|
+
turnStartedAt = adoptTurnStart(data, startTime, turnStartedAt);
|
|
162
308
|
// Prompt detected
|
|
163
309
|
if (data.isPromptWaiting && data.promptData) {
|
|
164
310
|
// Issue #1699: report a policy suppression on both exits — the human one
|
|
@@ -314,8 +460,41 @@ async function pollWorktree(client, worktreeId, options) {
|
|
|
314
460
|
// of an unreadable overlay (see the note above), and reporting it as
|
|
315
461
|
// `Completed` is how a stalled worker gets merged. Path A is untouched — a
|
|
316
462
|
// session that went away really is finished, and carries no flag anyway.
|
|
317
|
-
if (!data.isRunning
|
|
318
|
-
console.error(`Completed: ${worktreeId}`);
|
|
463
|
+
if (!data.isRunning) {
|
|
464
|
+
console.error(`Completed: ${worktreeId} (basis=${COMPLETION_BASIS.SESSION_GONE})`);
|
|
465
|
+
return { exitCode: types_1.WaitExitCode.SUCCESS };
|
|
466
|
+
}
|
|
467
|
+
if (data.sessionStatus === 'ready' && data.isUnclassifiedActive !== true) {
|
|
468
|
+
// Issue #1839: the agent is back at its composer with an upstream
|
|
469
|
+
// failure on the frame. Checked BEFORE the turn-boundary gate below,
|
|
470
|
+
// because it is the answer to the question that gate can only ask: the
|
|
471
|
+
// turn did not end, and this is why. Without it the same session runs
|
|
472
|
+
// to --timeout and reports 124, which says nothing about the cause.
|
|
473
|
+
const fault = data.upstreamFault ?? null;
|
|
474
|
+
if (options.failOnUpstreamFault && fault) {
|
|
475
|
+
console.error(`Upstream fault on ${worktreeId}: id=${fault.id} — the agent is back at its ` +
|
|
476
|
+
'composer with an upstream API failure on screen, so this turn did not run. ' +
|
|
477
|
+
`Matched: ${JSON.stringify(fault.matchedText)}`);
|
|
478
|
+
return { exitCode: types_1.WaitExitCode.UPSTREAM_FAULT };
|
|
479
|
+
}
|
|
480
|
+
// Issue #1839: `ready` off the terminal frame is the agent's composer,
|
|
481
|
+
// not the agent's verdict. Measured 2026-08-20 against a stub upstream
|
|
482
|
+
// answering 529: Claude returns to the composer ~3 s after the send
|
|
483
|
+
// having executed nothing, and never sends `Stop`. When this instance's
|
|
484
|
+
// hooks ARE reporting — the only case in which `turnStartedAt` is
|
|
485
|
+
// non-null — that missing `Stop` is the difference between "finished"
|
|
486
|
+
// and "never ran", and it is the only signal that carries it.
|
|
487
|
+
if (!turnSettled(data, turnStartedAt)) {
|
|
488
|
+
console.error(`Waiting: ${worktreeId} is back at its composer, but its agent has not reported ` +
|
|
489
|
+
`the end of this turn (turnStartedAt=${turnStartedAt}, ` +
|
|
490
|
+
`lastStopEventAt=${data.lastStopEventAt ?? 'none'}). ` +
|
|
491
|
+
'Not reporting completion; inspect with ' +
|
|
492
|
+
`\`commandmate capture ${worktreeId} --json\`.`);
|
|
493
|
+
await sleep(POLL_INTERVAL_MS);
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
const basis = turnStartedAt === null ? COMPLETION_BASIS.SCRAPER_READY : COMPLETION_BASIS.HOOK_STOP;
|
|
497
|
+
console.error(`Completed: ${worktreeId} (basis=${basis})`);
|
|
319
498
|
return { exitCode: types_1.WaitExitCode.SUCCESS };
|
|
320
499
|
}
|
|
321
500
|
// Progress indicator on stderr [DR1-05]
|
|
@@ -454,6 +633,7 @@ function createWaitCommand() {
|
|
|
454
633
|
.option('--instance <id>', agent_target_options_1.WAIT_INSTANCE_OPTION_DESCRIPTION)
|
|
455
634
|
.option('--verify', 'After completion, run every verification gate; exit 20 when a gate fails, 21 when there is nothing to verify')
|
|
456
635
|
.option('--require-work', 'After completion, run only the work-evidence gate; exit 21 when the worktree has no commits and no uncommitted changes')
|
|
636
|
+
.option('--fail-on-upstream-fault', 'Exit 11 instead of 0 when the agent returns to its composer with an upstream API failure (529/limit/API Error) on the frame')
|
|
457
637
|
.option('--token <token>', command_helpers_1.TOKEN_WARNING)
|
|
458
638
|
.action(async (worktreeIds, options) => {
|
|
459
639
|
try {
|