@fitlab-ai/agent-infra 0.9.4 → 0.9.6
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +13 -3
- package/dist/lib/agent-clients/adapter.js +2 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +492 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +175 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +255 -0
- package/dist/lib/agent-clients/adapters/codex.js +3 -3
- package/dist/lib/git/workflow.js +0 -1
- package/dist/lib/git/worktree-identity.js +64 -0
- package/dist/lib/internal/codex-lifecycle.js +300 -0
- package/dist/lib/internal/release-workflow.js +108 -11
- package/dist/lib/internal/sandbox-control.js +43 -6
- package/dist/lib/internal/task-create.js +56 -0
- package/dist/lib/internal/task-orchestration.js +71 -31
- package/dist/lib/platform/issue-comments.js +2 -1
- package/dist/lib/release/workflow.js +29 -21
- package/dist/lib/sandbox/commands/create.js +16 -16
- package/dist/lib/sandbox/commands/prune.js +40 -16
- package/dist/lib/sandbox/commands/rm.js +318 -89
- package/dist/lib/sandbox/control/client.js +152 -22
- package/dist/lib/sandbox/control/executor.js +136 -0
- package/dist/lib/sandbox/control/lifecycle.js +348 -0
- package/dist/lib/sandbox/control/protocol.js +73 -18
- package/dist/lib/sandbox/control/server.js +235 -80
- package/dist/lib/sandbox/control/state.js +181 -0
- package/dist/lib/sandbox/index.js +2 -2
- package/dist/lib/sandbox/managed-fs.js +14 -2
- package/dist/lib/sandbox/mounts.js +35 -24
- package/dist/lib/sandbox/recovery.js +262 -45
- package/dist/lib/sandbox/workspace-view.js +80 -11
- package/dist/lib/sandbox/worktree-safety.js +196 -0
- package/dist/lib/server/process-control.js +6 -0
- package/dist/lib/task/activity-log.js +32 -5
- package/dist/lib/task/artifact-lifecycle.js +5 -4
- package/dist/lib/task/codex-orchestration.js +161 -0
- package/dist/lib/task/commands/status.js +3 -0
- package/dist/lib/task/commands/validate.js +289 -0
- package/dist/lib/task/commit-finalization.js +50 -12
- package/dist/lib/task/create-service.js +137 -0
- package/dist/lib/task/create.js +333 -0
- package/dist/lib/task/delegation-receipts.js +25 -0
- package/dist/lib/task/events.js +10 -4
- package/dist/lib/task/finalization-artifacts.js +2 -1
- package/dist/lib/task/implementation-inputs.js +1 -7
- package/dist/lib/task/index.js +9 -0
- package/dist/lib/task/orchestration.js +283 -18
- package/dist/lib/task/review-fingerprint.js +4 -0
- package/dist/lib/task/verification-engine.js +38 -6
- package/dist/lib/task/verification.js +1 -0
- package/dist/lib/task/workspace-snapshot.js +78 -3
- package/lib/agent-clients/adapter.ts +4 -4
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +569 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +323 -0
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +298 -0
- package/lib/agent-clients/adapters/codex.ts +3 -3
- package/lib/git/workflow.ts +0 -1
- package/lib/git/worktree-identity.ts +86 -0
- package/lib/internal/codex-lifecycle.ts +308 -0
- package/lib/internal/release-workflow.ts +107 -11
- package/lib/internal/sandbox-control.ts +38 -6
- package/lib/internal/task-create.ts +61 -0
- package/lib/internal/task-orchestration.ts +57 -31
- package/lib/platform/issue-comments.ts +2 -1
- package/lib/release/workflow.ts +69 -20
- package/lib/sandbox/commands/create.ts +18 -21
- package/lib/sandbox/commands/prune.ts +53 -15
- package/lib/sandbox/commands/rm.ts +368 -103
- package/lib/sandbox/control/client.ts +173 -25
- package/lib/sandbox/control/executor.ts +163 -0
- package/lib/sandbox/control/lifecycle.ts +342 -0
- package/lib/sandbox/control/protocol.ts +110 -48
- package/lib/sandbox/control/server.ts +252 -85
- package/lib/sandbox/control/state.ts +196 -0
- package/lib/sandbox/index.ts +2 -2
- package/lib/sandbox/managed-fs.ts +17 -2
- package/lib/sandbox/mounts.ts +31 -13
- package/lib/sandbox/recovery.ts +301 -60
- package/lib/sandbox/workspace-view.ts +89 -12
- package/lib/sandbox/worktree-safety.ts +234 -0
- package/lib/server/process-control.ts +11 -0
- package/lib/task/activity-log.ts +46 -7
- package/lib/task/artifact-lifecycle.ts +5 -4
- package/lib/task/codex-orchestration.ts +233 -0
- package/lib/task/commands/status.ts +2 -0
- package/lib/task/commands/validate.ts +282 -0
- package/lib/task/commit-finalization.ts +75 -12
- package/lib/task/create-service.ts +171 -0
- package/lib/task/create.ts +372 -0
- package/lib/task/delegation-receipts.ts +52 -3
- package/lib/task/events.ts +9 -4
- package/lib/task/finalization-artifacts.ts +2 -1
- package/lib/task/implementation-inputs.ts +1 -5
- package/lib/task/index.ts +8 -0
- package/lib/task/orchestration.ts +381 -22
- package/lib/task/review-fingerprint.ts +9 -0
- package/lib/task/verification-engine.ts +42 -8
- package/lib/task/verification.ts +2 -1
- package/lib/task/workspace-snapshot.ts +98 -4
- package/package.json +7 -7
- package/templates/.agents/hooks/lifecycle-delegation.js +88 -8
- package/templates/.agents/rules/lifecycle-orchestration.en.md +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +1 -1
- package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -0
- package/templates/.agents/rules/task-management.en.md +1 -0
- package/templates/.agents/rules/task-management.zh-CN.md +1 -0
- package/templates/.agents/skills/commit/SKILL.en.md +3 -9
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -10
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +4 -3
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +5 -4
- package/templates/.agents/skills/create-task/SKILL.en.md +26 -43
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +26 -43
- package/templates/.agents/skills/post-release/SKILL.en.md +32 -6
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +32 -6
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +42 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +42 -0
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +14 -0
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +14 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +44 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +44 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +3 -3
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +3 -3
- package/templates/.claude/commands/run-manual-validation.en.md +9 -0
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +9 -0
- package/templates/.codex/hooks.json +50 -0
- package/templates/.opencode/commands/run-manual-validation.en.md +11 -0
- package/templates/.opencode/commands/run-manual-validation.zh-CN.md +11 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
|
|
2
|
-
import { abortCommitIntent, activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, beginCommitIntent, beginOrResumeOrchestration, checkpointCommitIntent, completeCommitIntent, pauseOrchestration,
|
|
2
|
+
import { abortCommitIntent, activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, beginCommitIntent, beginOrResumeOrchestration, checkpointCommitIntent, completeCommitIntent, pauseOrchestration, recoverCommitIntent, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, startCommitAttempt, statusCommitIntent, statusOrchestration, terminateCommitAttempt } from '../task/orchestration.js';
|
|
3
|
+
import { prepareCodexOrchestrationDelegation } from '../task/codex-orchestration.js';
|
|
3
4
|
import { isAgentClientId } from '../agent-clients/types.js';
|
|
4
|
-
const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|hook-start|hook-stop|advance|pause|status|commit-begin|commit-checkpoint|commit-complete|commit-recover|commit-abort|commit-status> [options]\n';
|
|
5
|
+
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';
|
|
5
6
|
function usageFailure(message) {
|
|
6
7
|
process.stdout.write(`${JSON.stringify({
|
|
7
8
|
status: 'failed', changed: false,
|
|
@@ -10,7 +11,7 @@ function usageFailure(message) {
|
|
|
10
11
|
process.stderr.write(USAGE);
|
|
11
12
|
process.exitCode = 2;
|
|
12
13
|
}
|
|
13
|
-
function taskOrchestration(args = []) {
|
|
14
|
+
async function taskOrchestration(args = []) {
|
|
14
15
|
if (args[0] === '--help' || args[0] === '-h') {
|
|
15
16
|
process.stdout.write(USAGE);
|
|
16
17
|
return;
|
|
@@ -22,7 +23,7 @@ function taskOrchestration(args = []) {
|
|
|
22
23
|
const [taskRef, intent] = args;
|
|
23
24
|
if (![
|
|
24
25
|
'begin-or-resume', 'route', 'prepare', 'hook-start', 'hook-stop', 'advance', 'pause', 'status',
|
|
25
|
-
'commit-begin', 'commit-checkpoint', 'commit-complete', 'commit-recover', 'commit-abort', 'commit-status'
|
|
26
|
+
'commit-start', 'commit-begin', 'commit-checkpoint', 'commit-complete', 'commit-recover', 'commit-abort', 'commit-terminate', 'commit-status'
|
|
26
27
|
].includes(intent)) {
|
|
27
28
|
usageFailure(`unknown intent '${intent}'`);
|
|
28
29
|
return;
|
|
@@ -48,7 +49,8 @@ function taskOrchestration(args = []) {
|
|
|
48
49
|
'--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
|
|
49
50
|
'--model-fallback-reason', '--reasoning-effort-fallback-reason',
|
|
50
51
|
'--exit-code', '--after-fingerprint', '--changed-paths', '--code', '--message', '--recoverable', '--agent',
|
|
51
|
-
'--baseline-head', '--token', '--kind', '--head', '--remote', '--ref', '--expected-head'
|
|
52
|
+
'--baseline-head', '--token', '--kind', '--head', '--remote', '--ref', '--expected-head',
|
|
53
|
+
'--attempt', '--git-worktree-root'
|
|
52
54
|
].includes(flag)) {
|
|
53
55
|
usageFailure(`unknown option '${flag}'`);
|
|
54
56
|
return;
|
|
@@ -66,12 +68,14 @@ function taskOrchestration(args = []) {
|
|
|
66
68
|
values[flag] = value;
|
|
67
69
|
}
|
|
68
70
|
const commitIntentFlags = {
|
|
69
|
-
'commit-
|
|
70
|
-
'commit-
|
|
71
|
-
'commit-
|
|
72
|
-
'commit-
|
|
73
|
-
'commit-
|
|
74
|
-
'commit-
|
|
71
|
+
'commit-start': ['--agent', '--git-worktree-root'],
|
|
72
|
+
'commit-begin': ['--agent', '--baseline-head', '--attempt', '--orchestrated', '--git-worktree-root'],
|
|
73
|
+
'commit-checkpoint': ['--token', '--kind', '--head', '--remote', '--ref', '--git-worktree-root'],
|
|
74
|
+
'commit-complete': ['--token', '--agent', '--git-worktree-root'],
|
|
75
|
+
'commit-recover': ['--agent', '--git-worktree-root'],
|
|
76
|
+
'commit-abort': ['--token', '--expected-head', '--git-worktree-root'],
|
|
77
|
+
'commit-terminate': ['--attempt', '--agent', '--code', '--git-worktree-root'],
|
|
78
|
+
'commit-status': ['--git-worktree-root']
|
|
75
79
|
};
|
|
76
80
|
if (intent in commitIntentFlags) {
|
|
77
81
|
const allowed = commitIntentFlags[intent];
|
|
@@ -83,7 +87,7 @@ function taskOrchestration(args = []) {
|
|
|
83
87
|
}
|
|
84
88
|
else {
|
|
85
89
|
const commitOnly = [...seen].find((flag) => [
|
|
86
|
-
'--orchestrated', '--baseline-head', '--token', '--kind', '--head', '--remote', '--ref', '--expected-head'
|
|
90
|
+
'--orchestrated', '--baseline-head', '--attempt', '--token', '--kind', '--head', '--remote', '--ref', '--expected-head'
|
|
87
91
|
].includes(flag));
|
|
88
92
|
if (commitOnly) {
|
|
89
93
|
usageFailure(`intent '${intent}' does not accept '${commitOnly}'`);
|
|
@@ -91,13 +95,29 @@ function taskOrchestration(args = []) {
|
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
const requireValues = (flags) => flags.find((flag) => values[flag] === undefined);
|
|
98
|
+
const coreOptions = values['--git-worktree-root'] === undefined
|
|
99
|
+
? {}
|
|
100
|
+
: { gitWorktreeRoot: values['--git-worktree-root'] };
|
|
94
101
|
if (values['--client'] !== undefined && !isAgentClientId(values['--client'])) {
|
|
95
102
|
usageFailure(`unknown client '${values['--client']}'`);
|
|
96
103
|
return;
|
|
97
104
|
}
|
|
98
105
|
let result;
|
|
99
|
-
if (intent === 'commit-
|
|
100
|
-
const missing = requireValues(['--agent'
|
|
106
|
+
if (intent === 'commit-start') {
|
|
107
|
+
const missing = requireValues(['--agent']);
|
|
108
|
+
if (missing) {
|
|
109
|
+
usageFailure(`intent 'commit-start' requires '${missing}'`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const agent = normalizeAgentToken(values['--agent']);
|
|
113
|
+
if (!agent) {
|
|
114
|
+
usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
result = startCommitAttempt(taskRef, { agent }, coreOptions);
|
|
118
|
+
}
|
|
119
|
+
else if (intent === 'commit-begin') {
|
|
120
|
+
const missing = requireValues(['--agent', '--baseline-head', '--attempt']);
|
|
101
121
|
if (missing) {
|
|
102
122
|
usageFailure(`intent 'commit-begin' requires '${missing}'`);
|
|
103
123
|
return;
|
|
@@ -108,8 +128,8 @@ function taskOrchestration(args = []) {
|
|
|
108
128
|
return;
|
|
109
129
|
}
|
|
110
130
|
result = beginCommitIntent(taskRef, {
|
|
111
|
-
agent, orchestrated, baselineHead: values['--baseline-head']
|
|
112
|
-
});
|
|
131
|
+
agent, orchestrated, baselineHead: values['--baseline-head'], attempt: values['--attempt']
|
|
132
|
+
}, coreOptions);
|
|
113
133
|
}
|
|
114
134
|
else if (intent === 'commit-checkpoint') {
|
|
115
135
|
const missing = requireValues(['--token', '--kind', '--head']);
|
|
@@ -135,7 +155,7 @@ function taskOrchestration(args = []) {
|
|
|
135
155
|
result = checkpointCommitIntent(taskRef, {
|
|
136
156
|
token: values['--token'], kind: values['--kind'],
|
|
137
157
|
head: values['--head'], remote: values['--remote'], ref: values['--ref']
|
|
138
|
-
});
|
|
158
|
+
}, coreOptions);
|
|
139
159
|
}
|
|
140
160
|
else if (intent === 'commit-complete') {
|
|
141
161
|
const missing = requireValues(['--token', '--agent']);
|
|
@@ -148,7 +168,7 @@ function taskOrchestration(args = []) {
|
|
|
148
168
|
usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`);
|
|
149
169
|
return;
|
|
150
170
|
}
|
|
151
|
-
result = completeCommitIntent(taskRef, { token: values['--token'], agent });
|
|
171
|
+
result = completeCommitIntent(taskRef, { token: values['--token'], agent }, coreOptions);
|
|
152
172
|
}
|
|
153
173
|
else if (intent === 'commit-recover') {
|
|
154
174
|
const missing = requireValues(['--agent']);
|
|
@@ -161,7 +181,7 @@ function taskOrchestration(args = []) {
|
|
|
161
181
|
usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`);
|
|
162
182
|
return;
|
|
163
183
|
}
|
|
164
|
-
result = recoverCommitIntent(taskRef, { agent });
|
|
184
|
+
result = recoverCommitIntent(taskRef, { agent }, coreOptions);
|
|
165
185
|
}
|
|
166
186
|
else if (intent === 'commit-abort') {
|
|
167
187
|
const missing = requireValues(['--token', '--expected-head']);
|
|
@@ -169,10 +189,29 @@ function taskOrchestration(args = []) {
|
|
|
169
189
|
usageFailure(`intent 'commit-abort' requires '${missing}'`);
|
|
170
190
|
return;
|
|
171
191
|
}
|
|
172
|
-
result = abortCommitIntent(taskRef, { token: values['--token'], expectedHead: values['--expected-head'] });
|
|
192
|
+
result = abortCommitIntent(taskRef, { token: values['--token'], expectedHead: values['--expected-head'] }, coreOptions);
|
|
193
|
+
}
|
|
194
|
+
else if (intent === 'commit-terminate') {
|
|
195
|
+
const missing = requireValues(['--attempt', '--agent', '--code']);
|
|
196
|
+
if (missing) {
|
|
197
|
+
usageFailure(`intent 'commit-terminate' requires '${missing}'`);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const agent = normalizeAgentToken(values['--agent']);
|
|
201
|
+
if (!agent) {
|
|
202
|
+
usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (!/^[A-Z][A-Z0-9_]{2,63}$/.test(values['--code'])) {
|
|
206
|
+
usageFailure('--code must be a stable uppercase identifier');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
result = terminateCommitAttempt(taskRef, {
|
|
210
|
+
attempt: values['--attempt'], agent, code: values['--code']
|
|
211
|
+
}, coreOptions);
|
|
173
212
|
}
|
|
174
213
|
else if (intent === 'commit-status') {
|
|
175
|
-
result = statusCommitIntent(taskRef);
|
|
214
|
+
result = statusCommitIntent(taskRef, coreOptions);
|
|
176
215
|
}
|
|
177
216
|
else if (intent === 'begin-or-resume') {
|
|
178
217
|
const missing = requireValues(['--client']);
|
|
@@ -195,6 +234,7 @@ function taskOrchestration(args = []) {
|
|
|
195
234
|
return;
|
|
196
235
|
}
|
|
197
236
|
result = beginOrResumeOrchestration(taskRef, {
|
|
237
|
+
...coreOptions,
|
|
198
238
|
maxSteps,
|
|
199
239
|
client: values['--client'],
|
|
200
240
|
modelPolicy: hasAnyPolicy ? {
|
|
@@ -210,10 +250,10 @@ function taskOrchestration(args = []) {
|
|
|
210
250
|
});
|
|
211
251
|
}
|
|
212
252
|
else if (intent === 'route') {
|
|
213
|
-
result = routeOrchestration(taskRef);
|
|
253
|
+
result = routeOrchestration(taskRef, coreOptions);
|
|
214
254
|
}
|
|
215
255
|
else if (intent === 'status') {
|
|
216
|
-
result = statusOrchestration(taskRef);
|
|
256
|
+
result = statusOrchestration(taskRef, coreOptions);
|
|
217
257
|
}
|
|
218
258
|
else if (intent === 'prepare') {
|
|
219
259
|
const missing = requireValues(['--client']);
|
|
@@ -221,11 +261,11 @@ function taskOrchestration(args = []) {
|
|
|
221
261
|
usageFailure(`intent 'prepare' requires '${missing}'`);
|
|
222
262
|
return;
|
|
223
263
|
}
|
|
224
|
-
result =
|
|
264
|
+
result = await prepareCodexOrchestrationDelegation(taskRef, {
|
|
225
265
|
client: values['--client'],
|
|
226
266
|
requestedModel: values['--requested-model'],
|
|
227
267
|
requestedReasoningEffort: values['--requested-reasoning-effort']
|
|
228
|
-
});
|
|
268
|
+
}, { orchestrationOptions: coreOptions });
|
|
229
269
|
}
|
|
230
270
|
else if (intent === 'hook-start') {
|
|
231
271
|
const missing = requireValues([
|
|
@@ -245,8 +285,8 @@ function taskOrchestration(args = []) {
|
|
|
245
285
|
reasoningEffortFallbackReason: values['--reasoning-effort-fallback-reason']
|
|
246
286
|
};
|
|
247
287
|
result = taskRef === 'auto'
|
|
248
|
-
? activateMatchingOrchestrationDelegation(values['--client'], event)
|
|
249
|
-
: activateOrchestrationDelegation(taskRef, event);
|
|
288
|
+
? activateMatchingOrchestrationDelegation(values['--client'], event, coreOptions)
|
|
289
|
+
: activateOrchestrationDelegation(taskRef, event, coreOptions);
|
|
250
290
|
}
|
|
251
291
|
else if (intent === 'hook-stop') {
|
|
252
292
|
if (taskRef === 'auto') {
|
|
@@ -257,7 +297,7 @@ function taskOrchestration(args = []) {
|
|
|
257
297
|
}
|
|
258
298
|
result = sealMatchingOrchestrationDelegation(values['--client'], {
|
|
259
299
|
nativeAgent: values['--native-agent'], childId: values['--child-id']
|
|
260
|
-
});
|
|
300
|
+
}, coreOptions);
|
|
261
301
|
}
|
|
262
302
|
else {
|
|
263
303
|
const missing = requireValues(['--child-id', '--exit-code', '--after-fingerprint']);
|
|
@@ -274,11 +314,11 @@ function taskOrchestration(args = []) {
|
|
|
274
314
|
childId: values['--child-id'], exitCode,
|
|
275
315
|
afterFingerprint: values['--after-fingerprint'],
|
|
276
316
|
changedPaths: values['--changed-paths'] ? values['--changed-paths'].split(',').filter(Boolean) : []
|
|
277
|
-
});
|
|
317
|
+
}, coreOptions);
|
|
278
318
|
}
|
|
279
319
|
}
|
|
280
320
|
else if (intent === 'advance') {
|
|
281
|
-
result = advanceOrchestration(taskRef);
|
|
321
|
+
result = advanceOrchestration(taskRef, coreOptions);
|
|
282
322
|
}
|
|
283
323
|
else {
|
|
284
324
|
const missing = requireValues(['--code', '--message', '--recoverable']);
|
|
@@ -290,7 +330,7 @@ function taskOrchestration(args = []) {
|
|
|
290
330
|
usageFailure('--recoverable must be true or false');
|
|
291
331
|
return;
|
|
292
332
|
}
|
|
293
|
-
result = pauseOrchestration(taskRef, values['--code'], values['--message'], values['--recoverable'] === 'true');
|
|
333
|
+
result = pauseOrchestration(taskRef, values['--code'], values['--message'], values['--recoverable'] === 'true', coreOptions);
|
|
294
334
|
}
|
|
295
335
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
296
336
|
if (result.status === 'failed')
|
|
@@ -20,6 +20,7 @@ const ARTIFACT_TITLES = {
|
|
|
20
20
|
code: '实现报告',
|
|
21
21
|
'review-code': '代码审查',
|
|
22
22
|
'manual-validation': '人工验证报告',
|
|
23
|
+
'validation-run': '验证运行证据',
|
|
23
24
|
'pr-review': 'PR 审查报告'
|
|
24
25
|
};
|
|
25
26
|
function normalizeCommentContent(content) {
|
|
@@ -52,7 +53,7 @@ function renderTaskComment(content, taskId, agent) {
|
|
|
52
53
|
}
|
|
53
54
|
function artifactIdentity(artifact) {
|
|
54
55
|
const stem = path.basename(artifact, '.md');
|
|
55
|
-
const match = stem.match(/^(analysis|review-analysis|plan|review-plan|code|review-code|manual-validation|pr-review)(?:-r(\d+))?$/);
|
|
56
|
+
const match = stem.match(/^(analysis|review-analysis|plan|review-plan|code|review-code|manual-validation|validation-run|pr-review)(?:-r(\d+))?$/);
|
|
56
57
|
if (!match)
|
|
57
58
|
throw new Error(`unsupported artifact '${artifact}'`);
|
|
58
59
|
const base = ARTIFACT_TITLES[match[1]];
|
|
@@ -1,34 +1,42 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
function byteSort(values) {
|
|
3
|
+
return [...values].sort((left, right) => Buffer.from(left).compare(Buffer.from(right)));
|
|
4
|
+
}
|
|
1
5
|
function deriveReleasePhase(facts) {
|
|
2
6
|
if (!facts.localTag && !facts.localTagAncestor)
|
|
3
7
|
return 'unprepared';
|
|
8
|
+
if (facts.post.commit)
|
|
9
|
+
return facts.post.published ? 'complete' : 'post-prepared';
|
|
4
10
|
if (!facts.remoteBranch && !facts.remoteTag)
|
|
5
11
|
return 'prepared';
|
|
6
12
|
if (!facts.remoteBranch || !facts.remoteTag)
|
|
7
13
|
return 'partially-published';
|
|
8
14
|
if (facts.githubRelease !== true || facts.npm !== true || facts.homebrew !== true)
|
|
9
15
|
return 'published';
|
|
10
|
-
|
|
11
|
-
return 'post-pending';
|
|
12
|
-
return 'complete';
|
|
16
|
+
return 'post-pending';
|
|
13
17
|
}
|
|
14
|
-
function
|
|
15
|
-
|
|
18
|
+
function createPostConfirmation(version, post) {
|
|
19
|
+
if (!post.commit || !post.isHead || post.worktree.length || post.staged.length)
|
|
20
|
+
return null;
|
|
21
|
+
const canonical = {
|
|
22
|
+
version,
|
|
23
|
+
commit: post.commit,
|
|
24
|
+
branch: post.branch,
|
|
25
|
+
upstream: post.upstream,
|
|
26
|
+
remoteHead: post.remoteHead,
|
|
27
|
+
newVersion: post.newVersion,
|
|
28
|
+
changedPaths: byteSort(post.changedPaths),
|
|
29
|
+
demoInputSha256: post.demoInputSha256,
|
|
30
|
+
worktree: byteSort(post.worktree),
|
|
31
|
+
staged: byteSort(post.staged)
|
|
32
|
+
};
|
|
33
|
+
const sha256 = `sha256:${crypto.createHash('sha256').update(JSON.stringify(canonical), 'utf8').digest('hex')}`;
|
|
34
|
+
return { ...canonical, sha256 };
|
|
16
35
|
}
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (before.facts.localTagConflict)
|
|
22
|
-
return { status: 'failed', changed: false, before, snapshot: before, error: { code: 'GIT_TAG_CONFLICT', message: `Tag ${before.tag} is not reachable from HEAD` } };
|
|
23
|
-
const allowed = input.action === 'prepare' ? before.phase === 'unprepared'
|
|
24
|
-
: input.action === 'publish' ? before.facts.localTag && ['prepared', 'partially-published'].includes(before.phase)
|
|
25
|
-
: ['published', 'post-pending'].includes(before.phase);
|
|
26
|
-
if (!allowed)
|
|
27
|
-
return { status: before.phase === 'complete' ? 'no-op' : 'failed', changed: false, before, snapshot: before, error: before.phase === 'complete' ? null : { code: 'RELEASE_PHASE_INVALID', message: `Cannot ${input.action} from ${before.phase}` } };
|
|
28
|
-
await input[input.action]?.();
|
|
29
|
-
const snapshot = releaseSnapshot(input.version, await input.inspect());
|
|
30
|
-
const progressed = snapshot.phase !== before.phase;
|
|
31
|
-
return { status: progressed ? 'applied' : 'blocked', changed: progressed, before, snapshot, error: progressed ? null : { code: 'RELEASE_PROGRESS_PENDING', message: `${input.action} has not reached the next observable phase` } };
|
|
36
|
+
function releaseSnapshot(version, facts) {
|
|
37
|
+
const snapshot = { version, tag: `v${version.replace(/^v/, '')}`, phase: deriveReleasePhase(facts), facts };
|
|
38
|
+
const postConfirmation = createPostConfirmation(version, facts.post);
|
|
39
|
+
return postConfirmation ? { ...snapshot, postConfirmation } : snapshot;
|
|
32
40
|
}
|
|
33
|
-
export { deriveReleasePhase, releaseSnapshot
|
|
41
|
+
export { createPostConfirmation, deriveReleasePhase, releaseSnapshot };
|
|
34
42
|
//# sourceMappingURL=workflow.js.map
|
|
@@ -2,9 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
|
-
import { execFileSync
|
|
5
|
+
import { execFileSync } from 'node:child_process';
|
|
6
6
|
import { parseArgs } from 'node:util';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
8
7
|
import * as p from '@clack/prompts';
|
|
9
8
|
import pc from 'picocolors';
|
|
10
9
|
import { listAgentClientAdapters } from '../../agent-clients/registry.js';
|
|
@@ -17,10 +16,10 @@ import { commandForEngine, execEngine, run, runEngine, runOk, runOkEngine, runSa
|
|
|
17
16
|
import { parseSandboxWorkspaceIdentity, resolveSandboxTarget, sameSandboxWorkspaceIdentity } from '../workspace-identity.js';
|
|
18
17
|
import { createSandboxCapabilityPlan, runSandboxHooks, runBoundedSandboxHookCommand } from '../agent-client-reconciler.js';
|
|
19
18
|
import { resolveTools, tmpfsSeedStagingPath, tmpfsSeedTargetPath, toolConfigDirCandidates } from '../tools.js';
|
|
20
|
-
import { assertFreshSandboxReady, hydrateTmpfsSeedEntries, prepareTmpfsMounts } from '../recovery.js';
|
|
19
|
+
import { assertFreshSandboxReady, hydrateTmpfsSeedEntries, prepareTmpfsMounts, startSandboxControlBroker } from '../recovery.js';
|
|
21
20
|
import { hostJoin, toEnginePath, volumeArg } from '../engines/wsl2-paths.js';
|
|
22
21
|
import { sandboxCoreBindMounts } from '../mounts.js';
|
|
23
|
-
import { assertSandboxTaskSource, materializeSandboxControl, materializeSandboxWorkspaceView } from '../workspace-view.js';
|
|
22
|
+
import { assertSandboxTaskSource, materializeSandboxControl, materializeSandboxWorkspaceView, prepareSandboxWorkspaceMountTargets } from '../workspace-view.js';
|
|
24
23
|
import { clipboardHostDir, CONTAINER_CLIPBOARD_MOUNT } from '../clipboard/paths.js';
|
|
25
24
|
import { validateSelinuxDisableEnv } from '../engines/selinux.js';
|
|
26
25
|
import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.js';
|
|
@@ -65,11 +64,6 @@ const MANAGED_GITHUB_GIT_CONFIG = [
|
|
|
65
64
|
'[url "https://github.com/"]',
|
|
66
65
|
'\tinsteadOf = git@github.com:'
|
|
67
66
|
];
|
|
68
|
-
function internalCliPath() {
|
|
69
|
-
const directory = path.dirname(fileURLToPath(import.meta.url));
|
|
70
|
-
const extension = path.extname(fileURLToPath(import.meta.url));
|
|
71
|
-
return path.resolve(directory, '..', '..', '..', 'bin', `internal-cli${extension}`);
|
|
72
|
-
}
|
|
73
67
|
function resolveToolDirs(config, tools, branch) {
|
|
74
68
|
return tools.map((tool) => {
|
|
75
69
|
const candidates = toolConfigDirCandidates(tool, config.project, branch);
|
|
@@ -895,8 +889,12 @@ export async function create(args) {
|
|
|
895
889
|
const requestedDescription = target.workspace.mode === 'task-bound'
|
|
896
890
|
? `task-bound:${target.workspace.taskId}`
|
|
897
891
|
: target.workspace.mode;
|
|
892
|
+
const recoveryTarget = target.workspace.mode === 'task-bound'
|
|
893
|
+
? target.workspace.taskId
|
|
894
|
+
: branch;
|
|
898
895
|
throw new Error(`SANDBOX_WORKSPACE_IDENTITY_CONFLICT: container '${name}' is ${existingDescription}, `
|
|
899
|
-
+ `but this request is ${requestedDescription}.
|
|
896
|
+
+ `but this request is ${requestedDescription}. `
|
|
897
|
+
+ `Run 'ai sandbox start --recreate ${recoveryTarget}' on the host, then retry.`);
|
|
900
898
|
}
|
|
901
899
|
}
|
|
902
900
|
message('Removing old container instance...');
|
|
@@ -962,9 +960,11 @@ export async function create(args) {
|
|
|
962
960
|
container,
|
|
963
961
|
identity: target.workspace
|
|
964
962
|
});
|
|
963
|
+
prepareSandboxWorkspaceMountTargets(worktree);
|
|
965
964
|
const control = materializeSandboxControl({
|
|
966
965
|
base: effectiveConfig.controlBase,
|
|
967
966
|
repoRoot: effectiveConfig.repoRoot,
|
|
967
|
+
worktreeRoot: worktree,
|
|
968
968
|
project: effectiveConfig.project,
|
|
969
969
|
container,
|
|
970
970
|
branch,
|
|
@@ -981,6 +981,7 @@ export async function create(args) {
|
|
|
981
981
|
shellConfigHostDir: hostShellConfig.hostDir,
|
|
982
982
|
workspaceViewRoot: workspaceView.root,
|
|
983
983
|
controlDir: control.channelDir,
|
|
984
|
+
controlStatusDir: control.statusDir,
|
|
984
985
|
...(target.workspace.mode === 'task-bound'
|
|
985
986
|
? {
|
|
986
987
|
taskSource: assertSandboxTaskSource(effectiveConfig.repoRoot, target.workspace.taskId),
|
|
@@ -1051,7 +1052,11 @@ export async function create(args) {
|
|
|
1051
1052
|
'-e',
|
|
1052
1053
|
`AGENT_INFRA_CONTROL_TOKEN=${control.token}`,
|
|
1053
1054
|
'-e',
|
|
1055
|
+
`AGENT_INFRA_CONTROL_GENERATION=${control.generation}`,
|
|
1056
|
+
'-e',
|
|
1054
1057
|
'AGENT_INFRA_CONTROL_DIR=/run/agent-infra/control',
|
|
1058
|
+
'-e',
|
|
1059
|
+
'AGENT_INFRA_CONTROL_STATUS_DIR=/run/agent-infra/control-status',
|
|
1055
1060
|
'--label',
|
|
1056
1061
|
`${sandboxRuntimeCapabilityLabel(effectiveConfig)}=${capabilityPlan.runtimeSignature}`,
|
|
1057
1062
|
...coreVolumes,
|
|
@@ -1069,12 +1074,7 @@ export async function create(args) {
|
|
|
1069
1074
|
'/workspace',
|
|
1070
1075
|
effectiveConfig.imageName
|
|
1071
1076
|
]);
|
|
1072
|
-
|
|
1073
|
-
broker.once('error', () => {
|
|
1074
|
-
// Fresh readiness verification below remains the source of truth;
|
|
1075
|
-
// consume detached spawn errors so they cannot crash the command.
|
|
1076
|
-
});
|
|
1077
|
-
broker.unref();
|
|
1077
|
+
await startSandboxControlBroker(effectiveConfig.repoRoot, control.manifestPath);
|
|
1078
1078
|
createdTmpfsSeedPlan = tmpfsSeedPlan;
|
|
1079
1079
|
}
|
|
1080
1080
|
finally {
|
|
@@ -5,12 +5,13 @@ import * as p from '@clack/prompts';
|
|
|
5
5
|
import pc from 'picocolors';
|
|
6
6
|
import { loadConfig } from '../config.js';
|
|
7
7
|
import { safeNameCandidates, sandboxBranchLabel, sandboxLabel } from '../constants.js';
|
|
8
|
-
import { detectEngine } from '../engine.js';
|
|
8
|
+
import { detectEngine, ENGINES } from '../engine.js';
|
|
9
9
|
import { hostJoin } from '../engines/wsl2-paths.js';
|
|
10
10
|
import { removeManagedDir, removeWorktreeDir } from '../managed-fs.js';
|
|
11
11
|
import { parseLabels } from './ls.js';
|
|
12
12
|
import { runEngine, runSafe } from '../shell.js';
|
|
13
13
|
import { createSandboxCapabilityPlan } from '../agent-client-reconciler.js';
|
|
14
|
+
import { createCleanPermit, formatWorktreeSnapshot, inspectWorktrees } from '../worktree-safety.js';
|
|
14
15
|
const USAGE = `Usage: ai sandbox prune [--dry-run]`;
|
|
15
16
|
function listChildDirs(base) {
|
|
16
17
|
if (!fs.existsSync(base)) {
|
|
@@ -83,12 +84,15 @@ export function collectOrphanGroups(config, tools, activeBranches) {
|
|
|
83
84
|
}
|
|
84
85
|
return groups;
|
|
85
86
|
}
|
|
86
|
-
export function removeOrphanGroups(config, groups) {
|
|
87
|
+
export function removeOrphanGroups(config, groups, permits, allowRegisteredPathFallback = false) {
|
|
87
88
|
let removedWorktrees = false;
|
|
88
89
|
for (const group of groups) {
|
|
89
90
|
for (const dir of group.dirs) {
|
|
90
91
|
if (group.kind === 'worktree') {
|
|
91
|
-
|
|
92
|
+
const permit = permits.get(path.resolve(dir));
|
|
93
|
+
if (!permit)
|
|
94
|
+
throw new Error(`Missing worktree removal permit: ${dir}`);
|
|
95
|
+
removeWorktreeDir(config.repoRoot, group.base, dir, permit, { allowRegisteredPathFallback });
|
|
92
96
|
removedWorktrees = true;
|
|
93
97
|
}
|
|
94
98
|
else {
|
|
@@ -115,7 +119,7 @@ function writeGroups(groups) {
|
|
|
115
119
|
}
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
|
-
export async function prune(args) {
|
|
122
|
+
export async function prune(args, dependencies = {}) {
|
|
119
123
|
const { values } = parseArgs({
|
|
120
124
|
args,
|
|
121
125
|
strict: true,
|
|
@@ -128,9 +132,9 @@ export async function prune(args) {
|
|
|
128
132
|
process.stdout.write(`${USAGE}\n`);
|
|
129
133
|
return;
|
|
130
134
|
}
|
|
131
|
-
const config = loadConfig();
|
|
132
|
-
const tools = [...createSandboxCapabilityPlan(config).cleanupInventory];
|
|
133
|
-
const engine = detectEngine(config);
|
|
135
|
+
const config = dependencies.config ?? loadConfig();
|
|
136
|
+
const tools = dependencies.tools ?? [...createSandboxCapabilityPlan(config).cleanupInventory];
|
|
137
|
+
const engine = dependencies.engine ?? detectEngine(config);
|
|
134
138
|
const psArgs = [
|
|
135
139
|
'ps',
|
|
136
140
|
'-a',
|
|
@@ -139,15 +143,27 @@ export async function prune(args) {
|
|
|
139
143
|
'--format',
|
|
140
144
|
'{{.Labels}}'
|
|
141
145
|
];
|
|
142
|
-
let labelsOutput;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
let labelsOutput = dependencies.labelsOutput;
|
|
147
|
+
if (labelsOutput === undefined) {
|
|
148
|
+
try {
|
|
149
|
+
labelsOutput = runEngine(engine, 'docker', psArgs);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
throw new Error('Unable to determine active sandbox branches: docker ps failed');
|
|
153
|
+
}
|
|
148
154
|
}
|
|
155
|
+
const confirm = dependencies.confirm ?? p.confirm;
|
|
156
|
+
const isCancel = dependencies.isCancel ?? p.isCancel;
|
|
149
157
|
const groups = collectOrphanGroups(config, tools, activeBranchesFromLabels(config, labelsOutput));
|
|
150
158
|
const count = orphanCount(groups);
|
|
159
|
+
const worktrees = groups.filter((group) => group.kind === 'worktree').flatMap((group) => group.dirs);
|
|
160
|
+
const inspections = inspectWorktrees(worktrees);
|
|
161
|
+
const blockers = inspections.filter((inspection) => inspection.status !== 'clean');
|
|
162
|
+
const permits = new Map();
|
|
163
|
+
for (const inspection of inspections) {
|
|
164
|
+
if (inspection.status === 'clean')
|
|
165
|
+
permits.set(inspection.snapshot.worktree, createCleanPermit(inspection.snapshot));
|
|
166
|
+
}
|
|
151
167
|
p.intro(pc.cyan(`Pruning orphaned sandbox state for ${config.project}`));
|
|
152
168
|
if (count === 0) {
|
|
153
169
|
p.log.success('No orphaned sandbox state dirs found');
|
|
@@ -155,19 +171,27 @@ export async function prune(args) {
|
|
|
155
171
|
return;
|
|
156
172
|
}
|
|
157
173
|
writeGroups(groups);
|
|
174
|
+
for (const blocker of blockers) {
|
|
175
|
+
p.log.error(blocker.status === 'failed'
|
|
176
|
+
? `${JSON.stringify(blocker.worktree)}: ${blocker.message}`
|
|
177
|
+
: formatWorktreeSnapshot(blocker.snapshot));
|
|
178
|
+
}
|
|
158
179
|
if (values['dry-run']) {
|
|
159
180
|
p.outro(pc.green('Dry run complete'));
|
|
160
181
|
return;
|
|
161
182
|
}
|
|
162
|
-
|
|
183
|
+
if (blockers.length > 0) {
|
|
184
|
+
throw new Error('Refusing to prune because worktree preflight found blocker(s); no orphan state was deleted');
|
|
185
|
+
}
|
|
186
|
+
const shouldRemove = await confirm({
|
|
163
187
|
message: `Remove ${count} orphaned sandbox state dirs?`,
|
|
164
188
|
initialValue: true
|
|
165
189
|
});
|
|
166
|
-
if (
|
|
190
|
+
if (isCancel(shouldRemove) || !shouldRemove) {
|
|
167
191
|
p.outro('Cancelled');
|
|
168
192
|
return;
|
|
169
193
|
}
|
|
170
|
-
const removedWorktrees = removeOrphanGroups(config, groups);
|
|
194
|
+
const removedWorktrees = removeOrphanGroups(config, groups, permits, engine === ENGINES.WSL2);
|
|
171
195
|
if (removedWorktrees) {
|
|
172
196
|
runSafe('git', ['-C', config.repoRoot, 'worktree', 'prune']);
|
|
173
197
|
}
|