@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
@@ -1,6 +1,70 @@
1
- import { requestSandboxControl, SandboxControlClientError } from '../sandbox/control/client.js';
1
+ import { requestSandboxControl, requestSandboxTaskFinalization, requestSandboxTaskControl, SandboxControlClientError } from '../sandbox/control/client.js';
2
+ import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
2
3
  import { serveSandboxControl } from '../sandbox/control/server.js';
3
4
  import { runSandboxControlExecutor } from '../sandbox/control/executor.js';
5
+ import { controllerProofFromContext, verifyCodexSandboxControllerContext } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.js';
6
+ function isCanonicalCodexPrepare(args) {
7
+ if (args[1] !== 'prepare')
8
+ return false;
9
+ const clients = [];
10
+ for (let index = 2; index < args.length; index += 1) {
11
+ if (args[index] === '--client')
12
+ clients.push(args[index + 1] ?? '');
13
+ else if (args[index]?.startsWith('--client='))
14
+ clients.push(args[index].slice('--client='.length));
15
+ }
16
+ return clients.length === 1 && clients[0] === 'codex';
17
+ }
18
+ function writeFinalizationEnvelope(status, accepted, error, changed = false, result = null) {
19
+ process.stdout.write(`${JSON.stringify({ version: 1, status, changed, accepted, result, error })}\n`);
20
+ }
21
+ function finalizationErrorStatus(error) {
22
+ if (error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN')
23
+ return 'unknown';
24
+ return error.retryable ? 'blocked' : 'failed';
25
+ }
26
+ function sandboxFinalizationClient(args) {
27
+ if (args.length !== 4 || !args[0] || args[1] !== 'complete' || args[2] !== '--agent' || !args[3]) {
28
+ const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message: 'task ref, complete intent, and --agent are required', retryable: false };
29
+ writeFinalizationEnvelope('failed', false, error);
30
+ process.stderr.write('Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n');
31
+ process.exitCode = 1;
32
+ return;
33
+ }
34
+ const agent = normalizeAgentToken(args[3]);
35
+ if (!agent) {
36
+ const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message: `invalid --agent: ${AGENT_USAGE_HINT}`, retryable: false };
37
+ writeFinalizationEnvelope('failed', false, error);
38
+ process.exitCode = 1;
39
+ return;
40
+ }
41
+ let response;
42
+ try {
43
+ response = requestSandboxTaskFinalization({ agent });
44
+ }
45
+ catch (error) {
46
+ if (!(error instanceof SandboxControlClientError))
47
+ throw error;
48
+ writeFinalizationEnvelope(finalizationErrorStatus(error.detail), error.accepted, error.detail);
49
+ process.stderr.write(`${error.detail.message}\n`);
50
+ process.exitCode = error.detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.detail.retryable ? 2 : 1;
51
+ return;
52
+ }
53
+ if (response.phase === 'rejected') {
54
+ const error = response.error ?? {
55
+ code: 'SANDBOX_CONTROL_REJECTED',
56
+ message: 'sandbox control rejected the finalization request',
57
+ retryable: false
58
+ };
59
+ writeFinalizationEnvelope(finalizationErrorStatus(error), error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN', error);
60
+ process.stderr.write(`${error.message}\n`);
61
+ process.exitCode = error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.retryable ? 2 : 1;
62
+ return;
63
+ }
64
+ process.stdout.write(response.stdout);
65
+ process.stderr.write(response.stderr);
66
+ process.exitCode = response.exitCode ?? 1;
67
+ }
4
68
  async function sandboxControl(args) {
5
69
  const [operation, ...rest] = args;
6
70
  if (operation === 'serve') {
@@ -33,9 +97,22 @@ async function sandboxControl(args) {
33
97
  }
34
98
  if (operation === 'client') {
35
99
  const [family = '', ...commandArgs] = rest;
100
+ if (family === 'task-finalization') {
101
+ sandboxFinalizationClient(commandArgs);
102
+ return;
103
+ }
36
104
  let response;
37
105
  try {
38
- response = requestSandboxControl({ family, args: commandArgs });
106
+ if (family === 'task-orchestration' && isCanonicalCodexPrepare(commandArgs)) {
107
+ const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
108
+ const proof = contextPath
109
+ ? controllerProofFromContext(verifyCodexSandboxControllerContext(contextPath))
110
+ : null;
111
+ response = requestSandboxTaskControl({ family, args: commandArgs, controllerProof: proof });
112
+ }
113
+ else {
114
+ response = requestSandboxControl({ family, args: commandArgs });
115
+ }
39
116
  }
40
117
  catch (error) {
41
118
  if (!(error instanceof SandboxControlClientError))
@@ -1,4 +1,7 @@
1
1
  import { applyPrReviewActivityIntent, inspectPrReviewActivity } from '../task/activity-intent.js';
2
+ import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
3
+ import { resolveTaskRef } from '../task/resolve-ref.js';
4
+ import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
2
5
  const USAGE = `Usage: agent-infra-internal task-activity <task-ref> pr-review-inspect
3
6
  agent-infra-internal task-activity <task-ref> pr-review-start --agent <agent> --artifact <canonical.md> --head <40hex> [--dry-run]
4
7
  agent-infra-internal task-activity <task-ref> pr-review-complete --agent <agent> --artifact <canonical.md> --head <40hex> --verdict <approved|changes-requested|commented> --blockers <N> --major <N> --minor <N> [--dry-run]
@@ -14,14 +17,15 @@ const FLAGS = {
14
17
  '--major': 'major',
15
18
  '--minor': 'minor',
16
19
  '--outcome': 'outcome',
17
- '--reason': 'reason'
20
+ '--reason': 'reason',
21
+ '--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
18
22
  };
19
23
  const COMMON = ['agent', 'artifact', 'head'];
20
24
  const ALLOWED = {
21
25
  'pr-review-inspect': new Set(['kind', 'taskRef']),
22
- 'pr-review-start': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'dryRun']),
23
- 'pr-review-complete': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'verdict', 'blockers', 'major', 'minor', 'dryRun']),
24
- 'pr-review-terminate': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'outcome', 'reason', 'dryRun'])
26
+ 'pr-review-start': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'dryRun', 'overrideTicket', 'overrideTarget', 'overrideScope']),
27
+ 'pr-review-complete': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'verdict', 'blockers', 'major', 'minor', 'dryRun', 'overrideTicket', 'overrideTarget', 'overrideScope']),
28
+ 'pr-review-terminate': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'outcome', 'reason', 'dryRun', 'overrideTicket', 'overrideTarget', 'overrideScope'])
25
29
  };
26
30
  function usageFailure(message) {
27
31
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'ACTIVITY_PAYLOAD_INVALID', message } })}\n`);
@@ -96,9 +100,55 @@ function taskActivity(args = []) {
96
100
  return;
97
101
  }
98
102
  }
99
- const result = applyPrReviewActivityIntent(values);
100
- process.stdout.write(`${JSON.stringify(result)}\n`);
101
- if (result.status === 'failed')
103
+ const dryRunConflict = overrideDryRunConflict(values);
104
+ if (dryRunConflict) {
105
+ usageFailure(dryRunConflict.message);
106
+ return;
107
+ }
108
+ const resolved = resolveTaskRef(taskRef);
109
+ if (!resolved.ok) {
110
+ usageFailure(resolved.message);
111
+ return;
112
+ }
113
+ let result;
114
+ let humanOverride = null;
115
+ try {
116
+ result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-activity.${kind}`, () => {
117
+ let current = applyPrReviewActivityIntent(values, { lockAlreadyHeld: true });
118
+ if (current.status !== 'failed' || !values.overrideTicket)
119
+ return current;
120
+ if (!values.overrideTarget || !values.overrideScope) {
121
+ usageFailure('override ticket requires target and scope');
122
+ return current;
123
+ }
124
+ const consumed = consumeHumanOverride({
125
+ taskRef, ticketId: String(values.overrideTicket),
126
+ failureId: failureId('activity-intent', current.error?.code ?? 'TASK_STATE_MISMATCH'),
127
+ target: String(values.overrideTarget), scope: String(values.overrideScope)
128
+ }, {
129
+ effectExecutor: (capability) => {
130
+ const retried = applyPrReviewActivityIntent(values, { lockAlreadyHeld: true, manualOverride: capability });
131
+ current = retried;
132
+ return retried.status === 'failed' || retried.status === 'planned'
133
+ ? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no activity effect was committed' : `${retried.error?.code ?? 'ACTIVITY_FAILED'}: ${retried.error?.message ?? 'manual activity effect failed'}` }
134
+ : null;
135
+ }
136
+ });
137
+ humanOverride = consumed;
138
+ return current;
139
+ });
140
+ }
141
+ catch (error) {
142
+ if (!(error instanceof TaskExecutionLockError))
143
+ throw error;
144
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
145
+ process.exitCode = 1;
146
+ return;
147
+ }
148
+ process.stdout.write(`${JSON.stringify(humanOverride ? { ...result, humanOverride } : result)}\n`);
149
+ const overrideFailed = Boolean(humanOverride && typeof humanOverride === 'object' &&
150
+ humanOverride.status === 'failed');
151
+ if (result.status === 'failed' || overrideFailed)
102
152
  process.exitCode = 1;
103
153
  }
104
154
  export { taskActivity };
@@ -1,5 +1,8 @@
1
1
  import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
2
2
  import { applyTaskEvent, eventCatalog } from '../task/events.js';
3
+ import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
4
+ import { resolveTaskRef } from '../task/resolve-ref.js';
5
+ import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
3
6
  const USAGE = `Usage: agent-infra-internal task-event <N | TASK-id> <event> --agent <agent> [event options] [--orchestrated] [--dry-run]
4
7
 
5
8
  Apply one closed-set task lifecycle event and print a structured JSON result.
@@ -10,7 +13,8 @@ const FLAGS = {
10
13
  '--fix-for': 'fixFor', '--implementation-input': 'implementationInput',
11
14
  '--verdict': 'verdict', '--blockers': 'blockers', '--major': 'major',
12
15
  '--minor': 'minor', '--manual-validation': 'manualValidation', '--files-modified': 'filesModified',
13
- '--tests-passed': 'testsPassed', '--summary-result': 'summaryResult'
16
+ '--tests-passed': 'testsPassed', '--summary-result': 'summaryResult',
17
+ '--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
14
18
  };
15
19
  const NUMERIC = new Set(['round', 'question', 'blockers', 'major', 'minor', 'manualValidation', 'filesModified', 'testsPassed']);
16
20
  function usageFailure(message) {
@@ -77,9 +81,58 @@ function taskEvent(args = []) {
77
81
  return;
78
82
  }
79
83
  request.agent = agent;
80
- const result = applyTaskEvent(request);
81
- process.stdout.write(`${JSON.stringify(result)}\n`);
82
- if (result.status === 'failed')
84
+ const dryRunConflict = overrideDryRunConflict(request);
85
+ if (dryRunConflict) {
86
+ usageFailure(dryRunConflict.message);
87
+ return;
88
+ }
89
+ const resolved = resolveTaskRef(request.taskRef);
90
+ if (!resolved.ok) {
91
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } })}\n`);
92
+ process.exitCode = 1;
93
+ return;
94
+ }
95
+ let result;
96
+ let humanOverride = null;
97
+ try {
98
+ result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-event.${request.event}`, () => {
99
+ let current = applyTaskEvent(request, { lockAlreadyHeld: true });
100
+ const values = request;
101
+ if (current.status !== 'failed' || !values.overrideTicket)
102
+ return current;
103
+ if (!values.overrideTarget || !values.overrideScope) {
104
+ return { ...current, humanOverride: { status: 'failed', error: { code: 'OVERRIDE_PAYLOAD_INVALID', message: 'override ticket requires target and scope' } } };
105
+ }
106
+ const consumed = consumeHumanOverride({
107
+ taskRef: request.taskRef,
108
+ ticketId: String(values.overrideTicket),
109
+ failureId: failureId('task-event', current.error?.code ?? 'EVENT_TRANSITION_INVALID'),
110
+ target: String(values.overrideTarget),
111
+ scope: String(values.overrideScope)
112
+ }, {
113
+ effectExecutor: (capability) => {
114
+ const retried = applyTaskEvent(request, { lockAlreadyHeld: true, manualOverride: capability });
115
+ current = retried;
116
+ return retried.status === 'failed' || retried.status === 'planned'
117
+ ? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no task event was committed' : `${retried.error?.code ?? 'EVENT_FAILED'}: ${retried.error?.message ?? 'manual task event effect failed'}` }
118
+ : null;
119
+ }
120
+ });
121
+ humanOverride = consumed;
122
+ return current;
123
+ });
124
+ }
125
+ catch (error) {
126
+ if (!(error instanceof TaskExecutionLockError))
127
+ throw error;
128
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
129
+ process.exitCode = 1;
130
+ return;
131
+ }
132
+ process.stdout.write(`${JSON.stringify(humanOverride ? { ...result, humanOverride } : result)}\n`);
133
+ const overrideFailed = Boolean(humanOverride && typeof humanOverride === 'object' &&
134
+ humanOverride.status === 'failed');
135
+ if (result.status === 'failed' || overrideFailed)
83
136
  process.exitCode = 1;
84
137
  }
85
138
  export { taskEvent };
@@ -0,0 +1,54 @@
1
+ import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
2
+ import { applyTaskFinalization } from '../task/finalization.js';
3
+ import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
4
+ import { verifyTaskEvent } from '../task/verification.js';
5
+ const USAGE = 'Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n';
6
+ function envelope(status, changed, accepted, result, error) {
7
+ return `${JSON.stringify({ version: 1, status, changed, accepted, result, error })}\n`;
8
+ }
9
+ function exitCode(status) {
10
+ return status === 'completed' ? 0 : status === 'blocked' ? 2 : 1;
11
+ }
12
+ function fail(message) {
13
+ const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message, retryable: false };
14
+ process.stdout.write(envelope('failed', false, false, null, error));
15
+ process.stderr.write(USAGE);
16
+ process.exitCode = 1;
17
+ }
18
+ function taskFinalization(args = []) {
19
+ if (args[0] === '--help' || args[0] === '-h') {
20
+ process.stdout.write(USAGE);
21
+ return;
22
+ }
23
+ if (args.length !== 4 || !args[0] || args[1] !== 'complete' || args[2] !== '--agent' || !args[3]) {
24
+ fail('task ref, complete intent, and --agent are required');
25
+ return;
26
+ }
27
+ const agent = normalizeAgentToken(args[3]);
28
+ if (!agent) {
29
+ fail(`invalid --agent: ${AGENT_USAGE_HINT}`);
30
+ return;
31
+ }
32
+ let repoRoot;
33
+ try {
34
+ repoRoot = detectRepoRoot();
35
+ }
36
+ catch (error) {
37
+ const detail = { code: 'REPO_ROOT_NOT_FOUND', message: error instanceof Error ? error.message : String(error), retryable: false };
38
+ process.stdout.write(envelope('failed', false, true, null, detail));
39
+ process.exitCode = 1;
40
+ return;
41
+ }
42
+ const resolved = resolveTaskRef(args[0], { repoRoot });
43
+ if (!resolved.ok) {
44
+ const detail = { code: resolved.code, message: resolved.message, retryable: false };
45
+ process.stdout.write(envelope('failed', false, true, null, detail));
46
+ process.exitCode = 1;
47
+ return;
48
+ }
49
+ const result = applyTaskFinalization({ taskRef: resolved.taskId, intent: 'complete', agent }, { repoRoot, preflight: (request, options) => verifyTaskEvent({ ...request, event: 'complete-task.hard-preflight' }, options) });
50
+ process.stdout.write(envelope(result.status, result.changed, true, result, result.error));
51
+ process.exitCode = exitCode(result.status);
52
+ }
53
+ export { taskFinalization };
54
+ //# sourceMappingURL=task-finalization.js.map
@@ -1,12 +1,15 @@
1
1
  import fs from 'node:fs';
2
2
  import { applyLedgerIntent } from '../task/ledger-intents.js';
3
+ import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
3
4
  import { isReviewStage, parseLedger, summarizeLedgerStage, validateLedgerRows } from '../task/ledger.js';
4
5
  import { resolveTaskRef } from '../task/resolve-ref.js';
6
+ import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
5
7
  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`;
6
8
  const FLAGS = {
7
9
  '--stage': 'stage', '--review-artifact': 'reviewArtifact', '--ordinal': 'ordinal',
8
10
  '--severity': 'severity', '--evidence': 'evidence', '--id': 'id', '--round': 'round',
9
- '--status': 'status', '--artifact': 'artifact', '--needs-implementation': 'needsImplementation'
11
+ '--status': 'status', '--artifact': 'artifact', '--needs-implementation': 'needsImplementation',
12
+ '--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
10
13
  };
11
14
  const NUMERIC = new Set(['ordinal', 'round']);
12
15
  const BOOLEAN = new Set(['needsImplementation']);
@@ -75,7 +78,10 @@ function taskLedger(args = []) {
75
78
  'finding-review': ['needsImplementation'],
76
79
  'decision-upsert': ['needsImplementation']
77
80
  };
78
- const allowed = new Set(['kind', 'taskRef', 'dryRun', ...required[kind], ...(optional[kind] ?? [])]);
81
+ const overrideFields = kind === 'stage-status' || kind === 'decision-next-id'
82
+ ? []
83
+ : ['overrideTicket', 'overrideTarget', 'overrideScope'];
84
+ const allowed = new Set(['kind', 'taskRef', 'dryRun', ...required[kind], ...(optional[kind] ?? []), ...overrideFields]);
79
85
  const unexpected = Object.keys(values).find((key) => !allowed.has(key));
80
86
  const missing = required[kind].find((key) => values[key] === undefined);
81
87
  if (unexpected) {
@@ -86,6 +92,11 @@ function taskLedger(args = []) {
86
92
  usageFailure(`${kind} requires '${missing}'`);
87
93
  return;
88
94
  }
95
+ const dryRunConflict = overrideDryRunConflict(values);
96
+ if (dryRunConflict) {
97
+ usageFailure(dryRunConflict.message);
98
+ return;
99
+ }
89
100
  if (kind === 'stage-status') {
90
101
  if (!isReviewStage(String(values.stage))) {
91
102
  usageFailure(`stage-status requires a valid review stage`);
@@ -114,9 +125,52 @@ function taskLedger(args = []) {
114
125
  }
115
126
  return;
116
127
  }
117
- const result = applyLedgerIntent(values);
118
- process.stdout.write(`${JSON.stringify(result)}\n`);
119
- if (result.status === 'failed')
128
+ const lockResolved = resolveTaskRef(taskRef);
129
+ if (!lockResolved.ok) {
130
+ usageFailure(lockResolved.message);
131
+ return;
132
+ }
133
+ let result;
134
+ let humanOverride = null;
135
+ try {
136
+ result = withTaskExecutionLock(lockResolved.repoRoot, lockResolved.taskId, `task-ledger.${kind}`, () => {
137
+ let current = applyLedgerIntent(values);
138
+ if (current.status !== 'failed' || !values.overrideTicket)
139
+ return current;
140
+ if (!values.overrideTarget || !values.overrideScope) {
141
+ usageFailure('override ticket requires target and scope');
142
+ return current;
143
+ }
144
+ const consumed = consumeHumanOverride({
145
+ taskRef,
146
+ ticketId: String(values.overrideTicket),
147
+ failureId: failureId('ledger-intent', current.error?.code ?? 'TASK_STATE_MISMATCH'),
148
+ target: String(values.overrideTarget),
149
+ scope: String(values.overrideScope)
150
+ }, {
151
+ effectExecutor: (capability) => {
152
+ const retried = applyLedgerIntent(values, { manualOverride: capability });
153
+ current = retried;
154
+ return retried.status === 'failed'
155
+ ? { code: 'OVERRIDE_EFFECT_FAILED', message: `${retried.error?.code ?? 'LEDGER_FAILED'}: ${retried.error?.message ?? 'manual ledger effect failed'}` }
156
+ : null;
157
+ }
158
+ });
159
+ humanOverride = consumed;
160
+ return current;
161
+ });
162
+ }
163
+ catch (error) {
164
+ if (!(error instanceof TaskExecutionLockError))
165
+ throw error;
166
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
167
+ process.exitCode = 1;
168
+ return;
169
+ }
170
+ process.stdout.write(`${JSON.stringify(humanOverride ? { ...result, humanOverride } : result)}\n`);
171
+ const overrideFailed = Boolean(humanOverride && typeof humanOverride === 'object' &&
172
+ humanOverride.status === 'failed');
173
+ if (result.status === 'failed' || overrideFailed)
120
174
  process.exitCode = 1;
121
175
  }
122
176
  export { taskLedger };
@@ -1,12 +1,14 @@
1
1
  import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
2
+ import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
2
3
  import { applyTaskLifecycle, lifecycleIntentCatalog } from '../task/lifecycle.js';
3
- import { resolveTaskRef } from '../task/resolve-ref.js';
4
+ import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
4
5
  import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
5
- const USAGE = `Usage: agent-infra-internal task-lifecycle <N | TASK-id> <intent> --agent <agent> [intent flags] [--dry-run]\n\nIntents: ${lifecycleIntentCatalog.join(', ')}\n`;
6
+ 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`;
6
7
  const FLAGS = {
7
8
  '--agent': 'agent', '--reason': 'reason', '--unblock-condition': 'unblockCondition',
8
9
  '--note': 'note', '--alert-number': 'alertNumber', '--staging-dir': 'stagingDir',
9
- '--issue-number': 'issueNumber'
10
+ '--issue-number': 'issueNumber', '--override-ticket': 'overrideTicket',
11
+ '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
10
12
  };
11
13
  function usageFailure(message) {
12
14
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'LIFECYCLE_PAYLOAD_INVALID', message } })}\n`);
@@ -58,15 +60,64 @@ function taskLifecycle(args = []) {
58
60
  return;
59
61
  }
60
62
  values.agent = agent;
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;
63
+ const dryRunConflict = overrideDryRunConflict(values);
64
+ if (dryRunConflict) {
65
+ usageFailure(dryRunConflict.message);
65
66
  return;
66
67
  }
68
+ let repoRoot;
69
+ let taskId;
70
+ if (values.intent === 'restore') {
71
+ // restore's precondition is that the task does NOT exist locally yet (it only lives
72
+ // in a staging dir), so resolveTaskRef's existence check is inapplicable here; identity
73
+ // is guaranteed by applyTaskLifecycle's own full-TASK-id check plus staging validation.
74
+ try {
75
+ repoRoot = detectRepoRoot();
76
+ }
77
+ catch (error) {
78
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'REPO_ROOT_NOT_FOUND', message: error instanceof Error ? error.message : String(error) } })}\n`);
79
+ process.exitCode = 1;
80
+ return;
81
+ }
82
+ taskId = String(values.taskRef);
83
+ }
84
+ else {
85
+ const resolved = resolveTaskRef(String(values.taskRef));
86
+ if (!resolved.ok) {
87
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } })}\n`);
88
+ process.exitCode = 1;
89
+ return;
90
+ }
91
+ repoRoot = resolved.repoRoot;
92
+ taskId = resolved.taskId;
93
+ }
67
94
  let result;
68
95
  try {
69
- result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-lifecycle.${String(values.intent)}`, () => applyTaskLifecycle(values));
96
+ result = withTaskExecutionLock(repoRoot, taskId, `task-lifecycle.${String(values.intent)}`, () => {
97
+ const lifecycleResult = applyTaskLifecycle(values);
98
+ if (lifecycleResult.status !== 'failed' || !values.overrideTicket)
99
+ return lifecycleResult;
100
+ if (!values.overrideTarget || !values.overrideScope) {
101
+ return {
102
+ ...lifecycleResult,
103
+ error: { code: 'OVERRIDE_PAYLOAD_INVALID', message: 'override ticket requires --override-target and --override-scope' }
104
+ };
105
+ }
106
+ const override = consumeHumanOverride({
107
+ taskRef: String(values.taskRef),
108
+ ticketId: String(values.overrideTicket),
109
+ failureId: failureId('lifecycle.apply', lifecycleResult.error?.code ?? 'LIFECYCLE_FAILED'),
110
+ target: String(values.overrideTarget),
111
+ scope: String(values.overrideScope),
112
+ intent: String(values.intent),
113
+ ...(values.alertNumber ? { alertNumber: Number(values.alertNumber) } : {}),
114
+ ...(values.issueNumber ? { issueNumber: Number(values.issueNumber) } : {}),
115
+ ...(values.stagingDir ? { stagingDir: String(values.stagingDir) } : {})
116
+ });
117
+ if (override.status === 'failed')
118
+ return { ...lifecycleResult, humanOverride: override };
119
+ return { ...lifecycleResult, ...override, humanOverride: override, error: null };
120
+ });
70
121
  }
71
122
  catch (error) {
72
123
  if (!(error instanceof TaskExecutionLockError))