@fitlab-ai/agent-infra 0.11.11 → 0.11.12

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 (124) 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 +222 -210
  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/task-operation-registry.js +1 -1
  22. package/dist/lib/internal/task-validate.js +1 -1
  23. package/dist/lib/platform/issues.js +18 -6
  24. package/dist/lib/runtime/agent-runtime.js +6 -29
  25. package/dist/lib/sandbox/commands/control-status.js +1 -0
  26. package/dist/lib/sandbox/commands/create.js +1 -0
  27. package/dist/lib/sandbox/control/agent-operation.js +34 -0
  28. package/dist/lib/sandbox/control/client.js +23 -150
  29. package/dist/lib/sandbox/control/executor.js +9 -76
  30. package/dist/lib/sandbox/control/protocol.js +14 -42
  31. package/dist/lib/sandbox/control/server.js +15 -49
  32. package/dist/lib/sandbox/mounts.js +2 -1
  33. package/dist/lib/sandbox/recovery.js +3 -1
  34. package/dist/lib/sandbox/removal.js +9 -5
  35. package/dist/lib/sandbox/workspace-view.js +8 -17
  36. package/dist/lib/task/control-authority.js +54 -264
  37. package/dist/lib/task/control-recovery.js +1 -1
  38. package/dist/lib/task/create.js +2 -2
  39. package/dist/lib/task/delegation-receipts.js +74 -161
  40. package/dist/lib/task/events.js +88 -44
  41. package/dist/lib/task/finalization.js +21 -6
  42. package/dist/lib/task/lifecycle.js +84 -97
  43. package/dist/lib/task/orchestration.js +182 -129
  44. package/dist/lib/task/runtime-paths.js +10 -0
  45. package/dist/lib/task/verification-engine.js +28 -15
  46. package/dist/lib/task/workspace-snapshot.js +1 -1
  47. package/dist/lifecycle-build-manifest.json +3 -3
  48. package/lib/agent-clients/adapter.ts +39 -0
  49. package/lib/agent-clients/adapters/claude-code.ts +5 -1
  50. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +105 -80
  51. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +147 -0
  52. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +510 -0
  53. package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +158 -0
  54. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +1 -1
  55. package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +151 -0
  56. package/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.ts +4 -4
  57. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +43 -0
  58. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +8 -5
  59. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +190 -65
  60. package/lib/{internal/codex-sandbox-controller.ts → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts} +2 -2
  61. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -5
  62. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +272 -223
  63. package/lib/agent-clients/adapters/codex-orchestration.ts +632 -0
  64. package/lib/agent-clients/adapters/codex.ts +44 -1
  65. package/lib/agent-clients/adapters/index.ts +7 -20
  66. package/lib/agent-clients/tokens.ts +4 -3
  67. package/lib/internal/agent-client.ts +142 -73
  68. package/lib/internal/cli-route-inventory.ts +2 -2
  69. package/lib/internal/task-operation-registry.ts +1 -1
  70. package/lib/internal/task-validate.ts +1 -1
  71. package/lib/platform/issues.ts +18 -6
  72. package/lib/runtime/agent-runtime.ts +6 -41
  73. package/lib/sandbox/commands/control-status.ts +1 -0
  74. package/lib/sandbox/commands/create.ts +1 -0
  75. package/lib/sandbox/control/agent-operation.ts +49 -0
  76. package/lib/sandbox/control/client.ts +35 -196
  77. package/lib/sandbox/control/executor.ts +12 -84
  78. package/lib/sandbox/control/protocol.ts +21 -51
  79. package/lib/sandbox/control/server.ts +12 -51
  80. package/lib/sandbox/mounts.ts +2 -1
  81. package/lib/sandbox/recovery.ts +3 -1
  82. package/lib/sandbox/removal.ts +12 -5
  83. package/lib/sandbox/workspace-view.ts +9 -16
  84. package/lib/task/control-authority.ts +65 -378
  85. package/lib/task/control-recovery.ts +2 -2
  86. package/lib/task/create.ts +3 -3
  87. package/lib/task/delegation-receipts.ts +87 -231
  88. package/lib/task/events.ts +100 -42
  89. package/lib/task/finalization.ts +23 -7
  90. package/lib/task/lifecycle.ts +82 -87
  91. package/lib/task/orchestration.ts +237 -176
  92. package/lib/task/runtime-paths.ts +12 -0
  93. package/lib/task/verification-engine.ts +27 -16
  94. package/lib/task/workspace-snapshot.ts +1 -1
  95. package/package.json +1 -1
  96. package/templates/.agents/hooks/lifecycle-delegation.js +1 -25
  97. package/templates/.agents/rules/README.en.md +1 -1
  98. package/templates/.agents/rules/README.zh-CN.md +1 -1
  99. package/templates/.agents/rules/next-step-output.en.md +6 -19
  100. package/templates/.agents/rules/next-step-output.zh-CN.md +6 -19
  101. package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
  102. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
  103. package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
  104. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
  105. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  106. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
  107. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +0 -1
  108. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +0 -1
  109. package/templates/.agents/skills/complete-task/SKILL.en.md +0 -3
  110. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +0 -3
  111. package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
  112. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
  113. package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
  114. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
  115. package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
  116. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
  117. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  118. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  119. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +0 -546
  120. package/dist/lib/internal/codex-lifecycle.js +0 -385
  121. package/dist/lib/task/codex-orchestration.js +0 -206
  122. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +0 -776
  123. package/lib/internal/codex-lifecycle.ts +0 -390
  124. 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'],
@@ -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);
@@ -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
@@ -741,6 +741,7 @@ export async function create(args, { runProjectInitCommand = true, workspace: wo
741
741
  const container = containerName(effectiveConfig, branch);
742
742
  const controlPaths = sandboxControlPaths({
743
743
  base: effectiveConfig.controlBase,
744
+ repoRoot: effectiveConfig.repoRoot,
744
745
  project: effectiveConfig.project,
745
746
  container,
746
747
  identity: target.workspace
@@ -0,0 +1,34 @@
1
+ import { getAgentClientAdapter } from '../../agent-clients/registry.js';
2
+ function operationFor(request) {
3
+ const adapter = getAgentClientAdapter(request.agentClient);
4
+ const operation = adapter.sandboxControlOperation;
5
+ if (!operation)
6
+ throw new Error('SANDBOX_CONTROL_AGENT_OPERATION_UNSUPPORTED');
7
+ return operation;
8
+ }
9
+ function validateAgentClientOperation(request, manifest) {
10
+ operationFor(request).validate({
11
+ operation: request.operation,
12
+ payload: request.payload,
13
+ manifest
14
+ });
15
+ }
16
+ function executeAgentClientOperation(request, manifest, manifestPath, override) {
17
+ return (override ?? operationFor(request)).execute({
18
+ operation: request.operation,
19
+ payload: request.payload,
20
+ manifest,
21
+ manifestPath
22
+ });
23
+ }
24
+ function recoverAgentClientOperation(params) {
25
+ const operation = operationFor(params.request);
26
+ return operation.recover?.({
27
+ operation: params.request.operation,
28
+ manifest: params.manifest,
29
+ manifestPath: params.manifestPath,
30
+ stdout: params.stdout
31
+ }) ?? { consistent: false };
32
+ }
33
+ export { executeAgentClientOperation, recoverAgentClientOperation, validateAgentClientOperation };
34
+ //# sourceMappingURL=agent-operation.js.map
@@ -2,6 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { SANDBOX_CONTROL_ADMISSION_WINDOW_MS, SANDBOX_CONTROL_MAX_BYTES, SANDBOX_CONTROL_STATUS_STALE_MS, isSandboxControlFamily } from './protocol.js';
5
+ import { isAgentClientId } from '../../agent-clients/types.js';
5
6
  import { normalizeAgentToken } from '../../agent-clients/tokens.js';
6
7
  import { readSandboxControlPayload, readSandboxControlStatus } from './state.js';
7
8
  import { accessSandboxTaskView, taskViewFromStatus } from './task-view.js';
@@ -368,7 +369,7 @@ function authority(params) {
368
369
  export function requestSandboxControl(params) {
369
370
  if (!isSandboxControlFamily(params.family))
370
371
  clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' is not allowed`, false);
371
- if (params.family === 'task-create' || params.family === 'codex-controller' || params.family === 'task-finalization') {
372
+ if (params.family === 'task-create' || params.family === 'agent-client' || params.family === 'task-finalization') {
372
373
  clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' requires a typed request`, false);
373
374
  }
374
375
  const auth = authority(params);
@@ -376,9 +377,26 @@ export function requestSandboxControl(params) {
376
377
  const request = {
377
378
  version: 4, id: randomUUID(), ...auth, issuedAt,
378
379
  expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
379
- family: params.family, args: params.args,
380
- controllerProcess: null,
381
- controllerProof: null
380
+ family: params.family, args: params.args
381
+ };
382
+ return exchangeSandboxControl(request, params);
383
+ }
384
+ export function requestSandboxAgentOperation(params) {
385
+ if (!isAgentClientId(params.agentClient)) {
386
+ clientError('SANDBOX_CONTROL_REQUEST_INVALID', 'agent-client id is invalid', false);
387
+ }
388
+ const auth = authority(params);
389
+ const issuedAt = Date.now();
390
+ const request = {
391
+ version: 4,
392
+ id: randomUUID(),
393
+ ...auth,
394
+ issuedAt,
395
+ expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
396
+ family: 'agent-client',
397
+ agentClient: params.agentClient,
398
+ operation: params.operation,
399
+ payload: params.payload
382
400
  };
383
401
  return exchangeSandboxControl(request, params);
384
402
  }
@@ -398,9 +416,7 @@ export function requestSandboxTaskFinalization(params) {
398
416
  family: 'task-finalization',
399
417
  operation: 'complete',
400
418
  agent,
401
- args: [],
402
- controllerProcess: null,
403
- controllerProof: null
419
+ args: []
404
420
  };
405
421
  let response;
406
422
  try {
@@ -440,147 +456,6 @@ export function recoverAcceptedTaskFinalization(requestId, params, recover = rec
440
456
  }
441
457
  clientError('SANDBOX_CONTROL_RESULT_UNKNOWN', 'accepted finalization request remained unknown after the automatic recovery budget; inspect the receipt and resume by request id', false, true, requestId);
442
458
  }
443
- function exactKeys(value, keys) {
444
- return Object.keys(value).sort().join(',') === [...keys].sort().join(',');
445
- }
446
- function validProcess(value) {
447
- if (!value || typeof value !== 'object' || Array.isArray(value))
448
- return false;
449
- const identity = value;
450
- return exactKeys(identity, ['pid', 'startTime'])
451
- && Number.isSafeInteger(identity.pid) && identity.pid > 0
452
- && Number.isSafeInteger(identity.startTime) && identity.startTime >= 0;
453
- }
454
- function validBuild(value) {
455
- if (!value || typeof value !== 'object' || Array.isArray(value))
456
- return false;
457
- const build = value;
458
- return exactKeys(build, ['internalExecutableBuildHash', 'lifecycleContractHash', 'packageVersion', 'protocolVersion'])
459
- && build.protocolVersion === 3
460
- && typeof build.packageVersion === 'string' && build.packageVersion.length > 0
461
- && typeof build.internalExecutableBuildHash === 'string' && /^[a-f0-9]{64}$/u.test(build.internalExecutableBuildHash)
462
- && typeof build.lifecycleContractHash === 'string' && /^[a-f0-9]{64}$/u.test(build.lifecycleContractHash);
463
- }
464
- export function parseCodexControllerResult(response) {
465
- if (response.phase !== 'completed' || response.error !== null || response.stderr !== '') {
466
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller result outer response is invalid', false, true);
467
- }
468
- let value;
469
- try {
470
- if (!response.stdout.endsWith('\n') || response.stdout.slice(0, -1).includes('\n'))
471
- throw new Error('not canonical');
472
- value = JSON.parse(response.stdout);
473
- }
474
- catch {
475
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller result payload is invalid', false, true);
476
- }
477
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
478
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller result payload is invalid', false, true);
479
- }
480
- const result = value;
481
- const resultKeys = result.status === 'verified'
482
- ? ['binding', 'changed', 'error', 'lease', 'status', 'version']
483
- : ['changed', 'error', 'lease', 'status', 'version'];
484
- if (!exactKeys(result, resultKeys)
485
- || result.version !== 1 || !['opened', 'closed', 'failed', 'verified'].includes(result.status)) {
486
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller result schema is invalid', false, true);
487
- }
488
- if (result.status === 'failed') {
489
- const error = result.error;
490
- if (response.exitCode !== 1 || result.changed !== false || result.lease !== null
491
- || !error || !exactKeys(error, ['code', 'message', 'retryable'])
492
- || typeof error.code !== 'string' || !/^[A-Z][A-Z0-9_]+$/u.test(error.code)
493
- || typeof error.message !== 'string' || error.retryable !== false) {
494
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller failure result is invalid', false, true);
495
- }
496
- clientError(error.code, error.message, false, true);
497
- }
498
- if (response.exitCode !== 0 || result.error !== null) {
499
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller success result is invalid', false, true);
500
- }
501
- if (result.status === 'closed') {
502
- if (typeof result.changed !== 'boolean' || result.lease !== null) {
503
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller close result is invalid', false, true);
504
- }
505
- return result;
506
- }
507
- if (result.status === 'verified') {
508
- const binding = result.binding;
509
- if (result.changed !== false || result.lease !== null || !binding
510
- || !exactKeys(binding, ['controllerInstanceDigest', 'controlGeneration', 'taskId'])
511
- || typeof binding.taskId !== 'string' || binding.taskId.length === 0
512
- || typeof binding.controlGeneration !== 'string' || binding.controlGeneration.length === 0
513
- || typeof binding.controllerInstanceDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(binding.controllerInstanceDigest)) {
514
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller verify result is invalid', false, true);
515
- }
516
- return result;
517
- }
518
- const lease = result.lease;
519
- if (result.changed !== true || !lease
520
- || !exactKeys(lease, [
521
- 'buildIdentity', 'controlGeneration', 'controllerInstanceDigest', 'controllerProcess',
522
- 'expiresAt', 'issuedAt', 'leaseId', 'leaseSecret', 'taskId', 'version'
523
- ])
524
- || lease.version !== 1
525
- || typeof lease.leaseId !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.leaseId)
526
- || typeof lease.leaseSecret !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.leaseSecret)
527
- || typeof lease.taskId !== 'string' || lease.taskId.length === 0
528
- || typeof lease.controlGeneration !== 'string' || lease.controlGeneration.length === 0
529
- || typeof lease.controllerInstanceDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.controllerInstanceDigest)
530
- || !validProcess(lease.controllerProcess) || !validBuild(lease.buildIdentity)
531
- || !Number.isSafeInteger(lease.issuedAt) || !Number.isSafeInteger(lease.expiresAt)
532
- || lease.expiresAt <= lease.issuedAt) {
533
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller open result is invalid', false, true);
534
- }
535
- return result;
536
- }
537
- function requestCodexController(params) {
538
- const auth = authority(params);
539
- const issuedAt = Date.now();
540
- const request = {
541
- version: 4,
542
- id: randomUUID(),
543
- ...auth,
544
- issuedAt,
545
- expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
546
- family: 'codex-controller',
547
- command: params.command,
548
- args: [],
549
- controllerProcess: params.controllerProcess,
550
- controllerProof: params.controllerProof
551
- };
552
- const result = parseCodexControllerResult(exchangeSandboxControl(request, params));
553
- if (result.status === 'opened'
554
- && (result.lease.controlGeneration !== auth.generation
555
- || result.lease.controllerProcess.pid !== params.controllerProcess.pid
556
- || result.lease.controllerProcess.startTime !== params.controllerProcess.startTime)) {
557
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller result does not match the request', false, true);
558
- }
559
- return result;
560
- }
561
- export function requestCodexControllerOpen(params) {
562
- const result = requestCodexController({ ...params, command: 'open', controllerProof: null });
563
- if (result.status !== 'opened')
564
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller open returned the wrong result', false, true);
565
- return result;
566
- }
567
- export function requestCodexControllerClose(params) {
568
- const result = requestCodexController({ ...params, command: 'close' });
569
- if (result.status !== 'closed')
570
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller close returned the wrong result', false, true);
571
- return result;
572
- }
573
- export function requestCodexControllerVerify(params) {
574
- const result = requestCodexController({
575
- ...params,
576
- controllerProcess: params.controllerProof.controllerProcess,
577
- command: 'verify'
578
- });
579
- if (result.status !== 'verified') {
580
- clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller verify returned the wrong result', false, true);
581
- }
582
- return result;
583
- }
584
459
  export function requestSandboxTaskCreate(params) {
585
460
  const auth = authority(params);
586
461
  const issuedAt = Date.now();
@@ -588,8 +463,6 @@ export function requestSandboxTaskCreate(params) {
588
463
  version: 4, id: randomUUID(), ...auth, issuedAt,
589
464
  expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
590
465
  family: 'task-create', candidate: params.candidate,
591
- controllerProcess: null,
592
- controllerProof: null
593
466
  };
594
467
  return exchangeSandboxControl(request, { ...params, timeoutMs: params.timeoutMs ?? 120_000 });
595
468
  }