@fitlab-ai/agent-infra 0.11.3 → 0.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -8
- package/README.zh-CN.md +17 -8
- package/dist/bin/internal-cli.js +1 -6
- package/dist/lib/decide.js +4 -41
- package/dist/lib/git/branch-worktree.js +45 -0
- package/dist/lib/internal/cli-route-inventory.js +1 -4
- package/dist/lib/internal/task-activity.js +7 -40
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-event.js +4 -37
- package/dist/lib/internal/task-finalization.js +48 -1
- package/dist/lib/internal/task-ledger.js +14 -44
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +2 -4
- package/dist/lib/internal/task-review.js +5 -37
- package/dist/lib/internal/task-verify.js +3 -47
- package/dist/lib/internal/task-warning.js +4 -40
- package/dist/lib/platform/completion-backfill.js +63 -17
- package/dist/lib/platform/pr-summary.js +4 -23
- package/dist/lib/platform/verification-sync.js +12 -49
- package/dist/lib/sandbox/control/client.js +4 -1
- package/dist/lib/sandbox/control/executor.js +12 -4
- package/dist/lib/sandbox/control/protocol.js +2 -1
- package/dist/lib/sandbox/control/server.js +26 -143
- package/dist/lib/sandbox/removal.js +15 -8
- package/dist/lib/sandbox/task-cleanup.js +13 -20
- package/dist/lib/task/activity-intent.js +1 -2
- package/dist/lib/task/artifact-command.js +11 -4
- package/dist/lib/task/artifact-lifecycle.js +217 -85
- package/dist/lib/task/artifact-operations.js +37 -28
- package/dist/lib/task/artifact-receipts.js +4 -1
- package/dist/lib/task/artifact-schema.js +8 -7
- package/dist/lib/task/artifact-selection.js +25 -0
- package/dist/lib/task/capabilities.js +158 -39
- package/dist/lib/task/commands/status.js +9 -2
- package/dist/lib/task/control-authority.js +12 -168
- package/dist/lib/task/decision-intents.js +14 -4
- package/dist/lib/task/delivery.js +1 -43
- package/dist/lib/task/events.js +68 -73
- package/dist/lib/task/finalization-handoff.js +92 -0
- package/dist/lib/task/finalization.js +423 -154
- package/dist/lib/task/intermediate-cleanup.js +9 -148
- package/dist/lib/task/ledger-intents.js +159 -20
- package/dist/lib/task/lifecycle-path.js +75 -0
- package/dist/lib/task/lifecycle.js +60 -11
- package/dist/lib/task/local-artifact-finalization.js +13 -47
- package/dist/lib/task/orchestration.js +17 -7
- package/dist/lib/task/review-command.js +1 -2
- package/dist/lib/task/review-finalization.js +4 -18
- package/dist/lib/task/rework-intent.js +70 -4
- package/dist/lib/task/snapshot.js +6 -6
- package/dist/lib/task/verification-engine.js +25 -30
- package/dist/lib/task/verification.js +2 -13
- package/dist/lib/task/workflow-warning-intents.js +23 -10
- package/dist/lib/task/workspace-snapshot.js +7 -2
- package/dist/lib/task/write.js +1 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/decide.ts +5 -35
- package/lib/git/branch-worktree.ts +41 -0
- package/lib/internal/cli-route-inventory.ts +1 -3
- package/lib/internal/task-activity.ts +6 -34
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-event.ts +4 -37
- package/lib/internal/task-finalization.ts +47 -1
- package/lib/internal/task-ledger.ts +15 -41
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-operation-registry.ts +2 -4
- package/lib/internal/task-review.ts +5 -31
- package/lib/internal/task-verify.ts +3 -39
- package/lib/internal/task-warning.ts +3 -32
- package/lib/platform/completion-backfill.ts +78 -16
- package/lib/platform/pr-summary.ts +4 -25
- package/lib/platform/verification-sync.ts +13 -53
- package/lib/sandbox/control/client.ts +5 -1
- package/lib/sandbox/control/executor.ts +13 -4
- package/lib/sandbox/control/protocol.ts +3 -2
- package/lib/sandbox/control/server.ts +21 -146
- package/lib/sandbox/removal.ts +23 -7
- package/lib/sandbox/task-cleanup.ts +12 -20
- package/lib/task/activity-intent.ts +1 -2
- package/lib/task/artifact-command.ts +14 -4
- package/lib/task/artifact-lifecycle.ts +236 -85
- package/lib/task/artifact-operations.ts +59 -28
- package/lib/task/artifact-receipts.ts +4 -1
- package/lib/task/artifact-schema.ts +10 -7
- package/lib/task/artifact-selection.ts +53 -0
- package/lib/task/capabilities.ts +154 -40
- package/lib/task/commands/status.ts +11 -2
- package/lib/task/control-authority.ts +17 -189
- package/lib/task/decision-intents.ts +11 -4
- package/lib/task/delivery.ts +1 -40
- package/lib/task/events.ts +73 -74
- package/lib/task/finalization-handoff.ts +104 -0
- package/lib/task/finalization.ts +388 -122
- package/lib/task/intermediate-cleanup.ts +9 -125
- package/lib/task/ledger-intents.ts +157 -20
- package/lib/task/lifecycle-path.ts +98 -0
- package/lib/task/lifecycle.ts +58 -13
- package/lib/task/local-artifact-finalization.ts +14 -56
- package/lib/task/orchestration.ts +26 -8
- package/lib/task/review-command.ts +2 -5
- package/lib/task/review-finalization.ts +4 -20
- package/lib/task/rework-intent.ts +75 -5
- package/lib/task/snapshot.ts +6 -6
- package/lib/task/verification-engine.ts +29 -30
- package/lib/task/verification.ts +3 -16
- package/lib/task/workflow-warning-intents.ts +20 -10
- package/lib/task/workspace-snapshot.ts +3 -2
- package/lib/task/write.ts +1 -4
- package/package.json +1 -1
- package/templates/.agents/rules/review-handshake.en.md +4 -4
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
- package/templates/.agents/skills/analyze-task/SKILL.en.md +21 -5
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +21 -5
- package/templates/.agents/skills/code-task/SKILL.en.md +14 -8
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +18 -13
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +2 -2
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +4 -4
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +7 -7
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +1 -3
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +1 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -10
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
- package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/review-pr/config/verify.en.json +3 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +3 -1
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +2 -2
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -2
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +11 -6
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/feature-development.en.yaml +11 -6
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/refactoring.en.yaml +11 -6
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +11 -6
- package/dist/lib/internal/task-override.js +0 -138
- package/dist/lib/sandbox/control/finalization-response.js +0 -33
- package/dist/lib/task/artifact-recovery.js +0 -476
- package/dist/lib/task/artifact-repair-intent.js +0 -181
- package/dist/lib/task/guard-override.js +0 -31
- package/dist/lib/task/human-override.js +0 -1259
- package/lib/internal/task-override.ts +0 -144
- package/lib/sandbox/control/finalization-response.ts +0 -35
- package/lib/task/artifact-recovery.ts +0 -572
- package/lib/task/artifact-repair-intent.ts +0 -227
- package/lib/task/guard-override.ts +0 -50
- package/lib/task/human-override.ts +0 -1522
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { finalizationTerminalResponse } from './finalization-response.js';
|
|
2
1
|
import { completedReentryView } from './completed-reentry.js';
|
|
3
2
|
import fs from 'node:fs';
|
|
4
3
|
import path from 'node:path';
|
|
@@ -21,7 +20,7 @@ import { nextSandboxControlBackoff } from './timing.js';
|
|
|
21
20
|
import { readTaskFinalizationReceipt } from '../../task/finalization.js';
|
|
22
21
|
import { readCodexControllerRegistration } from './controller-registration.js';
|
|
23
22
|
import { validateSandboxControlIdentity } from './identity-sentinel.js';
|
|
24
|
-
import { mergeSandboxTaskView,
|
|
23
|
+
import { mergeSandboxTaskView, taskViewForManifest } from './task-view.js';
|
|
25
24
|
import { taskCreateOutputUnavailableResult } from '../../task/create-service.js';
|
|
26
25
|
function appendBrokerAudit(manifest, event, fields = {}) {
|
|
27
26
|
appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
|
|
@@ -125,48 +124,6 @@ export function writeSandboxControlResponse(manifest, response) {
|
|
|
125
124
|
}
|
|
126
125
|
return true;
|
|
127
126
|
}
|
|
128
|
-
function finalizationRecoveryResponse(manifest, requestId, exitCode) {
|
|
129
|
-
if (!manifest.taskId || exitCode !== 0)
|
|
130
|
-
return { status: 'not-applicable' };
|
|
131
|
-
let receipt;
|
|
132
|
-
try {
|
|
133
|
-
receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
134
|
-
if (!receipt || !receipt.controlBinding
|
|
135
|
-
|| receipt.controlBinding.generation !== manifest.generation
|
|
136
|
-
|| receipt.controlBinding.requestId !== requestId)
|
|
137
|
-
return { status: 'deferred' };
|
|
138
|
-
const resolved = resolveTaskRef(manifest.taskId, { repoRoot: manifest.repoRoot });
|
|
139
|
-
if (!resolved.ok || resolved.state !== 'completed' || receipt.lifecycle !== 'done')
|
|
140
|
-
return { status: 'deferred' };
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
return { status: 'deferred' };
|
|
144
|
-
}
|
|
145
|
-
return { status: 'matched', response: finalizationTerminalResponse(manifest.taskId, requestId, receipt) };
|
|
146
|
-
}
|
|
147
|
-
function publishFinalizationTaskView(manifest, broker, requestId, state, reasonCode, activeRequestId) {
|
|
148
|
-
let view;
|
|
149
|
-
try {
|
|
150
|
-
const resolved = resolveTaskRef(manifest.taskId ?? '', { repoRoot: manifest.repoRoot });
|
|
151
|
-
const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId ?? '');
|
|
152
|
-
view = resolved.ok && resolved.state === 'completed'
|
|
153
|
-
? taskViewAfterFinalization({
|
|
154
|
-
taskId: manifest.taskId ?? '', generation: manifest.generation, requestId, receipt
|
|
155
|
-
})
|
|
156
|
-
: {
|
|
157
|
-
state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
|
|
158
|
-
reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED'
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
view = {
|
|
163
|
-
state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
|
|
164
|
-
reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, Date.now(), view);
|
|
168
|
-
return view;
|
|
169
|
-
}
|
|
170
127
|
function payloadReference(payload) {
|
|
171
128
|
return {
|
|
172
129
|
version: payload.version,
|
|
@@ -329,12 +286,7 @@ export async function recoveryResponse(manifest, manifestPath, request, evidence
|
|
|
329
286
|
if (JSON.stringify(payloadTerminal) !== JSON.stringify(terminalResult))
|
|
330
287
|
return unknown(request.id);
|
|
331
288
|
}
|
|
332
|
-
const
|
|
333
|
-
if (evidence.exitCode === 0 && finalization?.status === 'deferred')
|
|
334
|
-
return null;
|
|
335
|
-
const recovery = finalization
|
|
336
|
-
? { domain: { consistent: finalization.status === 'matched' } }
|
|
337
|
-
: await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
|
|
289
|
+
const recovery = await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
|
|
338
290
|
const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName);
|
|
339
291
|
const decision = classifySandboxControlRecovery({
|
|
340
292
|
operation,
|
|
@@ -351,23 +303,9 @@ export async function recoveryResponse(manifest, manifestPath, request, evidence
|
|
|
351
303
|
return notExecuted(request.id);
|
|
352
304
|
if (decision.outcome !== 'success' && decision.outcome !== 'failure')
|
|
353
305
|
return null;
|
|
354
|
-
if (request.family === 'task-finalization' && evidence.exitCode === 0) {
|
|
355
|
-
if (finalization?.status !== 'matched')
|
|
356
|
-
return null;
|
|
357
|
-
return finalization.response ?? null;
|
|
358
|
-
}
|
|
359
306
|
return genericRecoveryResponse(request, evidence.exitCode, payload);
|
|
360
307
|
}
|
|
361
|
-
async function terminalMatchesEvidence(manifest,
|
|
362
|
-
if (request.family === 'task-finalization' && evidence.exitCode === 0) {
|
|
363
|
-
if (!terminalResult)
|
|
364
|
-
return { valid: false, payloadReferenced: false };
|
|
365
|
-
const expected = await recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
|
|
366
|
-
return {
|
|
367
|
-
valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
|
|
368
|
-
payloadReferenced: false
|
|
369
|
-
};
|
|
370
|
-
}
|
|
308
|
+
async function terminalMatchesEvidence(manifest, request, response, evidence, payload, payloadInvalid, terminalResult) {
|
|
371
309
|
if (response.version !== 2 || response.id !== request.id || response.phase !== 'completed'
|
|
372
310
|
|| response.exitCode !== evidence.exitCode || response.error !== null) {
|
|
373
311
|
return { valid: false, payloadReferenced: false };
|
|
@@ -401,7 +339,7 @@ async function terminalMatchesEvidence(manifest, manifestPath, request, response
|
|
|
401
339
|
payloadReferenced: false
|
|
402
340
|
};
|
|
403
341
|
}
|
|
404
|
-
function publishExecutionResult(manifest, request, result,
|
|
342
|
+
function publishExecutionResult(manifest, request, result, brokerOwns) {
|
|
405
343
|
const normalized = sanitizeSandboxControlResult(manifest, result);
|
|
406
344
|
const terminalResult = writeSandboxControlTerminalResult(manifest, {
|
|
407
345
|
id: request.id,
|
|
@@ -413,46 +351,29 @@ function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
|
|
|
413
351
|
criticalRequestPhase(manifest, request, 'evidence-written', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
414
352
|
criticalRequestPhase(manifest, request, 'publish-authorized', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
415
353
|
}
|
|
416
|
-
let terminal =
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
terminal = finalization.response ?? null;
|
|
422
|
-
}
|
|
423
|
-
if (!terminal) {
|
|
424
|
-
const inline = {
|
|
425
|
-
version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
|
|
426
|
-
stdout: normalized.stdout, stderr: normalized.stderr, error: null
|
|
427
|
-
};
|
|
428
|
-
if (sandboxControlEncodedJsonBytes(inline) <= SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
|
|
429
|
-
terminal = inline;
|
|
430
|
-
}
|
|
354
|
+
let terminal = {
|
|
355
|
+
version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
|
|
356
|
+
stdout: normalized.stdout, stderr: normalized.stderr, error: null
|
|
357
|
+
};
|
|
358
|
+
if (sandboxControlEncodedJsonBytes(terminal) > SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
|
|
431
359
|
let payload = null;
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
writeSandboxControlPayload(manifest, request.id, normalized);
|
|
438
|
-
}
|
|
439
|
-
else {
|
|
440
|
-
payload = null;
|
|
441
|
-
}
|
|
360
|
+
try {
|
|
361
|
+
payload = createSandboxControlPayload(manifest, request.id, normalized);
|
|
362
|
+
const usage = sandboxControlGenerationUsage(manifest);
|
|
363
|
+
if (usage.bytes + sandboxControlEncodedJsonBytes(payload) <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
|
|
364
|
+
writeSandboxControlPayload(manifest, request.id, normalized);
|
|
442
365
|
}
|
|
443
|
-
|
|
366
|
+
else {
|
|
444
367
|
payload = null;
|
|
445
368
|
}
|
|
446
|
-
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
|
|
447
369
|
}
|
|
370
|
+
catch {
|
|
371
|
+
payload = null;
|
|
372
|
+
}
|
|
373
|
+
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
|
|
448
374
|
}
|
|
449
375
|
if (!brokerOwns())
|
|
450
376
|
return false;
|
|
451
|
-
if (request.family === 'task-finalization' && normalized.exitCode === 0) {
|
|
452
|
-
const view = publishFinalizationTaskView(manifest, broker, request.id, 'healthy', null, null);
|
|
453
|
-
if (view.state === 'unknown')
|
|
454
|
-
return false;
|
|
455
|
-
}
|
|
456
377
|
const committed = writeSandboxControlResponse(manifest, terminal);
|
|
457
378
|
if (committed && fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
|
|
458
379
|
criticalRequestPhase(manifest, request, 'published-committed', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
@@ -652,16 +573,11 @@ async function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
|
|
|
652
573
|
if (terminal && request && resultEvidence) {
|
|
653
574
|
if (!terminalResult && request.family !== 'task-create')
|
|
654
575
|
continue;
|
|
655
|
-
const reconciliation = await terminalMatchesEvidence(manifest,
|
|
576
|
+
const reconciliation = await terminalMatchesEvidence(manifest, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
|
|
656
577
|
if (!reconciliation.valid)
|
|
657
578
|
continue;
|
|
658
579
|
terminalReconciled = true;
|
|
659
580
|
payloadReferenced = reconciliation.payloadReferenced;
|
|
660
|
-
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
661
|
-
const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
|
|
662
|
-
if (view.state === 'unknown')
|
|
663
|
-
continue;
|
|
664
|
-
}
|
|
665
581
|
}
|
|
666
582
|
if (!terminalResult && !terminalReconciled) {
|
|
667
583
|
if (terminal)
|
|
@@ -688,11 +604,6 @@ async function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
|
|
|
688
604
|
const recovered = await recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult);
|
|
689
605
|
if (!recovered)
|
|
690
606
|
continue;
|
|
691
|
-
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
692
|
-
const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
|
|
693
|
-
if (view.state === 'unknown')
|
|
694
|
-
continue;
|
|
695
|
-
}
|
|
696
607
|
writeSandboxControlResponse(manifest, recovered);
|
|
697
608
|
const preserveRecoveryEvidence = recovered.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN';
|
|
698
609
|
if (transitionProtocolActive && !preserveRecoveryEvidence) {
|
|
@@ -700,7 +611,7 @@ async function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
|
|
|
700
611
|
}
|
|
701
612
|
if (preserveRecoveryEvidence)
|
|
702
613
|
continue;
|
|
703
|
-
payloadReferenced = Boolean(payload)
|
|
614
|
+
payloadReferenced = Boolean(payload);
|
|
704
615
|
}
|
|
705
616
|
}
|
|
706
617
|
else {
|
|
@@ -794,14 +705,10 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
794
705
|
startupReceipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
795
706
|
}
|
|
796
707
|
catch {
|
|
797
|
-
// Preserve malformed receipt evidence as unknown rather than treating it as no receipt.
|
|
798
708
|
startupReceipt = {};
|
|
799
709
|
}
|
|
800
710
|
}
|
|
801
|
-
let taskView = taskViewForManifest({
|
|
802
|
-
...manifest,
|
|
803
|
-
receipt: startupReceipt
|
|
804
|
-
});
|
|
711
|
+
let taskView = taskViewForManifest({ ...manifest, receipt: startupReceipt });
|
|
805
712
|
try {
|
|
806
713
|
const previous = readSandboxControlStatus(manifest.publicStatusDir);
|
|
807
714
|
if (previous.generation === manifest.generation)
|
|
@@ -983,17 +890,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
983
890
|
break;
|
|
984
891
|
let terminalCommitted = false;
|
|
985
892
|
if (settledExecution.result && settledExecution.resultEvidenceWritten) {
|
|
986
|
-
terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result,
|
|
987
|
-
if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
|
|
988
|
-
try {
|
|
989
|
-
const published = readSandboxControlStatus(manifest.publicStatusDir);
|
|
990
|
-
if (published.generation === manifest.generation)
|
|
991
|
-
taskView = published.taskView;
|
|
992
|
-
}
|
|
993
|
-
catch {
|
|
994
|
-
taskView = { state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null, reasonCode: 'SANDBOX_TASK_VIEW_STATUS_INVALID' };
|
|
995
|
-
}
|
|
996
|
-
}
|
|
893
|
+
terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, brokerOwns);
|
|
997
894
|
}
|
|
998
895
|
else {
|
|
999
896
|
terminalCommitted = writeSandboxControlResponse(manifest, unknown(settledExecution.request.id));
|
|
@@ -1255,7 +1152,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
1255
1152
|
}
|
|
1256
1153
|
}
|
|
1257
1154
|
if (owned && active.result && active.resultEvidenceWritten) {
|
|
1258
|
-
if (publishExecutionResult(manifest, active.request, active.result,
|
|
1155
|
+
if (publishExecutionResult(manifest, active.request, active.result, brokerOwns)) {
|
|
1259
1156
|
if (brokerOwns()) {
|
|
1260
1157
|
removeAcceptedResponse(manifest, active.request.id);
|
|
1261
1158
|
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
@@ -1264,22 +1161,8 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
1264
1161
|
}
|
|
1265
1162
|
}
|
|
1266
1163
|
if (active) {
|
|
1267
|
-
|
|
1268
|
-
if (owned && brokerOwns() && active
|
|
1269
|
-
const recovered = finalizationRecoveryResponse(manifest, active.request.id, 0);
|
|
1270
|
-
const view = recovered.status === 'matched'
|
|
1271
|
-
? publishFinalizationTaskView(manifest, broker, active.request.id, 'healthy', null, null)
|
|
1272
|
-
: null;
|
|
1273
|
-
if (view?.state !== 'unknown' && recovered.status === 'matched' && recovered.response
|
|
1274
|
-
&& writeSandboxControlResponse(manifest, recovered.response)) {
|
|
1275
|
-
if (terminationConfirmed && brokerOwns()) {
|
|
1276
|
-
removeAcceptedResponse(manifest, active.request.id);
|
|
1277
|
-
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
1278
|
-
active = null;
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
if (owned && brokerOwns() && active && active.request.family !== 'task-finalization' && !active.resultEvidenceWritten) {
|
|
1164
|
+
active.prepared.terminate(owned);
|
|
1165
|
+
if (owned && brokerOwns() && active && !active.resultEvidenceWritten) {
|
|
1283
1166
|
writeSandboxControlResponse(manifest, unknown(active.request.id));
|
|
1284
1167
|
}
|
|
1285
1168
|
}
|
|
@@ -17,6 +17,7 @@ import { acquireSandboxResourceLock } from './control/native-file-lock.js';
|
|
|
17
17
|
import { toolConfigDirCandidates, toolProjectDirCandidates } from './tools.js';
|
|
18
18
|
import { getProcessStartTime } from '../server/process-state.js';
|
|
19
19
|
import { releaseStaleShortIdRegistry } from '../task/short-id.js';
|
|
20
|
+
import { withRepositoryMutationLock } from '../task/task-execution-lock.js';
|
|
20
21
|
import { fetchSandboxRows } from './commands/list-running.js';
|
|
21
22
|
import { formatIntermediateCleanupReport, cleanupIntermediateUnderRemovalCoordinator, protectIntermediateCleanupReport, mergeIntermediateCleanupReports, scanIntermediateCleanup } from '../task/intermediate-cleanup.js';
|
|
22
23
|
import { createSandboxControlBindingEvidence } from './task-cleanup.js';
|
|
@@ -670,7 +671,7 @@ async function runRmOneUnderRepositoryLock(config, tools, branch, options = {})
|
|
|
670
671
|
return rmOneCore(config, tools, branch, { ...options, target });
|
|
671
672
|
}
|
|
672
673
|
async function rmOne(config, tools, branch, options = {}) {
|
|
673
|
-
await runRmOneUnderRepositoryLock(config, tools, branch, options);
|
|
674
|
+
await withRepositoryMutationLock(config.repoRoot, () => runRmOneUnderRepositoryLock(config, tools, branch, options));
|
|
674
675
|
}
|
|
675
676
|
async function removeUncheckedSandbox(config, target, options) {
|
|
676
677
|
const confirm = options.prompt?.confirm ?? p.confirm;
|
|
@@ -703,8 +704,12 @@ async function removeUncheckedSandbox(config, target, options) {
|
|
|
703
704
|
p.outro('Cancelled');
|
|
704
705
|
return null;
|
|
705
706
|
}
|
|
707
|
+
const worktreePermits = options.permits ?? await authorizeWorktrees(existingWorktrees, { allowDirtyDiscard: true, assumeYes: Boolean(options.assumeYes) }, { ...options.prompt, interactive: options.interactive });
|
|
706
708
|
for (const container of matchedContainers)
|
|
707
709
|
runSafeEngine(engine, 'docker', ['rm', '-f', container]);
|
|
710
|
+
if (!sandboxContainersRemoved(engine, matchedContainers)) {
|
|
711
|
+
throw new Error(`SANDBOX_REMOVAL_CONTAINER_STILL_PRESENT: ${matchedContainers.join(', ')}`);
|
|
712
|
+
}
|
|
708
713
|
for (const root of controlRoots) {
|
|
709
714
|
fs.rmSync(root, { recursive: true, force: true });
|
|
710
715
|
removeEmptyManagedParent(path.join(config.controlBase, config.project), root);
|
|
@@ -720,14 +725,16 @@ async function removeUncheckedSandbox(config, target, options) {
|
|
|
720
725
|
if (shouldRemoveShare)
|
|
721
726
|
fs.rmSync(sharePath, { recursive: true, force: true });
|
|
722
727
|
if (shouldRemoveWorktree) {
|
|
723
|
-
for (const worktree of existingWorktrees)
|
|
724
|
-
|
|
725
|
-
|
|
728
|
+
for (const worktree of existingWorktrees) {
|
|
729
|
+
const permit = worktreePermits.get(path.resolve(worktree));
|
|
730
|
+
if (!permit)
|
|
731
|
+
throw new Error(`SANDBOX_WORKTREE_REMOVAL_PERMIT_MISSING: ${worktree}`);
|
|
732
|
+
removeWorktreeDir(config.repoRoot, config.worktreeBase, worktree, permit);
|
|
733
|
+
}
|
|
726
734
|
}
|
|
727
|
-
if (shouldDeleteBranch)
|
|
735
|
+
if (shouldDeleteBranch) {
|
|
736
|
+
assertBranchRemovalIdentity(config, effectiveBranch, worktreePermits);
|
|
728
737
|
runSafe('git', ['-C', config.repoRoot, 'branch', '-D', effectiveBranch]);
|
|
729
|
-
if (!sandboxContainersRemoved(engine, matchedContainers)) {
|
|
730
|
-
throw new Error(`SANDBOX_REMOVAL_CONTAINER_STILL_PRESENT: ${matchedContainers.join(', ')}`);
|
|
731
738
|
}
|
|
732
739
|
if (target.workspace.mode === 'task-bound') {
|
|
733
740
|
releaseStaleShortIdRegistry(config.repoRoot, target.workspace.taskId);
|
|
@@ -741,7 +748,7 @@ async function rmOneCore(config, tools, branch, options = {}) {
|
|
|
741
748
|
return removeUncheckedSandbox(config, target, options);
|
|
742
749
|
}
|
|
743
750
|
async function rmPurge(config, tools, prompt = {}) {
|
|
744
|
-
return rmPurgeCore(config, tools, prompt);
|
|
751
|
+
return withRepositoryMutationLock(config.repoRoot, () => rmPurgeCore(config, tools, prompt));
|
|
745
752
|
}
|
|
746
753
|
async function rmPurgeCore(config, tools, prompt = {}) {
|
|
747
754
|
const engine = detectEngine(config);
|
|
@@ -8,7 +8,6 @@ import { enumerateAllTaskDirs } from '../task/resolve-ref.js';
|
|
|
8
8
|
import { resolveSandboxCleanupTarget } from './workspace-identity.js';
|
|
9
9
|
import { readSandboxControlManifest } from './control/lifecycle.js';
|
|
10
10
|
import { readJsonFile, readSandboxControlStatus } from './control/state.js';
|
|
11
|
-
import { finalizationTerminalResponse } from './control/finalization-response.js';
|
|
12
11
|
const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
13
12
|
function matchingControlManifest(repoRoot, controlRoot, taskId, binding) {
|
|
14
13
|
if (!controlRootIdentity(controlRoot))
|
|
@@ -77,11 +76,9 @@ function canonicalPath(input) {
|
|
|
77
76
|
return path.resolve(input);
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
|
-
function taskFinalizationReceiptComplete(receipt, taskId
|
|
79
|
+
function taskFinalizationReceiptComplete(receipt, taskId) {
|
|
81
80
|
return receipt.taskId === taskId
|
|
82
|
-
&& taskFinalizationReceiptState(receipt) === 'complete'
|
|
83
|
-
&& receipt.controlBinding?.generation === binding.generation
|
|
84
|
-
&& receipt.controlBinding.requestId === binding.requestId;
|
|
81
|
+
&& taskFinalizationReceiptState(receipt) === 'complete';
|
|
85
82
|
}
|
|
86
83
|
function terminalControlBindingEvidence(repoRootInput, controlRootInput, taskId, binding) {
|
|
87
84
|
const repoRoot = canonicalPath(repoRootInput);
|
|
@@ -105,26 +102,22 @@ function terminalControlBindingEvidence(repoRootInput, controlRootInput, taskId,
|
|
|
105
102
|
return false;
|
|
106
103
|
}
|
|
107
104
|
const receipt = readTaskFinalizationReceipt(repoRoot, taskId);
|
|
108
|
-
if (!receipt || !taskFinalizationReceiptComplete(receipt, taskId
|
|
105
|
+
if (!receipt || !taskFinalizationReceiptComplete(receipt, taskId)) {
|
|
109
106
|
return false;
|
|
110
107
|
}
|
|
111
|
-
const status = readSandboxControlStatus(manifest.publicStatusDir);
|
|
112
|
-
const viewReceipt = status.taskView.receipt;
|
|
113
|
-
const terminalTaskView = (status.taskView.state === 'current' && status.taskView.observedSource === 'completed')
|
|
114
|
-
|| (status.taskView.state === 'finalized-stale' && status.taskView.observedSource === 'active');
|
|
115
|
-
if (status.generation !== binding.generation || status.state !== 'healthy'
|
|
116
|
-
|| status.activeRequestId !== null || !terminalTaskView || status.taskView.taskId !== taskId
|
|
117
|
-
|| !viewReceipt || viewReceipt.generation !== binding.generation
|
|
118
|
-
|| viewReceipt.requestId !== binding.requestId
|
|
119
|
-
|| viewReceipt.receiptId !== receipt.receiptId
|
|
120
|
-
|| viewReceipt.revision !== receipt.revision)
|
|
121
|
-
return false;
|
|
122
108
|
const responsePath = path.join(manifest.channelDir, 'responses', `${binding.requestId}.json`);
|
|
123
109
|
if (!inspectOwnedPath(path.join(controlRoot, 'channel'), responsePath, 'file'))
|
|
124
110
|
return false;
|
|
125
111
|
const actual = readJsonFile(responsePath);
|
|
126
|
-
const
|
|
127
|
-
|
|
112
|
+
const response = actual;
|
|
113
|
+
if (response.version !== 2 || response.id !== binding.requestId || response.phase !== 'completed'
|
|
114
|
+
|| response.exitCode !== 0 || typeof response.stdout !== 'string')
|
|
115
|
+
return false;
|
|
116
|
+
const envelope = JSON.parse(response.stdout);
|
|
117
|
+
const result = envelope.result;
|
|
118
|
+
return envelope.status === 'completed' && envelope.accepted === true
|
|
119
|
+
&& result !== null && typeof result === 'object'
|
|
120
|
+
&& result.status === 'completed' && result.taskId === taskId;
|
|
128
121
|
}
|
|
129
122
|
catch {
|
|
130
123
|
return false;
|
|
@@ -153,7 +146,7 @@ function removedControlBindingEvidence(repoRootInput, journal, taskId, binding)
|
|
|
153
146
|
return false;
|
|
154
147
|
try {
|
|
155
148
|
const receipt = readTaskFinalizationReceipt(repoRoot, taskId);
|
|
156
|
-
return receipt !== null && taskFinalizationReceiptComplete(receipt, taskId
|
|
149
|
+
return receipt !== null && taskFinalizationReceiptComplete(receipt, taskId);
|
|
157
150
|
}
|
|
158
151
|
catch {
|
|
159
152
|
return false;
|
|
@@ -6,7 +6,6 @@ import { assertWritableInventory, buildArtifactLinkSection, inspectTaskArtifacts
|
|
|
6
6
|
import { parseArtifactName } from './artifact-name.js';
|
|
7
7
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
8
8
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
9
|
-
import { allowsManualOverride } from './guard-override.js';
|
|
10
9
|
const HEAD_RE = /^[0-9a-f]{40}$/;
|
|
11
10
|
const REVIEW_ACTION_RE = /^Review PR \(Round ([1-9]\d*)\)( \[started\])?$/;
|
|
12
11
|
const VERDICTS = new Set(['approved', 'changes-requested', 'commented']);
|
|
@@ -294,7 +293,7 @@ export function applyPrReviewActivityIntent(intent, options = {}) {
|
|
|
294
293
|
const resolved = resolveTaskRef(intent.taskRef, { repoRoot: options.repoRoot });
|
|
295
294
|
if (!resolved.ok)
|
|
296
295
|
return failed(intent, resolved.code, resolved.message, resolved.taskId);
|
|
297
|
-
if (resolved.state !== 'active'
|
|
296
|
+
if (resolved.state !== 'active') {
|
|
298
297
|
return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
299
298
|
}
|
|
300
299
|
if (options.lockAlreadyHeld)
|
|
@@ -13,7 +13,7 @@ export function parseArtifactCommand(args) {
|
|
|
13
13
|
}
|
|
14
14
|
const fields = {};
|
|
15
15
|
const allowed = {
|
|
16
|
-
inspect: ['--family'],
|
|
16
|
+
inspect: ['--family', '--source-finding', '--source-artifact', '--source-sha256'],
|
|
17
17
|
init: ['--family', '--artifact', '--locale'],
|
|
18
18
|
'preflight': ['--family', '--artifact'],
|
|
19
19
|
'finalize-local': ['--family', '--artifact']
|
|
@@ -37,8 +37,10 @@ export function parseArtifactCommand(args) {
|
|
|
37
37
|
if (locale !== undefined && locale !== 'zh-CN' && locale !== 'en')
|
|
38
38
|
throw new Error("option '--locale' must be 'zh-CN' or 'en'");
|
|
39
39
|
return {
|
|
40
|
-
taskRef, operation: operation, family: fields['--family'],
|
|
41
|
-
artifact: fields['--artifact'] ?? '', locale
|
|
40
|
+
taskRef, operation: operation, family: fields['--family'] ?? '',
|
|
41
|
+
artifact: fields['--artifact'] ?? '', locale,
|
|
42
|
+
sourceFinding: fields['--source-finding'], sourceArtifact: fields['--source-artifact'],
|
|
43
|
+
sourceSha256: fields['--source-sha256']
|
|
42
44
|
};
|
|
43
45
|
}
|
|
44
46
|
/** Resolve authoritative task metadata separately from a trusted candidate directory. */
|
|
@@ -46,7 +48,12 @@ export function executeArtifactCommand(command, options = {}) {
|
|
|
46
48
|
const { taskRef, operation, family, artifact, locale } = command;
|
|
47
49
|
const fail = (code, message) => ({ status: 'failed', changed: false, error: { code, message } });
|
|
48
50
|
if (operation === 'inspect') {
|
|
49
|
-
const result = resolveArtifactContext(taskRef, family, {
|
|
51
|
+
const result = resolveArtifactContext(taskRef, family, {
|
|
52
|
+
repoRoot: options.repoRoot,
|
|
53
|
+
sourceFinding: command.sourceFinding,
|
|
54
|
+
sourceArtifact: command.sourceArtifact,
|
|
55
|
+
sourceSha256: command.sourceSha256
|
|
56
|
+
});
|
|
50
57
|
return family === 'code' && result.codeMode ? {
|
|
51
58
|
...result, mode: result.codeMode.mode, code_max: result.codeMode.codeMax, rev_max: result.codeMode.reviewMax,
|
|
52
59
|
verdict: result.codeMode.verdict, next_round: result.next?.round ?? null, next_artifact: result.next?.name ?? null,
|