@fitlab-ai/agent-infra 0.9.7 → 0.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/bin/internal-cli.js +11 -1
  4. package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +60 -22
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +8 -1
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +175 -11
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +147 -0
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
  10. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +86 -69
  11. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +2 -1
  12. package/dist/lib/decide.js +68 -14
  13. package/dist/lib/git/workflow.js +10 -1
  14. package/dist/lib/init.js +1 -1
  15. package/dist/lib/internal/codex-lifecycle.js +19 -5
  16. package/dist/lib/internal/codex-sandbox-controller.js +6 -13
  17. package/dist/lib/internal/git-workflow.js +5 -7
  18. package/dist/lib/internal/platform-pr.js +20 -7
  19. package/dist/lib/internal/release-workflow.js +3 -4
  20. package/dist/lib/internal/sandbox-control.js +79 -2
  21. package/dist/lib/internal/task-activity.js +57 -7
  22. package/dist/lib/internal/task-event.js +57 -4
  23. package/dist/lib/internal/task-finalization.js +54 -0
  24. package/dist/lib/internal/task-ledger.js +59 -5
  25. package/dist/lib/internal/task-lifecycle.js +59 -8
  26. package/dist/lib/internal/task-orchestration.js +161 -288
  27. package/dist/lib/internal/task-override.js +131 -0
  28. package/dist/lib/internal/task-review.js +64 -6
  29. package/dist/lib/internal/task-verify.js +65 -3
  30. package/dist/lib/internal/task-warning.js +57 -4
  31. package/dist/lib/platform/pr-summary.js +53 -13
  32. package/dist/lib/platform/pull-requests.js +205 -41
  33. package/dist/lib/runtime/agent-runtime.js +56 -0
  34. package/dist/lib/sandbox/commands/create.js +389 -311
  35. package/dist/lib/sandbox/commands/rm.js +316 -55
  36. package/dist/lib/sandbox/control/client.js +190 -6
  37. package/dist/lib/sandbox/control/container-identity.js +1 -1
  38. package/dist/lib/sandbox/control/controller-registration.js +273 -0
  39. package/dist/lib/sandbox/control/executor.js +145 -5
  40. package/dist/lib/sandbox/control/lifecycle.js +395 -73
  41. package/dist/lib/sandbox/control/protocol.js +67 -6
  42. package/dist/lib/sandbox/control/state.js +27 -11
  43. package/dist/lib/sandbox/engine.js +14 -7
  44. package/dist/lib/sandbox/index.js +5 -3
  45. package/dist/lib/sandbox/managed-fs.js +5 -0
  46. package/dist/lib/sandbox/mounts.js +20 -5
  47. package/dist/lib/sandbox/recovery.js +78 -10
  48. package/dist/lib/sandbox/task-resolver.js +50 -6
  49. package/dist/lib/sandbox/workspace-identity.js +27 -0
  50. package/dist/lib/sandbox/workspace-view.js +62 -40
  51. package/dist/lib/sandbox/worktree-safety.js +215 -2
  52. package/dist/lib/server/process-state.js +51 -1
  53. package/dist/lib/task/activity-intent.js +4 -1
  54. package/dist/lib/task/codex-orchestration.js +158 -33
  55. package/dist/lib/task/commands/status.js +59 -1
  56. package/dist/lib/task/commit-operation.js +335 -0
  57. package/dist/lib/task/commit-policy.js +18 -0
  58. package/dist/lib/task/decision-intents.js +4 -2
  59. package/dist/lib/task/delegation-receipts.js +279 -17
  60. package/dist/lib/task/events.js +9 -5
  61. package/dist/lib/task/finalization.js +567 -0
  62. package/dist/lib/task/guard-override.js +31 -0
  63. package/dist/lib/task/human-override.js +1240 -0
  64. package/dist/lib/task/ledger-intents.js +6 -3
  65. package/dist/lib/task/lifecycle.js +69 -14
  66. package/dist/lib/task/operation-outcome.js +33 -0
  67. package/dist/lib/task/orchestration.js +199 -719
  68. package/dist/lib/task/review-finalization.js +3 -2
  69. package/dist/lib/task/short-id.js +59 -1
  70. package/dist/lib/task/task-execution-lock.js +6 -1
  71. package/dist/lib/task/verification-engine.js +138 -59
  72. package/dist/lib/task/verification.js +15 -2
  73. package/dist/lib/task/workflow-warning-intents.js +54 -3
  74. package/dist/lib/task/write.js +2 -1
  75. package/dist/lifecycle-build-manifest.json +3 -3
  76. package/lib/agent-clients/adapters/claude-code.ts +3 -3
  77. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +63 -20
  78. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +12 -0
  79. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +278 -13
  80. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +180 -0
  81. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
  82. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
  83. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +112 -93
  84. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +2 -1
  85. package/lib/decide.ts +59 -14
  86. package/lib/git/workflow.ts +11 -2
  87. package/lib/init.ts +1 -1
  88. package/lib/internal/codex-lifecycle.ts +18 -5
  89. package/lib/internal/codex-sandbox-controller.ts +6 -12
  90. package/lib/internal/git-workflow.ts +5 -6
  91. package/lib/internal/platform-pr.ts +14 -7
  92. package/lib/internal/release-workflow.ts +3 -4
  93. package/lib/internal/sandbox-control.ts +92 -2
  94. package/lib/internal/task-activity.ts +46 -6
  95. package/lib/internal/task-event.ts +53 -4
  96. package/lib/internal/task-finalization.ts +68 -0
  97. package/lib/internal/task-ledger.ts +48 -4
  98. package/lib/internal/task-lifecycle.ts +56 -12
  99. package/lib/internal/task-orchestration.ts +32 -107
  100. package/lib/internal/task-override.ts +138 -0
  101. package/lib/internal/task-review.ts +51 -6
  102. package/lib/internal/task-verify.ts +52 -3
  103. package/lib/internal/task-warning.ts +44 -3
  104. package/lib/platform/pr-summary.ts +65 -14
  105. package/lib/platform/pull-requests.ts +211 -41
  106. package/lib/runtime/agent-runtime.ts +76 -0
  107. package/lib/sandbox/commands/create.ts +88 -4
  108. package/lib/sandbox/commands/rm.ts +413 -61
  109. package/lib/sandbox/config.ts +2 -1
  110. package/lib/sandbox/control/client.ts +255 -6
  111. package/lib/sandbox/control/container-identity.ts +3 -3
  112. package/lib/sandbox/control/controller-registration.ts +347 -0
  113. package/lib/sandbox/control/executor.ts +172 -6
  114. package/lib/sandbox/control/lifecycle.ts +465 -77
  115. package/lib/sandbox/control/protocol.ts +85 -9
  116. package/lib/sandbox/control/state.ts +35 -18
  117. package/lib/sandbox/engine.ts +20 -9
  118. package/lib/sandbox/index.ts +5 -3
  119. package/lib/sandbox/managed-fs.ts +5 -0
  120. package/lib/sandbox/mounts.ts +23 -5
  121. package/lib/sandbox/recovery.ts +90 -9
  122. package/lib/sandbox/task-resolver.ts +58 -7
  123. package/lib/sandbox/workspace-identity.ts +50 -2
  124. package/lib/sandbox/workspace-view.ts +73 -43
  125. package/lib/sandbox/worktree-safety.ts +236 -2
  126. package/lib/server/process-state.ts +48 -1
  127. package/lib/task/activity-intent.ts +4 -2
  128. package/lib/task/codex-orchestration.ts +167 -33
  129. package/lib/task/commands/status.ts +78 -1
  130. package/lib/task/commit-operation.ts +430 -0
  131. package/lib/task/commit-policy.ts +30 -0
  132. package/lib/task/decision-intents.ts +4 -2
  133. package/lib/task/delegation-receipts.ts +304 -19
  134. package/lib/task/events.ts +10 -5
  135. package/lib/task/finalization.ts +719 -0
  136. package/lib/task/guard-override.ts +50 -0
  137. package/lib/task/human-override.ts +1503 -0
  138. package/lib/task/ledger-intents.ts +6 -3
  139. package/lib/task/lifecycle.ts +71 -14
  140. package/lib/task/operation-outcome.ts +48 -0
  141. package/lib/task/orchestration.ts +239 -922
  142. package/lib/task/review-finalization.ts +6 -2
  143. package/lib/task/short-id.ts +65 -1
  144. package/lib/task/task-execution-lock.ts +10 -1
  145. package/lib/task/verification-engine.ts +145 -64
  146. package/lib/task/verification.ts +18 -3
  147. package/lib/task/workflow-warning-intents.ts +61 -4
  148. package/lib/task/write.ts +4 -1
  149. package/package.json +2 -2
  150. package/templates/.agents/QUICKSTART.en.md +2 -2
  151. package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
  152. package/templates/.agents/hooks/lifecycle-delegation.js +9 -24
  153. package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -3
  154. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -3
  155. package/templates/.agents/rules/task-management.en.md +2 -2
  156. package/templates/.agents/rules/task-management.zh-CN.md +2 -2
  157. package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
  158. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
  159. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  160. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  161. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
  162. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
  163. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
  164. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
  165. package/templates/.agents/skills/commit/SKILL.en.md +41 -125
  166. package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
  167. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
  168. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
  169. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
  170. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
  171. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  172. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  173. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
  174. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
  175. package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
  176. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
  177. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
  178. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
  179. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  180. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  181. package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
  182. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
  183. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
  184. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
  185. package/templates/.agents/skills/run-task/SKILL.en.md +5 -5
  186. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -5
  187. package/templates/.agents/skills/run-task/reference/host-validation.en.md +3 -1
  188. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +3 -1
  189. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
  190. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
  191. package/templates/.codex/hooks.json +4 -4
  192. package/dist/lib/task/commit-finalization.js +0 -305
  193. package/dist/lib/task/commit-intent.js +0 -165
  194. package/lib/task/commit-finalization.ts +0 -419
  195. package/lib/task/commit-intent.ts +0 -230
@@ -0,0 +1,68 @@
1
+ import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.ts';
2
+ import { applyTaskFinalization } from '../task/finalization.ts';
3
+ import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.ts';
4
+ import { verifyTaskEvent } from '../task/verification.ts';
5
+
6
+ const USAGE = 'Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n';
7
+
8
+ type FinalizationError = Readonly<{ code: string; message: string; retryable: boolean }>;
9
+
10
+ function envelope(
11
+ status: 'completed' | 'failed' | 'blocked' | 'unknown',
12
+ changed: boolean,
13
+ accepted: boolean,
14
+ result: ReturnType<typeof applyTaskFinalization> | null,
15
+ error: FinalizationError | null
16
+ ): string {
17
+ return `${JSON.stringify({ version: 1, status, changed, accepted, result, error })}\n`;
18
+ }
19
+
20
+ function exitCode(status: 'completed' | 'failed' | 'blocked' | 'unknown'): number {
21
+ return status === 'completed' ? 0 : status === 'blocked' ? 2 : 1;
22
+ }
23
+
24
+ function fail(message: string): void {
25
+ const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message, retryable: false };
26
+ process.stdout.write(envelope('failed', false, false, null, error));
27
+ process.stderr.write(USAGE);
28
+ process.exitCode = 1;
29
+ }
30
+
31
+ function taskFinalization(args: string[] = []): void {
32
+ if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
33
+ if (args.length !== 4 || !args[0] || args[1] !== 'complete' || args[2] !== '--agent' || !args[3]) {
34
+ fail('task ref, complete intent, and --agent are required');
35
+ return;
36
+ }
37
+ const agent = normalizeAgentToken(args[3]);
38
+ if (!agent) {
39
+ fail(`invalid --agent: ${AGENT_USAGE_HINT}`);
40
+ return;
41
+ }
42
+ let repoRoot: string;
43
+ try {
44
+ repoRoot = detectRepoRoot();
45
+ } catch (error) {
46
+ const detail = { code: 'REPO_ROOT_NOT_FOUND', message: error instanceof Error ? error.message : String(error), retryable: false };
47
+ process.stdout.write(envelope('failed', false, true, null, detail));
48
+ process.exitCode = 1;
49
+ return;
50
+ }
51
+ const resolved = resolveTaskRef(args[0], { repoRoot });
52
+ if (!resolved.ok) {
53
+ const detail = { code: resolved.code, message: resolved.message, retryable: false };
54
+ process.stdout.write(envelope('failed', false, true, null, detail));
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+ const result = applyTaskFinalization(
59
+ { taskRef: resolved.taskId, intent: 'complete', agent },
60
+ { repoRoot, preflight: (request, options) => verifyTaskEvent(
61
+ { ...request, event: 'complete-task.hard-preflight' }, options
62
+ ) }
63
+ );
64
+ process.stdout.write(envelope(result.status, result.changed, true, result, result.error));
65
+ process.exitCode = exitCode(result.status);
66
+ }
67
+
68
+ export { taskFinalization };
@@ -2,14 +2,17 @@ import fs from 'node:fs';
2
2
 
3
3
  import { applyLedgerIntent } from '../task/ledger-intents.ts';
4
4
  import type { LedgerIntent } from '../task/ledger-intents.ts';
5
+ import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.ts';
5
6
  import { isReviewStage, parseLedger, summarizeLedgerStage, validateLedgerRows } from '../task/ledger.ts';
6
7
  import { resolveTaskRef } from '../task/resolve-ref.ts';
8
+ import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
7
9
 
8
10
  const USAGE = `Usage: agent-infra-internal task-ledger <task-ref> <intent> [intent flags] [--dry-run]\n\nIntents: finding-upsert, finding-respond, finding-review, decision-next-id, decision-upsert, stage-status\n`;
9
11
  const FLAGS: Record<string, string> = {
10
12
  '--stage': 'stage', '--review-artifact': 'reviewArtifact', '--ordinal': 'ordinal',
11
13
  '--severity': 'severity', '--evidence': 'evidence', '--id': 'id', '--round': 'round',
12
14
  '--status': 'status', '--artifact': 'artifact', '--needs-implementation': 'needsImplementation'
15
+ , '--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
13
16
  };
14
17
  const NUMERIC = new Set(['ordinal', 'round']);
15
18
  const BOOLEAN = new Set(['needsImplementation']);
@@ -58,11 +61,16 @@ function taskLedger(args: string[] = []): void {
58
61
  'finding-review': ['needsImplementation'],
59
62
  'decision-upsert': ['needsImplementation']
60
63
  };
61
- const allowed = new Set(['kind', 'taskRef', 'dryRun', ...required[kind]!, ...(optional[kind] ?? [])]);
64
+ const overrideFields = kind === 'stage-status' || kind === 'decision-next-id'
65
+ ? []
66
+ : ['overrideTicket', 'overrideTarget', 'overrideScope'];
67
+ const allowed = new Set(['kind', 'taskRef', 'dryRun', ...required[kind]!, ...(optional[kind] ?? []), ...overrideFields]);
62
68
  const unexpected = Object.keys(values).find((key) => !allowed.has(key));
63
69
  const missing = required[kind]!.find((key) => values[key] === undefined);
64
70
  if (unexpected) { usageFailure(`${kind} does not accept '${unexpected}'`); return; }
65
71
  if (missing) { usageFailure(`${kind} requires '${missing}'`); return; }
72
+ const dryRunConflict = overrideDryRunConflict(values);
73
+ if (dryRunConflict) { usageFailure(dryRunConflict.message); return; }
66
74
  if (kind === 'stage-status') {
67
75
  if (!isReviewStage(String(values.stage))) { usageFailure(`stage-status requires a valid review stage`); return; }
68
76
  const resolved = resolveTaskRef(taskRef);
@@ -87,9 +95,45 @@ function taskLedger(args: string[] = []): void {
87
95
  }
88
96
  return;
89
97
  }
90
- const result = applyLedgerIntent(values as LedgerIntent);
91
- process.stdout.write(`${JSON.stringify(result)}\n`);
92
- if (result.status === 'failed') process.exitCode = 1;
98
+ const lockResolved = resolveTaskRef(taskRef);
99
+ if (!lockResolved.ok) { usageFailure(lockResolved.message); return; }
100
+ let result;
101
+ let humanOverride: unknown = null;
102
+ try {
103
+ result = withTaskExecutionLock(lockResolved.repoRoot, lockResolved.taskId, `task-ledger.${kind}`, () => {
104
+ let current = applyLedgerIntent(values as LedgerIntent);
105
+ if (current.status !== 'failed' || !values.overrideTicket) return current;
106
+ if (!values.overrideTarget || !values.overrideScope) { usageFailure('override ticket requires target and scope'); return current; }
107
+ const consumed = consumeHumanOverride({
108
+ taskRef,
109
+ ticketId: String(values.overrideTicket),
110
+ failureId: failureId('ledger-intent', current.error?.code ?? 'TASK_STATE_MISMATCH'),
111
+ target: String(values.overrideTarget),
112
+ scope: String(values.overrideScope)
113
+ }, {
114
+ effectExecutor: (capability) => {
115
+ const retried = applyLedgerIntent(values as LedgerIntent, { manualOverride: capability });
116
+ current = retried;
117
+ return retried.status === 'failed'
118
+ ? { code: 'OVERRIDE_EFFECT_FAILED', message: `${retried.error?.code ?? 'LEDGER_FAILED'}: ${retried.error?.message ?? 'manual ledger effect failed'}` }
119
+ : null;
120
+ }
121
+ });
122
+ humanOverride = consumed;
123
+ return current;
124
+ });
125
+ } catch (error) {
126
+ if (!(error instanceof TaskExecutionLockError)) throw error;
127
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
128
+ process.exitCode = 1;
129
+ return;
130
+ }
131
+ process.stdout.write(`${JSON.stringify(humanOverride ? { ...result, humanOverride } : result)}\n`);
132
+ const overrideFailed = Boolean(
133
+ humanOverride && typeof humanOverride === 'object' &&
134
+ (humanOverride as { status?: unknown }).status === 'failed'
135
+ );
136
+ if (result.status === 'failed' || overrideFailed) process.exitCode = 1;
93
137
  }
94
138
 
95
139
  export { taskLedger };
@@ -1,14 +1,16 @@
1
1
  import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.ts';
2
+ import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.ts';
2
3
  import { applyTaskLifecycle, lifecycleIntentCatalog } from '../task/lifecycle.ts';
3
4
  import type { TaskLifecycleRequest } from '../task/lifecycle.ts';
4
- import { resolveTaskRef } from '../task/resolve-ref.ts';
5
+ import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.ts';
5
6
  import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
6
7
 
7
- const USAGE = `Usage: agent-infra-internal task-lifecycle <N | TASK-id> <intent> --agent <agent> [intent flags] [--dry-run]\n\nIntents: ${lifecycleIntentCatalog.join(', ')}\n`;
8
+ const USAGE = `Usage: agent-infra-internal task-lifecycle <N | TASK-id> <intent> --agent <agent> [intent flags] [--dry-run]\n\nIntents: ${lifecycleIntentCatalog.join(', ')}\nOverride: --override-ticket <ticket> --override-target <target> --override-scope <scope>\n`;
8
9
  const FLAGS: Record<string, string> = {
9
10
  '--agent': 'agent', '--reason': 'reason', '--unblock-condition': 'unblockCondition',
10
11
  '--note': 'note', '--alert-number': 'alertNumber', '--staging-dir': 'stagingDir',
11
- '--issue-number': 'issueNumber'
12
+ '--issue-number': 'issueNumber', '--override-ticket': 'overrideTicket',
13
+ '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
12
14
  };
13
15
 
14
16
  function usageFailure(message: string): void {
@@ -39,19 +41,61 @@ function taskLifecycle(args: string[] = []): void {
39
41
  const agent = normalizeAgentToken(String(values.agent ?? ''));
40
42
  if (!agent) { usageFailure(`invalid --agent '${values.agent}': ${AGENT_USAGE_HINT}`); return; }
41
43
  values.agent = agent;
42
- const resolved = resolveTaskRef(String(values.taskRef));
43
- if (!resolved.ok) {
44
- process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } })}\n`);
45
- process.exitCode = 1;
46
- return;
44
+ const dryRunConflict = overrideDryRunConflict(values);
45
+ if (dryRunConflict) { usageFailure(dryRunConflict.message); return; }
46
+ let repoRoot: string;
47
+ let taskId: string;
48
+ if (values.intent === 'restore') {
49
+ // restore's precondition is that the task does NOT exist locally yet (it only lives
50
+ // in a staging dir), so resolveTaskRef's existence check is inapplicable here; identity
51
+ // is guaranteed by applyTaskLifecycle's own full-TASK-id check plus staging validation.
52
+ try {
53
+ repoRoot = detectRepoRoot();
54
+ } catch (error) {
55
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'REPO_ROOT_NOT_FOUND', message: error instanceof Error ? error.message : String(error) } })}\n`);
56
+ process.exitCode = 1;
57
+ return;
58
+ }
59
+ taskId = String(values.taskRef);
60
+ } else {
61
+ const resolved = resolveTaskRef(String(values.taskRef));
62
+ if (!resolved.ok) {
63
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } })}\n`);
64
+ process.exitCode = 1;
65
+ return;
66
+ }
67
+ repoRoot = resolved.repoRoot;
68
+ taskId = resolved.taskId;
47
69
  }
48
- let result: ReturnType<typeof applyTaskLifecycle>;
70
+ let result: ReturnType<typeof applyTaskLifecycle> & { humanOverride?: unknown };
49
71
  try {
50
72
  result = withTaskExecutionLock(
51
- resolved.repoRoot,
52
- resolved.taskId,
73
+ repoRoot,
74
+ taskId,
53
75
  `task-lifecycle.${String(values.intent)}`,
54
- () => applyTaskLifecycle(values as TaskLifecycleRequest)
76
+ () => {
77
+ const lifecycleResult = applyTaskLifecycle(values as TaskLifecycleRequest);
78
+ if (lifecycleResult.status !== 'failed' || !values.overrideTicket) return lifecycleResult;
79
+ if (!values.overrideTarget || !values.overrideScope) {
80
+ return {
81
+ ...lifecycleResult,
82
+ error: { code: 'OVERRIDE_PAYLOAD_INVALID', message: 'override ticket requires --override-target and --override-scope' }
83
+ };
84
+ }
85
+ const override = consumeHumanOverride({
86
+ taskRef: String(values.taskRef),
87
+ ticketId: String(values.overrideTicket),
88
+ failureId: failureId('lifecycle.apply', lifecycleResult.error?.code ?? 'LIFECYCLE_FAILED'),
89
+ target: String(values.overrideTarget),
90
+ scope: String(values.overrideScope),
91
+ intent: String(values.intent) as TaskLifecycleRequest['intent'],
92
+ ...(values.alertNumber ? { alertNumber: Number(values.alertNumber) } : {}),
93
+ ...(values.issueNumber ? { issueNumber: Number(values.issueNumber) } : {}),
94
+ ...(values.stagingDir ? { stagingDir: String(values.stagingDir) } : {})
95
+ });
96
+ if (override.status === 'failed') return { ...lifecycleResult, humanOverride: override };
97
+ return { ...lifecycleResult, ...override, humanOverride: override, error: null };
98
+ }
55
99
  );
56
100
  } catch (error) {
57
101
  if (!(error instanceof TaskExecutionLockError)) throw error;
@@ -1,31 +1,23 @@
1
- import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.ts';
2
1
  import {
3
- abortCommitIntent,
4
2
  activateMatchingOrchestrationDelegation,
5
3
  activateOrchestrationDelegation,
6
4
  advanceOrchestration,
7
5
  awaitOrchestrationDelegationActivation,
8
- beginCommitIntent,
9
6
  beginOrResumeOrchestration,
10
- checkpointCommitIntent,
11
- completeCommitIntent,
12
7
  dispatchOrchestrationDelegation,
8
+ OrchestrationStateError,
13
9
  pauseOrchestration,
14
- recoverCommitIntent,
15
10
  recoverPreparedOrchestrationDelegation,
16
11
  routeOrchestration,
17
12
  sealMatchingOrchestrationDelegation,
18
13
  sealOrchestrationDelegation,
19
- startCommitAttempt,
20
- statusCommitIntent,
21
- statusOrchestration,
22
- terminateCommitAttempt
14
+ statusOrchestration
23
15
  } from '../task/orchestration.ts';
24
16
  import { prepareCodexOrchestrationDelegation } from '../task/codex-orchestration.ts';
25
17
  import { isAgentClientId } from '../agent-clients/types.ts';
26
18
  import type { AgentClientId } from '../agent-clients/types.ts';
27
19
 
28
- const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|dispatch|await-activation|recover-prepared|hook-start|hook-stop|advance|pause|status|commit-start|commit-begin|commit-checkpoint|commit-complete|commit-recover|commit-abort|commit-terminate|commit-status> [options]\n';
20
+ const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|dispatch|await-activation|recover-prepared|hook-start|hook-stop|advance|pause|status> [options]\n';
29
21
 
30
22
  function usageFailure(message: string): void {
31
23
  process.stdout.write(`${JSON.stringify({
@@ -48,23 +40,15 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
48
40
  const [taskRef, intent] = args;
49
41
  if (![
50
42
  'begin-or-resume', 'route', 'prepare', 'dispatch', 'await-activation', 'recover-prepared',
51
- 'hook-start', 'hook-stop', 'advance', 'pause', 'status',
52
- 'commit-start', 'commit-begin', 'commit-checkpoint', 'commit-complete', 'commit-recover', 'commit-abort', 'commit-terminate', 'commit-status'
43
+ 'hook-start', 'hook-stop', 'advance', 'pause', 'status'
53
44
  ].includes(intent!)) {
54
45
  usageFailure(`unknown intent '${intent}'`);
55
46
  return;
56
47
  }
57
48
  const values: Record<string, string> = {};
58
49
  const seen = new Set<string>();
59
- let orchestrated = false;
60
50
  for (let index = 2; index < args.length; index += 1) {
61
51
  const flag = args[index]!;
62
- if (flag === '--orchestrated') {
63
- if (seen.has(flag)) { usageFailure(`duplicate option '${flag}'`); return; }
64
- seen.add(flag);
65
- orchestrated = true;
66
- continue;
67
- }
68
52
  if (![
69
53
  '--max-steps', '--executor-model', '--executor-reasoning-effort',
70
54
  '--reviewer-model', '--reviewer-reasoning-effort',
@@ -74,8 +58,7 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
74
58
  '--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
75
59
  '--model-fallback-reason', '--reasoning-effort-fallback-reason',
76
60
  '--exit-code', '--after-fingerprint', '--changed-paths', '--code', '--message', '--recoverable', '--agent',
77
- '--baseline-head', '--token', '--kind', '--head', '--remote', '--ref', '--expected-head'
78
- , '--attempt', '--git-worktree-root'
61
+ '--git-worktree-root'
79
62
  ].includes(flag)) {
80
63
  usageFailure(`unknown option '${flag}'`);
81
64
  return;
@@ -92,26 +75,6 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
92
75
  }
93
76
  values[flag] = value;
94
77
  }
95
- const commitIntentFlags: Record<string, readonly string[]> = {
96
- 'commit-start': ['--agent', '--git-worktree-root'],
97
- 'commit-begin': ['--agent', '--baseline-head', '--attempt', '--orchestrated', '--git-worktree-root'],
98
- 'commit-checkpoint': ['--token', '--kind', '--head', '--remote', '--ref', '--git-worktree-root'],
99
- 'commit-complete': ['--token', '--agent', '--git-worktree-root'],
100
- 'commit-recover': ['--agent', '--git-worktree-root'],
101
- 'commit-abort': ['--token', '--expected-head', '--git-worktree-root'],
102
- 'commit-terminate': ['--attempt', '--agent', '--code', '--git-worktree-root'],
103
- 'commit-status': ['--git-worktree-root']
104
- };
105
- if (intent! in commitIntentFlags) {
106
- const allowed = commitIntentFlags[intent!]!;
107
- const invalid = [...seen].find((flag) => !allowed.includes(flag));
108
- if (invalid) { usageFailure(`intent '${intent}' does not accept '${invalid}'`); return; }
109
- } else {
110
- const commitOnly = [...seen].find((flag) => [
111
- '--orchestrated', '--baseline-head', '--attempt', '--token', '--kind', '--head', '--remote', '--ref', '--expected-head'
112
- ].includes(flag));
113
- if (commitOnly) { usageFailure(`intent '${intent}' does not accept '${commitOnly}'`); return; }
114
- }
115
78
  const requireValues = (flags: string[]) => flags.find((flag) => values[flag] === undefined);
116
79
  const coreOptions = values['--git-worktree-root'] === undefined
117
80
  ? {}
@@ -121,66 +84,8 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
121
84
  return;
122
85
  }
123
86
  let result;
124
- if (intent === 'commit-start') {
125
- const missing = requireValues(['--agent']);
126
- if (missing) { usageFailure(`intent 'commit-start' requires '${missing}'`); return; }
127
- const agent = normalizeAgentToken(values['--agent']!);
128
- if (!agent) { usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`); return; }
129
- result = startCommitAttempt(taskRef!, { agent }, coreOptions);
130
- } else if (intent === 'commit-begin') {
131
- const missing = requireValues(['--agent', '--baseline-head', '--attempt']);
132
- if (missing) { usageFailure(`intent 'commit-begin' requires '${missing}'`); return; }
133
- const agent = normalizeAgentToken(values['--agent']!);
134
- if (!agent) { usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`); return; }
135
- result = beginCommitIntent(taskRef!, {
136
- agent, orchestrated, baselineHead: values['--baseline-head']!, attempt: values['--attempt']!
137
- }, coreOptions);
138
- } else if (intent === 'commit-checkpoint') {
139
- const missing = requireValues(['--token', '--kind', '--head']);
140
- if (missing) { usageFailure(`intent 'commit-checkpoint' requires '${missing}'`); return; }
141
- if (!['committed', 'pushed'].includes(values['--kind']!)) {
142
- usageFailure('--kind must be committed or pushed'); return;
143
- }
144
- if (values['--kind'] === 'pushed') {
145
- const pushedMissing = requireValues(['--remote', '--ref']);
146
- if (pushedMissing) { usageFailure(`pushed checkpoint requires '${pushedMissing}'`); return; }
147
- } else if (values['--remote'] !== undefined || values['--ref'] !== undefined) {
148
- usageFailure('committed checkpoint does not accept --remote or --ref'); return;
149
- }
150
- result = checkpointCommitIntent(taskRef!, {
151
- token: values['--token']!, kind: values['--kind'] as 'committed' | 'pushed',
152
- head: values['--head']!, remote: values['--remote'], ref: values['--ref']
153
- }, coreOptions);
154
- } else if (intent === 'commit-complete') {
155
- const missing = requireValues(['--token', '--agent']);
156
- if (missing) { usageFailure(`intent 'commit-complete' requires '${missing}'`); return; }
157
- const agent = normalizeAgentToken(values['--agent']!);
158
- if (!agent) { usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`); return; }
159
- result = completeCommitIntent(taskRef!, { token: values['--token']!, agent }, coreOptions);
160
- } else if (intent === 'commit-recover') {
161
- const missing = requireValues(['--agent']);
162
- if (missing) { usageFailure(`intent 'commit-recover' requires '${missing}'`); return; }
163
- const agent = normalizeAgentToken(values['--agent']!);
164
- if (!agent) { usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`); return; }
165
- result = recoverCommitIntent(taskRef!, { agent }, coreOptions);
166
- } else if (intent === 'commit-abort') {
167
- const missing = requireValues(['--token', '--expected-head']);
168
- if (missing) { usageFailure(`intent 'commit-abort' requires '${missing}'`); return; }
169
- result = abortCommitIntent(taskRef!, { token: values['--token']!, expectedHead: values['--expected-head']! }, coreOptions);
170
- } else if (intent === 'commit-terminate') {
171
- const missing = requireValues(['--attempt', '--agent', '--code']);
172
- if (missing) { usageFailure(`intent 'commit-terminate' requires '${missing}'`); return; }
173
- const agent = normalizeAgentToken(values['--agent']!);
174
- if (!agent) { usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`); return; }
175
- if (!/^[A-Z][A-Z0-9_]{2,63}$/.test(values['--code']!)) {
176
- usageFailure('--code must be a stable uppercase identifier'); return;
177
- }
178
- result = terminateCommitAttempt(taskRef!, {
179
- attempt: values['--attempt']!, agent, code: values['--code']!
180
- }, coreOptions);
181
- } else if (intent === 'commit-status') {
182
- result = statusCommitIntent(taskRef!, coreOptions);
183
- } else if (intent === 'begin-or-resume') {
87
+ try {
88
+ if (intent === 'begin-or-resume') {
184
89
  const missing = requireValues(['--client']);
185
90
  if (missing) { usageFailure(`intent 'begin-or-resume' requires '${missing}'`); return; }
186
91
  const maxSteps = values['--max-steps'] === undefined ? undefined : Number(values['--max-steps']);
@@ -247,12 +152,12 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
247
152
  } else if (intent === 'hook-start') {
248
153
  const missing = requireValues([
249
154
  ...(taskRef === 'auto' ? ['--client'] : []),
250
- '--native-agent', '--child-id', '--parent-id', '--spawn-mode'
155
+ '--native-agent', '--child-id', '--parent-id'
251
156
  ]);
252
157
  if (missing) { usageFailure(`intent 'hook-start' requires '${missing}'`); return; }
253
158
  const event = {
254
159
  nativeAgent: values['--native-agent']!, childId: values['--child-id']!,
255
- parentId: values['--parent-id']!, spawnMode: values['--spawn-mode']!,
160
+ parentId: values['--parent-id']!, spawnMode: values['--spawn-mode'],
256
161
  actualModel: values['--actual-model'],
257
162
  actualReasoningEffort: values['--actual-reasoning-effort'],
258
163
  modelFallbackReason: values['--model-fallback-reason'],
@@ -266,7 +171,11 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
266
171
  const missing = requireValues(['--client', '--native-agent', '--child-id']);
267
172
  if (missing) { usageFailure(`intent 'hook-stop' requires '${missing}'`); return; }
268
173
  result = sealMatchingOrchestrationDelegation(values['--client'] as AgentClientId, {
269
- nativeAgent: values['--native-agent']!, childId: values['--child-id']!
174
+ nativeAgent: values['--native-agent']!, childId: values['--child-id']!,
175
+ actualModel: values['--actual-model'],
176
+ actualReasoningEffort: values['--actual-reasoning-effort'],
177
+ modelFallbackReason: values['--model-fallback-reason'],
178
+ reasoningEffortFallbackReason: values['--reasoning-effort-fallback-reason']
270
179
  }, coreOptions);
271
180
  } else {
272
181
  const missing = requireValues(['--child-id', '--exit-code', '--after-fingerprint']);
@@ -276,7 +185,11 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
276
185
  result = sealOrchestrationDelegation(taskRef!, {
277
186
  childId: values['--child-id']!, exitCode,
278
187
  afterFingerprint: values['--after-fingerprint']!,
279
- changedPaths: values['--changed-paths'] ? values['--changed-paths']!.split(',').filter(Boolean) : []
188
+ changedPaths: values['--changed-paths'] ? values['--changed-paths']!.split(',').filter(Boolean) : [],
189
+ actualModel: values['--actual-model'],
190
+ actualReasoningEffort: values['--actual-reasoning-effort'],
191
+ modelFallbackReason: values['--model-fallback-reason'],
192
+ reasoningEffortFallbackReason: values['--reasoning-effort-fallback-reason']
280
193
  }, coreOptions);
281
194
  }
282
195
  } else if (intent === 'advance') {
@@ -287,7 +200,19 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
287
200
  if (!['true', 'false'].includes(values['--recoverable']!)) { usageFailure('--recoverable must be true or false'); return; }
288
201
  result = pauseOrchestration(taskRef!, values['--code']!, values['--message']!, values['--recoverable'] === 'true', coreOptions);
289
202
  }
290
- process.stdout.write(`${JSON.stringify(result)}\n`);
203
+ } catch (error) {
204
+ if (!(error instanceof OrchestrationStateError)) throw error;
205
+ result = {
206
+ status: 'failed' as const,
207
+ changed: false,
208
+ taskId: error.taskId,
209
+ run: null,
210
+ next: null,
211
+ error: { code: error.code, message: error.message }
212
+ };
213
+ }
214
+ const output = result;
215
+ process.stdout.write(`${JSON.stringify(output)}\n`);
291
216
  if (result.status === 'failed') process.exitCode = 1;
292
217
  }
293
218
 
@@ -0,0 +1,138 @@
1
+ import {
2
+ consumeHumanOverride,
3
+ diagnoseHumanOverrideForTask,
4
+ issueHumanOverride
5
+ } from '../task/human-override.ts';
6
+ import type { ConsumeHumanOverrideRequest, HumanOverrideRequest } from '../task/human-override.ts';
7
+ import { resolveTaskRef } from '../task/resolve-ref.ts';
8
+ import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
9
+
10
+ const USAGE = `Usage: agent-infra-internal task-override <task-ref> <diagnose|issue|consume> [options]
11
+
12
+ Options: --failure-id <id> --target <target> --operator <operator> --reason <reason> --scope <scope>
13
+ --intent <lifecycle-intent> --alert-number <N> --issue-number <N> --staging-dir <path>
14
+ --expires-at <timestamp> --ticket <ticket-id> --pr-number <N>
15
+ `;
16
+
17
+ const FLAGS: Record<string, string> = {
18
+ '--failure-id': 'failureId',
19
+ '--target': 'target',
20
+ '--operator': 'operator',
21
+ '--reason': 'reason',
22
+ '--scope': 'scope',
23
+ '--intent': 'intent',
24
+ '--failure-digest': 'failureDigest',
25
+ '--expires-at': 'expiresAt',
26
+ '--ticket': 'ticketId',
27
+ '--facts': 'observedFacts',
28
+ '--alert-number': 'alertNumber',
29
+ '--issue-number': 'issueNumber',
30
+ '--pr-number': 'pullRequestNumber',
31
+ '--staging-dir': 'stagingDir'
32
+ };
33
+
34
+ function usageFailure(message: string): void {
35
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'OVERRIDE_PAYLOAD_INVALID', message } })}\n`);
36
+ process.stderr.write(USAGE);
37
+ process.exitCode = 1;
38
+ }
39
+
40
+ function taskOverride(args: string[] = []): void {
41
+ if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
42
+ const [taskRef, operation] = args;
43
+ if (!taskRef || !operation || !['diagnose', 'issue', 'consume'].includes(operation)) {
44
+ usageFailure('task ref and a valid operation are required');
45
+ return;
46
+ }
47
+ const values: Record<string, unknown> = { taskRef };
48
+ const seen = new Set<string>();
49
+ for (let index = 2; index < args.length; index += 1) {
50
+ const flag = args[index]!;
51
+ const key = FLAGS[flag];
52
+ if (!key) { usageFailure(`unknown option '${flag}'`); return; }
53
+ if (seen.has(flag)) { usageFailure(`duplicate option '${flag}'`); return; }
54
+ const value = args[++index];
55
+ if (value === undefined || value.startsWith('--')) { usageFailure(`option '${flag}' requires a value`); return; }
56
+ seen.add(flag);
57
+ values[key] = key === 'observedFacts' ? value.split(',').map((fact) => fact.trim()).filter(Boolean)
58
+ : key === 'alertNumber' || key === 'issueNumber' || key === 'pullRequestNumber' ? Number(value) : value;
59
+ }
60
+
61
+ const required: Record<string, string[]> = {
62
+ diagnose: [],
63
+ issue: ['failureId', 'target', 'operator', 'reason', 'scope', 'expiresAt'],
64
+ consume: ['failureId', 'target', 'scope', 'ticketId']
65
+ };
66
+ const missing = required[operation]!.find((key) => values[key] === undefined);
67
+ if (missing) { usageFailure(`${operation} requires '--${missing.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}'`); return; }
68
+
69
+ const resolved = resolveTaskRef(taskRef);
70
+ if (!resolved.ok) {
71
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } })}\n`);
72
+ process.exitCode = 1;
73
+ return;
74
+ }
75
+
76
+ let result;
77
+ try {
78
+ if (operation === 'diagnose') {
79
+ result = diagnoseHumanOverrideForTask(
80
+ taskRef,
81
+ values.failureId ? String(values.failureId) : undefined,
82
+ values.target ? String(values.target) : undefined
83
+ );
84
+ } else if (operation === 'issue') {
85
+ result = withTaskExecutionLock(
86
+ resolved.repoRoot,
87
+ resolved.taskId,
88
+ 'task-override.issue',
89
+ () => issueHumanOverride({
90
+ taskRef,
91
+ failureId: String(values.failureId),
92
+ target: String(values.target),
93
+ operator: String(values.operator),
94
+ reason: String(values.reason),
95
+ scope: String(values.scope),
96
+ expiresAt: String(values.expiresAt),
97
+ ...(values.intent ? { intent: String(values.intent) as HumanOverrideRequest['intent'] } : {}),
98
+ ...(values.alertNumber !== undefined ? { alertNumber: Number(values.alertNumber) } : {}),
99
+ ...(values.issueNumber !== undefined ? { issueNumber: Number(values.issueNumber) } : {}),
100
+ ...(values.pullRequestNumber !== undefined ? { pullRequestNumber: Number(values.pullRequestNumber) } : {}),
101
+ ...(values.stagingDir ? { stagingDir: String(values.stagingDir) } : {}),
102
+ ...(values.failureDigest ? { failureDigest: String(values.failureDigest) } : {})
103
+ } satisfies HumanOverrideRequest)
104
+ );
105
+ } else {
106
+ result = withTaskExecutionLock(
107
+ resolved.repoRoot,
108
+ resolved.taskId,
109
+ 'task-override.consume',
110
+ () => consumeHumanOverride({
111
+ taskRef,
112
+ ticketId: String(values.ticketId),
113
+ failureId: String(values.failureId),
114
+ target: String(values.target),
115
+ scope: String(values.scope),
116
+ ...(values.intent ? { intent: String(values.intent) as ConsumeHumanOverrideRequest['intent'] } : {}),
117
+ ...(values.alertNumber !== undefined ? { alertNumber: Number(values.alertNumber) } : {}),
118
+ ...(values.issueNumber !== undefined ? { issueNumber: Number(values.issueNumber) } : {}),
119
+ ...(values.pullRequestNumber !== undefined ? { pullRequestNumber: Number(values.pullRequestNumber) } : {}),
120
+ ...(values.stagingDir ? { stagingDir: String(values.stagingDir) } : {}),
121
+ ...(values.failureDigest ? { failureDigest: String(values.failureDigest) } : {}),
122
+ ...(values.observedFacts ? { observedFacts: values.observedFacts as string[] } : {})
123
+ } satisfies ConsumeHumanOverrideRequest)
124
+ );
125
+ }
126
+ } catch (cause) {
127
+ if (cause instanceof TaskExecutionLockError) {
128
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: cause.code, message: cause.message } })}\n`);
129
+ process.exitCode = 1;
130
+ return;
131
+ }
132
+ throw cause;
133
+ }
134
+ process.stdout.write(`${JSON.stringify(result)}\n`);
135
+ if (result.status === 'failed') process.exitCode = 1;
136
+ }
137
+
138
+ export { taskOverride };