@fitlab-ai/agent-infra 0.11.9 → 0.11.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/internal-cli.js +0 -6
- package/dist/lib/agent-clients/adapter.js +20 -0
- package/dist/lib/agent-clients/adapters/codex-sandbox.js +13 -0
- package/dist/lib/agent-clients/adapters/codex.js +4 -0
- package/dist/lib/git/workflow.js +1 -1
- package/dist/lib/internal/cli-route-inventory.js +0 -3
- package/dist/lib/internal/manual-validation.js +2 -2
- package/dist/lib/internal/platform-pr.js +4 -5
- package/dist/lib/internal/sandbox-control.js +30 -5
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-create.js +15 -1
- package/dist/lib/internal/task-finalization.js +14 -25
- package/dist/lib/internal/task-operation-registry.js +6 -141
- package/dist/lib/internal/task-platform-recovery.js +9 -4
- package/dist/lib/platform/completion-backfill.js +2 -1
- package/dist/lib/platform/context.js +4 -2
- package/dist/lib/platform/github-client.js +20 -17
- package/dist/lib/platform/github-provider.js +42 -22
- package/dist/lib/platform/github-release-notes.js +4 -2
- package/dist/lib/platform/in-label-sync.js +2 -1
- package/dist/lib/platform/issue-comments.js +13 -9
- package/dist/lib/platform/operation-coordinator.js +1 -1
- package/dist/lib/platform/pr-review.js +2 -1
- package/dist/lib/platform/pr-summary.js +45 -8
- package/dist/lib/platform/provider-bridge.js +4 -5
- package/dist/lib/platform/provider-contract.d.ts +3 -6
- package/dist/lib/platform/provider-contract.js +3 -15
- package/dist/lib/platform/provider-validation.js +9 -4
- package/dist/lib/platform/pull-requests.js +83 -16
- package/dist/lib/platform/verification-sync.js +17 -14
- package/dist/lib/sandbox/commands/create.js +18 -2
- package/dist/lib/sandbox/commands/enter.js +47 -33
- package/dist/lib/sandbox/control/client.js +99 -17
- package/dist/lib/sandbox/control/executor.js +12 -14
- package/dist/lib/sandbox/control/host-recovery.js +218 -0
- package/dist/lib/sandbox/control/protocol.js +2 -3
- package/dist/lib/sandbox/control/server.js +42 -2
- package/dist/lib/sandbox/environment.js +150 -0
- package/dist/lib/sandbox/recovery.js +8 -0
- package/dist/lib/sandbox/removal.js +0 -1
- package/dist/lib/sandbox/workspace-identity.js +0 -14
- package/dist/lib/sandbox/workspace-view.js +33 -6
- package/dist/lib/task/artifact-command.js +18 -3
- package/dist/lib/task/artifact-lifecycle.js +121 -63
- package/dist/lib/task/artifact-selection.js +1 -1
- package/dist/lib/task/capabilities.js +59 -22
- package/dist/lib/task/commands/status.js +0 -27
- package/dist/lib/task/commit-identity.js +22 -7
- package/dist/lib/task/commit-intent.js +67 -26
- package/dist/lib/task/commit-operation.js +19 -8
- package/dist/lib/task/completion-facts.js +32 -10
- package/dist/lib/task/control-authority.js +2 -6
- package/dist/lib/task/current-contract.js +1 -5
- package/dist/lib/task/events.js +9 -275
- package/dist/lib/task/execution-inputs.js +14 -0
- package/dist/lib/task/finalization-envelope.js +4 -0
- package/dist/lib/task/finalization.js +43 -86
- package/dist/lib/task/frontmatter.js +25 -2
- package/dist/lib/task/ledger-intents.js +6 -8
- package/dist/lib/task/orchestration.js +11 -10
- package/dist/lib/task/platform-operation-journal.js +29 -10
- package/dist/lib/task/platform-operation-recovery.js +109 -35
- package/dist/lib/task/resolve-ref.js +8 -3
- package/dist/lib/task/review-fingerprint.js +4 -26
- package/dist/lib/task/verification-engine.js +95 -6
- package/dist/lib/task/write.js +0 -11
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +22 -0
- package/lib/agent-clients/adapters/codex-sandbox.ts +13 -0
- package/lib/agent-clients/adapters/codex.ts +4 -0
- package/lib/git/workflow.ts +1 -1
- package/lib/internal/cli-route-inventory.ts +0 -2
- package/lib/internal/manual-validation.ts +2 -2
- package/lib/internal/platform-pr.ts +3 -4
- package/lib/internal/sandbox-control.ts +39 -6
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-create.ts +15 -2
- package/lib/internal/task-finalization.ts +16 -26
- package/lib/internal/task-operation-registry.ts +6 -153
- package/lib/internal/task-platform-recovery.ts +9 -4
- package/lib/platform/completion-backfill.ts +2 -1
- package/lib/platform/context.ts +8 -4
- package/lib/platform/github-client.ts +24 -20
- package/lib/platform/github-provider.ts +47 -26
- package/lib/platform/github-release-notes.ts +5 -3
- package/lib/platform/in-label-sync.ts +2 -1
- package/lib/platform/issue-comments.ts +14 -9
- package/lib/platform/operation-coordinator.ts +1 -1
- package/lib/platform/pr-review.ts +2 -1
- package/lib/platform/pr-summary.ts +43 -7
- package/lib/platform/provider-bridge.ts +10 -9
- package/lib/platform/provider-contract.ts +6 -21
- package/lib/platform/provider-loader.ts +7 -6
- package/lib/platform/provider-validation.ts +30 -8
- package/lib/platform/pull-requests.ts +78 -15
- package/lib/platform/repository-metadata.ts +3 -2
- package/lib/platform/security-alerts.ts +1 -1
- package/lib/platform/types.ts +2 -0
- package/lib/platform/verification-sync.ts +18 -14
- package/lib/sandbox/commands/create.ts +24 -2
- package/lib/sandbox/commands/enter.ts +42 -39
- package/lib/sandbox/control/client.ts +107 -19
- package/lib/sandbox/control/executor.ts +13 -15
- package/lib/sandbox/control/host-recovery.ts +195 -0
- package/lib/sandbox/control/protocol.ts +4 -5
- package/lib/sandbox/control/server.ts +42 -1
- package/lib/sandbox/environment.ts +162 -0
- package/lib/sandbox/recovery.ts +10 -0
- package/lib/sandbox/removal.ts +2 -10
- package/lib/sandbox/tool-types.ts +1 -0
- package/lib/sandbox/workspace-identity.ts +0 -26
- package/lib/sandbox/workspace-view.ts +33 -8
- package/lib/task/artifact-command.ts +19 -3
- package/lib/task/artifact-lifecycle.ts +102 -53
- package/lib/task/artifact-selection.ts +1 -1
- package/lib/task/capabilities.ts +52 -26
- package/lib/task/commands/status.ts +0 -31
- package/lib/task/commit-identity.ts +24 -7
- package/lib/task/commit-intent.ts +58 -24
- package/lib/task/commit-operation.ts +17 -8
- package/lib/task/completion-facts.ts +31 -9
- package/lib/task/control-authority.ts +1 -8
- package/lib/task/current-contract.ts +1 -6
- package/lib/task/events.ts +9 -248
- package/lib/task/execution-inputs.ts +25 -0
- package/lib/task/finalization-envelope.ts +21 -0
- package/lib/task/finalization.ts +40 -82
- package/lib/task/frontmatter.ts +26 -2
- package/lib/task/ledger-intents.ts +6 -8
- package/lib/task/orchestration.ts +11 -17
- package/lib/task/platform-operation-journal.ts +47 -13
- package/lib/task/platform-operation-recovery.ts +102 -37
- package/lib/task/resolve-ref.ts +8 -3
- package/lib/task/review-fingerprint.ts +4 -26
- package/lib/task/verification-engine.ts +103 -9
- package/lib/task/write.ts +0 -19
- package/package.json +1 -1
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/commit-and-pr.en.md +5 -5
- package/templates/.agents/rules/commit-and-pr.zh-CN.md +5 -5
- package/templates/.agents/rules/decision-qualification.en.md +1 -1
- package/templates/.agents/rules/decision-qualification.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +1 -1
- package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
- package/templates/.agents/rules/no-mid-flow-questions.en.md +19 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +19 -0
- package/templates/.agents/rules/review-handshake.en.md +3 -0
- package/templates/.agents/rules/review-handshake.zh-CN.md +3 -0
- package/templates/.agents/rules/task-management.en.md +1 -1
- package/templates/.agents/rules/task-management.zh-CN.md +1 -1
- package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -0
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -0
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -0
- package/templates/.agents/skills/code-task/SKILL.en.md +25 -9
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +27 -10
- package/templates/.agents/skills/code-task/reference/branch-management.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/branch-management.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +2 -0
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +2 -0
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +5 -16
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +5 -16
- 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-manual-validation/SKILL.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +15 -12
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +11 -10
- package/templates/.agents/skills/create-pr/SKILL.en.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/reference/branch-strategy.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/branch-strategy.zh-CN.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.en.md +3 -5
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +3 -4
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.en.md +4 -4
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/review-pr/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test/SKILL.en.md +4 -1
- package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -1
- package/templates/.agents/skills/test-integration/SKILL.en.md +5 -2
- package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +5 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -2
- package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +5 -2
- package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +5 -2
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +0 -12
- package/templates/.github/workflows/pr-label.yml +1 -2
- package/dist/lib/internal/task-invalidation.js +0 -63
- package/dist/lib/task/finalization-handoff.js +0 -93
- package/dist/lib/task/intermediate-cleanup.js +0 -327
- package/dist/lib/task/invalidation-command.js +0 -88
- package/dist/lib/task/invalidation.js +0 -242
- package/lib/internal/task-invalidation.ts +0 -41
- package/lib/task/finalization-handoff.ts +0 -105
- package/lib/task/intermediate-cleanup.ts +0 -393
- package/lib/task/invalidation-command.ts +0 -88
- package/lib/task/invalidation.ts +0 -313
|
@@ -19,7 +19,7 @@ import { inspectDecisionDetailDuplicates } from "./decision-details.js";
|
|
|
19
19
|
import { parseImplementationInputs } from "./implementation-inputs.js";
|
|
20
20
|
import { scanVisibleMarkdown } from "./markdown.js";
|
|
21
21
|
import { loadVerificationConfig } from "./verification-config.js";
|
|
22
|
-
import { inspectActivityLog } from "./activity-log.js";
|
|
22
|
+
import { inspectActivityLog, pairEntries } from "./activity-log.js";
|
|
23
23
|
import { parseWorkflowWarnings } from "./workflow-warnings.js";
|
|
24
24
|
import { OrchestrationStateError, readRun } from "./orchestration.js";
|
|
25
25
|
import { readPrDeliveryFact } from "./pr-delivery-fact.js";
|
|
@@ -30,9 +30,11 @@ import { normalizeVerificationRecord } from './gate-policy.js';
|
|
|
30
30
|
import { manualValidationFinalSummaryProjectionMatches } from "./manual-validation-receipt.js";
|
|
31
31
|
import { readManualValidationCompletion } from "./manual-validation-completion.js";
|
|
32
32
|
import { sha256File } from "./artifact-receipts.js";
|
|
33
|
+
import { inspectArtifactDirectory } from "./artifact-lifecycle.js";
|
|
34
|
+
import { parseArtifactName } from "./artifact-name.js";
|
|
33
35
|
import { summaryCommentState } from "../platform/pr-summary.js";
|
|
34
36
|
import { taskIssueIdentity } from "../platform/task-identities.js";
|
|
35
|
-
import { hasArtifactCompletionFact, hasArtifactCompletionLog } from './completion-facts.js';
|
|
37
|
+
import { hasArtifactCompletionFact, hasArtifactCompletionLog, parseCompletionFacts } from './completion-facts.js';
|
|
36
38
|
import { inspectReviewIdentity } from './review-identity.js';
|
|
37
39
|
const TASK_ENUMS = {
|
|
38
40
|
type: ["feature", "bugfix", "refactor", "docs", "chore"],
|
|
@@ -264,7 +266,7 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
264
266
|
return passResult('orchestration-evidence', 'Persisted orchestration model and delegation evidence is internally consistent');
|
|
265
267
|
}
|
|
266
268
|
// === Check Functions ===
|
|
267
|
-
function checkTaskMeta({ taskDir, config, repositoryRoot }) {
|
|
269
|
+
function checkTaskMeta({ taskDir, config, repositoryRoot, skillName, artifactFile }) {
|
|
268
270
|
const task = loadTask(taskDir);
|
|
269
271
|
if (!task.ok) {
|
|
270
272
|
return failResult("task-meta", task.message);
|
|
@@ -306,7 +308,22 @@ function checkTaskMeta({ taskDir, config, repositoryRoot }) {
|
|
|
306
308
|
}
|
|
307
309
|
const expectedStep = config.expected_step;
|
|
308
310
|
if (expectedStep && metadata.current_step !== expectedStep) {
|
|
309
|
-
|
|
311
|
+
let validLifecycleArtifact = false;
|
|
312
|
+
if (currentTask) {
|
|
313
|
+
const activityConfig = loadVerificationConfig(repositoryRoot, skillName).checks?.["activity-log"];
|
|
314
|
+
const { section, invalidEntries } = inspectActivityLog(task.content);
|
|
315
|
+
if (activityConfig && section && invalidEntries.length === 0) {
|
|
316
|
+
let latestAction = "";
|
|
317
|
+
for (const entry of section.entries) {
|
|
318
|
+
if (!ACTIVITY_LOG_STARTED_RE.test(entry.step))
|
|
319
|
+
latestAction = entry.step;
|
|
320
|
+
}
|
|
321
|
+
validLifecycleArtifact = checkArtifactLifecycleActivityLog(taskDir, artifactFile, skillName, activityConfig, task, section.entries, latestAction)?.status === "pass";
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (!validLifecycleArtifact) {
|
|
325
|
+
return failResult("task-meta", `Expected current_step '${expectedStep}', got '${metadata.current_step || "(empty)"}'`);
|
|
326
|
+
}
|
|
310
327
|
}
|
|
311
328
|
const expectedStatus = config.expected_status;
|
|
312
329
|
if (expectedStatus && metadata.status !== expectedStatus) {
|
|
@@ -604,7 +621,73 @@ function checkImplementationInput({ taskDir, artifactFile }) {
|
|
|
604
621
|
}
|
|
605
622
|
return passResult("implementation-input", `${actionDecision} matches Activity Log, report, and task table`);
|
|
606
623
|
}
|
|
607
|
-
|
|
624
|
+
const ARTIFACT_ACTIVITY_STAGES = {
|
|
625
|
+
"analyze-task": { family: "analysis", event: "analyze.completed" },
|
|
626
|
+
"plan-task": { family: "plan", event: "plan.completed" },
|
|
627
|
+
"review-analysis": { family: "review-analysis", event: "review-analysis.completed" },
|
|
628
|
+
"review-plan": { family: "review-plan", event: "review-plan.completed" },
|
|
629
|
+
"code-task": { family: "code", event: "code.completed" },
|
|
630
|
+
"review-code": { family: "review-code", event: "review-code.completed" }
|
|
631
|
+
};
|
|
632
|
+
function checkArtifactLifecycleActivityLog(taskDir, artifactFile, skillName, config, task, entries, latestAction) {
|
|
633
|
+
const stage = ARTIFACT_ACTIVITY_STAGES[skillName];
|
|
634
|
+
if (!stage)
|
|
635
|
+
return null;
|
|
636
|
+
const inventory = inspectArtifactDirectory(taskDir, stage.family);
|
|
637
|
+
if (inventory.status !== "ready" || !inventory.latest) {
|
|
638
|
+
return failResult("activity-log", `Current ${stage.family} artifact is unavailable: ${inventory.error?.message ?? "no artifact exists"}`);
|
|
639
|
+
}
|
|
640
|
+
const output = artifactFile || inventory.latest.name;
|
|
641
|
+
if (output !== inventory.latest.name) {
|
|
642
|
+
return failResult("activity-log", `Artifact '${output}' is not the current ${stage.family} artifact; rerun ${skillName} for '${inventory.latest.name}'`);
|
|
643
|
+
}
|
|
644
|
+
const identity = parseArtifactName(output);
|
|
645
|
+
if (!identity || identity.family !== stage.family) {
|
|
646
|
+
return failResult("activity-log", `Artifact '${output}' does not belong to ${stage.family}`);
|
|
647
|
+
}
|
|
648
|
+
if (!hasArtifactCompletionFact(task.content, path.join(taskDir, output), stage.event)) {
|
|
649
|
+
return failResult("activity-log", `Completion fact for '${output}' is missing or stale; rerun ${skillName} to record a current completion`);
|
|
650
|
+
}
|
|
651
|
+
const frontmatter = parseTypedTaskFrontmatter(task.content);
|
|
652
|
+
const fact = parseCompletionFacts(frontmatter.completion_facts).find((item) => item.event === stage.event && item.output === output);
|
|
653
|
+
if (!fact)
|
|
654
|
+
return failResult("activity-log", `Completion fact for '${output}' is missing; rerun ${skillName}`);
|
|
655
|
+
if (!Array.isArray(fact.lifecycleInputs)) {
|
|
656
|
+
return failResult("activity-log", `Completion fact for '${output}' has no input snapshot; rerun ${skillName}`);
|
|
657
|
+
}
|
|
658
|
+
for (const input of fact.lifecycleInputs) {
|
|
659
|
+
if (!input || typeof input.name !== "string" || !/^[a-f0-9]{64}$/u.test(input.sha256)) {
|
|
660
|
+
return failResult("activity-log", `Completion fact for '${output}' has an invalid input snapshot; rerun ${skillName}`);
|
|
661
|
+
}
|
|
662
|
+
const inputIdentity = parseArtifactName(input.name);
|
|
663
|
+
if (!inputIdentity)
|
|
664
|
+
return failResult("activity-log", `Completion fact for '${output}' has invalid input '${input.name}'; rerun ${skillName}`);
|
|
665
|
+
const inputInventory = inspectArtifactDirectory(taskDir, inputIdentity.family);
|
|
666
|
+
if (inputInventory.status !== "ready" || inputInventory.latest?.name !== input.name
|
|
667
|
+
|| sha256File(path.join(taskDir, input.name)) !== input.sha256) {
|
|
668
|
+
return failResult("activity-log", `Input '${input.name}' changed or is no longer current; rerun ${skillName} to refresh '${output}'`);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
const expectedAction = new RegExp(config.expected_action_pattern);
|
|
672
|
+
const roundAction = entries.find((entry) => {
|
|
673
|
+
if (ACTIVITY_LOG_STARTED_RE.test(entry.step))
|
|
674
|
+
return false;
|
|
675
|
+
if (!expectedAction.test(entry.step))
|
|
676
|
+
return false;
|
|
677
|
+
expectedAction.lastIndex = 0;
|
|
678
|
+
const actionRound = /\(Round (\d+)/.exec(entry.step)?.[1];
|
|
679
|
+
return actionRound === String(identity.round) && entry.note.includes(`→ ${output}`);
|
|
680
|
+
});
|
|
681
|
+
if (!roundAction) {
|
|
682
|
+
return failResult("activity-log", `No completed Activity Log row records '${output}' for ${skillName} Round ${identity.round}`);
|
|
683
|
+
}
|
|
684
|
+
if (skillName === "plan-task" && /^(?:Human Decision|Decision)\b/.test(latestAction)
|
|
685
|
+
&& expectedAction.test(latestAction)) {
|
|
686
|
+
return passResult("activity-log", `Plan completion '${output}' is current; latest action '${latestAction}' is a human decision`);
|
|
687
|
+
}
|
|
688
|
+
return passResult("activity-log", `Current ${stage.family} completion '${output}' is valid for Round ${identity.round}`);
|
|
689
|
+
}
|
|
690
|
+
function checkActivityLog({ taskDir, config, skillName, artifactFile }) {
|
|
608
691
|
const task = loadTask(taskDir);
|
|
609
692
|
if (!task.ok) {
|
|
610
693
|
return failResult("activity-log", task.message);
|
|
@@ -638,6 +721,9 @@ function checkActivityLog({ taskDir, config }) {
|
|
|
638
721
|
doneActions.push(action);
|
|
639
722
|
}
|
|
640
723
|
}
|
|
724
|
+
const artifactLifecycleResult = checkArtifactLifecycleActivityLog(taskDir, artifactFile, skillName, config, task, entries, latestAction);
|
|
725
|
+
if (artifactLifecycleResult)
|
|
726
|
+
return artifactLifecycleResult;
|
|
641
727
|
if (config.expected_action_pattern && !new RegExp(config.expected_action_pattern).test(latestAction)) {
|
|
642
728
|
const expected = new RegExp(config.expected_action_pattern);
|
|
643
729
|
let index = doneActions.length - 1;
|
|
@@ -799,7 +885,10 @@ async function checkManualValidation({ taskDir, repositoryRoot }) {
|
|
|
799
885
|
const inspectedLog = inspectActivityLog(task.content);
|
|
800
886
|
if (!inspectedLog.section)
|
|
801
887
|
return failResult("manual-validation", "Activity Log is missing or invalid");
|
|
802
|
-
const
|
|
888
|
+
const reviewRow = pairEntries(inspectedLog.section.entries).find((row) => row.step === `Review Code (Round ${review.round})` && row.started !== '' && row.done !== '');
|
|
889
|
+
const reviewDoneIndex = reviewRow
|
|
890
|
+
? inspectedLog.section.entries.findIndex((entry) => entry.step === reviewRow.step && entry.time === reviewRow.done)
|
|
891
|
+
: -1;
|
|
803
892
|
const completedIndex = inspectedLog.section.entries.findIndex((entry) => entry.step === "Complete Manual Validation"
|
|
804
893
|
&& entry.note.includes(`Manual validation passed → ${artifactName};`)
|
|
805
894
|
&& entry.note.includes(`transaction=${receipt.transactionId};`)
|
package/dist/lib/task/write.js
CHANGED
|
@@ -6,7 +6,6 @@ import { parseTypedTaskFrontmatter, updateTaskFrontmatter } from './frontmatter.
|
|
|
6
6
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
7
7
|
import { mutateTableRow, upsertSection } from './sections.js';
|
|
8
8
|
import { validateCurrentTaskContract } from './current-contract.js';
|
|
9
|
-
import { invalidationBlocks, parseInvalidationDocument } from './invalidation.js';
|
|
10
9
|
const DEFAULT_FILE_SYSTEM = {
|
|
11
10
|
readFileSync: (file) => fs.readFileSync(file, 'utf8'),
|
|
12
11
|
statModeSync: (file) => fs.statSync(file).mode,
|
|
@@ -91,16 +90,6 @@ function writeTaskCore(request, options = {}) {
|
|
|
91
90
|
if (!contract.ok) {
|
|
92
91
|
return failure(request, identity, contract.code, contract.message);
|
|
93
92
|
}
|
|
94
|
-
if (invalidationBlocks(contract.invalidation)
|
|
95
|
-
&& options.invalidationContext !== 'source-completion'
|
|
96
|
-
&& options.invalidationContext !== 'reconcile') {
|
|
97
|
-
return failure(request, identity, 'TASK_INVALIDATION_BLOCKED', 'task has an incomplete artifact invalidation operation; reconcile it before writing downstream state');
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
const invalidation = parseInvalidationDocument(original);
|
|
102
|
-
if (!invalidation.ok)
|
|
103
|
-
return failure(request, identity, 'TASK_INVALIDATION_INVALID', invalidation.message);
|
|
104
93
|
}
|
|
105
94
|
let candidate = original;
|
|
106
95
|
const operations = [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"protocolVersion": 3,
|
|
3
|
-
"packageVersion": "0.11.
|
|
4
|
-
"sourceInputHash": "
|
|
5
|
-
"internalExecutableBuildHash": "
|
|
3
|
+
"packageVersion": "0.11.11",
|
|
4
|
+
"sourceInputHash": "576098e3f88ceca11a0a0f94573a3ceca2b14c97b29a6b934ae07849e214c889",
|
|
5
|
+
"internalExecutableBuildHash": "065375c31d3564a65236fc43bad9947395ea0873ae2531d9b05f42956ef30f1e"
|
|
6
6
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
import {
|
|
2
3
|
AGENT_CLIENT_CAPABILITY_IDS,
|
|
3
4
|
AGENT_CLIENT_SUPPORT_LEVELS,
|
|
@@ -216,6 +217,26 @@ function freezeTool(tool: SandboxTool): SandboxTool {
|
|
|
216
217
|
if (!tool.containerMount.startsWith('/')) {
|
|
217
218
|
throw new Error(`Sandbox tool '${tool.id}' containerMount must be absolute`);
|
|
218
219
|
}
|
|
220
|
+
if (tool.hostStateMounts !== undefined && !Array.isArray(tool.hostStateMounts)) {
|
|
221
|
+
throw new Error(`Agent Client '${tool.id}' hostStateMounts must be an array`);
|
|
222
|
+
}
|
|
223
|
+
const hostStateMounts = tool.hostStateMounts?.map((entry) => {
|
|
224
|
+
if (!entry || typeof entry.hostSubdir !== 'string' || typeof entry.containerSubpath !== 'string') {
|
|
225
|
+
throw new Error(`Agent Client '${tool.id}' hostStateMounts must contain relative path pairs`);
|
|
226
|
+
}
|
|
227
|
+
for (const value of [entry.hostSubdir, entry.containerSubpath]) {
|
|
228
|
+
const normalized = path.posix.normalize(value);
|
|
229
|
+
if (
|
|
230
|
+
value === ''
|
|
231
|
+
|| path.posix.isAbsolute(value)
|
|
232
|
+
|| normalized === '..'
|
|
233
|
+
|| normalized.startsWith('../')
|
|
234
|
+
) {
|
|
235
|
+
throw new Error(`Agent Client '${tool.id}' hostStateMounts must stay within the tool directories`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return Object.freeze({ ...entry });
|
|
239
|
+
});
|
|
219
240
|
if (!tool.versionCmd) {
|
|
220
241
|
throw new Error(`Sandbox tool '${tool.id}' requires versionCmd`);
|
|
221
242
|
}
|
|
@@ -233,6 +254,7 @@ function freezeTool(tool: SandboxTool): SandboxTool {
|
|
|
233
254
|
...(tool.hostLiveMounts ? {
|
|
234
255
|
hostLiveMounts: Object.freeze(tool.hostLiveMounts.map((entry) => Object.freeze({ ...entry })))
|
|
235
256
|
} : {}),
|
|
257
|
+
...(hostStateMounts ? { hostStateMounts: Object.freeze(hostStateMounts) } : {}),
|
|
236
258
|
...(tool.postSetupCmds ? { postSetupCmds: Object.freeze([...tool.postSetupCmds]) } : {}),
|
|
237
259
|
...(tool.tmpfs ? {
|
|
238
260
|
tmpfs: Object.freeze({
|
|
@@ -285,6 +285,19 @@ const codexRecoveryChecks: readonly AgentClientSandboxRecoveryCheck[] = [
|
|
|
285
285
|
path: '/home/devuser/.codex'
|
|
286
286
|
}
|
|
287
287
|
},
|
|
288
|
+
{
|
|
289
|
+
id: 'app-server-package-writable',
|
|
290
|
+
probe: {
|
|
291
|
+
script: 'probe="$1/.agent-infra-app-server-package-$$"; trap \'rm -f -- "$probe"\' EXIT; : > "$probe"',
|
|
292
|
+
args: ['/home/devuser/.codex/packages/app-server-daemon'],
|
|
293
|
+
user: 'devuser'
|
|
294
|
+
},
|
|
295
|
+
finding: {
|
|
296
|
+
repairKind: 'hard-failure',
|
|
297
|
+
message: 'Codex app-server package directory is not writable by devuser.',
|
|
298
|
+
path: '/home/devuser/.codex/packages/app-server-daemon'
|
|
299
|
+
}
|
|
300
|
+
},
|
|
288
301
|
{
|
|
289
302
|
id: 'prompts-link',
|
|
290
303
|
when: {
|
|
@@ -55,6 +55,10 @@ const codexAdapter = defineAgentClientAdapter({
|
|
|
55
55
|
versionCmd: 'codex --version',
|
|
56
56
|
setupHint: 'Run codex once inside the container and choose Device Code login if needed.',
|
|
57
57
|
tmpfs: { size: '512m', seed: ['config.toml', 'model-catalogs'], exec: true },
|
|
58
|
+
hostStateMounts: [{
|
|
59
|
+
hostSubdir: 'packages/app-server-daemon',
|
|
60
|
+
containerSubpath: 'packages/app-server-daemon'
|
|
61
|
+
}],
|
|
58
62
|
hostLiveMounts: [
|
|
59
63
|
{
|
|
60
64
|
hostPath: hostJoin(home, '.codex', 'auth.json'),
|
package/lib/git/workflow.ts
CHANGED
|
@@ -100,7 +100,7 @@ function commitExplicitPaths(input: { cwd: string; paths: readonly string[]; mes
|
|
|
100
100
|
const tree = value(runner, input.cwd, ['write-tree']);
|
|
101
101
|
if (tree !== input.expectedTree) return { status: 'failed' as const, changed: false, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations: [{ name: 'stage', status: 'applied' as const }], error: { code: 'GIT_TREE_MISMATCH', message: `Expected staged tree ${input.expectedTree}, received ${tree ?? 'unavailable'}` } };
|
|
102
102
|
}
|
|
103
|
-
const commit = run(runner, input.cwd, ['commit', '-m', input.message]);
|
|
103
|
+
const commit = run(runner, input.cwd, ['commit', '--cleanup=verbatim', '-m', input.message]);
|
|
104
104
|
const after = inspectGitWorkflow(input.cwd, runner);
|
|
105
105
|
if (commit.status !== 0) return { status: 'failed' as const, changed: false, snapshot: after.snapshot, operations: [{ name: 'commit', status: 'failed' as const, message: commit.stderr.trim() }], error: { code: 'GIT_COMMIT_FAILED', message: commit.stderr.trim() } };
|
|
106
106
|
return { status: 'applied' as const, changed: true, snapshot: after.snapshot, operations: [{ name: 'stage', status: 'applied' as const }, { name: 'commit', status: 'applied' as const }], error: null };
|
|
@@ -60,7 +60,6 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
60
60
|
'task-orchestration': ['status', 'progress'],
|
|
61
61
|
'task-review': ['preflight', 'finalize-summary'],
|
|
62
62
|
'task-event': ['event'],
|
|
63
|
-
'task-invalidation': ['reconcile'],
|
|
64
63
|
'task-lifecycle': ['intent'],
|
|
65
64
|
'task-finalization': ['complete'],
|
|
66
65
|
'task-platform-recovery': ['inspect', 'recover'],
|
|
@@ -98,7 +97,6 @@ export function internalRouteSelector(command: string, args: readonly string[]):
|
|
|
98
97
|
if (command === 'task-short-id') return first(args) === 'list' && args.includes('--verify') ? 'list-verify' : first(args);
|
|
99
98
|
if (command === 'task-orchestration') return args[1] === 'status' ? 'status' : args[1] ? 'progress' : '';
|
|
100
99
|
if (command === 'task-review') return args[1] ?? '';
|
|
101
|
-
if (command === 'task-invalidation') return args[1] ?? '';
|
|
102
100
|
if (command === 'task-artifact') return args[1] ?? '';
|
|
103
101
|
if (command === 'platform-pr-review' && first(args) === 'publish') {
|
|
104
102
|
const scope = optionValue(args, '--scope') ?? '';
|
|
@@ -206,7 +206,7 @@ async function executeManualValidationTransactionLocked(
|
|
|
206
206
|
if (transaction.phase === 'prepared') {
|
|
207
207
|
const staged = await syncPullRequestSummary(taskRef, {
|
|
208
208
|
cwd, client: options.client, agent, body: pendingBody, changeReportFile: path.resolve(cwd, values.changeReportFile!), primaryResult: primaryResult as 'pr_created' | 'pr_reused' | 'no_op', strict: true,
|
|
209
|
-
manualValidation: { phase: 'pending' }, lockAlreadyHeld: true
|
|
209
|
+
manualValidation: { phase: 'pending', transactionId: transaction.transactionId }, lockAlreadyHeld: true
|
|
210
210
|
});
|
|
211
211
|
if (!['applied', 'no-op'].includes(staged.status)) transactionFailure(staged.error ?? { code: 'MANUAL_VALIDATION_TRANSACTION_FAILED', message: 'pending summary staging failed' });
|
|
212
212
|
transitionAndPersist('summary-staged');
|
|
@@ -264,7 +264,7 @@ async function executeManualValidationTransactionLocked(
|
|
|
264
264
|
try {
|
|
265
265
|
await syncPullRequestSummary(taskRef, {
|
|
266
266
|
cwd, client: options.client, agent, body: pendingBody, changeReportFile: path.resolve(cwd, values.changeReportFile!), primaryResult: primaryResult as 'pr_created' | 'pr_reused' | 'no_op', strict: true,
|
|
267
|
-
manualValidation: { phase: 'pending' }, lockAlreadyHeld: true
|
|
267
|
+
manualValidation: { phase: 'pending', transactionId: transaction.transactionId }, lockAlreadyHeld: true
|
|
268
268
|
});
|
|
269
269
|
} catch {
|
|
270
270
|
// Preserve recovery-required when pending compensation cannot be confirmed.
|
|
@@ -22,7 +22,7 @@ const USAGE = `Usage: agent-infra-internal platform-pr inspect <task-ref> [--cwd
|
|
|
22
22
|
agent-infra-internal platform-pr bind <task-ref> --pr <token> --agent <agent> [--dry-run] [--cwd <path>]
|
|
23
23
|
agent-infra-internal platform-pr skip <task-ref> --agent <agent> [--dry-run] [--cwd <path>]
|
|
24
24
|
agent-infra-internal platform-pr sync <task-ref> --agent <agent> [--metadata] [--closing-issue] --result <pr_created|pr_reused|no_op> [--dry-run] [--cwd <path>]
|
|
25
|
-
agent-infra-internal platform-pr sync-in-labels --pr <
|
|
25
|
+
agent-infra-internal platform-pr sync-in-labels --pr <token> [--dry-run] [--cwd <path>]
|
|
26
26
|
agent-infra-internal platform-pr summary-context <task-ref> [--cwd <path>]
|
|
27
27
|
agent-infra-internal platform-pr change-report <task-ref> --agent <agent> --mechanical-file <path> --precheck-file <path> [--dry-run] [--cwd <path>]
|
|
28
28
|
agent-infra-internal platform-pr summary-sync <task-ref> --agent <agent> --body-file <path|-> --change-report-file <path> --result <pr_created|pr_reused|no_op> [--manual-phase pending|final ...] [--strict] [--dry-run] [--cwd <path>]
|
|
@@ -89,10 +89,9 @@ async function platformPr(args: string[] = []): Promise<void> {
|
|
|
89
89
|
const values = parsed.values;
|
|
90
90
|
const unexpected = Object.keys(values).find((name) => !['cwd', 'pr', 'dryRun'].includes(name));
|
|
91
91
|
if (unexpected) { fail(`sync-in-labels does not accept --${unexpected}`); return; }
|
|
92
|
-
|
|
93
|
-
if (!Number.isInteger(pr) || pr <= 0) { fail('sync-in-labels requires a positive --pr'); return; }
|
|
92
|
+
if (typeof values.pr !== 'string' || !values.pr || values.pr.trim() !== values.pr) { fail('sync-in-labels requires a non-empty --pr token'); return; }
|
|
94
93
|
const cwd = path.resolve(typeof values.cwd === 'string' ? values.cwd : process.cwd());
|
|
95
|
-
finish(await syncPlatformPullRequestInLabels(pr, { cwd, dryRun: values.dryRun === true }));
|
|
94
|
+
finish(await syncPlatformPullRequestInLabels(values.pr, { cwd, dryRun: values.dryRun === true }));
|
|
96
95
|
return;
|
|
97
96
|
}
|
|
98
97
|
const taskRef = args[1];
|
|
@@ -9,6 +9,7 @@ import { serveSandboxControl } from '../sandbox/control/server.ts';
|
|
|
9
9
|
import { runSandboxControlExecutor } from '../sandbox/control/executor.ts';
|
|
10
10
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
|
|
11
11
|
import { parseTaskCreateResult, taskCreateExitCode } from '../task/create-service.ts';
|
|
12
|
+
import { serializeTaskFinalizationEnvelope, type TaskFinalizationEnvelope } from '../task/finalization-envelope.ts';
|
|
12
13
|
|
|
13
14
|
type FinalizationStatus = 'completed' | 'failed' | 'blocked' | 'unknown';
|
|
14
15
|
|
|
@@ -16,10 +17,11 @@ function writeFinalizationEnvelope(
|
|
|
16
17
|
status: FinalizationStatus,
|
|
17
18
|
accepted: boolean,
|
|
18
19
|
error: { code: string; message: string; retryable: boolean },
|
|
19
|
-
changed = false,
|
|
20
|
-
result: unknown = null
|
|
20
|
+
changed: boolean | null = false,
|
|
21
|
+
result: unknown = null,
|
|
22
|
+
requestId: string | null = null
|
|
21
23
|
): void {
|
|
22
|
-
process.stdout.write(
|
|
24
|
+
process.stdout.write(serializeTaskFinalizationEnvelope({ status, changed, accepted, requestId, result, error }));
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
function finalizationErrorStatus(error: { retryable: boolean; code: string }): FinalizationStatus {
|
|
@@ -43,6 +45,28 @@ function recoveredExitCode(response: Awaited<ReturnType<typeof recoverSandboxCon
|
|
|
43
45
|
return response.exitCode ?? 1;
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
function finalizationResponseOutput(response: Awaited<ReturnType<typeof requestSandboxTaskFinalization>>): string {
|
|
49
|
+
try {
|
|
50
|
+
const value = JSON.parse(response.stdout) as Partial<TaskFinalizationEnvelope>;
|
|
51
|
+
if (value.version === 2
|
|
52
|
+
&& value.status
|
|
53
|
+
&& ['completed', 'failed', 'blocked', 'unknown'].includes(value.status)
|
|
54
|
+
&& (typeof value.changed === 'boolean' || value.changed === null)) {
|
|
55
|
+
return serializeTaskFinalizationEnvelope({
|
|
56
|
+
status: value.status,
|
|
57
|
+
changed: value.changed,
|
|
58
|
+
accepted: true,
|
|
59
|
+
requestId: response.id,
|
|
60
|
+
result: value.result ?? null,
|
|
61
|
+
error: value.error ?? null
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
// Preserve non-envelope output for diagnostics from the executor.
|
|
66
|
+
}
|
|
67
|
+
return response.stdout;
|
|
68
|
+
}
|
|
69
|
+
|
|
46
70
|
function sandboxFinalizationClient(args: string[]): void {
|
|
47
71
|
if (args.length !== 4 || !args[0] || args[1] !== 'complete' || args[2] !== '--agent' || !args[3]) {
|
|
48
72
|
const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message: 'task ref, complete intent, and --agent are required', retryable: false };
|
|
@@ -63,7 +87,8 @@ function sandboxFinalizationClient(args: string[]): void {
|
|
|
63
87
|
response = requestSandboxTaskFinalization({ agent });
|
|
64
88
|
} catch (error) {
|
|
65
89
|
if (!(error instanceof SandboxControlClientError)) throw error;
|
|
66
|
-
|
|
90
|
+
const status = finalizationErrorStatus(error.detail);
|
|
91
|
+
writeFinalizationEnvelope(status, error.accepted, error.detail, status === 'unknown' ? null : false, null, error.requestId);
|
|
67
92
|
writeClientError(error);
|
|
68
93
|
process.exitCode = error.detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.detail.retryable ? 2 : 1;
|
|
69
94
|
return;
|
|
@@ -74,12 +99,20 @@ function sandboxFinalizationClient(args: string[]): void {
|
|
|
74
99
|
message: 'sandbox control rejected the finalization request',
|
|
75
100
|
retryable: false
|
|
76
101
|
};
|
|
77
|
-
|
|
102
|
+
const status = finalizationErrorStatus(error);
|
|
103
|
+
writeFinalizationEnvelope(
|
|
104
|
+
status,
|
|
105
|
+
error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN',
|
|
106
|
+
error,
|
|
107
|
+
status === 'unknown' ? null : false,
|
|
108
|
+
null,
|
|
109
|
+
response.id
|
|
110
|
+
);
|
|
78
111
|
process.stderr.write(`${error.message}\n`);
|
|
79
112
|
process.exitCode = error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.retryable ? 2 : 1;
|
|
80
113
|
return;
|
|
81
114
|
}
|
|
82
|
-
process.stdout.write(response
|
|
115
|
+
process.stdout.write(finalizationResponseOutput(response));
|
|
83
116
|
process.stderr.write(response.stderr);
|
|
84
117
|
process.exitCode = response.exitCode ?? 1;
|
|
85
118
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseArtifactCommand, executeArtifactCommand } from '../task/artifact-command.ts';
|
|
2
2
|
import { ensureInternalHandlerRoute } from './cli-route-inventory.ts';
|
|
3
3
|
|
|
4
|
-
const USAGE = `Usage: agent-infra-internal task-artifact <N | TASK-id> inspect --family <family> [--source-finding <id> --source-artifact <name> --source-sha256 <digest>]\n agent-infra-internal task-artifact <N | TASK-id> init --family <family> --artifact <artifact> [--locale <zh-CN|en>]\n agent-infra-internal task-artifact <N | TASK-id> preflight|finalize-local --family <analysis|plan|code> --artifact <artifact>\n\nInspect, initialize, preflight, or finalize workflow artifact facts.\n`;
|
|
4
|
+
const USAGE = `Usage: agent-infra-internal task-artifact <N | TASK-id> inspect --family <family> [--reason-code <reason>] [--source-finding <id> --source-artifact <name> --source-sha256 <digest>]\n agent-infra-internal task-artifact <N | TASK-id> init --family <family> --artifact <artifact> [--locale <zh-CN|en>]\n agent-infra-internal task-artifact <N | TASK-id> preflight|finalize-local --family <analysis|plan|code> --artifact <artifact>\n\nInspect, initialize, preflight, or finalize workflow artifact facts.\n`;
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
function taskArtifact(args: string[] = []): void {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
SandboxControlClientError
|
|
8
8
|
} from '../sandbox/control/client.ts';
|
|
9
9
|
import { SANDBOX_CONTROL_MAX_BYTES } from '../sandbox/control/protocol.ts';
|
|
10
|
-
import { validateTaskCreateCandidate } from '../task/create.ts';
|
|
10
|
+
import { validateTaskCreateCandidate, type TaskCreateCandidateV1 } from '../task/create.ts';
|
|
11
11
|
import {
|
|
12
12
|
createTask,
|
|
13
13
|
parseTaskCreateResult,
|
|
@@ -82,7 +82,20 @@ async function taskCreate(args: string[]): Promise<void> {
|
|
|
82
82
|
const stat = fs.lstatSync(inputPath);
|
|
83
83
|
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error('TASK_CREATE_INPUT_INVALID: input must be a regular file');
|
|
84
84
|
if (stat.size > SANDBOX_CONTROL_MAX_BYTES) throw new Error('TASK_CREATE_INPUT_TOO_LARGE: input exceeds the control limit');
|
|
85
|
-
const
|
|
85
|
+
const inputContent = fs.readFileSync(inputPath, 'utf8');
|
|
86
|
+
let candidate: TaskCreateCandidateV1;
|
|
87
|
+
try {
|
|
88
|
+
candidate = validateTaskCreateCandidate(JSON.parse(inputContent));
|
|
89
|
+
} catch (error) {
|
|
90
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
91
|
+
const code = /^([A-Z][A-Z0-9_]+)/.exec(message)?.[1] ?? 'TASK_CREATE_INPUT_INVALID';
|
|
92
|
+
output(failed(code, message, false, {
|
|
93
|
+
requestId: null,
|
|
94
|
+
accepted: false,
|
|
95
|
+
recovery: 'new-request-id'
|
|
96
|
+
}));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
86
99
|
const environment = resolveSandboxControlTransport();
|
|
87
100
|
if (environment.kind === 'fail-closed') {
|
|
88
101
|
output(failed(environment.reasonCode ?? 'TASK_CONTROL_TRANSPORT_INVALID', 'sandbox client control configuration is invalid'));
|
|
@@ -3,12 +3,11 @@ import {
|
|
|
3
3
|
dispatchTaskControlOperation,
|
|
4
4
|
parseTaskControlOperation
|
|
5
5
|
} from '../task/control-authority.ts';
|
|
6
|
-
import { applyTaskFinalization
|
|
7
|
-
import { publishTaskFinalizationHandoff } from '../task/finalization-handoff.ts';
|
|
6
|
+
import { applyTaskFinalization } from '../task/finalization.ts';
|
|
8
7
|
import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.ts';
|
|
9
|
-
import { verifyTaskEvent } from '../task/verification.ts';
|
|
10
8
|
import { resolveSandboxControlTransport } from './task-operation-registry.ts';
|
|
11
9
|
import { requestSandboxTaskFinalization, SandboxControlClientError } from '../sandbox/control/client.ts';
|
|
10
|
+
import { serializeTaskFinalizationEnvelope } from '../task/finalization-envelope.ts';
|
|
12
11
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
|
|
13
12
|
|
|
14
13
|
const USAGE = 'Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n';
|
|
@@ -17,12 +16,13 @@ type FinalizationError = Readonly<{ code: string; message: string; retryable: bo
|
|
|
17
16
|
|
|
18
17
|
function envelope(
|
|
19
18
|
status: 'completed' | 'failed' | 'blocked' | 'unknown',
|
|
20
|
-
changed: boolean,
|
|
19
|
+
changed: boolean | null,
|
|
21
20
|
accepted: boolean,
|
|
22
21
|
result: Awaited<ReturnType<typeof applyTaskFinalization>> | null,
|
|
23
|
-
error: FinalizationError | null
|
|
22
|
+
error: FinalizationError | null,
|
|
23
|
+
requestId: string | null = null
|
|
24
24
|
): string {
|
|
25
|
-
return
|
|
25
|
+
return serializeTaskFinalizationEnvelope({ status, changed, accepted, requestId, result, error });
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function exitCode(status: 'completed' | 'failed' | 'blocked' | 'unknown'): number {
|
|
@@ -79,33 +79,23 @@ async function taskFinalization(args: string[] = []): Promise<void> {
|
|
|
79
79
|
};
|
|
80
80
|
const transport = resolveSandboxControlTransport(process.env, { localWorkflow: true });
|
|
81
81
|
if (transport.kind === 'sandbox-local') {
|
|
82
|
-
const prepared = await prepareTaskFinalization(boundOperation.request, {
|
|
83
|
-
repoRoot,
|
|
84
|
-
preflight: (request, options) => verifyTaskEvent({ ...request, event: 'complete-task.hard-preflight' }, options)
|
|
85
|
-
});
|
|
86
|
-
if (prepared.status !== 'prepared') {
|
|
87
|
-
process.stdout.write(envelope(prepared.status, prepared.changed, true, prepared, prepared.error));
|
|
88
|
-
process.exitCode = exitCode(prepared.status);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
82
|
try {
|
|
92
|
-
const response = requestSandboxTaskFinalization({
|
|
93
|
-
agent: boundOperation.request.agent,
|
|
94
|
-
prepareHandoff: (binding) => {
|
|
95
|
-
const receipt = bindTaskFinalizationReceipt(repoRoot, boundOperation.request.taskRef, binding);
|
|
96
|
-
return publishTaskFinalizationHandoff('/share/branch', receipt, binding);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
83
|
+
const response = requestSandboxTaskFinalization({ agent: boundOperation.request.agent });
|
|
99
84
|
process.stdout.write(response.stdout);
|
|
100
85
|
process.stderr.write(response.stderr);
|
|
101
86
|
process.exitCode = response.exitCode ?? 1;
|
|
102
87
|
return;
|
|
103
88
|
} catch (error) {
|
|
104
|
-
const
|
|
105
|
-
|
|
89
|
+
const clientError = error instanceof SandboxControlClientError ? error : null;
|
|
90
|
+
const detail = clientError
|
|
91
|
+
? clientError.detail
|
|
106
92
|
: { code: 'SANDBOX_CONTROL_CLIENT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
|
|
107
|
-
|
|
108
|
-
|
|
93
|
+
const status = detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN'
|
|
94
|
+
? 'unknown'
|
|
95
|
+
: detail.retryable ? 'blocked' : 'failed';
|
|
96
|
+
const changed = status === 'unknown' ? null : false;
|
|
97
|
+
process.stdout.write(envelope(status, changed, clientError?.accepted ?? false, null, detail, clientError?.requestId ?? null));
|
|
98
|
+
process.exitCode = exitCode(status);
|
|
109
99
|
return;
|
|
110
100
|
}
|
|
111
101
|
}
|