@fitlab-ai/agent-infra 0.11.3 → 0.11.4
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 -1
- package/dist/lib/internal/cli-route-inventory.js +1 -1
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-finalization.js +41 -1
- package/dist/lib/internal/task-ledger.js +13 -5
- package/dist/lib/internal/task-operation-registry.js +1 -0
- package/dist/lib/platform/completion-backfill.js +63 -17
- package/dist/lib/sandbox/control/server.js +26 -143
- package/dist/lib/sandbox/task-cleanup.js +13 -20
- 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 +1 -26
- package/dist/lib/task/artifact-receipts.js +4 -1
- package/dist/lib/task/artifact-schema.js +1 -0
- 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 +3 -2
- package/dist/lib/task/decision-intents.js +12 -0
- package/dist/lib/task/delivery.js +1 -43
- package/dist/lib/task/events.js +80 -64
- package/dist/lib/task/finalization.js +301 -143
- package/dist/lib/task/ledger-intents.js +157 -15
- package/dist/lib/task/lifecycle-path.js +75 -0
- package/dist/lib/task/lifecycle.js +10 -0
- package/dist/lib/task/local-artifact-finalization.js +1 -20
- package/dist/lib/task/orchestration.js +17 -7
- package/dist/lib/task/review-finalization.js +3 -16
- package/dist/lib/task/rework-intent.js +70 -4
- package/dist/lib/task/verification-engine.js +20 -30
- package/dist/lib/task/verification.js +1 -0
- package/dist/lib/task/workflow-warning-intents.js +21 -6
- package/dist/lib/task/workspace-snapshot.js +7 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/internal/cli-route-inventory.ts +1 -1
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-finalization.ts +40 -1
- package/lib/internal/task-ledger.ts +12 -5
- package/lib/internal/task-operation-registry.ts +1 -0
- package/lib/platform/completion-backfill.ts +78 -16
- package/lib/sandbox/control/server.ts +21 -146
- package/lib/sandbox/task-cleanup.ts +12 -20
- package/lib/task/artifact-command.ts +14 -4
- package/lib/task/artifact-lifecycle.ts +236 -85
- package/lib/task/artifact-operations.ts +1 -26
- package/lib/task/artifact-receipts.ts +4 -1
- package/lib/task/artifact-schema.ts +1 -0
- 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 +3 -1
- package/lib/task/decision-intents.ts +9 -0
- package/lib/task/delivery.ts +1 -40
- package/lib/task/events.ts +82 -64
- package/lib/task/finalization.ts +269 -111
- package/lib/task/ledger-intents.ts +155 -15
- package/lib/task/lifecycle-path.ts +98 -0
- package/lib/task/lifecycle.ts +13 -0
- package/lib/task/local-artifact-finalization.ts +2 -25
- package/lib/task/orchestration.ts +26 -8
- package/lib/task/review-finalization.ts +3 -16
- package/lib/task/rework-intent.ts +75 -5
- package/lib/task/verification-engine.ts +19 -30
- package/lib/task/verification.ts +2 -1
- package/lib/task/workflow-warning-intents.ts +18 -6
- package/lib/task/workspace-snapshot.ts +3 -2
- 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 +11 -4
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -4
- 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 +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +1 -1
- 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/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-code/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
- 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/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/sandbox/control/finalization-response.js +0 -33
- package/lib/sandbox/control/finalization-response.ts +0 -35
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { finalizationTerminalResponse } from './finalization-response.ts';
|
|
2
1
|
import { completedReentryView } from './completed-reentry.ts';
|
|
3
2
|
import fs from 'node:fs';
|
|
4
3
|
import path from 'node:path';
|
|
@@ -77,7 +76,6 @@ import { readCodexControllerRegistration } from './controller-registration.ts';
|
|
|
77
76
|
import { validateSandboxControlIdentity } from './identity-sentinel.ts';
|
|
78
77
|
import {
|
|
79
78
|
mergeSandboxTaskView,
|
|
80
|
-
taskViewAfterFinalization,
|
|
81
79
|
taskViewForManifest,
|
|
82
80
|
type SandboxTaskView
|
|
83
81
|
} from './task-view.ts';
|
|
@@ -207,61 +205,6 @@ export function writeSandboxControlResponse(manifest: SandboxControlManifest, re
|
|
|
207
205
|
return true;
|
|
208
206
|
}
|
|
209
207
|
|
|
210
|
-
type FinalizationRecovery = Readonly<{
|
|
211
|
-
status: 'matched' | 'deferred' | 'not-applicable';
|
|
212
|
-
response?: SandboxControlResponse;
|
|
213
|
-
}>;
|
|
214
|
-
|
|
215
|
-
function finalizationRecoveryResponse(
|
|
216
|
-
manifest: SandboxControlManifest,
|
|
217
|
-
requestId: string,
|
|
218
|
-
exitCode: number
|
|
219
|
-
): FinalizationRecovery {
|
|
220
|
-
if (!manifest.taskId || exitCode !== 0) return { status: 'not-applicable' };
|
|
221
|
-
let receipt;
|
|
222
|
-
try {
|
|
223
|
-
receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
224
|
-
if (!receipt || !receipt.controlBinding
|
|
225
|
-
|| receipt.controlBinding.generation !== manifest.generation
|
|
226
|
-
|| receipt.controlBinding.requestId !== requestId) return { status: 'deferred' };
|
|
227
|
-
const resolved = resolveTaskRef(manifest.taskId, { repoRoot: manifest.repoRoot });
|
|
228
|
-
if (!resolved.ok || resolved.state !== 'completed' || receipt.lifecycle !== 'done') return { status: 'deferred' };
|
|
229
|
-
} catch {
|
|
230
|
-
return { status: 'deferred' };
|
|
231
|
-
}
|
|
232
|
-
return { status: 'matched', response: finalizationTerminalResponse(manifest.taskId, requestId, receipt) };
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function publishFinalizationTaskView(
|
|
236
|
-
manifest: SandboxControlManifest,
|
|
237
|
-
broker: BrokerOwner,
|
|
238
|
-
requestId: string,
|
|
239
|
-
state: 'starting' | 'healthy' | 'busy' | 'parked',
|
|
240
|
-
reasonCode: string | null,
|
|
241
|
-
activeRequestId: string | null
|
|
242
|
-
): SandboxTaskView {
|
|
243
|
-
let view: SandboxTaskView;
|
|
244
|
-
try {
|
|
245
|
-
const resolved = resolveTaskRef(manifest.taskId ?? '', { repoRoot: manifest.repoRoot });
|
|
246
|
-
const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId ?? '');
|
|
247
|
-
view = resolved.ok && resolved.state === 'completed'
|
|
248
|
-
? taskViewAfterFinalization({
|
|
249
|
-
taskId: manifest.taskId ?? '', generation: manifest.generation, requestId, receipt
|
|
250
|
-
})
|
|
251
|
-
: {
|
|
252
|
-
state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
|
|
253
|
-
reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED'
|
|
254
|
-
};
|
|
255
|
-
} catch {
|
|
256
|
-
view = {
|
|
257
|
-
state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
|
|
258
|
-
reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, Date.now(), view);
|
|
262
|
-
return view;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
208
|
function payloadReference(payload: ReturnType<typeof createSandboxControlPayload>) {
|
|
266
209
|
return {
|
|
267
210
|
version: payload.version,
|
|
@@ -458,11 +401,7 @@ export async function recoveryResponse(
|
|
|
458
401
|
}, payload.stdout);
|
|
459
402
|
if (JSON.stringify(payloadTerminal) !== JSON.stringify(terminalResult)) return unknown(request.id);
|
|
460
403
|
}
|
|
461
|
-
const
|
|
462
|
-
if (evidence.exitCode === 0 && finalization?.status === 'deferred') return null;
|
|
463
|
-
const recovery: RecoveryDomainEvidence = finalization
|
|
464
|
-
? { domain: { consistent: finalization.status === 'matched' } }
|
|
465
|
-
: await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
|
|
404
|
+
const recovery = await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
|
|
466
405
|
const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName!);
|
|
467
406
|
const decision = classifySandboxControlRecovery({
|
|
468
407
|
operation,
|
|
@@ -476,16 +415,11 @@ export async function recoveryResponse(
|
|
|
476
415
|
if (decision.outcome === 'unknown' || decision.outcome === 'in-progress') return unknown(request.id);
|
|
477
416
|
if (decision.outcome === 'not-executed') return notExecuted(request.id);
|
|
478
417
|
if (decision.outcome !== 'success' && decision.outcome !== 'failure') return null;
|
|
479
|
-
if (request.family === 'task-finalization' && evidence.exitCode === 0) {
|
|
480
|
-
if (finalization?.status !== 'matched') return null;
|
|
481
|
-
return finalization.response ?? null;
|
|
482
|
-
}
|
|
483
418
|
return genericRecoveryResponse(request, evidence.exitCode, payload);
|
|
484
419
|
}
|
|
485
420
|
|
|
486
421
|
async function terminalMatchesEvidence(
|
|
487
422
|
manifest: SandboxControlManifest,
|
|
488
|
-
manifestPath: string,
|
|
489
423
|
request: SandboxControlRequest,
|
|
490
424
|
response: SandboxControlResponse,
|
|
491
425
|
evidence: ReturnType<typeof readSandboxControlResultEvidence>,
|
|
@@ -493,14 +427,6 @@ async function terminalMatchesEvidence(
|
|
|
493
427
|
payloadInvalid: boolean,
|
|
494
428
|
terminalResult: ReturnType<typeof readSandboxControlTerminalResult> | null
|
|
495
429
|
): Promise<{ valid: boolean; payloadReferenced: boolean }> {
|
|
496
|
-
if (request.family === 'task-finalization' && evidence.exitCode === 0) {
|
|
497
|
-
if (!terminalResult) return { valid: false, payloadReferenced: false };
|
|
498
|
-
const expected = await recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
|
|
499
|
-
return {
|
|
500
|
-
valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
|
|
501
|
-
payloadReferenced: false
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
430
|
if (response.version !== 2 || response.id !== request.id || response.phase !== 'completed'
|
|
505
431
|
|| response.exitCode !== evidence.exitCode || response.error !== null) {
|
|
506
432
|
return { valid: false, payloadReferenced: false };
|
|
@@ -539,7 +465,6 @@ function publishExecutionResult(
|
|
|
539
465
|
manifest: SandboxControlManifest,
|
|
540
466
|
request: SandboxControlRequest,
|
|
541
467
|
result: SandboxControlExecutionResult,
|
|
542
|
-
broker: BrokerOwner,
|
|
543
468
|
brokerOwns: () => boolean
|
|
544
469
|
): boolean {
|
|
545
470
|
const normalized = sanitizeSandboxControlResult(manifest, result);
|
|
@@ -553,41 +478,26 @@ function publishExecutionResult(
|
|
|
553
478
|
criticalRequestPhase(manifest, request, 'evidence-written', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
554
479
|
criticalRequestPhase(manifest, request, 'publish-authorized', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
555
480
|
}
|
|
556
|
-
let terminal: SandboxControlResponse
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
562
|
-
if (!terminal) {
|
|
563
|
-
const inline: SandboxControlResponse = {
|
|
564
|
-
version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
|
|
565
|
-
stdout: normalized.stdout, stderr: normalized.stderr, error: null
|
|
566
|
-
};
|
|
567
|
-
if (sandboxControlEncodedJsonBytes(inline) <= SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
|
|
568
|
-
terminal = inline;
|
|
569
|
-
}
|
|
481
|
+
let terminal: SandboxControlResponse = {
|
|
482
|
+
version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
|
|
483
|
+
stdout: normalized.stdout, stderr: normalized.stderr, error: null
|
|
484
|
+
};
|
|
485
|
+
if (sandboxControlEncodedJsonBytes(terminal) > SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
|
|
570
486
|
let payload: ReturnType<typeof createSandboxControlPayload> | null = null;
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
} else {
|
|
578
|
-
payload = null;
|
|
579
|
-
}
|
|
580
|
-
} catch {
|
|
487
|
+
try {
|
|
488
|
+
payload = createSandboxControlPayload(manifest, request.id, normalized);
|
|
489
|
+
const usage = sandboxControlGenerationUsage(manifest);
|
|
490
|
+
if (usage.bytes + sandboxControlEncodedJsonBytes(payload) <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
|
|
491
|
+
writeSandboxControlPayload(manifest, request.id, normalized);
|
|
492
|
+
} else {
|
|
581
493
|
payload = null;
|
|
582
494
|
}
|
|
583
|
-
|
|
495
|
+
} catch {
|
|
496
|
+
payload = null;
|
|
584
497
|
}
|
|
498
|
+
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
|
|
585
499
|
}
|
|
586
500
|
if (!brokerOwns()) return false;
|
|
587
|
-
if (request.family === 'task-finalization' && normalized.exitCode === 0) {
|
|
588
|
-
const view = publishFinalizationTaskView(manifest, broker, request.id, 'healthy', null, null);
|
|
589
|
-
if (view.state === 'unknown') return false;
|
|
590
|
-
}
|
|
591
501
|
const committed = writeSandboxControlResponse(manifest, terminal);
|
|
592
502
|
if (committed && fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
|
|
593
503
|
criticalRequestPhase(manifest, request, 'published-committed', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
@@ -780,7 +690,6 @@ async function recoverProcessing(manifest: SandboxControlManifest, manifestPath:
|
|
|
780
690
|
if (!terminalResult && request.family !== 'task-create') continue;
|
|
781
691
|
const reconciliation = await terminalMatchesEvidence(
|
|
782
692
|
manifest,
|
|
783
|
-
manifestPath,
|
|
784
693
|
request,
|
|
785
694
|
existingTerminalResponse!,
|
|
786
695
|
resultEvidence,
|
|
@@ -791,10 +700,6 @@ async function recoverProcessing(manifest: SandboxControlManifest, manifestPath:
|
|
|
791
700
|
if (!reconciliation.valid) continue;
|
|
792
701
|
terminalReconciled = true;
|
|
793
702
|
payloadReferenced = reconciliation.payloadReferenced;
|
|
794
|
-
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
795
|
-
const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
|
|
796
|
-
if (view.state === 'unknown') continue;
|
|
797
|
-
}
|
|
798
703
|
}
|
|
799
704
|
if (!terminalResult && !terminalReconciled) {
|
|
800
705
|
if (terminal) continue;
|
|
@@ -814,17 +719,13 @@ async function recoverProcessing(manifest: SandboxControlManifest, manifestPath:
|
|
|
814
719
|
if (!resultEvidence || !request || payloadInvalid) continue;
|
|
815
720
|
const recovered = await recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult!);
|
|
816
721
|
if (!recovered) continue;
|
|
817
|
-
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
818
|
-
const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
|
|
819
|
-
if (view.state === 'unknown') continue;
|
|
820
|
-
}
|
|
821
722
|
writeSandboxControlResponse(manifest, recovered);
|
|
822
723
|
const preserveRecoveryEvidence = recovered.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN';
|
|
823
724
|
if (transitionProtocolActive && !preserveRecoveryEvidence) {
|
|
824
725
|
writeSandboxControlTransition(manifest, { requestId: entry.name, phase: 'recovered' });
|
|
825
726
|
}
|
|
826
727
|
if (preserveRecoveryEvidence) continue;
|
|
827
|
-
payloadReferenced = Boolean(payload)
|
|
728
|
+
payloadReferenced = Boolean(payload);
|
|
828
729
|
}
|
|
829
730
|
} else {
|
|
830
731
|
if (!brokerOwns()) return false;
|
|
@@ -922,14 +823,10 @@ export async function serveSandboxControl(
|
|
|
922
823
|
try {
|
|
923
824
|
startupReceipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
924
825
|
} catch {
|
|
925
|
-
// Preserve malformed receipt evidence as unknown rather than treating it as no receipt.
|
|
926
826
|
startupReceipt = {};
|
|
927
827
|
}
|
|
928
828
|
}
|
|
929
|
-
let taskView = taskViewForManifest({
|
|
930
|
-
...manifest,
|
|
931
|
-
receipt: startupReceipt
|
|
932
|
-
});
|
|
829
|
+
let taskView = taskViewForManifest({ ...manifest, receipt: startupReceipt });
|
|
933
830
|
try {
|
|
934
831
|
const previous = readSandboxControlStatus(manifest.publicStatusDir);
|
|
935
832
|
if (previous.generation === manifest.generation) taskView = mergeSandboxTaskView(taskView, previous.taskView);
|
|
@@ -1086,15 +983,7 @@ export async function serveSandboxControl(
|
|
|
1086
983
|
if (!brokerOwns()) break;
|
|
1087
984
|
let terminalCommitted = false;
|
|
1088
985
|
if (settledExecution.result && settledExecution.resultEvidenceWritten) {
|
|
1089
|
-
terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result,
|
|
1090
|
-
if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
|
|
1091
|
-
try {
|
|
1092
|
-
const published = readSandboxControlStatus(manifest.publicStatusDir);
|
|
1093
|
-
if (published.generation === manifest.generation) taskView = published.taskView;
|
|
1094
|
-
} catch {
|
|
1095
|
-
taskView = { state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null, reasonCode: 'SANDBOX_TASK_VIEW_STATUS_INVALID' };
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
986
|
+
terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, brokerOwns);
|
|
1098
987
|
} else {
|
|
1099
988
|
terminalCommitted = writeSandboxControlResponse(manifest, unknown(settledExecution.request.id));
|
|
1100
989
|
}
|
|
@@ -1341,7 +1230,7 @@ export async function serveSandboxControl(
|
|
|
1341
1230
|
}
|
|
1342
1231
|
}
|
|
1343
1232
|
if (owned && active.result && active.resultEvidenceWritten) {
|
|
1344
|
-
if (publishExecutionResult(manifest, active.request, active.result,
|
|
1233
|
+
if (publishExecutionResult(manifest, active.request, active.result, brokerOwns)) {
|
|
1345
1234
|
if (brokerOwns()) {
|
|
1346
1235
|
removeAcceptedResponse(manifest, active.request.id);
|
|
1347
1236
|
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
@@ -1350,22 +1239,8 @@ export async function serveSandboxControl(
|
|
|
1350
1239
|
}
|
|
1351
1240
|
}
|
|
1352
1241
|
if (active) {
|
|
1353
|
-
|
|
1354
|
-
if (owned && brokerOwns() && active
|
|
1355
|
-
const recovered = finalizationRecoveryResponse(manifest, active.request.id, 0);
|
|
1356
|
-
const view = recovered.status === 'matched'
|
|
1357
|
-
? publishFinalizationTaskView(manifest, broker, active.request.id, 'healthy', null, null)
|
|
1358
|
-
: null;
|
|
1359
|
-
if (view?.state !== 'unknown' && recovered.status === 'matched' && recovered.response
|
|
1360
|
-
&& writeSandboxControlResponse(manifest, recovered.response)) {
|
|
1361
|
-
if (terminationConfirmed && brokerOwns()) {
|
|
1362
|
-
removeAcceptedResponse(manifest, active.request.id);
|
|
1363
|
-
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
1364
|
-
active = null;
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
if (owned && brokerOwns() && active && active.request.family !== 'task-finalization' && !active.resultEvidenceWritten) {
|
|
1242
|
+
active.prepared.terminate(owned);
|
|
1243
|
+
if (owned && brokerOwns() && active && !active.resultEvidenceWritten) {
|
|
1369
1244
|
writeSandboxControlResponse(manifest, unknown(active.request.id));
|
|
1370
1245
|
}
|
|
1371
1246
|
}
|
|
@@ -8,7 +8,6 @@ import { enumerateAllTaskDirs } from '../task/resolve-ref.ts';
|
|
|
8
8
|
import { resolveSandboxCleanupTarget } from './workspace-identity.ts';
|
|
9
9
|
import { readSandboxControlManifest } from './control/lifecycle.ts';
|
|
10
10
|
import { readJsonFile, readSandboxControlStatus } from './control/state.ts';
|
|
11
|
-
import { finalizationTerminalResponse } from './control/finalization-response.ts';
|
|
12
11
|
|
|
13
12
|
const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
14
13
|
|
|
@@ -83,13 +82,10 @@ function canonicalPath(input: string): string {
|
|
|
83
82
|
|
|
84
83
|
function taskFinalizationReceiptComplete(
|
|
85
84
|
receipt: TaskFinalizationReceipt,
|
|
86
|
-
taskId: string
|
|
87
|
-
binding: SandboxControlBinding
|
|
85
|
+
taskId: string
|
|
88
86
|
): boolean {
|
|
89
87
|
return receipt.taskId === taskId
|
|
90
|
-
&& taskFinalizationReceiptState(receipt) === 'complete'
|
|
91
|
-
&& receipt.controlBinding?.generation === binding.generation
|
|
92
|
-
&& receipt.controlBinding.requestId === binding.requestId;
|
|
88
|
+
&& taskFinalizationReceiptState(receipt) === 'complete';
|
|
93
89
|
}
|
|
94
90
|
|
|
95
91
|
function terminalControlBindingEvidence(
|
|
@@ -114,24 +110,20 @@ function terminalControlBindingEvidence(
|
|
|
114
110
|
if (errorCode(error) !== 'ENOENT') return false;
|
|
115
111
|
}
|
|
116
112
|
const receipt = readTaskFinalizationReceipt(repoRoot, taskId);
|
|
117
|
-
if (!receipt || !taskFinalizationReceiptComplete(receipt, taskId
|
|
113
|
+
if (!receipt || !taskFinalizationReceiptComplete(receipt, taskId)) {
|
|
118
114
|
return false;
|
|
119
115
|
}
|
|
120
|
-
const status = readSandboxControlStatus(manifest.publicStatusDir);
|
|
121
|
-
const viewReceipt = status.taskView.receipt;
|
|
122
|
-
const terminalTaskView = (status.taskView.state === 'current' && status.taskView.observedSource === 'completed')
|
|
123
|
-
|| (status.taskView.state === 'finalized-stale' && status.taskView.observedSource === 'active');
|
|
124
|
-
if (status.generation !== binding.generation || status.state !== 'healthy'
|
|
125
|
-
|| status.activeRequestId !== null || !terminalTaskView || status.taskView.taskId !== taskId
|
|
126
|
-
|| !viewReceipt || viewReceipt.generation !== binding.generation
|
|
127
|
-
|| viewReceipt.requestId !== binding.requestId
|
|
128
|
-
|| viewReceipt.receiptId !== receipt.receiptId
|
|
129
|
-
|| viewReceipt.revision !== receipt.revision) return false;
|
|
130
116
|
const responsePath = path.join(manifest.channelDir, 'responses', `${binding.requestId}.json`);
|
|
131
117
|
if (!inspectOwnedPath(path.join(controlRoot, 'channel'), responsePath, 'file')) return false;
|
|
132
118
|
const actual = readJsonFile(responsePath);
|
|
133
|
-
const
|
|
134
|
-
|
|
119
|
+
const response = actual as Record<string, unknown>;
|
|
120
|
+
if (response.version !== 2 || response.id !== binding.requestId || response.phase !== 'completed'
|
|
121
|
+
|| response.exitCode !== 0 || typeof response.stdout !== 'string') return false;
|
|
122
|
+
const envelope = JSON.parse(response.stdout) as Record<string, unknown>;
|
|
123
|
+
const result = envelope.result as Record<string, unknown> | null;
|
|
124
|
+
return envelope.status === 'completed' && envelope.accepted === true
|
|
125
|
+
&& result !== null && typeof result === 'object'
|
|
126
|
+
&& result.status === 'completed' && result.taskId === taskId;
|
|
135
127
|
} catch {
|
|
136
128
|
return false;
|
|
137
129
|
}
|
|
@@ -157,7 +149,7 @@ function removedControlBindingEvidence(
|
|
|
157
149
|
if (frontmatter.id !== taskId || frontmatter.branch !== journal.target.branch) return false;
|
|
158
150
|
try {
|
|
159
151
|
const receipt = readTaskFinalizationReceipt(repoRoot, taskId);
|
|
160
|
-
return receipt !== null && taskFinalizationReceiptComplete(receipt, taskId
|
|
152
|
+
return receipt !== null && taskFinalizationReceiptComplete(receipt, taskId);
|
|
161
153
|
} catch {
|
|
162
154
|
return false;
|
|
163
155
|
}
|
|
@@ -13,6 +13,9 @@ export type ArtifactCommand = Readonly<{
|
|
|
13
13
|
family: string;
|
|
14
14
|
artifact: string;
|
|
15
15
|
locale?: 'zh-CN' | 'en';
|
|
16
|
+
sourceFinding?: string;
|
|
17
|
+
sourceArtifact?: string;
|
|
18
|
+
sourceSha256?: string;
|
|
16
19
|
}>;
|
|
17
20
|
|
|
18
21
|
/** One option contract for the CLI and trusted projection executor. */
|
|
@@ -23,7 +26,7 @@ export function parseArtifactCommand(args: readonly string[]): ArtifactCommand {
|
|
|
23
26
|
}
|
|
24
27
|
const fields: Record<string, string> = {};
|
|
25
28
|
const allowed = {
|
|
26
|
-
inspect: ['--family'],
|
|
29
|
+
inspect: ['--family', '--source-finding', '--source-artifact', '--source-sha256'],
|
|
27
30
|
init: ['--family', '--artifact', '--locale'],
|
|
28
31
|
'preflight': ['--family', '--artifact'],
|
|
29
32
|
'finalize-local': ['--family', '--artifact']
|
|
@@ -41,8 +44,10 @@ export function parseArtifactCommand(args: readonly string[]): ArtifactCommand {
|
|
|
41
44
|
const locale = fields['--locale'];
|
|
42
45
|
if (locale !== undefined && locale !== 'zh-CN' && locale !== 'en') throw new Error("option '--locale' must be 'zh-CN' or 'en'");
|
|
43
46
|
return {
|
|
44
|
-
taskRef, operation: operation as ArtifactCommand['operation'], family: fields['--family'],
|
|
45
|
-
artifact: fields['--artifact'] ?? '', locale
|
|
47
|
+
taskRef, operation: operation as ArtifactCommand['operation'], family: fields['--family'] ?? '',
|
|
48
|
+
artifact: fields['--artifact'] ?? '', locale,
|
|
49
|
+
sourceFinding: fields['--source-finding'], sourceArtifact: fields['--source-artifact'],
|
|
50
|
+
sourceSha256: fields['--source-sha256']
|
|
46
51
|
};
|
|
47
52
|
}
|
|
48
53
|
/** Resolve authoritative task metadata separately from a trusted candidate directory. */
|
|
@@ -53,7 +58,12 @@ export function executeArtifactCommand(
|
|
|
53
58
|
const { taskRef, operation, family, artifact, locale } = command;
|
|
54
59
|
const fail = (code: string, message: string) => ({ status: 'failed', changed: false, error: { code, message } });
|
|
55
60
|
if (operation === 'inspect') {
|
|
56
|
-
const result = resolveArtifactContext(taskRef, family, {
|
|
61
|
+
const result = resolveArtifactContext(taskRef, family, {
|
|
62
|
+
repoRoot: options.repoRoot,
|
|
63
|
+
sourceFinding: command.sourceFinding,
|
|
64
|
+
sourceArtifact: command.sourceArtifact,
|
|
65
|
+
sourceSha256: command.sourceSha256
|
|
66
|
+
});
|
|
57
67
|
return family === 'code' && result.codeMode ? {
|
|
58
68
|
...result, mode: result.codeMode.mode, code_max: result.codeMode.codeMax, rev_max: result.codeMode.reviewMax,
|
|
59
69
|
verdict: result.codeMode.verdict, next_round: result.next?.round ?? null, next_artifact: result.next?.name ?? null,
|