@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
package/dist/bin/internal-cli.js
CHANGED
|
@@ -13,7 +13,6 @@ const taskWorkflowCommand = command === 'task-artifact'
|
|
|
13
13
|
|| command === 'task-review'
|
|
14
14
|
|| command === 'task-event'
|
|
15
15
|
|| command === 'task-ledger'
|
|
16
|
-
|| command === 'task-invalidation'
|
|
17
16
|
|| command === 'task-warning';
|
|
18
17
|
const taskPlatformRecoveryCommand = command === 'task-platform-recovery';
|
|
19
18
|
const manualValidationWorkflowCommand = command === 'manual-validation';
|
|
@@ -231,11 +230,6 @@ if (!controlRouted && !taskViewGuardFailed && internalRouteRegistered)
|
|
|
231
230
|
await taskEvent(process.argv.slice(3));
|
|
232
231
|
break;
|
|
233
232
|
}
|
|
234
|
-
case 'task-invalidation': {
|
|
235
|
-
const { taskInvalidation } = await import('../lib/internal/task-invalidation.js');
|
|
236
|
-
taskInvalidation(process.argv.slice(3));
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
233
|
case 'task-lifecycle': {
|
|
240
234
|
const { taskLifecycle } = await import('../lib/internal/task-lifecycle.js');
|
|
241
235
|
await taskLifecycle(process.argv.slice(3));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
import { AGENT_CLIENT_CAPABILITY_IDS, AGENT_CLIENT_SUPPORT_LEVELS, isAgentClientId } from './types.js';
|
|
2
3
|
import { SANDBOX_HOOK_PHASES } from '../sandbox/tool-types.js';
|
|
3
4
|
const PROJECT_ASSET_CATEGORIES = ['managed', 'merged', 'ejected'];
|
|
@@ -65,6 +66,24 @@ function freezeTool(tool) {
|
|
|
65
66
|
if (!tool.containerMount.startsWith('/')) {
|
|
66
67
|
throw new Error(`Sandbox tool '${tool.id}' containerMount must be absolute`);
|
|
67
68
|
}
|
|
69
|
+
if (tool.hostStateMounts !== undefined && !Array.isArray(tool.hostStateMounts)) {
|
|
70
|
+
throw new Error(`Agent Client '${tool.id}' hostStateMounts must be an array`);
|
|
71
|
+
}
|
|
72
|
+
const hostStateMounts = tool.hostStateMounts?.map((entry) => {
|
|
73
|
+
if (!entry || typeof entry.hostSubdir !== 'string' || typeof entry.containerSubpath !== 'string') {
|
|
74
|
+
throw new Error(`Agent Client '${tool.id}' hostStateMounts must contain relative path pairs`);
|
|
75
|
+
}
|
|
76
|
+
for (const value of [entry.hostSubdir, entry.containerSubpath]) {
|
|
77
|
+
const normalized = path.posix.normalize(value);
|
|
78
|
+
if (value === ''
|
|
79
|
+
|| path.posix.isAbsolute(value)
|
|
80
|
+
|| normalized === '..'
|
|
81
|
+
|| normalized.startsWith('../')) {
|
|
82
|
+
throw new Error(`Agent Client '${tool.id}' hostStateMounts must stay within the tool directories`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return Object.freeze({ ...entry });
|
|
86
|
+
});
|
|
68
87
|
if (!tool.versionCmd) {
|
|
69
88
|
throw new Error(`Sandbox tool '${tool.id}' requires versionCmd`);
|
|
70
89
|
}
|
|
@@ -82,6 +101,7 @@ function freezeTool(tool) {
|
|
|
82
101
|
...(tool.hostLiveMounts ? {
|
|
83
102
|
hostLiveMounts: Object.freeze(tool.hostLiveMounts.map((entry) => Object.freeze({ ...entry })))
|
|
84
103
|
} : {}),
|
|
104
|
+
...(hostStateMounts ? { hostStateMounts: Object.freeze(hostStateMounts) } : {}),
|
|
85
105
|
...(tool.postSetupCmds ? { postSetupCmds: Object.freeze([...tool.postSetupCmds]) } : {}),
|
|
86
106
|
...(tool.tmpfs ? {
|
|
87
107
|
tmpfs: Object.freeze({
|
|
@@ -241,6 +241,19 @@ const codexRecoveryChecks = [
|
|
|
241
241
|
path: '/home/devuser/.codex'
|
|
242
242
|
}
|
|
243
243
|
},
|
|
244
|
+
{
|
|
245
|
+
id: 'app-server-package-writable',
|
|
246
|
+
probe: {
|
|
247
|
+
script: 'probe="$1/.agent-infra-app-server-package-$$"; trap \'rm -f -- "$probe"\' EXIT; : > "$probe"',
|
|
248
|
+
args: ['/home/devuser/.codex/packages/app-server-daemon'],
|
|
249
|
+
user: 'devuser'
|
|
250
|
+
},
|
|
251
|
+
finding: {
|
|
252
|
+
repairKind: 'hard-failure',
|
|
253
|
+
message: 'Codex app-server package directory is not writable by devuser.',
|
|
254
|
+
path: '/home/devuser/.codex/packages/app-server-daemon'
|
|
255
|
+
}
|
|
256
|
+
},
|
|
244
257
|
{
|
|
245
258
|
id: 'prompts-link',
|
|
246
259
|
when: {
|
|
@@ -51,6 +51,10 @@ const codexAdapter = defineAgentClientAdapter({
|
|
|
51
51
|
versionCmd: 'codex --version',
|
|
52
52
|
setupHint: 'Run codex once inside the container and choose Device Code login if needed.',
|
|
53
53
|
tmpfs: { size: '512m', seed: ['config.toml', 'model-catalogs'], exec: true },
|
|
54
|
+
hostStateMounts: [{
|
|
55
|
+
hostSubdir: 'packages/app-server-daemon',
|
|
56
|
+
containerSubpath: 'packages/app-server-daemon'
|
|
57
|
+
}],
|
|
54
58
|
hostLiveMounts: [
|
|
55
59
|
{
|
|
56
60
|
hostPath: hostJoin(home, '.codex', 'auth.json'),
|
package/dist/lib/git/workflow.js
CHANGED
|
@@ -103,7 +103,7 @@ function commitExplicitPaths(input, runner = defaultRunner) {
|
|
|
103
103
|
if (tree !== input.expectedTree)
|
|
104
104
|
return { status: 'failed', changed: false, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations: [{ name: 'stage', status: 'applied' }], error: { code: 'GIT_TREE_MISMATCH', message: `Expected staged tree ${input.expectedTree}, received ${tree ?? 'unavailable'}` } };
|
|
105
105
|
}
|
|
106
|
-
const commit = run(runner, input.cwd, ['commit', '-m', input.message]);
|
|
106
|
+
const commit = run(runner, input.cwd, ['commit', '--cleanup=verbatim', '-m', input.message]);
|
|
107
107
|
const after = inspectGitWorkflow(input.cwd, runner);
|
|
108
108
|
if (commit.status !== 0)
|
|
109
109
|
return { status: 'failed', changed: false, snapshot: after.snapshot, operations: [{ name: 'commit', status: 'failed', message: commit.stderr.trim() }], error: { code: 'GIT_COMMIT_FAILED', message: commit.stderr.trim() } };
|
|
@@ -57,7 +57,6 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
57
57
|
'task-orchestration': ['status', 'progress'],
|
|
58
58
|
'task-review': ['preflight', 'finalize-summary'],
|
|
59
59
|
'task-event': ['event'],
|
|
60
|
-
'task-invalidation': ['reconcile'],
|
|
61
60
|
'task-lifecycle': ['intent'],
|
|
62
61
|
'task-finalization': ['complete'],
|
|
63
62
|
'task-platform-recovery': ['inspect', 'recover'],
|
|
@@ -105,8 +104,6 @@ export function internalRouteSelector(command, args) {
|
|
|
105
104
|
return args[1] === 'status' ? 'status' : args[1] ? 'progress' : '';
|
|
106
105
|
if (command === 'task-review')
|
|
107
106
|
return args[1] ?? '';
|
|
108
|
-
if (command === 'task-invalidation')
|
|
109
|
-
return args[1] ?? '';
|
|
110
107
|
if (command === 'task-artifact')
|
|
111
108
|
return args[1] ?? '';
|
|
112
109
|
if (command === 'platform-pr-review' && first(args) === 'publish') {
|
|
@@ -196,7 +196,7 @@ async function executeManualValidationTransactionLocked(taskRef, values, cwd, re
|
|
|
196
196
|
if (transaction.phase === 'prepared') {
|
|
197
197
|
const staged = await syncPullRequestSummary(taskRef, {
|
|
198
198
|
cwd, client: options.client, agent, body: pendingBody, changeReportFile: path.resolve(cwd, values.changeReportFile), primaryResult: primaryResult, strict: true,
|
|
199
|
-
manualValidation: { phase: 'pending' }, lockAlreadyHeld: true
|
|
199
|
+
manualValidation: { phase: 'pending', transactionId: transaction.transactionId }, lockAlreadyHeld: true
|
|
200
200
|
});
|
|
201
201
|
if (!['applied', 'no-op'].includes(staged.status))
|
|
202
202
|
transactionFailure(staged.error ?? { code: 'MANUAL_VALIDATION_TRANSACTION_FAILED', message: 'pending summary staging failed' });
|
|
@@ -261,7 +261,7 @@ async function executeManualValidationTransactionLocked(taskRef, values, cwd, re
|
|
|
261
261
|
try {
|
|
262
262
|
await syncPullRequestSummary(taskRef, {
|
|
263
263
|
cwd, client: options.client, agent, body: pendingBody, changeReportFile: path.resolve(cwd, values.changeReportFile), primaryResult: primaryResult, strict: true,
|
|
264
|
-
manualValidation: { phase: 'pending' }, lockAlreadyHeld: true
|
|
264
|
+
manualValidation: { phase: 'pending', transactionId: transaction.transactionId }, lockAlreadyHeld: true
|
|
265
265
|
});
|
|
266
266
|
}
|
|
267
267
|
catch {
|
|
@@ -10,7 +10,7 @@ const USAGE = `Usage: agent-infra-internal platform-pr inspect <task-ref> [--cwd
|
|
|
10
10
|
agent-infra-internal platform-pr bind <task-ref> --pr <token> --agent <agent> [--dry-run] [--cwd <path>]
|
|
11
11
|
agent-infra-internal platform-pr skip <task-ref> --agent <agent> [--dry-run] [--cwd <path>]
|
|
12
12
|
agent-infra-internal platform-pr sync <task-ref> --agent <agent> [--metadata] [--closing-issue] --result <pr_created|pr_reused|no_op> [--dry-run] [--cwd <path>]
|
|
13
|
-
agent-infra-internal platform-pr sync-in-labels --pr <
|
|
13
|
+
agent-infra-internal platform-pr sync-in-labels --pr <token> [--dry-run] [--cwd <path>]
|
|
14
14
|
agent-infra-internal platform-pr summary-context <task-ref> [--cwd <path>]
|
|
15
15
|
agent-infra-internal platform-pr change-report <task-ref> --agent <agent> --mechanical-file <path> --precheck-file <path> [--dry-run] [--cwd <path>]
|
|
16
16
|
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>]
|
|
@@ -91,13 +91,12 @@ async function platformPr(args = []) {
|
|
|
91
91
|
fail(`sync-in-labels does not accept --${unexpected}`);
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
fail('sync-in-labels requires a positive --pr');
|
|
94
|
+
if (typeof values.pr !== 'string' || !values.pr || values.pr.trim() !== values.pr) {
|
|
95
|
+
fail('sync-in-labels requires a non-empty --pr token');
|
|
97
96
|
return;
|
|
98
97
|
}
|
|
99
98
|
const cwd = path.resolve(typeof values.cwd === 'string' ? values.cwd : process.cwd());
|
|
100
|
-
finish(await syncPlatformPullRequestInLabels(pr, { cwd, dryRun: values.dryRun === true }));
|
|
99
|
+
finish(await syncPlatformPullRequestInLabels(values.pr, { cwd, dryRun: values.dryRun === true }));
|
|
101
100
|
return;
|
|
102
101
|
}
|
|
103
102
|
const taskRef = args[1];
|
|
@@ -4,8 +4,9 @@ import { serveSandboxControl } from '../sandbox/control/server.js';
|
|
|
4
4
|
import { runSandboxControlExecutor } from '../sandbox/control/executor.js';
|
|
5
5
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
6
6
|
import { parseTaskCreateResult, taskCreateExitCode } from '../task/create-service.js';
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { serializeTaskFinalizationEnvelope } from '../task/finalization-envelope.js';
|
|
8
|
+
function writeFinalizationEnvelope(status, accepted, error, changed = false, result = null, requestId = null) {
|
|
9
|
+
process.stdout.write(serializeTaskFinalizationEnvelope({ status, changed, accepted, requestId, result, error }));
|
|
9
10
|
}
|
|
10
11
|
function finalizationErrorStatus(error) {
|
|
11
12
|
if (error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN')
|
|
@@ -30,6 +31,28 @@ function recoveredExitCode(response) {
|
|
|
30
31
|
}
|
|
31
32
|
return response.exitCode ?? 1;
|
|
32
33
|
}
|
|
34
|
+
function finalizationResponseOutput(response) {
|
|
35
|
+
try {
|
|
36
|
+
const value = JSON.parse(response.stdout);
|
|
37
|
+
if (value.version === 2
|
|
38
|
+
&& value.status
|
|
39
|
+
&& ['completed', 'failed', 'blocked', 'unknown'].includes(value.status)
|
|
40
|
+
&& (typeof value.changed === 'boolean' || value.changed === null)) {
|
|
41
|
+
return serializeTaskFinalizationEnvelope({
|
|
42
|
+
status: value.status,
|
|
43
|
+
changed: value.changed,
|
|
44
|
+
accepted: true,
|
|
45
|
+
requestId: response.id,
|
|
46
|
+
result: value.result ?? null,
|
|
47
|
+
error: value.error ?? null
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Preserve non-envelope output for diagnostics from the executor.
|
|
53
|
+
}
|
|
54
|
+
return response.stdout;
|
|
55
|
+
}
|
|
33
56
|
function sandboxFinalizationClient(args) {
|
|
34
57
|
if (args.length !== 4 || !args[0] || args[1] !== 'complete' || args[2] !== '--agent' || !args[3]) {
|
|
35
58
|
const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message: 'task ref, complete intent, and --agent are required', retryable: false };
|
|
@@ -52,7 +75,8 @@ function sandboxFinalizationClient(args) {
|
|
|
52
75
|
catch (error) {
|
|
53
76
|
if (!(error instanceof SandboxControlClientError))
|
|
54
77
|
throw error;
|
|
55
|
-
|
|
78
|
+
const status = finalizationErrorStatus(error.detail);
|
|
79
|
+
writeFinalizationEnvelope(status, error.accepted, error.detail, status === 'unknown' ? null : false, null, error.requestId);
|
|
56
80
|
writeClientError(error);
|
|
57
81
|
process.exitCode = error.detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.detail.retryable ? 2 : 1;
|
|
58
82
|
return;
|
|
@@ -63,12 +87,13 @@ function sandboxFinalizationClient(args) {
|
|
|
63
87
|
message: 'sandbox control rejected the finalization request',
|
|
64
88
|
retryable: false
|
|
65
89
|
};
|
|
66
|
-
|
|
90
|
+
const status = finalizationErrorStatus(error);
|
|
91
|
+
writeFinalizationEnvelope(status, error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN', error, status === 'unknown' ? null : false, null, response.id);
|
|
67
92
|
process.stderr.write(`${error.message}\n`);
|
|
68
93
|
process.exitCode = error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.retryable ? 2 : 1;
|
|
69
94
|
return;
|
|
70
95
|
}
|
|
71
|
-
process.stdout.write(response
|
|
96
|
+
process.stdout.write(finalizationResponseOutput(response));
|
|
72
97
|
process.stderr.write(response.stderr);
|
|
73
98
|
process.exitCode = response.exitCode ?? 1;
|
|
74
99
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseArtifactCommand, executeArtifactCommand } from '../task/artifact-command.js';
|
|
2
2
|
import { ensureInternalHandlerRoute } from './cli-route-inventory.js';
|
|
3
|
-
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`;
|
|
3
|
+
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`;
|
|
4
4
|
function taskArtifact(args = []) {
|
|
5
5
|
if (!ensureInternalHandlerRoute('task-artifact', args))
|
|
6
6
|
return;
|
|
@@ -54,7 +54,21 @@ async function taskCreate(args) {
|
|
|
54
54
|
throw new Error('TASK_CREATE_INPUT_INVALID: input must be a regular file');
|
|
55
55
|
if (stat.size > SANDBOX_CONTROL_MAX_BYTES)
|
|
56
56
|
throw new Error('TASK_CREATE_INPUT_TOO_LARGE: input exceeds the control limit');
|
|
57
|
-
const
|
|
57
|
+
const inputContent = fs.readFileSync(inputPath, 'utf8');
|
|
58
|
+
let candidate;
|
|
59
|
+
try {
|
|
60
|
+
candidate = validateTaskCreateCandidate(JSON.parse(inputContent));
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
64
|
+
const code = /^([A-Z][A-Z0-9_]+)/.exec(message)?.[1] ?? 'TASK_CREATE_INPUT_INVALID';
|
|
65
|
+
output(failed(code, message, false, {
|
|
66
|
+
requestId: null,
|
|
67
|
+
accepted: false,
|
|
68
|
+
recovery: 'new-request-id'
|
|
69
|
+
}));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
58
72
|
const environment = resolveSandboxControlTransport();
|
|
59
73
|
if (environment.kind === 'fail-closed') {
|
|
60
74
|
output(failed(environment.reasonCode ?? 'TASK_CONTROL_TRANSPORT_INVALID', 'sandbox client control configuration is invalid'));
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { createDirectHostExecutionContext, dispatchTaskControlOperation, parseTaskControlOperation } from '../task/control-authority.js';
|
|
2
|
-
import { applyTaskFinalization
|
|
3
|
-
import { publishTaskFinalizationHandoff } from '../task/finalization-handoff.js';
|
|
2
|
+
import { applyTaskFinalization } from '../task/finalization.js';
|
|
4
3
|
import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
|
|
5
|
-
import { verifyTaskEvent } from '../task/verification.js';
|
|
6
4
|
import { resolveSandboxControlTransport } from './task-operation-registry.js';
|
|
7
5
|
import { requestSandboxTaskFinalization, SandboxControlClientError } from '../sandbox/control/client.js';
|
|
6
|
+
import { serializeTaskFinalizationEnvelope } from '../task/finalization-envelope.js';
|
|
8
7
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
9
8
|
const USAGE = 'Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n';
|
|
10
|
-
function envelope(status, changed, accepted, result, error) {
|
|
11
|
-
return
|
|
9
|
+
function envelope(status, changed, accepted, result, error, requestId = null) {
|
|
10
|
+
return serializeTaskFinalizationEnvelope({ status, changed, accepted, requestId, result, error });
|
|
12
11
|
}
|
|
13
12
|
function exitCode(status) {
|
|
14
13
|
return status === 'completed' ? 0 : status === 'blocked' ? 2 : 1;
|
|
@@ -65,34 +64,24 @@ async function taskFinalization(args = []) {
|
|
|
65
64
|
};
|
|
66
65
|
const transport = resolveSandboxControlTransport(process.env, { localWorkflow: true });
|
|
67
66
|
if (transport.kind === 'sandbox-local') {
|
|
68
|
-
const prepared = await prepareTaskFinalization(boundOperation.request, {
|
|
69
|
-
repoRoot,
|
|
70
|
-
preflight: (request, options) => verifyTaskEvent({ ...request, event: 'complete-task.hard-preflight' }, options)
|
|
71
|
-
});
|
|
72
|
-
if (prepared.status !== 'prepared') {
|
|
73
|
-
process.stdout.write(envelope(prepared.status, prepared.changed, true, prepared, prepared.error));
|
|
74
|
-
process.exitCode = exitCode(prepared.status);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
67
|
try {
|
|
78
|
-
const response = requestSandboxTaskFinalization({
|
|
79
|
-
agent: boundOperation.request.agent,
|
|
80
|
-
prepareHandoff: (binding) => {
|
|
81
|
-
const receipt = bindTaskFinalizationReceipt(repoRoot, boundOperation.request.taskRef, binding);
|
|
82
|
-
return publishTaskFinalizationHandoff('/share/branch', receipt, binding);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
68
|
+
const response = requestSandboxTaskFinalization({ agent: boundOperation.request.agent });
|
|
85
69
|
process.stdout.write(response.stdout);
|
|
86
70
|
process.stderr.write(response.stderr);
|
|
87
71
|
process.exitCode = response.exitCode ?? 1;
|
|
88
72
|
return;
|
|
89
73
|
}
|
|
90
74
|
catch (error) {
|
|
91
|
-
const
|
|
92
|
-
|
|
75
|
+
const clientError = error instanceof SandboxControlClientError ? error : null;
|
|
76
|
+
const detail = clientError
|
|
77
|
+
? clientError.detail
|
|
93
78
|
: { code: 'SANDBOX_CONTROL_CLIENT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
|
|
94
|
-
|
|
95
|
-
|
|
79
|
+
const status = detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN'
|
|
80
|
+
? 'unknown'
|
|
81
|
+
: detail.retryable ? 'blocked' : 'failed';
|
|
82
|
+
const changed = status === 'unknown' ? null : false;
|
|
83
|
+
process.stdout.write(envelope(status, changed, clientError?.accepted ?? false, null, detail, clientError?.requestId ?? null));
|
|
84
|
+
process.exitCode = exitCode(status);
|
|
96
85
|
return;
|
|
97
86
|
}
|
|
98
87
|
}
|
|
@@ -3,7 +3,8 @@ import path from 'node:path';
|
|
|
3
3
|
import { accessSandboxTaskView, parseSandboxTaskView, taskViewFromStatus } from '../sandbox/control/task-view.js';
|
|
4
4
|
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
5
5
|
import { PUBLIC_CLI_COMMAND_ALIASES, PUBLIC_CLI_SELECTOR_ALIASES, internalRouteSelector } from './cli-route-inventory.js';
|
|
6
|
-
import {
|
|
6
|
+
import { hasSandboxControlMarker, resolveSandboxControlTransport, sandboxTaskMarkerState } from '../sandbox/environment.js';
|
|
7
|
+
export { resolveSandboxControlTransport } from '../sandbox/environment.js';
|
|
7
8
|
const descriptor = (dispatcher, command, selector, scope, effect, taskRefSource = scope === 'non-task' ? 'none' : 'argv') => ({ dispatcher, command, selector, scope, effect, taskRefSource, guardBeforeImport: true });
|
|
8
9
|
function internalTaskRoutes() {
|
|
9
10
|
return [
|
|
@@ -102,9 +103,8 @@ function internalTaskRoutes() {
|
|
|
102
103
|
descriptor('internal', 'task-review', 'preflight', 'task-bound', 'artifact-write'),
|
|
103
104
|
descriptor('internal', 'task-review', 'finalize-summary', 'task-bound', 'progress'),
|
|
104
105
|
descriptor('internal', 'task-event', 'event', 'task-bound', 'progress'),
|
|
105
|
-
descriptor('internal', 'task-invalidation', 'reconcile', 'task-bound', 'progress'),
|
|
106
106
|
descriptor('internal', 'task-lifecycle', 'intent', 'task-bound', 'progress'),
|
|
107
|
-
descriptor('internal', 'task-finalization', 'complete', 'task-bound', '
|
|
107
|
+
descriptor('internal', 'task-finalization', 'complete', 'task-bound', 'recovery'),
|
|
108
108
|
descriptor('internal', 'task-platform-recovery', 'inspect', 'task-bound', 'diagnostic'),
|
|
109
109
|
descriptor('internal', 'task-platform-recovery', 'recover', 'task-bound', 'recovery'),
|
|
110
110
|
descriptor('internal', 'task-short-id', 'list', 'conditional', 'diagnostic'),
|
|
@@ -179,7 +179,7 @@ export const INTERNAL_DISPATCHER_ROUTES = Object.freeze([
|
|
|
179
179
|
'git-workflow', 'task-delivery', 'release-workflow', 'platform-release-notes', 'platform-security', 'platform-metadata', 'platform-context',
|
|
180
180
|
'platform-comment', 'platform-issue', 'platform-pr', 'platform-pr-review', 'pr-review-grade',
|
|
181
181
|
'platform-checks', 'task-context', 'task-ledger', 'task-warning', 'task-activity', 'task-artifact',
|
|
182
|
-
'task-orchestration', 'task-review', 'task-event', 'task-
|
|
182
|
+
'task-orchestration', 'task-review', 'task-event', 'task-lifecycle',
|
|
183
183
|
'task-finalization', 'task-platform-recovery', 'task-short-id', 'task-snapshot', 'task-verify', 'task-validate', 'manual-validation'
|
|
184
184
|
]);
|
|
185
185
|
export const PUBLIC_DISPATCHER_ROUTES = Object.freeze([
|
|
@@ -233,141 +233,6 @@ export function resolveDelegatedTaskOperation(args) {
|
|
|
233
233
|
return null;
|
|
234
234
|
return resolveTaskOperation('internal', rest[0], rest.slice(1));
|
|
235
235
|
}
|
|
236
|
-
const TASK_MARKER_KEYS = [
|
|
237
|
-
'AGENT_INFRA_TASK_ID',
|
|
238
|
-
'AGENT_INFRA_CONTROL_TOKEN',
|
|
239
|
-
'AGENT_INFRA_CONTROL_GENERATION',
|
|
240
|
-
'AGENT_INFRA_CONTROL_DIR',
|
|
241
|
-
'AGENT_INFRA_CONTROL_STATUS_DIR',
|
|
242
|
-
'AGENT_INFRA_RUNTIME_DIR'
|
|
243
|
-
];
|
|
244
|
-
const TASK_CONTROL_MARKER_KEYS = [
|
|
245
|
-
'AGENT_INFRA_CONTROL_TOKEN',
|
|
246
|
-
'AGENT_INFRA_CONTROL_GENERATION',
|
|
247
|
-
'AGENT_INFRA_CONTROL_DIR',
|
|
248
|
-
'AGENT_INFRA_CONTROL_STATUS_DIR'
|
|
249
|
-
];
|
|
250
|
-
const TASK_CONTROL_CONFIG_KEYS = [
|
|
251
|
-
'AGENT_INFRA_CONTROL_TOKEN',
|
|
252
|
-
'AGENT_INFRA_CONTROL_GENERATION',
|
|
253
|
-
'AGENT_INFRA_CONTROL_ROOT_ID',
|
|
254
|
-
'AGENT_INFRA_CONTROL_DIR',
|
|
255
|
-
'AGENT_INFRA_CONTROL_STATUS_DIR'
|
|
256
|
-
];
|
|
257
|
-
export const SANDBOX_CONTROL_STATUS_MOUNT = '/run/agent-infra/control-status';
|
|
258
|
-
function nativeDirectoryProbe(candidate) {
|
|
259
|
-
try {
|
|
260
|
-
// The fixed mount is a trust anchor. Do not use a mutable node:fs export:
|
|
261
|
-
// a preload must not turn a mounted sandbox into the direct-host path by
|
|
262
|
-
// replacing the ordinary filesystem probe.
|
|
263
|
-
const binding = process.binding('fs');
|
|
264
|
-
if (!Function.prototype.toString.call(binding.internalModuleStat).includes('[native code]'))
|
|
265
|
-
return 'unknown';
|
|
266
|
-
const result = binding.internalModuleStat(candidate);
|
|
267
|
-
if (result === 1)
|
|
268
|
-
return 'present';
|
|
269
|
-
if (result === -2)
|
|
270
|
-
return 'absent';
|
|
271
|
-
return 'unknown';
|
|
272
|
-
}
|
|
273
|
-
catch {
|
|
274
|
-
return 'unknown';
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
function taskMarkerState(env) {
|
|
278
|
-
const present = (key) => Boolean(env[key]);
|
|
279
|
-
const taskId = present('AGENT_INFRA_TASK_ID');
|
|
280
|
-
const controls = TASK_CONTROL_MARKER_KEYS.every(present);
|
|
281
|
-
const runtime = present('AGENT_INFRA_RUNTIME_DIR');
|
|
282
|
-
const any = TASK_MARKER_KEYS.some(present);
|
|
283
|
-
if (!any)
|
|
284
|
-
return 'none';
|
|
285
|
-
if (taskId && controls && runtime)
|
|
286
|
-
return 'task-bound';
|
|
287
|
-
if (!taskId && controls && !runtime)
|
|
288
|
-
return 'branch-only';
|
|
289
|
-
return 'incomplete';
|
|
290
|
-
}
|
|
291
|
-
export function resolveSandboxControlTransport(env = process.env, options = {}) {
|
|
292
|
-
const hasAnyMarker = TASK_MARKER_KEYS.some((key) => Boolean(env[key]))
|
|
293
|
-
|| Boolean(env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING)
|
|
294
|
-
|| Boolean(env.AGENT_INFRA_EXECUTOR_MANIFEST);
|
|
295
|
-
const fixedStatusDir = options.statusMountPath
|
|
296
|
-
?? SANDBOX_CONTROL_STATUS_MOUNT;
|
|
297
|
-
const fixedStatusProbe = path.isAbsolute(fixedStatusDir)
|
|
298
|
-
? nativeDirectoryProbe(fixedStatusDir) : 'absent';
|
|
299
|
-
if (fixedStatusProbe === 'unknown') {
|
|
300
|
-
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_UNAVAILABLE' };
|
|
301
|
-
}
|
|
302
|
-
const fixedStatusMounted = fixedStatusProbe === 'present';
|
|
303
|
-
// A fixed status mount is installed only in a task-bound sandbox. Its
|
|
304
|
-
// presence is not mutable by the sandbox process, so marker removal must
|
|
305
|
-
// never make that process look like a direct host invocation.
|
|
306
|
-
if (!hasAnyMarker) {
|
|
307
|
-
return fixedStatusMounted
|
|
308
|
-
? { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' }
|
|
309
|
-
: { kind: 'direct-host', reasonCode: null };
|
|
310
|
-
}
|
|
311
|
-
const hasCompleteConfig = TASK_CONTROL_CONFIG_KEYS.every((key) => Boolean(env[key]));
|
|
312
|
-
if (!hasCompleteConfig) {
|
|
313
|
-
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' };
|
|
314
|
-
}
|
|
315
|
-
const configuredStatusDir = env.AGENT_INFRA_CONTROL_STATUS_DIR;
|
|
316
|
-
const configuredStatusProbe = configuredStatusDir && path.isAbsolute(configuredStatusDir)
|
|
317
|
-
? nativeDirectoryProbe(configuredStatusDir) : 'absent';
|
|
318
|
-
if (configuredStatusProbe === 'unknown') {
|
|
319
|
-
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_UNAVAILABLE' };
|
|
320
|
-
}
|
|
321
|
-
const statusDir = configuredStatusDir && path.isAbsolute(configuredStatusDir)
|
|
322
|
-
? configuredStatusProbe === 'present' ? configuredStatusDir : null
|
|
323
|
-
: fixedStatusMounted ? fixedStatusDir : null;
|
|
324
|
-
const statusMounted = statusDir !== null;
|
|
325
|
-
if (env.AGENT_INFRA_EXECUTOR_MANIFEST || env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING) {
|
|
326
|
-
return { kind: 'fail-closed', reasonCode: 'TASK_CONTROL_TRANSPORT_INVALID' };
|
|
327
|
-
}
|
|
328
|
-
const taskBound = Boolean(env.AGENT_INFRA_TASK_ID);
|
|
329
|
-
const runtime = Boolean(env.AGENT_INFRA_RUNTIME_DIR);
|
|
330
|
-
if (statusMounted) {
|
|
331
|
-
let sentinel;
|
|
332
|
-
try {
|
|
333
|
-
sentinel = readSandboxControlIdentitySentinel(statusDir);
|
|
334
|
-
}
|
|
335
|
-
catch (error) {
|
|
336
|
-
const message = error instanceof Error ? error.message : '';
|
|
337
|
-
return { kind: 'fail-closed', reasonCode: message.endsWith('MISSING')
|
|
338
|
-
? 'SANDBOX_CONTROL_IDENTITY_MISSING'
|
|
339
|
-
: 'SANDBOX_CONTROL_IDENTITY_MALFORMED' };
|
|
340
|
-
}
|
|
341
|
-
if (sentinel.generation !== env.AGENT_INFRA_CONTROL_GENERATION) {
|
|
342
|
-
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_GENERATION_MISMATCH' };
|
|
343
|
-
}
|
|
344
|
-
if (env.AGENT_INFRA_CONTROL_ROOT_ID && sentinel.controlRootId !== env.AGENT_INFRA_CONTROL_ROOT_ID) {
|
|
345
|
-
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_ROOT_ID_MISMATCH' };
|
|
346
|
-
}
|
|
347
|
-
if (sentinel.mode === 'task-bound'
|
|
348
|
-
&& (sentinel.taskId !== env.AGENT_INFRA_TASK_ID || !runtime)) {
|
|
349
|
-
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_TOPOLOGY_MISMATCH' };
|
|
350
|
-
}
|
|
351
|
-
if (sentinel.mode === 'branch-only' && (taskBound || runtime)) {
|
|
352
|
-
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_TOPOLOGY_MISMATCH' };
|
|
353
|
-
}
|
|
354
|
-
if (!hasCompleteConfig) {
|
|
355
|
-
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' };
|
|
356
|
-
}
|
|
357
|
-
return { kind: options.localWorkflow ? 'sandbox-local' : 'broker-client', reasonCode: null };
|
|
358
|
-
}
|
|
359
|
-
if (!hasAnyMarker)
|
|
360
|
-
return { kind: 'direct-host', reasonCode: null };
|
|
361
|
-
if (!hasCompleteConfig || (taskBound && !runtime) || (!taskBound && runtime)) {
|
|
362
|
-
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' };
|
|
363
|
-
}
|
|
364
|
-
return options.localWorkflow
|
|
365
|
-
? { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_MISSING' }
|
|
366
|
-
: { kind: 'broker-client', reasonCode: null };
|
|
367
|
-
}
|
|
368
|
-
export function hasTaskBoundMarker(env = process.env) {
|
|
369
|
-
return taskMarkerState(env) === 'task-bound';
|
|
370
|
-
}
|
|
371
236
|
function explicitTaskRefForOperation(dispatcher, command, args, selected) {
|
|
372
237
|
if (selected.taskRefSource === 'none' || selected.taskRefSource === 'environment')
|
|
373
238
|
return null;
|
|
@@ -447,7 +312,7 @@ function isSandboxExecutorRoute(dispatcher, command, args, env) {
|
|
|
447
312
|
&& first(args) === 'execute'
|
|
448
313
|
&& Boolean(env.AGENT_INFRA_EXECUTOR_MANIFEST)
|
|
449
314
|
&& Boolean(env.AGENT_INFRA_RUNTIME_DIR)
|
|
450
|
-
&& !
|
|
315
|
+
&& !hasSandboxControlMarker(env);
|
|
451
316
|
}
|
|
452
317
|
function viewFromEnvironment(env) {
|
|
453
318
|
const statusDir = env.AGENT_INFRA_CONTROL_STATUS_DIR;
|
|
@@ -493,7 +358,7 @@ export function guardTaskOperation(dispatcher, command, args = [], options = {})
|
|
|
493
358
|
taskView: null
|
|
494
359
|
};
|
|
495
360
|
}
|
|
496
|
-
const markerState =
|
|
361
|
+
const markerState = sandboxTaskMarkerState(env);
|
|
497
362
|
if (markerState === 'incomplete' && !isSandboxExecutorRoute(dispatcher, command, args, env)) {
|
|
498
363
|
throw new TaskViewOperationError('SANDBOX_TASK_VIEW_MARKER_INVALID', 'sandbox task markers are incomplete; refusing to infer host-direct authority');
|
|
499
364
|
}
|
|
@@ -2,7 +2,7 @@ import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
|
|
|
2
2
|
import { readPlatformOperationJournal } from '../task/platform-operation-journal.js';
|
|
3
3
|
import { recoverPlatformOperations } from '../task/platform-operation-recovery.js';
|
|
4
4
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
5
|
-
const USAGE = 'Usage: agent-infra-internal task-platform-recovery <N | TASK-id> <inspect | recover> [--agent <agent>] [--selection required|deferred|all]\n';
|
|
5
|
+
const USAGE = 'Usage: agent-infra-internal task-platform-recovery <N | TASK-id> <inspect | recover> [--agent <agent>] [--selection required|deferred|all] [--attempts <positive-integer>]\n';
|
|
6
6
|
function option(args, name) {
|
|
7
7
|
const index = args.indexOf(name);
|
|
8
8
|
return index >= 0 ? args[index + 1] : undefined;
|
|
@@ -41,14 +41,19 @@ async function taskPlatformRecovery(args = []) {
|
|
|
41
41
|
}
|
|
42
42
|
const agent = option(args, '--agent');
|
|
43
43
|
const selection = option(args, '--selection') ?? 'all';
|
|
44
|
+
const attemptsInput = option(args, '--attempts');
|
|
45
|
+
const attempts = attemptsInput === undefined ? undefined : Number(attemptsInput);
|
|
46
|
+
const validAttempts = attemptsInput === undefined
|
|
47
|
+
|| (attempts !== undefined && Number.isSafeInteger(attempts) && attempts > 0);
|
|
44
48
|
if (!agent || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u.test(agent)
|
|
45
|
-
|| !['required', 'deferred', 'all'].includes(selection)
|
|
46
|
-
|
|
49
|
+
|| !['required', 'deferred', 'all'].includes(selection)
|
|
50
|
+
|| !validAttempts) {
|
|
51
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'PLATFORM_OPERATION_RECOVERY_PAYLOAD_INVALID', message: 'recover requires a valid --agent, --selection, and positive --attempts when supplied' } })}\n`);
|
|
47
52
|
process.stderr.write(USAGE);
|
|
48
53
|
process.exitCode = 1;
|
|
49
54
|
return;
|
|
50
55
|
}
|
|
51
|
-
const result = await recoverPlatformOperations(task.taskId, selection, { agent, cwd: repoRoot });
|
|
56
|
+
const result = await recoverPlatformOperations(task.taskId, selection, { agent, cwd: repoRoot, ...(attempts === undefined ? {} : { attempts }) });
|
|
52
57
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
53
58
|
process.exitCode = result.status === 'applied' || result.status === 'no-op' ? 0 : result.status === 'blocked' ? 2 : 1;
|
|
54
59
|
}
|
|
@@ -8,6 +8,7 @@ import { getOpenWorkflowWarnings } from '../task/workflow-warnings.js';
|
|
|
8
8
|
import { applyWorkflowWarningIntent } from '../task/workflow-warning-intents.js';
|
|
9
9
|
import { captureTaskWriteMetadata } from '../task/write.js';
|
|
10
10
|
import { syncPlatformComment } from './issue-comments.js';
|
|
11
|
+
import { providerErrorRetryable } from './provider-validation.js';
|
|
11
12
|
import { platformResult } from './types.js';
|
|
12
13
|
import { taskIssueIdentity } from './task-identities.js';
|
|
13
14
|
function result(base, artifacts, warnings) {
|
|
@@ -124,7 +125,7 @@ async function backfillCompletionComments(taskRef, options) {
|
|
|
124
125
|
if (latest.status === 'failed' || latest.status === 'blocked')
|
|
125
126
|
return result({ ...latest, operations }, artifacts, []);
|
|
126
127
|
if (latest.error)
|
|
127
|
-
return result(platformResult(latest.error.retryable ? 'blocked' : 'failed', {
|
|
128
|
+
return result(platformResult(latest.error.retryable || providerErrorRetryable(latest.error.code) ? 'blocked' : 'failed', {
|
|
128
129
|
platform: latest.platform,
|
|
129
130
|
resource: latest.resource,
|
|
130
131
|
capabilities: latest.capabilities,
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { loadPlatformProvider } from './provider-loader.js';
|
|
3
3
|
import { defaultGitRemote, parseGitHubRemote } from './github-provider.js';
|
|
4
|
+
import { providerErrorRetryable } from './provider-validation.js';
|
|
4
5
|
import { platformResult } from './types.js';
|
|
5
6
|
function errorStatus(error) {
|
|
6
|
-
if (error.retryable || error.code === 'AUTH_REQUIRED' || error.code === 'PLATFORM_DEPENDENCY_MISSING')
|
|
7
|
+
if (error.retryable || providerErrorRetryable(error.code) || error.code === 'AUTH_REQUIRED' || error.code === 'PLATFORM_DEPENDENCY_MISSING')
|
|
7
8
|
return 'blocked';
|
|
8
9
|
if (error.code === 'REMOTE_MISSING' || error.code === 'PLATFORM_UNSUPPORTED')
|
|
9
10
|
return 'no-op';
|
|
10
11
|
return 'failed';
|
|
11
12
|
}
|
|
12
13
|
function contextError(providerType, error, repository = null) {
|
|
14
|
+
const retryable = error.retryable ?? providerErrorRetryable(error.code);
|
|
13
15
|
return platformResult(errorStatus(error), {
|
|
14
16
|
platform: { type: providerType || null, repository, currentUser: null },
|
|
15
|
-
error: { code: error.code, message: error.message, retryable
|
|
17
|
+
error: { code: error.code, message: error.message, retryable }
|
|
16
18
|
});
|
|
17
19
|
}
|
|
18
20
|
function contextFromSnapshot(providerType, snapshot) {
|