@fitlab-ai/agent-infra 0.9.3 → 0.9.5

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 (203) hide show
  1. package/README.md +3 -1
  2. package/README.zh-CN.md +3 -1
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +144 -123
  6. package/dist/lib/agent-clients/adapter.js +94 -4
  7. package/dist/lib/{sandbox/credentials.js → agent-clients/adapters/claude-code-credentials.js} +4 -14
  8. package/dist/lib/agent-clients/adapters/claude-code-refresh.js +85 -0
  9. package/dist/lib/agent-clients/adapters/claude-code-sandbox.js +247 -0
  10. package/dist/lib/agent-clients/adapters/claude-code.js +17 -3
  11. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +382 -0
  12. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +163 -0
  13. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +211 -0
  14. package/dist/lib/agent-clients/adapters/codex.js +2 -2
  15. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +108 -0
  16. package/dist/lib/agent-clients/adapters/opencode.js +15 -3
  17. package/dist/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
  18. package/dist/lib/agent-clients/registry.js +9 -1
  19. package/dist/lib/git/worktree-identity.js +64 -0
  20. package/dist/lib/internal/codex-lifecycle.js +202 -0
  21. package/dist/lib/internal/sandbox-control.js +24 -0
  22. package/dist/lib/internal/task-activity.js +45 -13
  23. package/dist/lib/internal/task-create.js +48 -0
  24. package/dist/lib/internal/task-ledger.js +12 -3
  25. package/dist/lib/internal/task-lifecycle.js +19 -1
  26. package/dist/lib/internal/task-orchestration.js +170 -27
  27. package/dist/lib/platform/github-client.js +19 -2
  28. package/dist/lib/platform/pr-checks.js +1 -1
  29. package/dist/lib/platform/pull-requests.js +47 -1
  30. package/dist/lib/platform/releases.js +13 -5
  31. package/dist/lib/process-data/index.js +239 -0
  32. package/dist/lib/process-data/normalize.js +198 -0
  33. package/dist/lib/process-data/privacy.js +33 -0
  34. package/dist/lib/process-data/reconcile.js +95 -0
  35. package/dist/lib/process-data/repair.js +58 -0
  36. package/dist/lib/process-data/sources.js +345 -0
  37. package/dist/lib/process-data/store.js +282 -0
  38. package/dist/lib/process-data/types.js +2 -0
  39. package/dist/lib/run/index.js +4 -3
  40. package/dist/lib/run/tui.js +7 -8
  41. package/dist/lib/sandbox/agent-client-reconciler.js +26 -2
  42. package/dist/lib/sandbox/capture.js +7 -2
  43. package/dist/lib/sandbox/commands/create.js +110 -228
  44. package/dist/lib/sandbox/commands/enter.js +15 -47
  45. package/dist/lib/sandbox/commands/list-running.js +16 -6
  46. package/dist/lib/sandbox/commands/ls.js +10 -6
  47. package/dist/lib/sandbox/commands/rebuild.js +4 -3
  48. package/dist/lib/sandbox/commands/refresh.js +4 -84
  49. package/dist/lib/sandbox/commands/rm.js +24 -3
  50. package/dist/lib/sandbox/commands/show.js +12 -3
  51. package/dist/lib/sandbox/commands/start.js +7 -4
  52. package/dist/lib/sandbox/config.js +2 -0
  53. package/dist/lib/sandbox/constants.js +6 -0
  54. package/dist/lib/sandbox/control/client.js +58 -0
  55. package/dist/lib/sandbox/control/protocol.js +54 -0
  56. package/dist/lib/sandbox/control/server.js +144 -0
  57. package/dist/lib/sandbox/dockerfile.js +36 -13
  58. package/dist/lib/sandbox/mounts.js +17 -4
  59. package/dist/lib/sandbox/recovery.js +128 -5
  60. package/dist/lib/sandbox/redaction.js +12 -0
  61. package/dist/lib/sandbox/runtimes/base.dockerfile +24 -74
  62. package/dist/lib/sandbox/workspace-identity.js +95 -0
  63. package/dist/lib/sandbox/workspace-view.js +109 -0
  64. package/dist/lib/task/activity-intent.js +257 -51
  65. package/dist/lib/task/activity-log.js +32 -5
  66. package/dist/lib/task/commit-finalization.js +278 -0
  67. package/dist/lib/task/commit-intent.js +165 -0
  68. package/dist/lib/task/create-service.js +137 -0
  69. package/dist/lib/task/create.js +333 -0
  70. package/dist/lib/task/decision-intents.js +31 -17
  71. package/dist/lib/task/delegation-receipts.js +2 -2
  72. package/dist/lib/task/implementation-inputs.js +54 -7
  73. package/dist/lib/task/ledger-intents.js +35 -1
  74. package/dist/lib/task/orchestration.js +505 -34
  75. package/dist/lib/task/resolve-ref.js +41 -1
  76. package/dist/lib/task/review-fingerprint.js +4 -0
  77. package/dist/lib/task/workspace-snapshot.js +78 -3
  78. package/lib/agent-clients/adapter.ts +130 -6
  79. package/lib/{sandbox/credentials.ts → agent-clients/adapters/claude-code-credentials.ts} +3 -18
  80. package/lib/agent-clients/adapters/claude-code-refresh.ts +128 -0
  81. package/lib/agent-clients/adapters/claude-code-sandbox.ts +295 -0
  82. package/lib/agent-clients/adapters/claude-code.ts +21 -3
  83. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +442 -0
  84. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +307 -0
  85. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +250 -0
  86. package/lib/agent-clients/adapters/codex.ts +2 -2
  87. package/lib/agent-clients/adapters/opencode-sandbox.ts +136 -0
  88. package/lib/agent-clients/adapters/opencode.ts +18 -3
  89. package/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
  90. package/lib/agent-clients/registry.ts +9 -1
  91. package/lib/git/worktree-identity.ts +86 -0
  92. package/lib/internal/codex-lifecycle.ts +204 -0
  93. package/lib/internal/sandbox-control.ts +24 -0
  94. package/lib/internal/task-activity.ts +50 -14
  95. package/lib/internal/task-create.ts +49 -0
  96. package/lib/internal/task-ledger.ts +12 -3
  97. package/lib/internal/task-lifecycle.ts +22 -1
  98. package/lib/internal/task-orchestration.ts +117 -21
  99. package/lib/platform/github-client.ts +20 -2
  100. package/lib/platform/pr-checks.ts +1 -1
  101. package/lib/platform/pull-requests.ts +49 -1
  102. package/lib/platform/releases.ts +16 -4
  103. package/lib/process-data/index.ts +224 -0
  104. package/lib/process-data/normalize.ts +202 -0
  105. package/lib/process-data/privacy.ts +41 -0
  106. package/lib/process-data/reconcile.ts +110 -0
  107. package/lib/process-data/repair.ts +64 -0
  108. package/lib/process-data/sources.ts +354 -0
  109. package/lib/process-data/store.ts +315 -0
  110. package/lib/process-data/types.ts +135 -0
  111. package/lib/run/index.ts +5 -3
  112. package/lib/run/tui.ts +11 -8
  113. package/lib/sandbox/agent-client-reconciler.ts +44 -2
  114. package/lib/sandbox/capture.ts +9 -2
  115. package/lib/sandbox/commands/create.ts +127 -260
  116. package/lib/sandbox/commands/enter.ts +13 -59
  117. package/lib/sandbox/commands/list-running.ts +23 -6
  118. package/lib/sandbox/commands/ls.ts +22 -6
  119. package/lib/sandbox/commands/rebuild.ts +4 -3
  120. package/lib/sandbox/commands/refresh.ts +4 -126
  121. package/lib/sandbox/commands/rm.ts +24 -3
  122. package/lib/sandbox/commands/show.ts +29 -3
  123. package/lib/sandbox/commands/start.ts +6 -4
  124. package/lib/sandbox/config.ts +4 -0
  125. package/lib/sandbox/constants.ts +8 -0
  126. package/lib/sandbox/control/client.ts +79 -0
  127. package/lib/sandbox/control/protocol.ts +110 -0
  128. package/lib/sandbox/control/server.ts +155 -0
  129. package/lib/sandbox/dockerfile.ts +66 -13
  130. package/lib/sandbox/mounts.ts +24 -4
  131. package/lib/sandbox/recovery.ts +163 -6
  132. package/lib/sandbox/redaction.ts +14 -0
  133. package/lib/sandbox/runtimes/base.dockerfile +24 -74
  134. package/lib/sandbox/tool-types.ts +16 -0
  135. package/lib/sandbox/workspace-identity.ts +123 -0
  136. package/lib/sandbox/workspace-view.ts +150 -0
  137. package/lib/task/activity-intent.ts +350 -61
  138. package/lib/task/activity-log.ts +46 -7
  139. package/lib/task/commit-finalization.ts +376 -0
  140. package/lib/task/commit-intent.ts +230 -0
  141. package/lib/task/create-service.ts +171 -0
  142. package/lib/task/create.ts +372 -0
  143. package/lib/task/decision-intents.ts +32 -12
  144. package/lib/task/delegation-receipts.ts +2 -2
  145. package/lib/task/implementation-inputs.ts +62 -8
  146. package/lib/task/ledger-intents.ts +43 -3
  147. package/lib/task/orchestration.ts +673 -41
  148. package/lib/task/resolve-ref.ts +46 -1
  149. package/lib/task/review-fingerprint.ts +9 -0
  150. package/lib/task/workspace-snapshot.ts +98 -4
  151. package/package.json +7 -7
  152. package/templates/.agents/hooks/lifecycle-delegation.js +47 -3
  153. package/templates/.agents/rules/README.en.md +1 -1
  154. package/templates/.agents/rules/README.zh-CN.md +1 -1
  155. package/templates/.agents/rules/human-decision-context.en.md +11 -2
  156. package/templates/.agents/rules/human-decision-context.zh-CN.md +11 -2
  157. package/templates/.agents/rules/pr-checks-commands.en.md +1 -1
  158. package/templates/.agents/rules/pr-checks-commands.github.en.md +1 -1
  159. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +2 -2
  160. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +1 -1
  161. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  162. package/templates/.agents/rules/task-management.en.md +3 -0
  163. package/templates/.agents/rules/task-management.zh-CN.md +3 -0
  164. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  165. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
  166. package/templates/.agents/skills/commit/SKILL.en.md +21 -17
  167. package/templates/.agents/skills/commit/SKILL.zh-CN.md +28 -16
  168. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +50 -0
  169. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +56 -0
  170. package/templates/.agents/skills/commit/reference/task-status-update.en.md +4 -4
  171. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +4 -4
  172. package/templates/.agents/skills/complete-task/SKILL.en.md +1 -1
  173. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +1 -1
  174. package/templates/.agents/skills/create-pr/SKILL.en.md +4 -4
  175. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +4 -4
  176. package/templates/.agents/skills/create-task/SKILL.en.md +26 -43
  177. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +26 -43
  178. package/templates/.agents/skills/create-task/reference/context-capture.en.md +34 -0
  179. package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +34 -0
  180. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  181. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  182. package/templates/.agents/skills/review-code/reference/output-templates.en.md +1 -1
  183. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +1 -1
  184. package/templates/.agents/skills/review-pr/SKILL.en.md +36 -8
  185. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +36 -8
  186. package/templates/.agents/skills/review-pr/reference/report-template.en.md +1 -1
  187. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +1 -1
  188. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +67 -50
  189. package/templates/.agents/skills/watch-pr/SKILL.en.md +4 -4
  190. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +4 -4
  191. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
  192. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
  193. package/templates/.agents/templates/task.en.md +1 -1
  194. package/templates/.agents/templates/task.zh-CN.md +1 -1
  195. package/templates/.claude/commands/watch-pr.en.md +1 -1
  196. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  197. package/templates/.claude/rules/tool-preferences.en.md +20 -0
  198. package/templates/.claude/rules/tool-preferences.zh-CN.md +20 -0
  199. package/templates/.codex/hooks.json +50 -0
  200. package/templates/.opencode/commands/watch-pr.en.md +1 -1
  201. package/templates/.opencode/commands/watch-pr.zh-CN.md +1 -1
  202. package/templates/.gemini/commands/_project_/review-pr.en.toml +0 -10
  203. package/templates/.gemini/commands/_project_/review-pr.zh-CN.toml +0 -10
@@ -0,0 +1,64 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ function normalizeCanonicalPath(value, platform = process.platform) {
5
+ return platform === 'win32'
6
+ ? path.win32.normalize(value).replaceAll('/', '\\').toLowerCase()
7
+ : path.posix.normalize(value);
8
+ }
9
+ function sameFilesystemEntry(left, right, platform = process.platform) {
10
+ const leftReal = fs.realpathSync.native(left);
11
+ const rightReal = fs.realpathSync.native(right);
12
+ const leftStat = fs.statSync(leftReal, { bigint: true });
13
+ const rightStat = fs.statSync(rightReal, { bigint: true });
14
+ if (leftStat.ino !== 0n && rightStat.ino !== 0n) {
15
+ return leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;
16
+ }
17
+ return normalizeCanonicalPath(leftReal, platform) === normalizeCanonicalPath(rightReal, platform);
18
+ }
19
+ function git(root, args) {
20
+ return execFileSync('git', args, {
21
+ cwd: root,
22
+ encoding: 'utf8',
23
+ stdio: ['ignore', 'pipe', 'pipe']
24
+ }).trim();
25
+ }
26
+ function resolveGitWorktreeIdentity(worktreeRoot) {
27
+ try {
28
+ const canonical = fs.realpathSync.native(worktreeRoot);
29
+ const topLevel = fs.realpathSync.native(git(canonical, ['rev-parse', '--show-toplevel']));
30
+ if (!sameFilesystemEntry(canonical, topLevel)) {
31
+ throw new Error('worktree root is not the Git top-level directory');
32
+ }
33
+ const commonDir = fs.realpathSync.native(git(canonical, [
34
+ 'rev-parse', '--path-format=absolute', '--git-common-dir'
35
+ ]));
36
+ const branch = git(canonical, ['symbolic-ref', '--short', 'HEAD']);
37
+ return { worktreeRoot: canonical, commonDir, branch };
38
+ }
39
+ catch (error) {
40
+ const message = error instanceof Error ? error.message : String(error);
41
+ throw new Error(`SANDBOX_CONTROL_WORKTREE_INVALID: ${message}`);
42
+ }
43
+ }
44
+ function assertGitWorktreeBinding(stateRoot, worktreeRoot, expectedBranch) {
45
+ const state = resolveGitWorktreeIdentity(stateRoot);
46
+ const worktree = resolveGitWorktreeIdentity(worktreeRoot);
47
+ if (!sameFilesystemEntry(state.commonDir, worktree.commonDir)) {
48
+ throw new Error('SANDBOX_CONTROL_WORKTREE_INVALID: state and worktree roots have different Git common directories');
49
+ }
50
+ if (worktree.branch !== expectedBranch) {
51
+ throw new Error(`SANDBOX_CONTROL_WORKTREE_INVALID: expected branch '${expectedBranch}', got '${worktree.branch}'`);
52
+ }
53
+ return worktree;
54
+ }
55
+ function assertGitRepositoryBinding(stateRoot, worktreeRoot) {
56
+ const state = resolveGitWorktreeIdentity(stateRoot);
57
+ const worktree = resolveGitWorktreeIdentity(worktreeRoot);
58
+ if (!sameFilesystemEntry(state.commonDir, worktree.commonDir)) {
59
+ throw new Error('SANDBOX_CONTROL_WORKTREE_INVALID: state and worktree roots have different Git common directories');
60
+ }
61
+ return worktree;
62
+ }
63
+ export { assertGitWorktreeBinding, assertGitRepositoryBinding, normalizeCanonicalPath, resolveGitWorktreeIdentity, sameFilesystemEntry };
64
+ //# sourceMappingURL=worktree-identity.js.map
@@ -0,0 +1,202 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import crypto from 'node:crypto';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { preflightCodexLifecycleEvidence, resolveCodexTerminal, resolveCodexThread } from '../agent-clients/adapters/codex-lifecycle/app-server.js';
6
+ import { createCodexLifecycleStore } from '../agent-clients/adapters/codex-lifecycle/store.js';
7
+ const USAGE = 'Usage: agent-infra-internal codex-lifecycle <hook-event|resolve-start|resolve-stop|preflight|consume> [options]\n';
8
+ const MANAGED_AGENT = /^agent-infra-lifecycle-(executor|reviewer)$/;
9
+ function output(value) {
10
+ process.stdout.write(`${JSON.stringify(value)}\n`);
11
+ }
12
+ function failure(code, message) {
13
+ output({ status: 'failed', changed: false, evidence: null, diagnostics: [], error: { code, message } });
14
+ process.exitCode = 1;
15
+ }
16
+ function parse(args) {
17
+ if (args[0] === '--help' || args[0] === '-h') {
18
+ process.stdout.write(USAGE);
19
+ return null;
20
+ }
21
+ const operation = args[0];
22
+ if (!operation) {
23
+ failure('CODEX_LIFECYCLE_PAYLOAD_INVALID', 'operation is required');
24
+ return null;
25
+ }
26
+ const allowed = {
27
+ 'hook-event': ['--event'],
28
+ 'resolve-start': ['--child-id'],
29
+ 'resolve-stop': ['--child-id'],
30
+ preflight: ['--format', '--session-id', '--turn-id', '--tool-use-id'],
31
+ consume: ['--child-id', '--consumer']
32
+ }[operation];
33
+ if (!allowed) {
34
+ failure('CODEX_LIFECYCLE_PAYLOAD_INVALID', 'unknown operation');
35
+ return null;
36
+ }
37
+ const values = {};
38
+ for (let index = 1; index < args.length; index += 1) {
39
+ const flag = args[index];
40
+ if (!allowed.includes(flag) || Object.hasOwn(values, flag)) {
41
+ failure('CODEX_LIFECYCLE_PAYLOAD_INVALID', `invalid or duplicate option '${flag}'`);
42
+ return null;
43
+ }
44
+ const value = args[++index];
45
+ if (!value || value.startsWith('--')) {
46
+ failure('CODEX_LIFECYCLE_PAYLOAD_INVALID', `option '${flag}' requires a value`);
47
+ return null;
48
+ }
49
+ values[flag] = value;
50
+ }
51
+ return Object.freeze({ operation, values: Object.freeze(values) });
52
+ }
53
+ function cliVersion() {
54
+ const result = spawnSync('codex', ['--version'], { encoding: 'utf8' });
55
+ const version = /codex-cli\s+(\d+\.\d+\.\d+)/.exec(result.stdout ?? '')?.[1];
56
+ if (result.status !== 0 || !version)
57
+ throw new Error('Codex CLI version is unavailable');
58
+ return version;
59
+ }
60
+ function hookDefinitionHash() {
61
+ const file = path.join(process.cwd(), '.codex', 'hooks.json');
62
+ return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
63
+ }
64
+ async function readStdin() {
65
+ let input = '';
66
+ process.stdin.setEncoding('utf8');
67
+ for await (const chunk of process.stdin) {
68
+ input += chunk;
69
+ if (Buffer.byteLength(input) > 64 * 1024)
70
+ throw new Error('Codex lifecycle input exceeds 64 KiB');
71
+ }
72
+ return JSON.parse(input || '{}');
73
+ }
74
+ function recordFromPayload(phase, payload) {
75
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
76
+ throw new Error('Codex lifecycle hook payload must be an object');
77
+ }
78
+ const value = payload;
79
+ const text = (key) => typeof value[key] === 'string' ? value[key] : '';
80
+ if (!MANAGED_AGENT.test(text('nativeAgent')))
81
+ return null;
82
+ if (phase === 'pre-tool')
83
+ return {
84
+ type: 'hook-spawn',
85
+ sessionId: text('sessionId'),
86
+ turnId: text('turnId'),
87
+ toolUseId: text('toolUseId'),
88
+ nativeAgent: text('nativeAgent'),
89
+ ...(text('requestedModel') ? { requestedModel: text('requestedModel') } : {}),
90
+ ...(text('requestedReasoningEffort') ? { requestedReasoningEffort: text('requestedReasoningEffort') } : {}),
91
+ hookDefinitionHash: text('hookDefinitionHash')
92
+ };
93
+ if (phase === 'subagent-start')
94
+ return {
95
+ type: 'hook-child',
96
+ sessionId: text('sessionId'),
97
+ turnId: text('turnId'),
98
+ childThreadId: text('childThreadId'),
99
+ nativeAgent: text('nativeAgent')
100
+ };
101
+ if (phase === 'subagent-stop')
102
+ return {
103
+ type: 'hook-stop',
104
+ sessionId: text('sessionId'),
105
+ turnId: text('turnId'),
106
+ childThreadId: text('childThreadId'),
107
+ nativeAgent: text('nativeAgent')
108
+ };
109
+ if (phase === 'post-tool')
110
+ return null;
111
+ throw new Error(`unknown Codex lifecycle hook phase '${phase}'`);
112
+ }
113
+ async function codexLifecycle(args = []) {
114
+ const parsed = parse(args);
115
+ if (!parsed || process.exitCode)
116
+ return;
117
+ try {
118
+ if (parsed.operation === 'preflight') {
119
+ const format = parsed.values['--format'] ?? 'json';
120
+ if (!['text', 'json'].includes(format))
121
+ throw new Error("format must be 'text' or 'json'");
122
+ const runtimeFields = [
123
+ parsed.values['--session-id'],
124
+ parsed.values['--turn-id'],
125
+ parsed.values['--tool-use-id']
126
+ ];
127
+ if (runtimeFields.some(Boolean) && !runtimeFields.every(Boolean)) {
128
+ throw new Error('preflight runtime identity requires --session-id, --turn-id, and --tool-use-id together');
129
+ }
130
+ const runtimeIdentity = runtimeFields.every(Boolean) ? {
131
+ sessionId: runtimeFields[0],
132
+ turnId: runtimeFields[1],
133
+ toolUseId: runtimeFields[2]
134
+ } : undefined;
135
+ const result = await preflightCodexLifecycleEvidence(process.cwd(), runtimeIdentity);
136
+ if (format === 'text') {
137
+ process.stdout.write(`Codex lifecycle static preflight: ready\nCLI: ${result.cliVersion}\nRuntime hook liveness: ${result.runtimeLiveness ? 'observed' : 'not-yet-observed'}\n`);
138
+ }
139
+ else
140
+ output({ status: 'ready', changed: false, evidence: result, diagnostics: result.diagnostics, error: null });
141
+ return;
142
+ }
143
+ const store = createCodexLifecycleStore({ cliVersion: cliVersion() });
144
+ if (parsed.operation === 'hook-event') {
145
+ const phase = parsed.values['--event'];
146
+ if (!phase || !['pre-tool', 'subagent-start', 'post-tool', 'subagent-stop'].includes(phase)) {
147
+ throw new Error('hook-event requires a known --event');
148
+ }
149
+ const event = recordFromPayload(phase, await readStdin());
150
+ if (!event) {
151
+ output({ status: 'ignored', changed: false, evidence: null, diagnostics: [], error: null });
152
+ return;
153
+ }
154
+ const result = store.apply(event);
155
+ output({ status: result.state.status, changed: true, evidence: result.state, diagnostics: [], error: result.state.error });
156
+ if (result.state.status === 'invalid')
157
+ process.exitCode = 1;
158
+ return;
159
+ }
160
+ const childThreadId = parsed.values['--child-id'];
161
+ if (!childThreadId)
162
+ throw new Error(`operation '${parsed.operation}' requires --child-id`);
163
+ if (parsed.operation === 'resolve-start') {
164
+ if (store.read(childThreadId).state.spawn?.hookDefinitionHash !== hookDefinitionHash()) {
165
+ throw new Error('Codex lifecycle hook definition hash is stale');
166
+ }
167
+ const resolved = await resolveCodexThread(childThreadId);
168
+ let latest = store.apply(resolved.resolution.thread);
169
+ for (const reroute of resolved.reroutes)
170
+ latest = store.apply(reroute);
171
+ latest = store.apply(resolved.resolution.settings);
172
+ output({ status: latest.state.status, changed: true, evidence: latest.state.startEvidence, diagnostics: resolved.diagnostics, error: latest.state.error });
173
+ if (latest.state.status !== 'start-ready')
174
+ process.exitCode = 1;
175
+ return;
176
+ }
177
+ if (parsed.operation === 'resolve-stop') {
178
+ const terminal = await resolveCodexTerminal(childThreadId);
179
+ const latest = store.apply(terminal);
180
+ if (latest.state.status !== 'stop-ready') {
181
+ throw new Error(`Codex lifecycle stop evidence is not ready (status=${latest.state.status})`);
182
+ }
183
+ output({ status: latest.state.status, changed: true, evidence: latest.state.stopEvidence, diagnostics: [], error: latest.state.error });
184
+ return;
185
+ }
186
+ if (parsed.operation === 'consume') {
187
+ const consumer = parsed.values['--consumer'];
188
+ if (!consumer)
189
+ throw new Error('consume requires --consumer');
190
+ const consumed = store.consume(childThreadId, consumer, hookDefinitionHash());
191
+ output({ status: 'consumed', changed: true, evidence: {
192
+ start: consumed.state.startEvidence,
193
+ stop: consumed.state.stopEvidence
194
+ }, diagnostics: [], error: null });
195
+ }
196
+ }
197
+ catch (error) {
198
+ failure('CODEX_LIFECYCLE_FAILED', error instanceof Error ? error.message : String(error));
199
+ }
200
+ }
201
+ export { codexLifecycle };
202
+ //# sourceMappingURL=codex-lifecycle.js.map
@@ -0,0 +1,24 @@
1
+ import { requestSandboxControl } from '../sandbox/control/client.js';
2
+ import { serveSandboxControl } from '../sandbox/control/server.js';
3
+ function sandboxControl(args) {
4
+ const [operation, ...rest] = args;
5
+ if (operation === 'serve') {
6
+ const manifestIndex = rest.indexOf('--manifest');
7
+ const manifest = manifestIndex >= 0 ? rest[manifestIndex + 1] : undefined;
8
+ if (!manifest)
9
+ throw new Error('sandbox-control serve requires --manifest');
10
+ serveSandboxControl(manifest);
11
+ return;
12
+ }
13
+ if (operation === 'client') {
14
+ const [family = '', ...commandArgs] = rest;
15
+ const response = requestSandboxControl({ family, args: commandArgs });
16
+ process.stdout.write(response.stdout);
17
+ process.stderr.write(response.stderr);
18
+ process.exitCode = response.exitCode;
19
+ return;
20
+ }
21
+ throw new Error('Usage: agent-infra-internal sandbox-control serve --manifest <path> | client <family> [args...]');
22
+ }
23
+ export { sandboxControl };
24
+ //# sourceMappingURL=sandbox-control.js.map
@@ -1,11 +1,27 @@
1
- import { applyActivityAppendIntent } from '../task/activity-intent.js';
2
- const USAGE = `Usage: agent-infra-internal task-activity <task-ref> append --step <step> --agent <agent> --note <note> [--artifact <canonical.md>] [--dry-run]
1
+ import { applyPrReviewActivityIntent, inspectPrReviewActivity } from '../task/activity-intent.js';
2
+ const USAGE = `Usage: agent-infra-internal task-activity <task-ref> pr-review-inspect
3
+ agent-infra-internal task-activity <task-ref> pr-review-start --agent <agent> --artifact <canonical.md> --head <40hex> [--dry-run]
4
+ 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]
5
+ agent-infra-internal task-activity <task-ref> pr-review-terminate --agent <agent> --artifact <canonical.md> --head <40hex> --outcome <aborted|superseded> --reason <single-line> [--dry-run]
3
6
  `;
7
+ const OPERATIONS = new Set(['pr-review-inspect', 'pr-review-start', 'pr-review-complete', 'pr-review-terminate']);
4
8
  const FLAGS = {
5
- '--step': 'step',
6
9
  '--agent': 'agent',
7
- '--note': 'note',
8
- '--artifact': 'artifact'
10
+ '--artifact': 'artifact',
11
+ '--head': 'head',
12
+ '--verdict': 'verdict',
13
+ '--blockers': 'blockers',
14
+ '--major': 'major',
15
+ '--minor': 'minor',
16
+ '--outcome': 'outcome',
17
+ '--reason': 'reason'
18
+ };
19
+ const COMMON = ['agent', 'artifact', 'head'];
20
+ const ALLOWED = {
21
+ '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'])
9
25
  };
10
26
  function usageFailure(message) {
11
27
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'ACTIVITY_PAYLOAD_INVALID', message } })}\n`);
@@ -18,8 +34,8 @@ function taskActivity(args = []) {
18
34
  return;
19
35
  }
20
36
  const [taskRef, kind] = args;
21
- if (!taskRef || taskRef.startsWith('--') || !kind || kind !== 'append') {
22
- usageFailure('task ref and the append intent are required');
37
+ if (!taskRef || taskRef.startsWith('--') || !kind || !OPERATIONS.has(kind)) {
38
+ usageFailure('task ref and a supported PR review intent are required');
23
39
  return;
24
40
  }
25
41
  const values = { kind, taskRef };
@@ -50,21 +66,37 @@ function taskActivity(args = []) {
50
66
  return;
51
67
  }
52
68
  seen.add(flag);
53
- values[key] = value;
69
+ values[key] = ['blockers', 'major', 'minor'].includes(key) ? Number(value) : value;
54
70
  }
55
- const allowed = new Set(['kind', 'taskRef', 'dryRun', 'step', 'agent', 'note', 'artifact']);
71
+ const allowed = ALLOWED[kind];
56
72
  const unexpected = Object.keys(values).find((key) => !allowed.has(key));
57
73
  if (unexpected) {
58
- usageFailure(`append does not accept '${unexpected}'`);
74
+ usageFailure(`${kind} does not accept '${unexpected}'`);
75
+ return;
76
+ }
77
+ if (kind === 'pr-review-inspect') {
78
+ const result = inspectPrReviewActivity(values);
79
+ process.stdout.write(`${JSON.stringify(result)}\n`);
80
+ if (result.status === 'failed')
81
+ process.exitCode = 1;
59
82
  return;
60
83
  }
61
- for (const key of ['step', 'agent', 'note']) {
84
+ for (const key of COMMON) {
85
+ if (values[key] === undefined) {
86
+ usageFailure(`${kind} requires '${key}'`);
87
+ return;
88
+ }
89
+ }
90
+ const operationRequired = kind === 'pr-review-complete'
91
+ ? ['verdict', 'blockers', 'major', 'minor']
92
+ : kind === 'pr-review-terminate' ? ['outcome', 'reason'] : [];
93
+ for (const key of operationRequired) {
62
94
  if (values[key] === undefined) {
63
- usageFailure(`append requires '${key}'`);
95
+ usageFailure(`${kind} requires '${key}'`);
64
96
  return;
65
97
  }
66
98
  }
67
- const result = applyActivityAppendIntent(values);
99
+ const result = applyPrReviewActivityIntent(values);
68
100
  process.stdout.write(`${JSON.stringify(result)}\n`);
69
101
  if (result.status === 'failed')
70
102
  process.exitCode = 1;
@@ -0,0 +1,48 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { requestSandboxTaskCreate } from '../sandbox/control/client.js';
4
+ import { SANDBOX_CONTROL_MAX_BYTES } from '../sandbox/control/protocol.js';
5
+ import { validateTaskCreateCandidate } from '../task/create.js';
6
+ import { createTask } from '../task/create-service.js';
7
+ function failed(code, message) {
8
+ return {
9
+ status: 'failed', changed: false, task: { id: null, shortId: null }, issue: null,
10
+ operations: [], warnings: [], error: { code, message, retryable: false }
11
+ };
12
+ }
13
+ function output(result) {
14
+ process.stdout.write(`${JSON.stringify(result)}\n`);
15
+ process.exitCode = result.status === 'blocked' ? 2 : result.status === 'failed' ? 1 : 0;
16
+ }
17
+ function taskCreate(args) {
18
+ const inputIndex = args.indexOf('--input');
19
+ const input = inputIndex >= 0 ? args[inputIndex + 1] : undefined;
20
+ if (!input || args.length !== 2 || inputIndex !== 0) {
21
+ output(failed('TASK_CREATE_PAYLOAD_INVALID', 'Usage: agent-infra-internal task-create --input <candidate.json>'));
22
+ return;
23
+ }
24
+ try {
25
+ const inputPath = path.resolve(input);
26
+ const stat = fs.lstatSync(inputPath);
27
+ if (!stat.isFile() || stat.isSymbolicLink())
28
+ throw new Error('TASK_CREATE_INPUT_INVALID: input must be a regular file');
29
+ if (stat.size > SANDBOX_CONTROL_MAX_BYTES)
30
+ throw new Error('TASK_CREATE_INPUT_TOO_LARGE: input exceeds the control limit');
31
+ const candidate = validateTaskCreateCandidate(JSON.parse(fs.readFileSync(inputPath, 'utf8')));
32
+ if (process.env.AGENT_INFRA_CONTROL_TOKEN) {
33
+ const response = requestSandboxTaskCreate({ candidate });
34
+ process.stdout.write(response.stdout);
35
+ process.stderr.write(response.stderr);
36
+ process.exitCode = response.exitCode;
37
+ return;
38
+ }
39
+ output(createTask(candidate, { repoRoot: process.cwd() }));
40
+ }
41
+ catch (error) {
42
+ const message = error instanceof Error ? error.message : String(error);
43
+ const code = /^([A-Z][A-Z0-9_]+)/.exec(message)?.[1] ?? 'TASK_CREATE_INPUT_INVALID';
44
+ output(failed(code, message));
45
+ }
46
+ }
47
+ export { taskCreate };
48
+ //# sourceMappingURL=task-create.js.map
@@ -6,9 +6,10 @@ const USAGE = `Usage: agent-infra-internal task-ledger <task-ref> <intent> [inte
6
6
  const FLAGS = {
7
7
  '--stage': 'stage', '--review-artifact': 'reviewArtifact', '--ordinal': 'ordinal',
8
8
  '--severity': 'severity', '--evidence': 'evidence', '--id': 'id', '--round': 'round',
9
- '--status': 'status', '--artifact': 'artifact'
9
+ '--status': 'status', '--artifact': 'artifact', '--needs-implementation': 'needsImplementation'
10
10
  };
11
11
  const NUMERIC = new Set(['ordinal', 'round']);
12
+ const BOOLEAN = new Set(['needsImplementation']);
12
13
  function usageFailure(message) {
13
14
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'LEDGER_PAYLOAD_INVALID', message } })}\n`);
14
15
  process.stderr.write(USAGE);
@@ -55,8 +56,12 @@ function taskLedger(args = []) {
55
56
  usageFailure(`option '${flag}' requires a value`);
56
57
  return;
57
58
  }
59
+ if (BOOLEAN.has(key) && value !== 'true' && value !== 'false') {
60
+ usageFailure(`option '${flag}' must be true or false`);
61
+ return;
62
+ }
58
63
  seen.add(flag);
59
- values[key] = NUMERIC.has(key) ? Number(value) : value;
64
+ values[key] = NUMERIC.has(key) ? Number(value) : BOOLEAN.has(key) ? value === 'true' : value;
60
65
  }
61
66
  const required = {
62
67
  'finding-upsert': ['stage', 'reviewArtifact', 'ordinal', 'severity', 'evidence'],
@@ -66,7 +71,11 @@ function taskLedger(args = []) {
66
71
  'decision-upsert': ['id', 'stage', 'artifact'],
67
72
  'stage-status': ['stage']
68
73
  };
69
- const allowed = new Set(['kind', 'taskRef', 'dryRun', ...required[kind]]);
74
+ const optional = {
75
+ 'finding-review': ['needsImplementation'],
76
+ 'decision-upsert': ['needsImplementation']
77
+ };
78
+ const allowed = new Set(['kind', 'taskRef', 'dryRun', ...required[kind], ...(optional[kind] ?? [])]);
70
79
  const unexpected = Object.keys(values).find((key) => !allowed.has(key));
71
80
  const missing = required[kind].find((key) => values[key] === undefined);
72
81
  if (unexpected) {
@@ -1,5 +1,7 @@
1
1
  import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
2
2
  import { applyTaskLifecycle, lifecycleIntentCatalog } from '../task/lifecycle.js';
3
+ import { resolveTaskRef } from '../task/resolve-ref.js';
4
+ import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
3
5
  const USAGE = `Usage: agent-infra-internal task-lifecycle <N | TASK-id> <intent> --agent <agent> [intent flags] [--dry-run]\n\nIntents: ${lifecycleIntentCatalog.join(', ')}\n`;
4
6
  const FLAGS = {
5
7
  '--agent': 'agent', '--reason': 'reason', '--unblock-condition': 'unblockCondition',
@@ -56,7 +58,23 @@ function taskLifecycle(args = []) {
56
58
  return;
57
59
  }
58
60
  values.agent = agent;
59
- const result = applyTaskLifecycle(values);
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
+ let result;
68
+ try {
69
+ result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-lifecycle.${String(values.intent)}`, () => applyTaskLifecycle(values));
70
+ }
71
+ catch (error) {
72
+ if (!(error instanceof TaskExecutionLockError))
73
+ throw error;
74
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
75
+ process.exitCode = 1;
76
+ return;
77
+ }
60
78
  process.stdout.write(`${JSON.stringify(result)}\n`);
61
79
  if (result.status === 'failed')
62
80
  process.exitCode = 1;