@fitlab-ai/agent-infra 0.11.11 → 0.11.13

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 (154) hide show
  1. package/dist/bin/internal-cli.js +2 -2
  2. package/dist/lib/agent-clients/adapter.js +7 -1
  3. package/dist/lib/agent-clients/adapters/claude-code.js +5 -1
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +78 -63
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +114 -0
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +447 -0
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +130 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +131 -0
  9. package/dist/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.js +2 -2
  10. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +48 -1
  11. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +124 -62
  12. package/dist/lib/{internal/codex-sandbox-controller.js → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js} +3 -3
  13. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -5
  14. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +211 -213
  15. package/dist/lib/agent-clients/adapters/codex-orchestration.js +468 -0
  16. package/dist/lib/agent-clients/adapters/codex.js +40 -1
  17. package/dist/lib/agent-clients/adapters/index.js +2 -10
  18. package/dist/lib/agent-clients/tokens.js +3 -2
  19. package/dist/lib/internal/agent-client.js +132 -72
  20. package/dist/lib/internal/cli-route-inventory.js +2 -2
  21. package/dist/lib/internal/sandbox-control.js +1 -81
  22. package/dist/lib/internal/task-finalization.js +26 -19
  23. package/dist/lib/internal/task-lifecycle.js +1 -1
  24. package/dist/lib/internal/task-operation-registry.js +1 -1
  25. package/dist/lib/internal/task-validate.js +1 -1
  26. package/dist/lib/platform/github-client.js +33 -3
  27. package/dist/lib/platform/issues.js +18 -6
  28. package/dist/lib/runtime/agent-runtime.js +6 -29
  29. package/dist/lib/sandbox/commands/control-status.js +1 -0
  30. package/dist/lib/sandbox/commands/create.js +7 -2
  31. package/dist/lib/sandbox/commands/ls.js +4 -5
  32. package/dist/lib/sandbox/commands/rm.js +1 -1
  33. package/dist/lib/sandbox/control/agent-operation.js +34 -0
  34. package/dist/lib/sandbox/control/client.js +14 -201
  35. package/dist/lib/sandbox/control/executor.js +12 -125
  36. package/dist/lib/sandbox/control/lifecycle.js +66 -4
  37. package/dist/lib/sandbox/control/protocol.js +15 -54
  38. package/dist/lib/sandbox/control/server.js +216 -281
  39. package/dist/lib/sandbox/control/task-view.js +0 -13
  40. package/dist/lib/sandbox/mounts.js +9 -1
  41. package/dist/lib/sandbox/recovery.js +31 -10
  42. package/dist/lib/sandbox/removal.js +28 -5
  43. package/dist/lib/sandbox/workspace-identity.js +18 -6
  44. package/dist/lib/sandbox/workspace-view.js +56 -19
  45. package/dist/lib/task/commands/ls.js +3 -3
  46. package/dist/lib/task/control-authority.js +56 -264
  47. package/dist/lib/task/control-recovery.js +2 -3
  48. package/dist/lib/task/create.js +2 -2
  49. package/dist/lib/task/delegation-receipts.js +74 -161
  50. package/dist/lib/task/events.js +88 -44
  51. package/dist/lib/task/finalization.js +35 -6
  52. package/dist/lib/task/lifecycle.js +274 -115
  53. package/dist/lib/task/orchestration.js +201 -194
  54. package/dist/lib/task/runtime-paths.js +10 -0
  55. package/dist/lib/task/short-id.js +37 -27
  56. package/dist/lib/task/verification-engine.js +32 -26
  57. package/dist/lib/task/workspace-snapshot.js +1 -1
  58. package/dist/lifecycle-build-manifest.json +3 -3
  59. package/lib/agent-clients/adapter.ts +39 -0
  60. package/lib/agent-clients/adapters/claude-code.ts +5 -1
  61. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +105 -80
  62. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +147 -0
  63. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +510 -0
  64. package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +158 -0
  65. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +1 -1
  66. package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +151 -0
  67. package/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.ts +4 -4
  68. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +43 -0
  69. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +8 -5
  70. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +190 -65
  71. package/lib/{internal/codex-sandbox-controller.ts → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts} +2 -2
  72. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -5
  73. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +270 -224
  74. package/lib/agent-clients/adapters/codex-orchestration.ts +632 -0
  75. package/lib/agent-clients/adapters/codex.ts +44 -1
  76. package/lib/agent-clients/adapters/index.ts +7 -20
  77. package/lib/agent-clients/tokens.ts +4 -3
  78. package/lib/internal/agent-client.ts +142 -73
  79. package/lib/internal/cli-route-inventory.ts +2 -2
  80. package/lib/internal/sandbox-control.ts +0 -97
  81. package/lib/internal/task-finalization.ts +30 -18
  82. package/lib/internal/task-lifecycle.ts +1 -1
  83. package/lib/internal/task-operation-registry.ts +1 -1
  84. package/lib/internal/task-validate.ts +1 -1
  85. package/lib/platform/github-client.ts +34 -3
  86. package/lib/platform/issues.ts +18 -6
  87. package/lib/runtime/agent-runtime.ts +6 -41
  88. package/lib/sandbox/commands/control-status.ts +1 -0
  89. package/lib/sandbox/commands/create.ts +7 -1
  90. package/lib/sandbox/commands/ls.ts +4 -5
  91. package/lib/sandbox/commands/rm.ts +1 -1
  92. package/lib/sandbox/control/agent-operation.ts +49 -0
  93. package/lib/sandbox/control/client.ts +20 -254
  94. package/lib/sandbox/control/executor.ts +13 -136
  95. package/lib/sandbox/control/lifecycle.ts +67 -4
  96. package/lib/sandbox/control/protocol.ts +22 -66
  97. package/lib/sandbox/control/server.ts +21 -95
  98. package/lib/sandbox/control/task-view.ts +0 -19
  99. package/lib/sandbox/mounts.ts +10 -1
  100. package/lib/sandbox/recovery.ts +38 -17
  101. package/lib/sandbox/removal.ts +33 -7
  102. package/lib/sandbox/workspace-identity.ts +16 -6
  103. package/lib/sandbox/workspace-view.ts +50 -18
  104. package/lib/task/commands/ls.ts +3 -3
  105. package/lib/task/control-authority.ts +66 -378
  106. package/lib/task/control-recovery.ts +3 -4
  107. package/lib/task/create.ts +3 -3
  108. package/lib/task/delegation-receipts.ts +87 -231
  109. package/lib/task/events.ts +100 -42
  110. package/lib/task/finalization.ts +37 -7
  111. package/lib/task/lifecycle.ts +257 -109
  112. package/lib/task/orchestration.ts +260 -237
  113. package/lib/task/runtime-paths.ts +12 -0
  114. package/lib/task/short-id.ts +33 -40
  115. package/lib/task/verification-engine.ts +30 -25
  116. package/lib/task/workspace-snapshot.ts +1 -1
  117. package/package.json +1 -1
  118. package/templates/.agents/hooks/lifecycle-delegation.js +1 -25
  119. package/templates/.agents/rules/README.en.md +2 -1
  120. package/templates/.agents/rules/README.zh-CN.md +2 -1
  121. package/templates/.agents/rules/next-step-output.en.md +6 -19
  122. package/templates/.agents/rules/next-step-output.zh-CN.md +6 -19
  123. package/templates/.agents/rules/strategy-pattern.en.md +25 -0
  124. package/templates/.agents/rules/strategy-pattern.zh-CN.md +25 -0
  125. package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
  126. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
  127. package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
  128. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
  129. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  130. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
  131. package/templates/.agents/skills/code-task/reference/code-rules.en.md +4 -0
  132. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +4 -0
  133. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +0 -1
  134. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +0 -1
  135. package/templates/.agents/skills/complete-task/SKILL.en.md +5 -29
  136. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +5 -26
  137. package/templates/.agents/skills/plan-task/SKILL.en.md +7 -4
  138. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +7 -4
  139. package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
  140. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
  141. package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
  142. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
  143. package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
  144. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
  145. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  146. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  147. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  148. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  149. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +0 -546
  150. package/dist/lib/internal/codex-lifecycle.js +0 -385
  151. package/dist/lib/task/codex-orchestration.js +0 -206
  152. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +0 -776
  153. package/lib/internal/codex-lifecycle.ts +0 -390
  154. package/lib/task/codex-orchestration.ts +0 -276
@@ -3,10 +3,10 @@ import path from 'node:path';
3
3
  import { AgentClientConfigError, normalizeAgentClients } from '../agent-clients/config.js';
4
4
  import { normalizeCustomToolInvocations } from '../agent-clients/custom-tool-invocations.js';
5
5
  import { renderNextStepCommands } from '../agent-clients/next-steps.js';
6
- import { getAgentClientModelSelection } from '../agent-clients/registry.js';
6
+ import { getAgentClientAdapter, getAgentClientModelSelection } from '../agent-clients/registry.js';
7
7
  import { isAgentClientId } from '../agent-clients/types.js';
8
8
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
9
- const USAGE = 'Usage: agent-infra-internal agent-client <next-steps|model-selection> [options]\n';
9
+ const USAGE = 'Usage: agent-infra-internal agent-client <next-steps|model-selection|launch-carrier> [options]\n';
10
10
  function failure(code, message) {
11
11
  process.stdout.write(`${JSON.stringify({
12
12
  status: 'failed',
@@ -72,71 +72,113 @@ function parseArgs(args) {
72
72
  format
73
73
  };
74
74
  }
75
- function agentClient(args = []) {
76
- if (!ensureInternalHandlerRoute('agent-client', args))
77
- return;
78
- if (internalHandlerRoute('agent-client', 'model-selection', args[0] ?? '')) {
79
- const values = {};
80
- const seen = new Set();
81
- for (let index = 1; index < args.length; index += 1) {
82
- const flag = args[index];
83
- if (!['--client', '--format'].includes(flag) || seen.has(flag)) {
84
- failure('AGENT_CLIENT_PAYLOAD_INVALID', `invalid or duplicate option '${flag}'`);
85
- return;
86
- }
87
- const value = args[++index];
88
- if (!value || value.startsWith('--')) {
89
- failure('AGENT_CLIENT_PAYLOAD_INVALID', `option '${flag}' requires a value`);
90
- return;
91
- }
92
- seen.add(flag);
93
- values[flag] = value;
75
+ function parseLaunchCarrierArgs(args) {
76
+ const allowed = new Set(['--client', '--adapter-context', '--stage', '--round', '--role', '--format']);
77
+ const values = {};
78
+ const seen = new Set();
79
+ for (let index = 1; index < args.length; index += 1) {
80
+ const flag = args[index];
81
+ if (!allowed.has(flag) || seen.has(flag)) {
82
+ failure('AGENT_CLIENT_PAYLOAD_INVALID', `invalid or duplicate option '${flag}'`);
83
+ return null;
94
84
  }
95
- const client = values['--client'];
96
- const format = values['--format'] ?? 'text';
97
- if (!isAgentClientId(client) || !['text', 'json'].includes(format)) {
98
- failure('AGENT_CLIENT_PAYLOAD_INVALID', 'model-selection requires a known --client and text|json format');
99
- return;
85
+ const value = args[++index];
86
+ if (!value || value.startsWith('--')) {
87
+ failure('AGENT_CLIENT_PAYLOAD_INVALID', `option '${flag}' requires a value`);
88
+ return null;
100
89
  }
101
- const context = getAgentClientModelSelection(client);
102
- if (format === 'json') {
103
- process.stdout.write(`${JSON.stringify({
104
- status: 'resolved', changed: false, client, context, error: null
105
- })}\n`);
90
+ seen.add(flag);
91
+ values[flag] = value;
92
+ }
93
+ const round = Number(values['--round']);
94
+ if (!isAgentClientId(values['--client']) || !values['--adapter-context'] || !values['--stage'] || !values['--role']
95
+ || !Number.isSafeInteger(round) || round < 1 || (values['--format'] ?? 'json') !== 'json') {
96
+ failure('AGENT_CLIENT_PAYLOAD_INVALID', 'launch-carrier requires client, adapter context, stage, positive round, role, and json format');
97
+ return null;
98
+ }
99
+ return {
100
+ client: values['--client'],
101
+ adapterContext: values['--adapter-context'],
102
+ stage: values['--stage'],
103
+ round,
104
+ role: values['--role']
105
+ };
106
+ }
107
+ function launchCarrier(args) {
108
+ const parsed = parseLaunchCarrierArgs(args);
109
+ if (!parsed)
110
+ return;
111
+ const createCarrier = getAgentClientAdapter(parsed.client).orchestrationAdapter?.createLaunchCarrier;
112
+ if (!createCarrier) {
113
+ process.stdout.write(`${JSON.stringify({
114
+ status: 'failed', changed: false, client: parsed.client, carrier: null,
115
+ error: { code: 'AGENT_CLIENT_LAUNCH_CARRIER_UNSUPPORTED', message: `Agent Client '${parsed.client}' does not support lifecycle launch carriers` }
116
+ })}\n`);
117
+ process.exitCode = 1;
118
+ return;
119
+ }
120
+ try {
121
+ const carrier = createCarrier(parsed.adapterContext, { stage: parsed.stage, round: parsed.round, role: parsed.role });
122
+ process.stdout.write(`${JSON.stringify({ status: 'ready', changed: false, client: parsed.client, carrier, error: null })}\n`);
123
+ }
124
+ catch (error) {
125
+ process.stdout.write(`${JSON.stringify({
126
+ status: 'failed', changed: false, client: parsed.client, carrier: null,
127
+ error: { code: error instanceof Error && error.name ? error.name : 'AGENT_CLIENT_LAUNCH_CARRIER_INVALID', message: error instanceof Error ? error.message : String(error) }
128
+ })}\n`);
129
+ process.exitCode = 1;
130
+ }
131
+ }
132
+ function modelSelection(args) {
133
+ const values = {};
134
+ const seen = new Set();
135
+ for (let index = 1; index < args.length; index += 1) {
136
+ const flag = args[index];
137
+ if (!['--client', '--format'].includes(flag) || seen.has(flag)) {
138
+ failure('AGENT_CLIENT_PAYLOAD_INVALID', `invalid or duplicate option '${flag}'`);
106
139
  return;
107
140
  }
108
- if (context.kind === 'interactive-only') {
109
- process.stdout.write(`Model selection: interactive-only\nCommand: ${context.command}\n${context.guidance}\n`);
110
- }
111
- else {
112
- process.stdout.write(`Model selection: ${context.completeness} catalog\nSource: ${context.source}\n`);
113
- for (const model of context.models) {
114
- const efforts = model.reasoningEfforts?.length
115
- ? ` (${model.reasoningEfforts.join(', ')})`
116
- : '';
117
- process.stdout.write(`- ${model.id}${efforts}\n`);
118
- }
119
- if (context.guidance)
120
- process.stdout.write(`${context.guidance}\n`);
141
+ const value = args[++index];
142
+ if (!value || value.startsWith('--')) {
143
+ failure('AGENT_CLIENT_PAYLOAD_INVALID', `option '${flag}' requires a value`);
144
+ return;
121
145
  }
146
+ seen.add(flag);
147
+ values[flag] = value;
148
+ }
149
+ const client = values['--client'];
150
+ const format = values['--format'] ?? 'text';
151
+ if (!isAgentClientId(client) || !['text', 'json'].includes(format)) {
152
+ failure('AGENT_CLIENT_PAYLOAD_INVALID', 'model-selection requires a known --client and text|json format');
122
153
  return;
123
154
  }
124
- const parsed = parseArgs(args);
125
- if (!parsed || process.exitCode)
155
+ const context = getAgentClientModelSelection(client);
156
+ if (format === 'json') {
157
+ process.stdout.write(`${JSON.stringify({ status: 'resolved', changed: false, client, context, error: null })}\n`);
126
158
  return;
127
- let config;
128
- try {
129
- const configPath = path.join(process.cwd(), '.agents', '.airc.json');
130
- const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
131
- if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
132
- throw new Error('configuration root must be an object');
133
- }
134
- config = raw;
135
159
  }
136
- catch (error) {
137
- failure('AGENT_CLIENT_CONFIG_INVALID', String(error));
160
+ renderModelSelection(context);
161
+ }
162
+ function renderModelSelection(context) {
163
+ if (context.kind === 'interactive-only') {
164
+ process.stdout.write(`Model selection: interactive-only\nCommand: ${context.command}\n${context.guidance}\n`);
138
165
  return;
139
166
  }
167
+ process.stdout.write(`Model selection: ${context.completeness} catalog\nSource: ${context.source}\n`);
168
+ for (const model of context.models) {
169
+ const efforts = model.reasoningEfforts?.length ? ` (${model.reasoningEfforts.join(', ')})` : '';
170
+ process.stdout.write(`- ${model.id}${efforts}\n`);
171
+ }
172
+ if (context.guidance)
173
+ process.stdout.write(`${context.guidance}\n`);
174
+ }
175
+ function renderNextSteps(args) {
176
+ const parsed = parseArgs(args);
177
+ if (!parsed || process.exitCode)
178
+ return;
179
+ const config = readAgentClientConfig();
180
+ if (!config)
181
+ return;
140
182
  try {
141
183
  const clients = normalizeAgentClients(config);
142
184
  const custom = normalizeCustomToolInvocations(config);
@@ -148,22 +190,7 @@ function agentClient(args = []) {
148
190
  ...(parsed.taskRef ? { taskRef: parsed.taskRef } : {}),
149
191
  ...(parsed.version ? { version: parsed.version } : {})
150
192
  });
151
- if (parsed.format === 'json') {
152
- process.stdout.write(`${JSON.stringify({
153
- status: 'rendered',
154
- changed: false,
155
- commands,
156
- diagnostics: custom.diagnostics,
157
- error: null
158
- })}\n`);
159
- return;
160
- }
161
- for (const command of commands) {
162
- process.stdout.write(` - ${command.displayName}: ${command.command}\n`);
163
- }
164
- for (const diagnostic of custom.diagnostics) {
165
- process.stderr.write(`${diagnostic.code} at ${diagnostic.path}\n`);
166
- }
193
+ writeNextStepOutput(parsed.format, commands, custom.diagnostics);
167
194
  }
168
195
  catch (error) {
169
196
  if (error instanceof AgentClientConfigError) {
@@ -173,5 +200,38 @@ function agentClient(args = []) {
173
200
  failure('AGENT_CLIENT_RENDER_INVALID', error instanceof Error ? error.message : String(error));
174
201
  }
175
202
  }
203
+ function readAgentClientConfig() {
204
+ try {
205
+ const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), '.agents', '.airc.json'), 'utf8'));
206
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
207
+ throw new Error('configuration root must be an object');
208
+ return raw;
209
+ }
210
+ catch (error) {
211
+ failure('AGENT_CLIENT_CONFIG_INVALID', String(error));
212
+ return null;
213
+ }
214
+ }
215
+ function writeNextStepOutput(format, commands, diagnostics) {
216
+ if (format === 'json') {
217
+ process.stdout.write(`${JSON.stringify({ status: 'rendered', changed: false, commands, diagnostics, error: null })}\n`);
218
+ return;
219
+ }
220
+ for (const command of commands)
221
+ process.stdout.write(` - ${command.displayName}: ${command.command}\n`);
222
+ for (const diagnostic of diagnostics)
223
+ process.stderr.write(`${diagnostic.code} at ${diagnostic.path}\n`);
224
+ }
225
+ function agentClient(args = []) {
226
+ if (!ensureInternalHandlerRoute('agent-client', args))
227
+ return;
228
+ if (internalHandlerRoute('agent-client', 'launch-carrier', args[0] ?? '')) {
229
+ return launchCarrier(args);
230
+ }
231
+ if (internalHandlerRoute('agent-client', 'model-selection', args[0] ?? '')) {
232
+ return modelSelection(args);
233
+ }
234
+ renderNextSteps(args);
235
+ }
176
236
  export { agentClient };
177
237
  //# sourceMappingURL=agent-client.js.map
@@ -33,8 +33,8 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
33
33
  'task-create': ['input'],
34
34
  'task-qualification': ['proposal', 'confirm', 'supersede', 'revoke'],
35
35
  'sandbox-control': ['serve', 'execute', 'recover', 'client'],
36
- 'agent-client': ['next-steps', 'model-selection'],
37
- 'codex-lifecycle': ['preflight', 'capability-arm', 'hook-event', 'resolve-start', 'resolve-stop', 'consume'],
36
+ 'agent-client': ['next-steps', 'model-selection', 'launch-carrier'],
37
+ 'codex-lifecycle': ['preflight', 'hook-event', 'resolve-start', 'resolve-stop', 'consume'],
38
38
  'codex-sandbox-controller': ['verify-context', 'run'],
39
39
  'git-workflow': ['inspect', 'preview-tree', 'snapshot', 'compare-trees', 'commit', 'push-rebased'],
40
40
  'task-delivery': ['deliver'],
@@ -1,18 +1,8 @@
1
- import { recoverSandboxControl, requestSandboxControl, requestSandboxTaskFinalization, SandboxControlClientError } from '../sandbox/control/client.js';
2
- import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
1
+ import { recoverSandboxControl, requestSandboxControl, SandboxControlClientError } from '../sandbox/control/client.js';
3
2
  import { serveSandboxControl } from '../sandbox/control/server.js';
4
3
  import { runSandboxControlExecutor } from '../sandbox/control/executor.js';
5
4
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
6
5
  import { parseTaskCreateResult, taskCreateExitCode } from '../task/create-service.js';
7
- import { serializeTaskFinalizationEnvelope } from '../task/finalization-envelope.js';
8
- function writeFinalizationEnvelope(status, accepted, error, changed = false, result = null, requestId = null) {
9
- process.stdout.write(serializeTaskFinalizationEnvelope({ status, changed, accepted, requestId, result, error }));
10
- }
11
- function finalizationErrorStatus(error) {
12
- if (error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN')
13
- return 'unknown';
14
- return error.retryable ? 'blocked' : 'failed';
15
- }
16
6
  function writeClientError(error) {
17
7
  process.stderr.write(`${error.detail.message}\n`);
18
8
  if (error.requestId)
@@ -31,72 +21,6 @@ function recoveredExitCode(response) {
31
21
  }
32
22
  return response.exitCode ?? 1;
33
23
  }
34
- function finalizationResponseOutput(response) {
35
- try {
36
- const value = JSON.parse(response.stdout);
37
- if (value.version === 2
38
- && value.status
39
- && ['completed', 'failed', 'blocked', 'unknown'].includes(value.status)
40
- && (typeof value.changed === 'boolean' || value.changed === null)) {
41
- return serializeTaskFinalizationEnvelope({
42
- status: value.status,
43
- changed: value.changed,
44
- accepted: true,
45
- requestId: response.id,
46
- result: value.result ?? null,
47
- error: value.error ?? null
48
- });
49
- }
50
- }
51
- catch {
52
- // Preserve non-envelope output for diagnostics from the executor.
53
- }
54
- return response.stdout;
55
- }
56
- function sandboxFinalizationClient(args) {
57
- if (args.length !== 4 || !args[0] || args[1] !== 'complete' || args[2] !== '--agent' || !args[3]) {
58
- const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message: 'task ref, complete intent, and --agent are required', retryable: false };
59
- writeFinalizationEnvelope('failed', false, error);
60
- process.stderr.write('Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n');
61
- process.exitCode = 1;
62
- return;
63
- }
64
- const agent = normalizeAgentToken(args[3]);
65
- if (!agent) {
66
- const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message: `invalid --agent: ${AGENT_USAGE_HINT}`, retryable: false };
67
- writeFinalizationEnvelope('failed', false, error);
68
- process.exitCode = 1;
69
- return;
70
- }
71
- let response;
72
- try {
73
- response = requestSandboxTaskFinalization({ agent });
74
- }
75
- catch (error) {
76
- if (!(error instanceof SandboxControlClientError))
77
- throw error;
78
- const status = finalizationErrorStatus(error.detail);
79
- writeFinalizationEnvelope(status, error.accepted, error.detail, status === 'unknown' ? null : false, null, error.requestId);
80
- writeClientError(error);
81
- process.exitCode = error.detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.detail.retryable ? 2 : 1;
82
- return;
83
- }
84
- if (response.phase === 'rejected') {
85
- const error = response.error ?? {
86
- code: 'SANDBOX_CONTROL_REJECTED',
87
- message: 'sandbox control rejected the finalization request',
88
- retryable: false
89
- };
90
- const status = finalizationErrorStatus(error);
91
- writeFinalizationEnvelope(status, error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN', error, status === 'unknown' ? null : false, null, response.id);
92
- process.stderr.write(`${error.message}\n`);
93
- process.exitCode = error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.retryable ? 2 : 1;
94
- return;
95
- }
96
- process.stdout.write(finalizationResponseOutput(response));
97
- process.stderr.write(response.stderr);
98
- process.exitCode = response.exitCode ?? 1;
99
- }
100
24
  async function sandboxControl(args) {
101
25
  if (!ensureInternalHandlerRoute('sandbox-control', args))
102
26
  return;
@@ -152,10 +76,6 @@ async function sandboxControl(args) {
152
76
  }
153
77
  if (internalHandlerRoute('sandbox-control', 'client', operation ?? '')) {
154
78
  const [family = '', ...commandArgs] = rest;
155
- if (family === 'task-finalization') {
156
- sandboxFinalizationClient(commandArgs);
157
- return;
158
- }
159
79
  let response;
160
80
  try {
161
81
  response = requestSandboxControl({ family, args: commandArgs });
@@ -1,9 +1,12 @@
1
+ import path from 'node:path';
1
2
  import { createDirectHostExecutionContext, dispatchTaskControlOperation, parseTaskControlOperation } from '../task/control-authority.js';
2
3
  import { applyTaskFinalization } from '../task/finalization.js';
3
4
  import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
4
5
  import { resolveSandboxControlTransport } from './task-operation-registry.js';
5
- import { requestSandboxTaskFinalization, SandboxControlClientError } from '../sandbox/control/client.js';
6
+ import { quiesceSandboxControlForTaskCompletion } from '../sandbox/control/lifecycle.js';
7
+ import { resolveTaskRuntimeRoot } from '../task/runtime-paths.js';
6
8
  import { serializeTaskFinalizationEnvelope } from '../task/finalization-envelope.js';
9
+ import { verifyTaskEvent } from '../task/verification.js';
7
10
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
8
11
  const USAGE = 'Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n';
9
12
  function envelope(status, changed, accepted, result, error, requestId = null) {
@@ -64,26 +67,30 @@ async function taskFinalization(args = []) {
64
67
  };
65
68
  const transport = resolveSandboxControlTransport(process.env, { localWorkflow: true });
66
69
  if (transport.kind === 'sandbox-local') {
67
- try {
68
- const response = requestSandboxTaskFinalization({ agent: boundOperation.request.agent });
69
- process.stdout.write(response.stdout);
70
- process.stderr.write(response.stderr);
71
- process.exitCode = response.exitCode ?? 1;
72
- return;
73
- }
74
- catch (error) {
75
- const clientError = error instanceof SandboxControlClientError ? error : null;
76
- const detail = clientError
77
- ? clientError.detail
78
- : { code: 'SANDBOX_CONTROL_CLIENT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
79
- const status = detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN'
80
- ? 'unknown'
81
- : detail.retryable ? 'blocked' : 'failed';
82
- const changed = status === 'unknown' ? null : false;
83
- process.stdout.write(envelope(status, changed, clientError?.accepted ?? false, null, detail, clientError?.requestId ?? null));
84
- process.exitCode = exitCode(status);
70
+ const statusDir = process.env.AGENT_INFRA_CONTROL_STATUS_DIR;
71
+ const taskId = process.env.AGENT_INFRA_TASK_ID;
72
+ const generation = process.env.AGENT_INFRA_CONTROL_GENERATION;
73
+ const controlRootId = process.env.AGENT_INFRA_CONTROL_ROOT_ID;
74
+ if (!statusDir || !taskId || !generation || !controlRootId || taskId !== resolved.taskId) {
75
+ const detail = { code: 'TASK_CONTROL_TRANSPORT_INVALID', message: 'sandbox task control identity is incomplete', retryable: false };
76
+ process.stdout.write(envelope('failed', false, true, null, detail));
77
+ process.exitCode = 1;
85
78
  return;
86
79
  }
80
+ const result = await applyTaskFinalization(boundOperation.request, {
81
+ repoRoot,
82
+ beforeLifecycle: async (currentTaskId) => {
83
+ if (currentTaskId !== taskId)
84
+ throw new Error('SANDBOX_CONTROL_IDENTITY_MISMATCH');
85
+ await quiesceSandboxControlForTaskCompletion(path.join(resolveTaskRuntimeRoot(taskId, { repoRoot }), 'sandbox-control'), { taskId, generation, controlRootId, statusDir });
86
+ },
87
+ preflight: (request, options) => verifyTaskEvent({ ...request, event: 'complete-task.hard-preflight' }, options)
88
+ });
89
+ const status = result.status === 'prepared' ? 'failed' : result.status;
90
+ process.stdout.write(envelope(status, result.changed, true, result, result.error
91
+ ?? (result.status === 'prepared' ? { code: 'TASK_FINALIZATION_PREPARATION_INCOMPLETE', message: 'finalization did not commit', retryable: true } : null)));
92
+ process.exitCode = exitCode(status);
93
+ return;
87
94
  }
88
95
  const result = await dispatchTaskControlOperation(createDirectHostExecutionContext({ repoRoot }), boundOperation);
89
96
  if (result.status === 'prepared') {
@@ -2,7 +2,7 @@ import { createDirectHostExecutionContext, dispatchTaskControlOperation, parseTa
2
2
  import { lifecycleIntentCatalog } from '../task/lifecycle.js';
3
3
  import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
4
4
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
5
- const USAGE = `Usage: agent-infra-internal task-lifecycle <N | TASK-id> <intent> --agent <agent> [intent flags] [--dry-run]\n\nIntents: ${lifecycleIntentCatalog.join(', ')}\nrecover-started: --auto\n`;
5
+ const USAGE = `Usage: agent-infra-internal task-lifecycle <N | TASK-id> <intent> --agent <agent> [intent flags] [--dry-run]\n\nIntents: ${lifecycleIntentCatalog.filter((intent) => intent !== 'complete').join(', ')}\nrecover-started: --auto\n`;
6
6
  function usageFailure(message) {
7
7
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'LIFECYCLE_PAYLOAD_INVALID', message } })}\n`);
8
8
  process.stderr.write(USAGE);
@@ -19,8 +19,8 @@ function internalTaskRoutes() {
19
19
  descriptor('internal', 'sandbox-control', 'client', 'conditional', 'progress', 'delegated'),
20
20
  descriptor('internal', 'agent-client', 'next-steps', 'non-task', 'diagnostic', 'none'),
21
21
  descriptor('internal', 'agent-client', 'model-selection', 'non-task', 'diagnostic', 'none'),
22
+ descriptor('internal', 'agent-client', 'launch-carrier', 'non-task', 'diagnostic', 'none'),
22
23
  descriptor('internal', 'codex-lifecycle', 'preflight', 'non-task', 'diagnostic', 'none'),
23
- descriptor('internal', 'codex-lifecycle', 'capability-arm', 'conditional', 'progress'),
24
24
  descriptor('internal', 'codex-lifecycle', 'hook-event', 'conditional', 'progress'),
25
25
  descriptor('internal', 'codex-lifecycle', 'resolve-start', 'conditional', 'progress'),
26
26
  descriptor('internal', 'codex-lifecycle', 'resolve-stop', 'conditional', 'progress'),
@@ -152,7 +152,7 @@ function inplaceValidation(config, target, options) {
152
152
  if (!row)
153
153
  throw new Error('SANDBOX_VALIDATION_CONTAINER_NOT_FOUND');
154
154
  const control = sandboxControlPaths({
155
- base: config.controlBase, project: config.project,
155
+ base: config.controlBase, repoRoot: config.repoRoot, project: config.project,
156
156
  container: row.name, identity: target.workspace
157
157
  });
158
158
  const manifest = readSandboxControlManifest(control.manifestPath);
@@ -18,6 +18,32 @@ function boundedFailureDetail(result) {
18
18
  ? diagnostic
19
19
  : `${diagnostic.slice(0, ERROR_DETAIL_LIMIT)}… [truncated]`;
20
20
  }
21
+ function requestDiagnostics(args, request, attempt, errorMessage) {
22
+ if (process.env.AGENT_INFRA_GH_DIAGNOSTICS !== '1' && !/rate.?limit/iu.test(errorMessage))
23
+ return '';
24
+ const apiIndex = args.indexOf('api');
25
+ const endpoint = apiIndex < 0
26
+ ? args.slice(0, 2).filter((arg) => !arg.startsWith('-')).join(' ')
27
+ : args.slice(apiIndex + 1).find((arg) => !arg.startsWith('-') && !arg.includes('=')) || 'unknown';
28
+ const safeEndpoint = endpoint.startsWith('http')
29
+ ? (() => {
30
+ try {
31
+ const url = new URL(endpoint);
32
+ return `${url.host}${url.pathname}`;
33
+ }
34
+ catch {
35
+ return 'unknown';
36
+ }
37
+ })()
38
+ : endpoint.split('?')[0] || 'unknown';
39
+ const methodIndex = args.findIndex((arg) => arg === '-X' || arg === '--method');
40
+ const methodFromArgs = methodIndex >= 0 ? args[methodIndex + 1] : undefined;
41
+ const method = methodFromArgs || request.method || (safeEndpoint === 'graphql' ? 'POST' : 'GET');
42
+ const tokenEnvironment = ['GH_TOKEN', 'GITHUB_TOKEN', 'GH_ENTERPRISE_TOKEN', 'GITHUB_ENTERPRISE_TOKEN']
43
+ .filter((name) => Boolean(process.env[name]));
44
+ const configDirOverride = Boolean(process.env.GH_CONFIG_DIR);
45
+ return ` [gh-diagnostic endpoint=${safeEndpoint} method=${method} attempt=${attempt} tokenEnv=${tokenEnvironment.join(',') || 'none'} ghConfigDirOverride=${configDirOverride}]`;
46
+ }
21
47
  function defaultRunner(args, options) {
22
48
  const command = process.env.AGENT_INFRA_GH_BIN || 'gh';
23
49
  let prefix = [];
@@ -135,14 +161,18 @@ function createGitHubClient(options = {}) {
135
161
  if (result.status === 0)
136
162
  return { ok: true, value: result.stdout };
137
163
  const error = classifyGitHubFailure(result);
164
+ const requestDetail = requestDiagnostics(args, request, attempt + 1, error.message);
165
+ const contextualError = requestDetail
166
+ ? { ...error, message: `${error.message}${requestDetail}` }
167
+ : error;
138
168
  if (!retryableMethod || !error.requestRetryable)
139
- return { ok: false, error: clientError(error) };
169
+ return { ok: false, error: clientError(contextualError) };
140
170
  if (attempt >= delays.length) {
141
171
  return {
142
172
  ok: false,
143
173
  error: error.code === 'NETWORK_TRANSIENT'
144
- ? { ...clientError(error), code: 'NETWORK_RETRY_EXHAUSTED', message: `${error.message} (retry exhausted)` }
145
- : clientError(error)
174
+ ? { ...clientError(contextualError), code: 'NETWORK_RETRY_EXHAUSTED', message: `${contextualError.message} (retry exhausted)` }
175
+ : clientError(contextualError)
146
176
  };
147
177
  }
148
178
  sleep(delays[attempt]);
@@ -472,7 +472,8 @@ async function syncPlatformIssueImpl(taskRef, options) {
472
472
  let providerMetadata = null;
473
473
  let repositoryLabels = [];
474
474
  let milestones = [];
475
- if (base.context.capabilities.triage && (options.status !== undefined || options.inLabels !== undefined || options.milestone !== undefined || options.issueType || options.fields)) {
475
+ if (base.context.capabilities.triage && (options.status !== undefined || options.inLabels !== undefined || options.milestone !== undefined
476
+ || (base.context.capabilities.push && (options.issueType || options.fields)))) {
476
477
  const metadata = base.provider.issues?.describeRepository
477
478
  ? await base.provider.issues.describeRepository({ context: providerOperationContext(base.loadedContext) })
478
479
  : unsupportedProviderOperation(base.provider, 'issues.describeRepository');
@@ -551,18 +552,29 @@ async function syncPlatformIssueImpl(taskRef, options) {
551
552
  }
552
553
  }
553
554
  if (options.issueType || options.fields) {
554
- plan.operations.push(...(providerMetadata
555
- ? planProviderMetadata(inspected.value, providerMetadata, base.frontmatter, options, base.context.capabilities)
556
- : [{ name: 'issue-type', status: 'skipped', reasonCode: 'ISSUE_TYPES_UNSUPPORTED' }]));
555
+ plan.operations.push(...(!base.context.capabilities.push
556
+ ? [
557
+ ...(options.issueType ? [{ name: 'issue-type', status: 'skipped', reasonCode: 'PUSH_REQUIRED' }] : []),
558
+ ...(options.fields ? [{ name: 'fields', status: 'skipped', reasonCode: 'PUSH_REQUIRED' }] : [])
559
+ ]
560
+ : providerMetadata
561
+ ? planProviderMetadata(inspected.value, providerMetadata, base.frontmatter, options, base.context.capabilities)
562
+ : [{ name: 'issue-type', status: 'skipped', reasonCode: 'ISSUE_TYPES_UNSUPPORTED' }]));
557
563
  }
558
564
  const failure = plan.operations.find((operation) => operation.status === 'failed');
559
565
  if (failure)
560
566
  return result('failed', base.resolved.taskId, base.issueNumber, { issue: snapshot, operations: plan.operations, error: { code: failure.reasonCode || 'ISSUE_SYNC_FAILED', message: `Operation ${failure.name} failed`, retryable: false } });
561
567
  const planned = plan.operations.filter((operation) => operation.status === 'planned');
562
568
  if (options.dryRun)
563
- return result(planned.length ? 'planned' : 'no-op', base.resolved.taskId, base.issueNumber, { issue: snapshot, operations: plan.operations, error: null });
569
+ return result(planned.length ? 'planned' : 'no-op', base.resolved.taskId, base.issueNumber, {
570
+ platform: base.context.platform, capabilities: base.context.capabilities,
571
+ issue: snapshot, operations: plan.operations, error: null
572
+ });
564
573
  if (planned.length === 0)
565
- return result('no-op', base.resolved.taskId, base.issueNumber, { issue: snapshot, operations: plan.operations, error: null });
574
+ return result('no-op', base.resolved.taskId, base.issueNumber, {
575
+ platform: base.context.platform, capabilities: base.context.capabilities,
576
+ issue: snapshot, operations: plan.operations, error: null
577
+ });
566
578
  const payload = applyRestOperations(snapshot, planned);
567
579
  const updated = base.provider.issues?.update
568
580
  ? await base.provider.issues.update({ context: providerOperationContext(base.loadedContext), target: base.issueIdentity, currentLabels: snapshot.labels, patch: payload, mutation: { idempotencyKey: `issue:update:${base.resolved.taskId}` } })
@@ -1,5 +1,5 @@
1
1
  import path from 'node:path';
2
- const CLIENT_NAMESPACE = /^[a-z][a-z0-9-]{0,31}$/u;
2
+ import { resolveTaskRuntimeRoot } from '../task/runtime-paths.js';
3
3
  function runtimeError(code, message) {
4
4
  const error = new Error(`${code}: ${message}`);
5
5
  error.name = code;
@@ -9,16 +9,9 @@ function boundControlContext(env) {
9
9
  return [
10
10
  'AGENT_INFRA_CONTROL_TOKEN',
11
11
  'AGENT_INFRA_CONTROL_GENERATION',
12
- 'AGENT_INFRA_EXECUTOR_MANIFEST',
13
- 'AGENT_INFRA_CODEX_CONTROLLER_CONTEXT'
12
+ 'AGENT_INFRA_EXECUTOR_MANIFEST'
14
13
  ].some((key) => typeof env[key] === 'string' && env[key].length > 0);
15
14
  }
16
- function clientNamespace(client) {
17
- if (!CLIENT_NAMESPACE.test(client)) {
18
- throw runtimeError('AGENT_INFRA_RUNTIME_CLIENT_INVALID', `invalid runtime client namespace '${client}'`);
19
- }
20
- return client;
21
- }
22
15
  export function resolveAgentRuntimeRoot(options = {}) {
23
16
  const env = options.env ?? process.env;
24
17
  const configured = env.AGENT_INFRA_RUNTIME_DIR;
@@ -28,29 +21,13 @@ export function resolveAgentRuntimeRoot(options = {}) {
28
21
  }
29
22
  return path.resolve(configured);
30
23
  }
24
+ const taskId = env.AGENT_INFRA_TASK_ID;
25
+ if (taskId) {
26
+ return resolveTaskRuntimeRoot(taskId, { repoRoot: options.repoRoot });
27
+ }
31
28
  if (boundControlContext(env)) {
32
29
  throw runtimeError('AGENT_INFRA_RUNTIME_DIR_REQUIRED', 'task-bound control context requires AGENT_INFRA_RUNTIME_DIR');
33
30
  }
34
31
  return path.join(path.resolve(options.repoRoot ?? process.cwd()), '.agents', 'workspace', '.runtime');
35
32
  }
36
- export function resolveAgentRuntimeStoreRoot(options = {}) {
37
- const client = clientNamespace(options.client ?? 'codex');
38
- if (options.explicitRoot)
39
- return path.resolve(options.explicitRoot);
40
- const env = options.env ?? process.env;
41
- const runtimeRoot = resolveAgentRuntimeRoot({ repoRoot: options.repoRoot, env });
42
- if (env.AGENT_INFRA_RUNTIME_DIR) {
43
- if (!options.store) {
44
- throw runtimeError('AGENT_INFRA_RUNTIME_STORE_INVALID', 'store kind is required for task-bound runtime');
45
- }
46
- return path.join(runtimeRoot, 'clients', client, options.store);
47
- }
48
- if (client === 'codex' && options.store) {
49
- return path.join(runtimeRoot, `codex-${options.store}`);
50
- }
51
- if (!options.store) {
52
- throw runtimeError('AGENT_INFRA_RUNTIME_STORE_INVALID', 'store kind is required');
53
- }
54
- return path.join(runtimeRoot, 'clients', client, options.store);
55
- }
56
33
  //# sourceMappingURL=agent-runtime.js.map
@@ -8,6 +8,7 @@ export function readSandboxControlStatusForRow(config, row) {
8
8
  : { mode: 'branch-only' };
9
9
  const control = sandboxControlPaths({
10
10
  base: config.controlBase,
11
+ repoRoot: config.repoRoot,
11
12
  project: config.project,
12
13
  container: row.name,
13
14
  identity