@fitlab-ai/agent-infra 0.9.6 → 0.9.7
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.
- package/dist/bin/internal-cli.js +5 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +45 -4
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +162 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +244 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +370 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +21 -5
- package/dist/lib/defaults.json +10 -2
- package/dist/lib/git/workflow.js +30 -3
- package/dist/lib/internal/codex-lifecycle.js +69 -1
- package/dist/lib/internal/codex-sandbox-controller.js +106 -0
- package/dist/lib/internal/task-orchestration.js +37 -5
- package/dist/lib/platform/context.js +6 -4
- package/dist/lib/platform/github-client.js +55 -2
- package/dist/lib/process-data/checkpoint.js +319 -0
- package/dist/lib/process-data/index.js +128 -60
- package/dist/lib/process-data/materialize.js +84 -0
- package/dist/lib/process-data/normalize.js +34 -1
- package/dist/lib/process-data/reconcile.js +55 -1
- package/dist/lib/process-data/sources.js +422 -1
- package/dist/lib/process-data/store.js +341 -11
- package/dist/lib/sandbox/commands/create.js +103 -51
- package/dist/lib/sandbox/commands/rm.js +64 -15
- package/dist/lib/sandbox/commands/start.js +4 -3
- package/dist/lib/sandbox/control/container-identity.js +64 -0
- package/dist/lib/sandbox/control/executor.js +13 -7
- package/dist/lib/sandbox/control/lifecycle.js +238 -29
- package/dist/lib/sandbox/control/protocol.js +7 -0
- package/dist/lib/sandbox/control/server.js +202 -26
- package/dist/lib/sandbox/control/state.js +25 -15
- package/dist/lib/sandbox/control/timing.js +8 -0
- package/dist/lib/sandbox/mounts.js +5 -13
- package/dist/lib/sandbox/recovery.js +59 -42
- package/dist/lib/sandbox/workspace-view.js +57 -40
- package/dist/lib/server/daemon.js +1 -1
- package/dist/lib/server/process-control.js +11 -1
- package/dist/lib/server/process-state.js +57 -20
- package/dist/lib/task/artifact-lifecycle.js +82 -11
- package/dist/lib/task/artifact-receipts.js +128 -0
- package/dist/lib/task/codex-orchestration.js +81 -6
- package/dist/lib/task/commands/validate.js +1 -1
- package/dist/lib/task/create.js +10 -2
- package/dist/lib/task/delegation-receipts.js +84 -2
- package/dist/lib/task/events.js +139 -2
- package/dist/lib/task/ledger.js +13 -1
- package/dist/lib/task/orchestration.js +235 -14
- package/dist/lib/task/sections.js +5 -1
- package/dist/lib/task/task-execution-lock.js +5 -4
- package/dist/lib/task/verification-engine.js +40 -8
- package/dist/lifecycle-build-manifest.json +6 -0
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +60 -3
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +227 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +321 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +26 -0
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +453 -0
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +33 -4
- package/lib/defaults.json +10 -2
- package/lib/git/workflow.ts +25 -2
- package/lib/internal/codex-lifecycle.ts +68 -1
- package/lib/internal/codex-sandbox-controller.ts +109 -0
- package/lib/internal/task-orchestration.ts +31 -4
- package/lib/platform/context.ts +8 -5
- package/lib/platform/github-client.ts +61 -3
- package/lib/process-data/checkpoint.ts +335 -0
- package/lib/process-data/index.ts +112 -60
- package/lib/process-data/materialize.ts +83 -0
- package/lib/process-data/normalize.ts +29 -1
- package/lib/process-data/reconcile.ts +64 -2
- package/lib/process-data/sources.ts +449 -2
- package/lib/process-data/store.ts +304 -11
- package/lib/process-data/types.ts +124 -2
- package/lib/sandbox/commands/create.ts +56 -7
- package/lib/sandbox/commands/rm.ts +64 -14
- package/lib/sandbox/commands/start.ts +4 -3
- package/lib/sandbox/control/container-identity.ts +93 -0
- package/lib/sandbox/control/executor.ts +14 -10
- package/lib/sandbox/control/lifecycle.ts +264 -33
- package/lib/sandbox/control/protocol.ts +31 -5
- package/lib/sandbox/control/server.ts +184 -24
- package/lib/sandbox/control/state.ts +34 -17
- package/lib/sandbox/control/timing.ts +12 -0
- package/lib/sandbox/mounts.ts +5 -15
- package/lib/sandbox/recovery.ts +71 -42
- package/lib/sandbox/workspace-view.ts +61 -40
- package/lib/server/daemon.ts +1 -1
- package/lib/server/process-control.ts +13 -3
- package/lib/server/process-state.ts +82 -27
- package/lib/task/artifact-lifecycle.ts +73 -10
- package/lib/task/artifact-receipts.ts +161 -0
- package/lib/task/codex-orchestration.ts +91 -5
- package/lib/task/commands/validate.ts +1 -1
- package/lib/task/create.ts +11 -2
- package/lib/task/delegation-receipts.ts +160 -8
- package/lib/task/events.ts +122 -1
- package/lib/task/ledger.ts +16 -1
- package/lib/task/orchestration.ts +356 -17
- package/lib/task/sections.ts +6 -2
- package/lib/task/task-execution-lock.ts +11 -9
- package/lib/task/verification-engine.ts +51 -16
- package/package.json +1 -1
- package/templates/.agents/README.en.md +7 -0
- package/templates/.agents/README.zh-CN.md +5 -0
- package/templates/.agents/hooks/lifecycle-delegation.js +25 -1
- package/templates/.agents/rules/lifecycle-orchestration.en.md +8 -2
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +8 -2
- package/templates/.agents/rules/pr-checks-commands.github.en.md +1 -1
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +1 -1
- package/templates/.agents/rules/review-handshake.en.md +3 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +3 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +3 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +4 -0
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +4 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +6 -6
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +14 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +14 -3
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +67 -0
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +67 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +25 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +25 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +9 -7
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -7
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -0
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +75 -3
- package/templates/.agents/workspace/README.en.md +0 -26
- package/templates/.agents/workspace/README.zh-CN.md +0 -26
|
@@ -9,7 +9,11 @@ import {
|
|
|
9
9
|
resolveCodexThread
|
|
10
10
|
} from '../agent-clients/adapters/codex-lifecycle/app-server.ts';
|
|
11
11
|
import { createCodexLifecycleStore } from '../agent-clients/adapters/codex-lifecycle/store.ts';
|
|
12
|
+
import { createCodexCapabilityStore } from '../agent-clients/adapters/codex-lifecycle/capability-store.ts';
|
|
13
|
+
import { computeLifecycleBuildIdentity } from '../agent-clients/adapters/codex-lifecycle/build-identity.ts';
|
|
14
|
+
import { verifyCodexSandboxControllerContext } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.ts';
|
|
12
15
|
import type { CodexLifecycleEvent } from '../agent-clients/adapters/codex-lifecycle/evidence.ts';
|
|
16
|
+
import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
13
17
|
import {
|
|
14
18
|
activateCodexOrchestrationDelegation,
|
|
15
19
|
activateCodexSpawnDelegation,
|
|
@@ -18,7 +22,7 @@ import {
|
|
|
18
22
|
sealCodexParentDelegation
|
|
19
23
|
} from '../task/codex-orchestration.ts';
|
|
20
24
|
|
|
21
|
-
const USAGE = 'Usage: agent-infra-internal codex-lifecycle <hook-event|resolve-start|resolve-stop|preflight|consume> [options]\n';
|
|
25
|
+
const USAGE = 'Usage: agent-infra-internal codex-lifecycle <capability-arm|hook-event|resolve-start|resolve-stop|preflight|consume> [options]\n';
|
|
22
26
|
const MANAGED_AGENT = /^agent-infra-lifecycle-(executor|reviewer)$/;
|
|
23
27
|
|
|
24
28
|
type Parsed = Readonly<{ operation: string; values: Readonly<Record<string, string>> }>;
|
|
@@ -53,6 +57,7 @@ function parse(args: string[]): Parsed | null {
|
|
|
53
57
|
return null;
|
|
54
58
|
}
|
|
55
59
|
const allowed = {
|
|
60
|
+
'capability-arm': ['--task-id'],
|
|
56
61
|
'hook-event': ['--event', '--bridge'],
|
|
57
62
|
'resolve-start': ['--child-id'],
|
|
58
63
|
'resolve-stop': ['--child-id'],
|
|
@@ -92,6 +97,16 @@ function hookDefinitionHash(): string {
|
|
|
92
97
|
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
93
98
|
}
|
|
94
99
|
|
|
100
|
+
function controllerBinding(taskId: string) {
|
|
101
|
+
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
102
|
+
if (!contextPath) return undefined;
|
|
103
|
+
const context = verifyCodexSandboxControllerContext(contextPath, taskId, { repoRoot: process.cwd() });
|
|
104
|
+
return {
|
|
105
|
+
instanceDigest: context.controllerInstanceDigest,
|
|
106
|
+
controlGeneration: context.controlGeneration
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
95
110
|
async function readStdin(): Promise<unknown> {
|
|
96
111
|
let input = '';
|
|
97
112
|
process.stdin.setEncoding('utf8');
|
|
@@ -171,6 +186,28 @@ async function codexLifecycle(args: string[] = []): Promise<void> {
|
|
|
171
186
|
return;
|
|
172
187
|
}
|
|
173
188
|
|
|
189
|
+
if (parsed.operation === 'capability-arm') {
|
|
190
|
+
const taskId = parsed.values['--task-id'];
|
|
191
|
+
if (!taskId) throw new Error('capability-arm requires --task-id');
|
|
192
|
+
const resolved = resolveTaskRef(taskId, { repoRoot: process.cwd() });
|
|
193
|
+
if (!resolved.ok) throw new Error(`${resolved.code}: ${resolved.message}`);
|
|
194
|
+
const armed = createCodexCapabilityStore().arm({
|
|
195
|
+
taskId: resolved.taskId,
|
|
196
|
+
buildIdentity: computeLifecycleBuildIdentity(process.cwd()),
|
|
197
|
+
controller: controllerBinding(resolved.taskId)
|
|
198
|
+
});
|
|
199
|
+
output({
|
|
200
|
+
status: 'armed',
|
|
201
|
+
changed: true,
|
|
202
|
+
capabilityToken: armed.token,
|
|
203
|
+
marker: armed.marker,
|
|
204
|
+
expiresAt: armed.expiresAt,
|
|
205
|
+
buildIdentity: armed.buildIdentity,
|
|
206
|
+
error: null
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
174
211
|
const store = createCodexLifecycleStore({ cliVersion: cliVersion() });
|
|
175
212
|
if (parsed.operation === 'hook-event') {
|
|
176
213
|
const phase = parsed.values['--event'];
|
|
@@ -181,6 +218,36 @@ async function codexLifecycle(args: string[] = []): Promise<void> {
|
|
|
181
218
|
throw new Error("hook-event --bridge must be 'true'");
|
|
182
219
|
}
|
|
183
220
|
const payload = await readStdin();
|
|
221
|
+
if (phase === 'post-tool') {
|
|
222
|
+
const capabilityToken = payloadText(payload, 'capabilityToken');
|
|
223
|
+
if (capabilityToken) {
|
|
224
|
+
const capabilityStore = createCodexCapabilityStore();
|
|
225
|
+
const armed = capabilityStore.inspect(capabilityToken);
|
|
226
|
+
const capability = createCodexCapabilityStore().attest({
|
|
227
|
+
token: capabilityToken,
|
|
228
|
+
sessionId: payloadText(payload, 'sessionId'),
|
|
229
|
+
turnId: payloadText(payload, 'turnId'),
|
|
230
|
+
toolUseId: payloadText(payload, 'toolUseId'),
|
|
231
|
+
hookDefinitionHash: payloadText(payload, 'hookDefinitionHash'),
|
|
232
|
+
buildIdentity: computeLifecycleBuildIdentity(process.cwd()),
|
|
233
|
+
controller: controllerBinding(armed.taskId)
|
|
234
|
+
});
|
|
235
|
+
output({
|
|
236
|
+
status: capability.status,
|
|
237
|
+
changed: true,
|
|
238
|
+
evidence: {
|
|
239
|
+
revision: capability.revision,
|
|
240
|
+
sessionId: capability.sessionId,
|
|
241
|
+
turnId: capability.turnId,
|
|
242
|
+
toolUseId: capability.toolUseId,
|
|
243
|
+
expiresAt: capability.expiresAt
|
|
244
|
+
},
|
|
245
|
+
diagnostics: [],
|
|
246
|
+
error: null
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
184
251
|
if (phase === 'post-tool' && parsed.values['--bridge'] === 'true') {
|
|
185
252
|
const toolName = payloadText(payload, 'toolName');
|
|
186
253
|
if (toolName === 'collaborationspawn_agent') {
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runCodexSandboxController,
|
|
3
|
+
verifyCodexSandboxControllerContext
|
|
4
|
+
} from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.ts';
|
|
5
|
+
|
|
6
|
+
const USAGE = 'Usage: agent-infra-internal codex-sandbox-controller <run|verify-context> [options]\n';
|
|
7
|
+
|
|
8
|
+
function fail(code: string, message: string, exitCode = 1): void {
|
|
9
|
+
process.stdout.write(`${JSON.stringify({
|
|
10
|
+
status: 'failed',
|
|
11
|
+
changed: false,
|
|
12
|
+
error: { code, message }
|
|
13
|
+
})}\n`);
|
|
14
|
+
process.exitCode = exitCode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parse(args: string[]): Readonly<{ operation: string; values: Readonly<Record<string, string>> }> | null {
|
|
18
|
+
if (args[0] === '--help' || args[0] === '-h') {
|
|
19
|
+
process.stdout.write(USAGE);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const operation = args[0];
|
|
23
|
+
if (!operation || !['run', 'verify-context'].includes(operation)) {
|
|
24
|
+
process.stderr.write(USAGE);
|
|
25
|
+
fail('CODEX_SANDBOX_CONTROLLER_PAYLOAD_INVALID', 'operation must be run or verify-context', 2);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const allowed = new Set([
|
|
29
|
+
'--task-id',
|
|
30
|
+
'--task-ref',
|
|
31
|
+
'--context',
|
|
32
|
+
'--executor-model',
|
|
33
|
+
'--executor-reasoning-effort',
|
|
34
|
+
'--reviewer-model',
|
|
35
|
+
'--reviewer-reasoning-effort'
|
|
36
|
+
]);
|
|
37
|
+
const values: Record<string, string> = {};
|
|
38
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
39
|
+
const flag = args[index]!;
|
|
40
|
+
const value = args[++index];
|
|
41
|
+
if (!allowed.has(flag) || Object.hasOwn(values, flag) || !value || value.startsWith('--')) {
|
|
42
|
+
fail('CODEX_SANDBOX_CONTROLLER_PAYLOAD_INVALID', `invalid option '${flag}'`, 2);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
values[flag] = value;
|
|
46
|
+
}
|
|
47
|
+
return { operation, values };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function codexSandboxController(args: string[] = []): Promise<void> {
|
|
51
|
+
const parsed = parse(args);
|
|
52
|
+
if (!parsed || process.exitCode) return;
|
|
53
|
+
const taskId = parsed.values['--task-id'];
|
|
54
|
+
if (!taskId) {
|
|
55
|
+
fail('CODEX_SANDBOX_CONTROLLER_PAYLOAD_INVALID', '--task-id is required', 2);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
if (parsed.operation === 'verify-context') {
|
|
60
|
+
const contextPath = parsed.values['--context']
|
|
61
|
+
?? process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
62
|
+
if (!contextPath) throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_MISSING');
|
|
63
|
+
const context = verifyCodexSandboxControllerContext(contextPath, taskId);
|
|
64
|
+
process.stdout.write(`${JSON.stringify({
|
|
65
|
+
status: 'ready',
|
|
66
|
+
changed: false,
|
|
67
|
+
context: {
|
|
68
|
+
taskId: context.taskId,
|
|
69
|
+
controlGeneration: context.controlGeneration,
|
|
70
|
+
controllerInstanceDigest: context.controllerInstanceDigest,
|
|
71
|
+
expiresAt: context.expiresAt,
|
|
72
|
+
buildIdentity: context.buildIdentity,
|
|
73
|
+
hookDefinitionHash: context.hookDefinitionHash,
|
|
74
|
+
lifecycleProfilesHash: context.lifecycleProfilesHash
|
|
75
|
+
},
|
|
76
|
+
error: null
|
|
77
|
+
})}\n`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const taskRef = parsed.values['--task-ref'];
|
|
81
|
+
if (!taskRef) throw new Error('CODEX_SANDBOX_CONTROLLER_TASK_REF_REQUIRED');
|
|
82
|
+
const policy = [
|
|
83
|
+
'--executor-model',
|
|
84
|
+
'--executor-reasoning-effort',
|
|
85
|
+
'--reviewer-model',
|
|
86
|
+
'--reviewer-reasoning-effort'
|
|
87
|
+
];
|
|
88
|
+
if (policy.some((flag) => parsed.values[flag] !== undefined)
|
|
89
|
+
&& policy.some((flag) => parsed.values[flag] === undefined)) {
|
|
90
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_MODEL_POLICY_INVALID');
|
|
91
|
+
}
|
|
92
|
+
const exitCode = await runCodexSandboxController({
|
|
93
|
+
taskId,
|
|
94
|
+
taskRef,
|
|
95
|
+
executorModel: parsed.values['--executor-model'],
|
|
96
|
+
executorReasoningEffort: parsed.values['--executor-reasoning-effort'],
|
|
97
|
+
reviewerModel: parsed.values['--reviewer-model'],
|
|
98
|
+
reviewerReasoningEffort: parsed.values['--reviewer-reasoning-effort']
|
|
99
|
+
});
|
|
100
|
+
process.exitCode = exitCode;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
103
|
+
const code = /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1]
|
|
104
|
+
?? 'CODEX_SANDBOX_CONTROLLER_FAILED';
|
|
105
|
+
fail(code, message);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { codexSandboxController };
|
|
@@ -4,12 +4,15 @@ import {
|
|
|
4
4
|
activateMatchingOrchestrationDelegation,
|
|
5
5
|
activateOrchestrationDelegation,
|
|
6
6
|
advanceOrchestration,
|
|
7
|
+
awaitOrchestrationDelegationActivation,
|
|
7
8
|
beginCommitIntent,
|
|
8
9
|
beginOrResumeOrchestration,
|
|
9
10
|
checkpointCommitIntent,
|
|
10
11
|
completeCommitIntent,
|
|
12
|
+
dispatchOrchestrationDelegation,
|
|
11
13
|
pauseOrchestration,
|
|
12
14
|
recoverCommitIntent,
|
|
15
|
+
recoverPreparedOrchestrationDelegation,
|
|
13
16
|
routeOrchestration,
|
|
14
17
|
sealMatchingOrchestrationDelegation,
|
|
15
18
|
sealOrchestrationDelegation,
|
|
@@ -22,7 +25,7 @@ import { prepareCodexOrchestrationDelegation } from '../task/codex-orchestration
|
|
|
22
25
|
import { isAgentClientId } from '../agent-clients/types.ts';
|
|
23
26
|
import type { AgentClientId } from '../agent-clients/types.ts';
|
|
24
27
|
|
|
25
|
-
const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|hook-start|hook-stop|advance|pause|status|commit-start|commit-begin|commit-checkpoint|commit-complete|commit-recover|commit-abort|commit-terminate|commit-status> [options]\n';
|
|
28
|
+
const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|dispatch|await-activation|recover-prepared|hook-start|hook-stop|advance|pause|status|commit-start|commit-begin|commit-checkpoint|commit-complete|commit-recover|commit-abort|commit-terminate|commit-status> [options]\n';
|
|
26
29
|
|
|
27
30
|
function usageFailure(message: string): void {
|
|
28
31
|
process.stdout.write(`${JSON.stringify({
|
|
@@ -44,7 +47,8 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
|
|
|
44
47
|
}
|
|
45
48
|
const [taskRef, intent] = args;
|
|
46
49
|
if (![
|
|
47
|
-
'begin-or-resume', 'route', 'prepare', '
|
|
50
|
+
'begin-or-resume', 'route', 'prepare', 'dispatch', 'await-activation', 'recover-prepared',
|
|
51
|
+
'hook-start', 'hook-stop', 'advance', 'pause', 'status',
|
|
48
52
|
'commit-start', 'commit-begin', 'commit-checkpoint', 'commit-complete', 'commit-recover', 'commit-abort', 'commit-terminate', 'commit-status'
|
|
49
53
|
].includes(intent!)) {
|
|
50
54
|
usageFailure(`unknown intent '${intent}'`);
|
|
@@ -64,7 +68,9 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
|
|
|
64
68
|
if (![
|
|
65
69
|
'--max-steps', '--executor-model', '--executor-reasoning-effort',
|
|
66
70
|
'--reviewer-model', '--reviewer-reasoning-effort',
|
|
67
|
-
'--client', '--requested-model', '--requested-reasoning-effort', '--
|
|
71
|
+
'--client', '--requested-model', '--requested-reasoning-effort', '--capability-token',
|
|
72
|
+
'--parent-id', '--before-fingerprint',
|
|
73
|
+
'--stage', '--round', '--artifact', '--role',
|
|
68
74
|
'--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
|
|
69
75
|
'--model-fallback-reason', '--reasoning-effort-fallback-reason',
|
|
70
76
|
'--exit-code', '--after-fingerprint', '--changed-paths', '--code', '--message', '--recoverable', '--agent',
|
|
@@ -215,8 +221,29 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
|
|
|
215
221
|
result = await prepareCodexOrchestrationDelegation(taskRef!, {
|
|
216
222
|
client: values['--client'] as AgentClientId,
|
|
217
223
|
requestedModel: values['--requested-model'],
|
|
218
|
-
requestedReasoningEffort: values['--requested-reasoning-effort']
|
|
224
|
+
requestedReasoningEffort: values['--requested-reasoning-effort'],
|
|
225
|
+
capabilityToken: values['--capability-token']
|
|
219
226
|
}, { orchestrationOptions: coreOptions });
|
|
227
|
+
} else if (intent === 'dispatch') {
|
|
228
|
+
result = dispatchOrchestrationDelegation(taskRef!, coreOptions);
|
|
229
|
+
} else if (intent === 'await-activation') {
|
|
230
|
+
const missing = requireValues(['--stage', '--round', '--artifact', '--role']);
|
|
231
|
+
if (missing) { usageFailure(`intent 'await-activation' requires '${missing}'`); return; }
|
|
232
|
+
const round = Number(values['--round']);
|
|
233
|
+
if (!Number.isSafeInteger(round) || round < 1) {
|
|
234
|
+
usageFailure('--round must be a positive integer'); return;
|
|
235
|
+
}
|
|
236
|
+
if (!['executor', 'reviewer'].includes(values['--role']!)) {
|
|
237
|
+
usageFailure('--role must be executor or reviewer'); return;
|
|
238
|
+
}
|
|
239
|
+
result = await awaitOrchestrationDelegationActivation(taskRef!, {
|
|
240
|
+
stage: values['--stage'] as Parameters<typeof awaitOrchestrationDelegationActivation>[1]['stage'],
|
|
241
|
+
round,
|
|
242
|
+
artifact: values['--artifact']!,
|
|
243
|
+
role: values['--role'] as 'executor' | 'reviewer'
|
|
244
|
+
}, coreOptions);
|
|
245
|
+
} else if (intent === 'recover-prepared') {
|
|
246
|
+
result = recoverPreparedOrchestrationDelegation(taskRef!, coreOptions);
|
|
220
247
|
} else if (intent === 'hook-start') {
|
|
221
248
|
const missing = requireValues([
|
|
222
249
|
...(taskRef === 'auto' ? ['--client'] : []),
|
package/lib/platform/context.ts
CHANGED
|
@@ -20,6 +20,8 @@ type ContextOptions = {
|
|
|
20
20
|
platformType?: string;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
const CURRENT_USER_QUERY = 'query { viewer { login } }';
|
|
24
|
+
|
|
23
25
|
function findRepoRoot(cwd: string): string {
|
|
24
26
|
try {
|
|
25
27
|
return execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
@@ -105,16 +107,17 @@ function resolveGitHubContext(options: ContextOptions & { cwd: string }): Platfo
|
|
|
105
107
|
error: { code: 'UPSTREAM_UNRESOLVED', message: 'Unable to resolve the upstream repository', retryable: false }
|
|
106
108
|
});
|
|
107
109
|
}
|
|
108
|
-
const user = client.json(['api', '
|
|
110
|
+
const user = client.json(['api', 'graphql', '-f', `query=${CURRENT_USER_QUERY}`], { cwd });
|
|
109
111
|
if (!user.ok) return failure(user.error, upstream);
|
|
110
|
-
const userValue = user.value as { login?: string } | null;
|
|
112
|
+
const userValue = user.value as { data?: { viewer?: { login?: string } } } | null;
|
|
113
|
+
const currentUser = userValue?.data?.viewer?.login || null;
|
|
111
114
|
const permissions = client.json(['api', `repos/${upstream}`], { cwd });
|
|
112
115
|
if (!permissions.ok) return failure(permissions.error, upstream);
|
|
113
116
|
const permissionValue = permissions.value as { permissions?: Record<string, boolean> } | null;
|
|
114
117
|
const values = permissionValue?.permissions || {};
|
|
115
118
|
const capabilities = {
|
|
116
|
-
authenticated: Boolean(
|
|
117
|
-
comment: Boolean(
|
|
119
|
+
authenticated: Boolean(currentUser),
|
|
120
|
+
comment: Boolean(currentUser),
|
|
118
121
|
triage: Boolean(values.triage || values.push || values.admin),
|
|
119
122
|
push: Boolean(values.push || values.admin),
|
|
120
123
|
admin: Boolean(values.admin)
|
|
@@ -122,7 +125,7 @@ function resolveGitHubContext(options: ContextOptions & { cwd: string }): Platfo
|
|
|
122
125
|
const status = Object.values(capabilities).every(Boolean) ? 'no-op' : 'degraded';
|
|
123
126
|
return platformResult(status, {
|
|
124
127
|
changed: false,
|
|
125
|
-
platform: { type: platform, repository: upstream, currentUser
|
|
128
|
+
platform: { type: platform, repository: upstream, currentUser },
|
|
126
129
|
resource: { kind: 'repository', number: null },
|
|
127
130
|
capabilities,
|
|
128
131
|
operations: [{ name: 'resolve', status: 'no-op', reasonCode: null }]
|
|
@@ -10,10 +10,18 @@ type RunOptions = { cwd?: string; input?: string };
|
|
|
10
10
|
type Runner = (args: string[], options: RunOptions) => RunResult;
|
|
11
11
|
type RequestOptions = RunOptions & { method?: 'GET' | 'PATCH' | 'POST' | 'DELETE' };
|
|
12
12
|
type ClientResult<T> = { ok: true; value: T } | { ok: false; error: PlatformError };
|
|
13
|
+
type ResponseMetadata = {
|
|
14
|
+
status: number;
|
|
15
|
+
requestUrl: string;
|
|
16
|
+
date?: string;
|
|
17
|
+
links: string[];
|
|
18
|
+
};
|
|
19
|
+
type JsonResponse<T> = { value: T; metadata: ResponseMetadata };
|
|
13
20
|
|
|
14
21
|
type GitHubClient = {
|
|
15
22
|
version(options?: RunOptions): ClientResult<string>;
|
|
16
23
|
json<T = unknown>(args: string[], options?: RequestOptions): ClientResult<T>;
|
|
24
|
+
jsonWithMetadata?<T = unknown>(args: string[], options?: RequestOptions): ClientResult<JsonResponse<T>>;
|
|
17
25
|
text(args: string[], options?: RequestOptions): ClientResult<string>;
|
|
18
26
|
};
|
|
19
27
|
|
|
@@ -23,7 +31,9 @@ type ClientOptions = {
|
|
|
23
31
|
sleep?: (delayMs: number) => void;
|
|
24
32
|
};
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
// closingIssuesReferences (introduced in gh 2.72.0) is the highest-versioned gh feature
|
|
35
|
+
// the platform layer depends on; see github-release-notes.ts:102.
|
|
36
|
+
const MINIMUM_GITHUB_CLI_VERSION = '2.72.0';
|
|
27
37
|
const GITHUB_CLI_MAX_BUFFER = 64 * 1024 * 1024;
|
|
28
38
|
const ERROR_DETAIL_LIMIT = 4096;
|
|
29
39
|
|
|
@@ -101,6 +111,39 @@ function classifyGitHubFailure(result: RunResult): PlatformError {
|
|
|
101
111
|
return { code: 'PLATFORM_REQUEST_FAILED', message: detail || 'GitHub request failed', retryable: false };
|
|
102
112
|
}
|
|
103
113
|
|
|
114
|
+
function responseUrl(args: string[]): string {
|
|
115
|
+
const endpoint = args.find((arg, index) => index > 0 && !arg.startsWith('-')) || '';
|
|
116
|
+
return endpoint.startsWith('http') ? endpoint : `https://api.github.com/${endpoint.replace(/^\//, '')}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function parseIncludedResponse<T>(stdout: string, args: string[]): JsonResponse<T> | null {
|
|
120
|
+
const statusBlocks = [...stdout.matchAll(/^HTTP\/[^\r\n]+/gim)];
|
|
121
|
+
const headerStart = statusBlocks.at(-1)?.index;
|
|
122
|
+
if (headerStart === undefined) return null;
|
|
123
|
+
const response = stdout.slice(headerStart);
|
|
124
|
+
const separator = response.match(/\r?\n\r?\n/);
|
|
125
|
+
if (!separator || separator.index === undefined) return null;
|
|
126
|
+
const header = response.slice(0, separator.index);
|
|
127
|
+
const body = response.slice(separator.index + separator[0].length);
|
|
128
|
+
const status = header.match(/^HTTP\/[^ ]+\s+(\d{3})/mi)?.[1];
|
|
129
|
+
if (!status) return null;
|
|
130
|
+
const date = header.match(/^Date:\s*(.+)$/mi)?.[1]?.trim();
|
|
131
|
+
const links = [...header.matchAll(/^Link:\s*(.+)$/gim)].flatMap((match) => match[1]!.split(',').map((value) => value.trim()));
|
|
132
|
+
try {
|
|
133
|
+
return {
|
|
134
|
+
value: JSON.parse(body || 'null') as T,
|
|
135
|
+
metadata: {
|
|
136
|
+
status: Number(status),
|
|
137
|
+
requestUrl: responseUrl(args),
|
|
138
|
+
...(date ? { date } : {}),
|
|
139
|
+
links
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
} catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
104
147
|
function createGitHubClient(options: ClientOptions = {}): GitHubClient {
|
|
105
148
|
const runner = options.runner || defaultRunner;
|
|
106
149
|
const delays = options.retryDelaysMs || retryDelaysFromEnvironment();
|
|
@@ -156,9 +199,24 @@ function createGitHubClient(options: ClientOptions = {}): GitHubClient {
|
|
|
156
199
|
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub returned invalid JSON', retryable: true }
|
|
157
200
|
};
|
|
158
201
|
}
|
|
202
|
+
},
|
|
203
|
+
jsonWithMetadata<T>(args: string[], request: RequestOptions = {}): ClientResult<JsonResponse<T>> {
|
|
204
|
+
const includeArgs = args[0] === 'api' && args[1] !== '--include'
|
|
205
|
+
? [args[0], '--include', ...args.slice(1)]
|
|
206
|
+
: args;
|
|
207
|
+
const result = run(includeArgs, request);
|
|
208
|
+
if (!result.ok) return result;
|
|
209
|
+
const parsed = parseIncludedResponse<T>(result.value, args);
|
|
210
|
+
if (!parsed) {
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub response metadata or JSON is invalid', retryable: true }
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return { ok: true, value: parsed };
|
|
159
217
|
}
|
|
160
218
|
};
|
|
161
219
|
}
|
|
162
220
|
|
|
163
|
-
export { classifyGitHubFailure, createGitHubClient, MINIMUM_GITHUB_CLI_VERSION };
|
|
164
|
-
export type { ClientResult, GitHubClient, RequestOptions, RunOptions, RunResult, Runner };
|
|
221
|
+
export { classifyGitHubFailure, createGitHubClient, MINIMUM_GITHUB_CLI_VERSION, parseIncludedResponse };
|
|
222
|
+
export type { ClientResult, GitHubClient, JsonResponse, RequestOptions, ResponseMetadata, RunOptions, RunResult, Runner };
|