@fitlab-ai/agent-infra 0.9.14 → 0.9.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/README.zh-CN.md +10 -0
- package/bin/internal-cli.sh +45 -0
- package/dist/bin/internal-cli.js +93 -30
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +37 -0
- package/dist/lib/fs/file-lock.js +111 -0
- package/dist/lib/host-control/audit.js +19 -0
- package/dist/lib/host-control/client.js +142 -0
- package/dist/lib/host-control/command.js +49 -0
- package/dist/lib/host-control/path.js +160 -0
- package/dist/lib/host-control/secure-fs.js +140 -0
- package/dist/lib/host-control/server.js +159 -0
- package/dist/lib/host-control/service.js +112 -0
- package/dist/lib/init.js +11 -2
- package/dist/lib/internal/cli-route-inventory.js +3 -1
- package/dist/lib/internal/codex-lifecycle.js +6 -6
- package/dist/lib/internal/demo-promotion.js +322 -0
- package/dist/lib/internal/demo-transcript.js +454 -0
- package/dist/lib/internal/host-control.js +28 -0
- package/dist/lib/internal/manual-validation.js +322 -0
- package/dist/lib/internal/platform-pr.js +18 -4
- package/dist/lib/internal/release-workflow.js +63 -84
- package/dist/lib/internal/sandbox-control.js +149 -2
- package/dist/lib/internal/task-artifact.js +15 -262
- package/dist/lib/internal/task-create.js +6 -5
- package/dist/lib/internal/task-event.js +37 -37
- package/dist/lib/internal/task-operation-registry.js +101 -3
- package/dist/lib/internal/task-review.js +8 -53
- package/dist/lib/internal/task-validate.js +30 -10
- package/dist/lib/owned-path.js +33 -0
- package/dist/lib/platform/check-status.js +11 -0
- package/dist/lib/platform/comment-safety.js +4 -1
- package/dist/lib/platform/completion-backfill.js +1 -1
- package/dist/lib/platform/github-data.js +241 -0
- package/dist/lib/platform/github-provider.js +15 -20
- package/dist/lib/platform/issue-comments.js +2 -2
- package/dist/lib/platform/issues.js +1 -179
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +55 -3
- package/dist/lib/platform/pull-requests.js +2 -105
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/verification-sync.js +127 -142
- package/dist/lib/release/workflow.js +1 -1
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/rm.js +3 -1514
- package/dist/lib/sandbox/control/audit.js +203 -0
- package/dist/lib/sandbox/control/client.js +93 -54
- package/dist/lib/sandbox/control/executor.js +129 -65
- package/dist/lib/sandbox/control/finalization-response.js +27 -0
- package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
- package/dist/lib/sandbox/control/lifecycle.js +36 -2
- package/dist/lib/sandbox/control/native-file-lock.js +6 -98
- package/dist/lib/sandbox/control/protocol.js +55 -4
- package/dist/lib/sandbox/control/server.js +491 -120
- package/dist/lib/sandbox/control/state.js +58 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +99 -0
- package/dist/lib/sandbox/index.js +2 -2
- package/dist/lib/sandbox/recovery.js +28 -4
- package/dist/lib/sandbox/removal.js +1717 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- package/dist/lib/sandbox/workspace-view.js +13 -14
- package/dist/lib/task/activity-intent.js +2 -1
- package/dist/lib/task/activity-log.js +30 -24
- package/dist/lib/task/artifact-command.js +101 -0
- package/dist/lib/task/artifact-lifecycle.js +30 -28
- package/dist/lib/task/artifact-name.js +36 -0
- package/dist/lib/task/artifact-operations.js +23 -69
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-repair-intent.js +87 -16
- package/dist/lib/task/artifact-schema.js +6 -14
- package/dist/lib/task/capabilities.js +12 -19
- package/dist/lib/task/codex-orchestration.js +71 -25
- package/dist/lib/task/commit-identity.js +28 -0
- package/dist/lib/task/commit-operation.js +2 -16
- package/dist/lib/task/control-authority.js +371 -5
- package/dist/lib/task/control-recovery.js +128 -0
- package/dist/lib/task/create.js +62 -9
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/events.js +120 -9
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +3 -6
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/intermediate-cleanup.js +383 -0
- package/dist/lib/task/invalidation.js +18 -65
- package/dist/lib/task/ledger-intents.js +7 -22
- package/dist/lib/task/ledger.js +1 -3
- package/dist/lib/task/lifecycle.js +19 -1
- package/dist/lib/task/local-artifact-finalization.js +132 -157
- package/dist/lib/task/manual-validation-completion.js +26 -0
- package/dist/lib/task/manual-validation-evidence.js +130 -0
- package/dist/lib/task/manual-validation-receipt.js +101 -0
- package/dist/lib/task/manual-validation-shared.js +40 -0
- package/dist/lib/task/manual-validation-transaction.js +243 -0
- package/dist/lib/task/markdown.js +47 -0
- package/dist/lib/task/orchestration.js +14 -17
- package/dist/lib/task/qualification-audit.js +20 -34
- package/dist/lib/task/review-artifacts.js +2 -18
- package/dist/lib/task/review-command.js +25 -0
- package/dist/lib/task/review-finalization.js +125 -193
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -56
- package/dist/lib/task/verification-engine.js +116 -323
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +17 -0
- package/dist/lib/task/workflow-warning-intents.js +0 -16
- package/dist/lib/task/workflow-warnings.js +28 -2
- package/dist/lib/update.js +2 -0
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
- package/lib/fs/durable-write.ts +29 -0
- package/lib/fs/file-lock.ts +113 -0
- package/lib/host-control/audit.ts +22 -0
- package/lib/host-control/client.ts +172 -0
- package/lib/host-control/command.ts +60 -0
- package/lib/host-control/path.ts +167 -0
- package/lib/host-control/secure-fs.ts +135 -0
- package/lib/host-control/server.ts +180 -0
- package/lib/host-control/service.ts +133 -0
- package/lib/init.ts +15 -2
- package/lib/internal/cli-route-inventory.ts +3 -1
- package/lib/internal/codex-lifecycle.ts +6 -6
- package/lib/internal/demo-promotion.ts +354 -0
- package/lib/internal/demo-transcript.ts +419 -0
- package/lib/internal/host-control.ts +28 -0
- package/lib/internal/manual-validation.ts +306 -0
- package/lib/internal/platform-pr.ts +15 -4
- package/lib/internal/release-workflow.ts +74 -82
- package/lib/internal/sandbox-control.ts +162 -1
- package/lib/internal/task-artifact.ts +13 -216
- package/lib/internal/task-create.ts +5 -5
- package/lib/internal/task-event.ts +20 -11
- package/lib/internal/task-operation-registry.ts +111 -3
- package/lib/internal/task-review.ts +6 -51
- package/lib/internal/task-validate.ts +26 -10
- package/lib/owned-path.ts +26 -0
- package/lib/platform/check-status.ts +9 -0
- package/lib/platform/comment-safety.ts +5 -0
- package/lib/platform/completion-backfill.ts +1 -1
- package/lib/platform/github-data.ts +322 -0
- package/lib/platform/github-provider.ts +15 -20
- package/lib/platform/issue-comments.ts +3 -4
- package/lib/platform/issues.ts +4 -245
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +61 -5
- package/lib/platform/pull-requests.ts +3 -170
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/verification-sync.ts +145 -155
- package/lib/release/workflow.ts +3 -3
- package/lib/sandbox/commands/create.ts +7 -6
- package/lib/sandbox/commands/rm.ts +3 -1909
- package/lib/sandbox/control/audit.ts +253 -0
- package/lib/sandbox/control/client.ts +109 -66
- package/lib/sandbox/control/executor.ts +134 -74
- package/lib/sandbox/control/finalization-response.ts +29 -0
- package/lib/sandbox/control/identity-sentinel.ts +137 -0
- package/lib/sandbox/control/lifecycle.ts +45 -3
- package/lib/sandbox/control/native-file-lock.ts +6 -107
- package/lib/sandbox/control/protocol.ts +75 -6
- package/lib/sandbox/control/server.ts +538 -123
- package/lib/sandbox/control/state.ts +78 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +102 -0
- package/lib/sandbox/index.ts +2 -2
- package/lib/sandbox/recovery.ts +28 -5
- package/lib/sandbox/removal.ts +2144 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- package/lib/sandbox/workspace-view.ts +17 -15
- package/lib/task/activity-intent.ts +2 -2
- package/lib/task/activity-log.ts +27 -19
- package/lib/task/artifact-command.ts +104 -0
- package/lib/task/artifact-lifecycle.ts +31 -26
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +22 -68
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-repair-intent.ts +99 -18
- package/lib/task/artifact-schema.ts +5 -34
- package/lib/task/capabilities.ts +13 -22
- package/lib/task/codex-orchestration.ts +85 -24
- package/lib/task/commit-identity.ts +31 -0
- package/lib/task/commit-operation.ts +2 -16
- package/lib/task/control-authority.ts +502 -5
- package/lib/task/control-recovery.ts +174 -0
- package/lib/task/create.ts +63 -8
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/events.ts +131 -10
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +3 -8
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/intermediate-cleanup.ts +455 -0
- package/lib/task/invalidation.ts +18 -62
- package/lib/task/ledger-intents.ts +7 -24
- package/lib/task/ledger.ts +1 -3
- package/lib/task/lifecycle.ts +17 -1
- package/lib/task/local-artifact-finalization.ts +148 -182
- package/lib/task/manual-validation-completion.ts +40 -0
- package/lib/task/manual-validation-evidence.ts +209 -0
- package/lib/task/manual-validation-receipt.ts +152 -0
- package/lib/task/manual-validation-shared.ts +50 -0
- package/lib/task/manual-validation-transaction.ts +304 -0
- package/lib/task/markdown.ts +70 -0
- package/lib/task/orchestration.ts +19 -19
- package/lib/task/qualification-audit.ts +22 -33
- package/lib/task/review-artifacts.ts +1 -20
- package/lib/task/review-command.ts +23 -0
- package/lib/task/review-finalization.ts +131 -259
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -54
- package/lib/task/verification-engine.ts +110 -366
- package/lib/task/verification-types.ts +17 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +18 -0
- package/lib/task/workflow-warning-intents.ts +0 -13
- package/lib/task/workflow-warnings.ts +21 -2
- package/lib/update.ts +6 -0
- package/package.json +6 -6
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- package/templates/.agents/rules/testing-discipline.en.md +2 -0
- package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
- package/templates/.agents/scripts/package.json +3 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +32 -20
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +32 -20
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +7 -3
- package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test/SKILL.en.md +17 -6
- package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/dist/lib/platform/adapters.js +0 -56
- package/lib/platform/adapters.ts +0 -197
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
accessSandboxTaskView,
|
|
@@ -13,12 +14,18 @@ import {
|
|
|
13
14
|
PUBLIC_CLI_SELECTOR_ALIASES,
|
|
14
15
|
internalRouteSelector
|
|
15
16
|
} from './cli-route-inventory.ts';
|
|
17
|
+
import { readSandboxControlIdentitySentinel } from '../sandbox/control/identity-sentinel.ts';
|
|
16
18
|
|
|
17
19
|
export type TaskOperationDispatcher = 'public' | 'internal';
|
|
18
20
|
export type TaskOperationScope = 'task-bound' | 'non-task' | 'conditional';
|
|
19
21
|
export type TaskOperationEffect = TaskViewAccessEffect;
|
|
20
22
|
export type TaskRefSource = 'argv' | 'environment' | 'none' | 'delegated' | 'input';
|
|
21
23
|
|
|
24
|
+
export type SandboxControlTransportDecision = Readonly<{
|
|
25
|
+
kind: 'direct-host' | 'broker-client' | 'fail-closed';
|
|
26
|
+
reasonCode: string | null;
|
|
27
|
+
}>;
|
|
28
|
+
|
|
22
29
|
export type TaskOperationDescriptor = Readonly<{
|
|
23
30
|
dispatcher: TaskOperationDispatcher;
|
|
24
31
|
command: string;
|
|
@@ -49,6 +56,10 @@ function internalTaskRoutes(): TaskOperationDescriptor[] {
|
|
|
49
56
|
descriptor('internal', 'sandbox-control', 'execute', 'non-task', 'progress', 'none'),
|
|
50
57
|
descriptor('internal', 'sandbox-control', 'recover', 'conditional', 'recovery', 'delegated'),
|
|
51
58
|
descriptor('internal', 'sandbox-control', 'client', 'conditional', 'progress', 'delegated'),
|
|
59
|
+
descriptor('internal', 'host-control', 'serve', 'non-task', 'progress', 'none'),
|
|
60
|
+
descriptor('internal', 'host-control', 'status', 'non-task', 'diagnostic', 'none'),
|
|
61
|
+
descriptor('internal', 'host-control', 'install', 'non-task', 'progress', 'none'),
|
|
62
|
+
descriptor('internal', 'host-control', 'uninstall', 'non-task', 'progress', 'none'),
|
|
52
63
|
descriptor('internal', 'agent-client', 'next-steps', 'non-task', 'diagnostic', 'none'),
|
|
53
64
|
descriptor('internal', 'agent-client', 'model-selection', 'non-task', 'diagnostic', 'none'),
|
|
54
65
|
descriptor('internal', 'codex-lifecycle', 'preflight', 'non-task', 'diagnostic', 'none'),
|
|
@@ -145,7 +156,8 @@ function internalTaskRoutes(): TaskOperationDescriptor[] {
|
|
|
145
156
|
descriptor('internal', 'task-snapshot', 'snapshot', 'conditional', 'diagnostic'),
|
|
146
157
|
descriptor('internal', 'task-verify', 'event', 'task-bound', 'terminal-verdict'),
|
|
147
158
|
descriptor('internal', 'task-validate', 'snapshot', 'conditional', 'diagnostic'),
|
|
148
|
-
descriptor('internal', 'task-validate', 'inplace', 'task-bound', 'progress')
|
|
159
|
+
descriptor('internal', 'task-validate', 'inplace', 'task-bound', 'progress'),
|
|
160
|
+
descriptor('internal', 'manual-validation', 'transaction', 'task-bound', 'remote-write')
|
|
149
161
|
];
|
|
150
162
|
}
|
|
151
163
|
|
|
@@ -207,12 +219,12 @@ export const TASK_OPERATION_DESCRIPTORS = Object.freeze([
|
|
|
207
219
|
]);
|
|
208
220
|
|
|
209
221
|
export const INTERNAL_DISPATCHER_ROUTES = Object.freeze([
|
|
210
|
-
'task-create', 'task-qualification', 'sandbox-control', 'agent-client', 'codex-lifecycle', 'codex-sandbox-controller',
|
|
222
|
+
'task-create', 'task-qualification', 'sandbox-control', 'host-control', 'agent-client', 'codex-lifecycle', 'codex-sandbox-controller',
|
|
211
223
|
'git-workflow', 'task-delivery', 'release-workflow', 'platform-release-notes', 'platform-security', 'platform-metadata', 'platform-context',
|
|
212
224
|
'platform-comment', 'platform-issue', 'platform-pr', 'platform-pr-review', 'pr-review-grade',
|
|
213
225
|
'platform-checks', 'task-context', 'task-ledger', 'task-warning', 'task-activity', 'task-artifact',
|
|
214
226
|
'task-orchestration', 'task-review', 'task-event', 'task-invalidation', 'task-lifecycle',
|
|
215
|
-
'task-finalization', 'task-override', 'task-short-id', 'task-snapshot', 'task-verify', 'task-validate'
|
|
227
|
+
'task-finalization', 'task-override', 'task-short-id', 'task-snapshot', 'task-verify', 'task-validate', 'manual-validation'
|
|
216
228
|
]);
|
|
217
229
|
export const PUBLIC_DISPATCHER_ROUTES = Object.freeze([
|
|
218
230
|
'agent-client', 'cp', 'data', 'decide', 'help', 'init', 'merge', 'run', 'sandbox', 'server', 'task', 'sync', 'update', 'version'
|
|
@@ -284,6 +296,35 @@ const TASK_CONTROL_MARKER_KEYS = [
|
|
|
284
296
|
'AGENT_INFRA_CONTROL_STATUS_DIR'
|
|
285
297
|
] as const;
|
|
286
298
|
|
|
299
|
+
const TASK_CONTROL_CONFIG_KEYS = [
|
|
300
|
+
'AGENT_INFRA_CONTROL_TOKEN',
|
|
301
|
+
'AGENT_INFRA_CONTROL_GENERATION',
|
|
302
|
+
'AGENT_INFRA_CONTROL_ROOT_ID',
|
|
303
|
+
'AGENT_INFRA_CONTROL_DIR',
|
|
304
|
+
'AGENT_INFRA_CONTROL_STATUS_DIR'
|
|
305
|
+
] as const;
|
|
306
|
+
export const SANDBOX_CONTROL_STATUS_MOUNT = '/run/agent-infra/control-status';
|
|
307
|
+
|
|
308
|
+
type NativeDirectoryProbe = 'present' | 'absent' | 'unknown';
|
|
309
|
+
|
|
310
|
+
function nativeDirectoryProbe(candidate: string): NativeDirectoryProbe {
|
|
311
|
+
try {
|
|
312
|
+
// The fixed mount is a trust anchor. Do not use a mutable node:fs export:
|
|
313
|
+
// a preload must not turn a mounted sandbox into the direct-host path by
|
|
314
|
+
// replacing the ordinary filesystem probe.
|
|
315
|
+
const binding = (process as unknown as {
|
|
316
|
+
binding(name: string): { internalModuleStat(filePath: string): number }
|
|
317
|
+
}).binding('fs');
|
|
318
|
+
if (!Function.prototype.toString.call(binding.internalModuleStat).includes('[native code]')) return 'unknown';
|
|
319
|
+
const result = binding.internalModuleStat(candidate);
|
|
320
|
+
if (result === 1) return 'present';
|
|
321
|
+
if (result === -2) return 'absent';
|
|
322
|
+
return 'unknown';
|
|
323
|
+
} catch {
|
|
324
|
+
return 'unknown';
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
287
328
|
type TaskMarkerState = 'none' | 'branch-only' | 'task-bound' | 'incomplete';
|
|
288
329
|
|
|
289
330
|
function taskMarkerState(env: NodeJS.ProcessEnv): TaskMarkerState {
|
|
@@ -298,6 +339,73 @@ function taskMarkerState(env: NodeJS.ProcessEnv): TaskMarkerState {
|
|
|
298
339
|
return 'incomplete';
|
|
299
340
|
}
|
|
300
341
|
|
|
342
|
+
export function resolveSandboxControlTransport(
|
|
343
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
344
|
+
options: Readonly<{ statusMountPath?: string }> = {}
|
|
345
|
+
): SandboxControlTransportDecision {
|
|
346
|
+
const configuredStatusDir = env.AGENT_INFRA_CONTROL_STATUS_DIR;
|
|
347
|
+
const fixedStatusDir = options.statusMountPath
|
|
348
|
+
?? SANDBOX_CONTROL_STATUS_MOUNT;
|
|
349
|
+
const hasAnyMarker = TASK_MARKER_KEYS.some((key) => Boolean(env[key]))
|
|
350
|
+
|| Boolean(env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING)
|
|
351
|
+
|| Boolean(env.AGENT_INFRA_EXECUTOR_MANIFEST);
|
|
352
|
+
const fixedStatusProbe = !env.AGENT_INFRA_TEST_HOST_CONTROL_ENDPOINT && path.isAbsolute(fixedStatusDir)
|
|
353
|
+
? nativeDirectoryProbe(fixedStatusDir) : 'absent';
|
|
354
|
+
if (fixedStatusProbe === 'unknown') {
|
|
355
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_UNAVAILABLE' };
|
|
356
|
+
}
|
|
357
|
+
const fixedStatusMounted = fixedStatusProbe === 'present';
|
|
358
|
+
const configuredStatusProbe = configuredStatusDir && path.isAbsolute(configuredStatusDir)
|
|
359
|
+
? nativeDirectoryProbe(configuredStatusDir) : 'absent';
|
|
360
|
+
if (configuredStatusProbe === 'unknown') {
|
|
361
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_UNAVAILABLE' };
|
|
362
|
+
}
|
|
363
|
+
const statusDir = configuredStatusDir && path.isAbsolute(configuredStatusDir) && configuredStatusProbe === 'present'
|
|
364
|
+
? configuredStatusDir
|
|
365
|
+
: fixedStatusMounted ? fixedStatusDir : null;
|
|
366
|
+
const statusMounted = statusDir !== null;
|
|
367
|
+
const hasCompleteConfig = TASK_CONTROL_CONFIG_KEYS.every((key) => Boolean(env[key]));
|
|
368
|
+
if (env.AGENT_INFRA_EXECUTOR_MANIFEST || env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING) {
|
|
369
|
+
return { kind: 'fail-closed', reasonCode: 'TASK_CONTROL_TRANSPORT_INVALID' };
|
|
370
|
+
}
|
|
371
|
+
const taskBound = Boolean(env.AGENT_INFRA_TASK_ID);
|
|
372
|
+
const runtime = Boolean(env.AGENT_INFRA_RUNTIME_DIR);
|
|
373
|
+
|
|
374
|
+
if (statusMounted) {
|
|
375
|
+
let sentinel;
|
|
376
|
+
try {
|
|
377
|
+
sentinel = readSandboxControlIdentitySentinel(statusDir);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
const message = error instanceof Error ? error.message : '';
|
|
380
|
+
return { kind: 'fail-closed', reasonCode: message.endsWith('MISSING')
|
|
381
|
+
? 'SANDBOX_CONTROL_IDENTITY_MISSING'
|
|
382
|
+
: 'SANDBOX_CONTROL_IDENTITY_MALFORMED' };
|
|
383
|
+
}
|
|
384
|
+
if (sentinel.generation !== env.AGENT_INFRA_CONTROL_GENERATION) {
|
|
385
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_GENERATION_MISMATCH' };
|
|
386
|
+
}
|
|
387
|
+
if (env.AGENT_INFRA_CONTROL_ROOT_ID && sentinel.controlRootId !== env.AGENT_INFRA_CONTROL_ROOT_ID) {
|
|
388
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_ROOT_ID_MISMATCH' };
|
|
389
|
+
}
|
|
390
|
+
if (sentinel.mode === 'task-bound'
|
|
391
|
+
&& (sentinel.taskId !== env.AGENT_INFRA_TASK_ID || !runtime)) {
|
|
392
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_TOPOLOGY_MISMATCH' };
|
|
393
|
+
}
|
|
394
|
+
if (sentinel.mode === 'branch-only' && (taskBound || runtime)) {
|
|
395
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_TOPOLOGY_MISMATCH' };
|
|
396
|
+
}
|
|
397
|
+
if (!hasCompleteConfig) {
|
|
398
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' };
|
|
399
|
+
}
|
|
400
|
+
return { kind: 'broker-client', reasonCode: null };
|
|
401
|
+
}
|
|
402
|
+
if (!hasAnyMarker) return { kind: 'direct-host', reasonCode: null };
|
|
403
|
+
if (!hasCompleteConfig || (taskBound && !runtime) || (!taskBound && runtime)) {
|
|
404
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' };
|
|
405
|
+
}
|
|
406
|
+
return { kind: 'broker-client', reasonCode: null };
|
|
407
|
+
}
|
|
408
|
+
|
|
301
409
|
export function hasTaskBoundMarker(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
302
410
|
return taskMarkerState(env) === 'task-bound';
|
|
303
411
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { parseReviewCommand } from '../task/review-command.ts';
|
|
1
2
|
import { finalizeReviewSummary } from '../task/review-finalization.ts';
|
|
2
3
|
import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.ts';
|
|
3
4
|
import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
4
5
|
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
|
|
5
|
-
import { ensureInternalHandlerRoute
|
|
6
|
+
import { ensureInternalHandlerRoute } from './cli-route-inventory.ts';
|
|
6
7
|
|
|
7
8
|
const USAGE = 'Usage: agent-infra-internal task-review <task-ref> finalize-summary --stage <analysis|plan|code> --artifact <review-*.md> [--orchestrated] [--dry-run] [--override-ticket <ticket> --override-target <target> --override-scope <scope>]\n';
|
|
8
9
|
|
|
@@ -23,56 +24,10 @@ async function taskReview(args: string[] = []): Promise<void> {
|
|
|
23
24
|
process.stdout.write(USAGE);
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
if (!internalHandlerRoute('task-review', 'finalize-summary', args[1] ?? '')) {
|
|
31
|
-
failUsage(`unknown intent '${args[1]}'`);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
let stage = '';
|
|
35
|
-
let artifact = '';
|
|
36
|
-
let dryRun = false;
|
|
37
|
-
let orchestrated = false;
|
|
38
|
-
let overrideTicket = '';
|
|
39
|
-
let overrideTarget = '';
|
|
40
|
-
let overrideScope = '';
|
|
41
|
-
const seen = new Set<string>();
|
|
42
|
-
for (let index = 2; index < args.length; index += 1) {
|
|
43
|
-
const flag = args[index]!;
|
|
44
|
-
if (!['--stage', '--artifact', '--orchestrated', '--dry-run', '--override-ticket', '--override-target', '--override-scope'].includes(flag)) {
|
|
45
|
-
failUsage(`unknown option '${flag}'`);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
if (seen.has(flag)) {
|
|
49
|
-
failUsage(`duplicate option '${flag}'`);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
seen.add(flag);
|
|
53
|
-
if (flag === '--dry-run') {
|
|
54
|
-
dryRun = true;
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
if (flag === '--orchestrated') {
|
|
58
|
-
orchestrated = true;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
const value = args[++index];
|
|
62
|
-
if (!value || value.startsWith('--')) {
|
|
63
|
-
failUsage(`option '${flag}' requires a value`);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
if (flag === '--stage') stage = value;
|
|
67
|
-
else if (flag === '--artifact') artifact = value;
|
|
68
|
-
else if (flag === '--override-ticket') overrideTicket = value;
|
|
69
|
-
else if (flag === '--override-target') overrideTarget = value;
|
|
70
|
-
else if (flag === '--override-scope') overrideScope = value;
|
|
71
|
-
}
|
|
72
|
-
if (!stage || !artifact) {
|
|
73
|
-
failUsage("options '--stage' and '--artifact' are required");
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
27
|
+
let request;
|
|
28
|
+
try { request = parseReviewCommand(args); }
|
|
29
|
+
catch (error) { failUsage(error instanceof Error ? error.message : String(error)); return; }
|
|
30
|
+
const { stage, artifact, dryRun, orchestrated, overrideTicket, overrideTarget, overrideScope } = request;
|
|
76
31
|
const dryRunConflict = overrideDryRunConflict({ dryRun, overrideTicket, overrideTarget, overrideScope });
|
|
77
32
|
if (dryRunConflict) { failUsage(dryRunConflict.message); return; }
|
|
78
33
|
const resolved = resolveTaskRef(args[0]!);
|
|
@@ -24,9 +24,12 @@ import {
|
|
|
24
24
|
} from '../sandbox/control/protocol.ts';
|
|
25
25
|
import { getProcessStartTime } from '../server/process-state.ts';
|
|
26
26
|
import { assertGitWorktreeBinding } from '../git/worktree-identity.ts';
|
|
27
|
+
import { createManualValidationEvidence, manualValidationEvidenceSummary } from '../task/manual-validation-evidence.ts';
|
|
28
|
+
import type { ManualValidationCleanup } from '../task/manual-validation-evidence.ts';
|
|
29
|
+
import { writeJsonAtomic } from '../task/manual-validation-shared.ts';
|
|
27
30
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
|
|
28
31
|
|
|
29
|
-
const USAGE = `Usage: agent-infra-internal task-validate <branch | TASK-id | N> [--scope snapshot|inplace] [--timeout <ms>] [--format text|json] -- <command> [args...]`;
|
|
32
|
+
const USAGE = `Usage: agent-infra-internal task-validate <branch | TASK-id | N> [--scope snapshot|inplace] [--timeout <ms>] [--format text|json] [--evidence-file <path>] -- <command> [args...]`;
|
|
30
33
|
const MAX_TIMEOUT_MS = 60 * 60 * 1000;
|
|
31
34
|
const RECOVERY_GRACE_MS = 30_000;
|
|
32
35
|
|
|
@@ -35,13 +38,14 @@ type ValidateOptions = {
|
|
|
35
38
|
scope: 'snapshot' | 'inplace';
|
|
36
39
|
timeoutMs: number;
|
|
37
40
|
format: 'text' | 'json';
|
|
41
|
+
evidenceFile: string | null;
|
|
38
42
|
command: string[];
|
|
39
43
|
help: boolean;
|
|
40
44
|
};
|
|
41
45
|
|
|
42
46
|
export function parseValidateArgs(args: string[]): ValidateOptions {
|
|
43
47
|
if (args.includes('--help') || args.includes('-h')) {
|
|
44
|
-
return { target: '', scope: 'snapshot', timeoutMs: 300_000, format: 'text', command: [], help: true };
|
|
48
|
+
return { target: '', scope: 'snapshot', timeoutMs: 300_000, format: 'text', evidenceFile: null, command: [], help: true };
|
|
45
49
|
}
|
|
46
50
|
const separator = args.indexOf('--');
|
|
47
51
|
if (separator < 0) throw new Error(`${USAGE}\nA literal -- must separate validation options from the command.`);
|
|
@@ -50,6 +54,7 @@ export function parseValidateArgs(args: string[]): ValidateOptions {
|
|
|
50
54
|
let scope: ValidateOptions['scope'] = 'snapshot';
|
|
51
55
|
let timeoutMs = 300_000;
|
|
52
56
|
let format: ValidateOptions['format'] = 'text';
|
|
57
|
+
let evidenceFile: string | null = null;
|
|
53
58
|
const positionals: string[] = [];
|
|
54
59
|
for (let index = 0; index < optionArgs.length; index += 1) {
|
|
55
60
|
const arg = optionArgs[index]!;
|
|
@@ -65,6 +70,10 @@ export function parseValidateArgs(args: string[]): ValidateOptions {
|
|
|
65
70
|
const value = optionArgs[++index];
|
|
66
71
|
if (value !== 'text' && value !== 'json') throw new Error('validate --format must be text or json');
|
|
67
72
|
format = value;
|
|
73
|
+
} else if (arg === '--evidence-file') {
|
|
74
|
+
const value = optionArgs[++index];
|
|
75
|
+
if (!value || value.startsWith('-')) throw new Error('validate --evidence-file requires a path');
|
|
76
|
+
evidenceFile = value;
|
|
68
77
|
} else if (arg.startsWith('-')) {
|
|
69
78
|
throw new Error(`Unknown option: ${arg}`);
|
|
70
79
|
} else {
|
|
@@ -72,7 +81,7 @@ export function parseValidateArgs(args: string[]): ValidateOptions {
|
|
|
72
81
|
}
|
|
73
82
|
}
|
|
74
83
|
if (positionals.length !== 1 || command.length === 0) throw new Error(USAGE);
|
|
75
|
-
return { target: positionals[0]!, scope, timeoutMs, format, command, help: false };
|
|
84
|
+
return { target: positionals[0]!, scope, timeoutMs, format, evidenceFile, command, help: false };
|
|
76
85
|
}
|
|
77
86
|
|
|
78
87
|
function runValidationCommand(command: string[], cwd: string, scope: string, timeoutMs: number, json: boolean) {
|
|
@@ -294,23 +303,30 @@ function taskValidate(args: string[]): void {
|
|
|
294
303
|
: internalHandlerRoute('task-validate', 'inplace', options.scope)
|
|
295
304
|
? inplaceValidation(config, target, options)
|
|
296
305
|
: (() => { throw new Error('TASK_VALIDATE_SCOPE_UNREGISTERED'); })();
|
|
297
|
-
const
|
|
298
|
-
|
|
306
|
+
const cleanup = result.cleanup() as ManualValidationCleanup;
|
|
307
|
+
const evidence = createManualValidationEvidence({
|
|
308
|
+
mode: target.workspace.mode,
|
|
299
309
|
taskId: target.workspace.mode === 'task-bound' ? target.workspace.taskId : null,
|
|
300
310
|
branch: target.branch,
|
|
301
|
-
scope: options.scope,
|
|
302
311
|
commit,
|
|
312
|
+
recoverable: target.workspace.mode === 'task-bound',
|
|
313
|
+
scope: options.scope,
|
|
303
314
|
command: path.basename(options.command[0]!),
|
|
304
315
|
startedAt,
|
|
305
316
|
completedAt: new Date().toISOString(),
|
|
306
317
|
exitCode: result.exitCode,
|
|
307
318
|
signal: result.signal,
|
|
308
|
-
cleanup
|
|
309
|
-
};
|
|
319
|
+
cleanup
|
|
320
|
+
});
|
|
321
|
+
if (options.evidenceFile) {
|
|
322
|
+
try { writeJsonAtomic(path.resolve(options.evidenceFile), evidence); }
|
|
323
|
+
catch (error) { throw new Error(`MANUAL_VALIDATION_EVIDENCE_WRITE_FAILED: ${error instanceof Error ? error.message : String(error)}`); }
|
|
324
|
+
}
|
|
325
|
+
const evidenceSummary = manualValidationEvidenceSummary(evidence);
|
|
310
326
|
if (options.format === 'json') {
|
|
311
|
-
process.stdout.write(`${JSON.stringify({ status: 'applied', changed: false, evidence, error: null })}\n`);
|
|
327
|
+
process.stdout.write(`${JSON.stringify({ status: 'applied', changed: false, evidence: evidenceSummary, evidenceFile: options.evidenceFile, error: null })}\n`);
|
|
312
328
|
} else {
|
|
313
|
-
process.stdout.write(`Validation ${result.exitCode === 0 ? 'passed' : 'failed'} (${options.scope}, ${
|
|
329
|
+
process.stdout.write(`Validation ${result.exitCode === 0 ? 'passed' : 'failed'} (${options.scope}, ${evidenceSummary.command}, cleanup=${evidenceSummary.cleanup}).\n`);
|
|
314
330
|
}
|
|
315
331
|
process.exitCode = result.exitCode;
|
|
316
332
|
} catch (error) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
// Inspect descendants without following symlinks. The caller supplies the trusted root.
|
|
5
|
+
export function inspectOwnedPath(rootInput: string, targetInput: string, expect: 'file' | 'directory'): fs.Stats | null {
|
|
6
|
+
const root = path.resolve(rootInput);
|
|
7
|
+
const target = path.resolve(targetInput);
|
|
8
|
+
const relative = path.relative(root, target);
|
|
9
|
+
if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) return null;
|
|
10
|
+
try {
|
|
11
|
+
const rootStat = fs.lstatSync(root);
|
|
12
|
+
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) return null;
|
|
13
|
+
let current = root;
|
|
14
|
+
for (const segment of relative.split(path.sep)) {
|
|
15
|
+
current = path.join(current, segment);
|
|
16
|
+
const stat = fs.lstatSync(current);
|
|
17
|
+
if (stat.isSymbolicLink()) return null;
|
|
18
|
+
if (process.platform !== 'win32' && typeof process.getuid === 'function' && stat.uid !== process.getuid()) return null;
|
|
19
|
+
if (current === target) return (expect === 'file' ? stat.isFile() : stat.isDirectory()) ? stat : null;
|
|
20
|
+
if (!stat.isDirectory()) return null;
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PlatformCheckSnapshot } from './snapshots.ts';
|
|
2
|
+
|
|
3
|
+
export function checkStatusBucket(status: string): PlatformCheckSnapshot['bucket'] {
|
|
4
|
+
const value = status.toLowerCase();
|
|
5
|
+
if (['pass', 'success', 'successful', 'neutral'].includes(value)) return 'pass';
|
|
6
|
+
if (['fail', 'failure', 'failed', 'error', 'timed_out', 'action_required'].includes(value)) return 'fail';
|
|
7
|
+
if (['cancel', 'cancelled', 'canceled', 'skipped', 'stale'].includes(value)) return 'cancel';
|
|
8
|
+
return 'pending';
|
|
9
|
+
}
|
|
@@ -185,6 +185,10 @@ function sanitizeMarkdownDocument(value: string, options: SanitizationOptions =
|
|
|
185
185
|
return ok(result + tail.value);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
function canonicalizeCommentBody(value: string): SanitizationResult<string> {
|
|
189
|
+
return sanitizeMarkdownDocument(value, { reservedMarkers: [CONTROL_MARKER_PATTERN] });
|
|
190
|
+
}
|
|
191
|
+
|
|
188
192
|
function renderSafeCodeFence(value: string, language = '', reservedMarkers: readonly RegExp[] = [CONTROL_MARKER_PATTERN]): string {
|
|
189
193
|
const content = markerSafe(normalized(value), reservedMarkers);
|
|
190
194
|
const longestRun = Math.max(0, ...(content.match(/`+/g) || []).map((run) => run.length));
|
|
@@ -244,6 +248,7 @@ export {
|
|
|
244
248
|
escapeMarkdownLiteral,
|
|
245
249
|
fenceRanges,
|
|
246
250
|
renderSafeCodeFence,
|
|
251
|
+
canonicalizeCommentBody,
|
|
247
252
|
sanitizeMarkdownDocument,
|
|
248
253
|
splitDocumentPlaceholder
|
|
249
254
|
};
|
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
import { COMPLETION_BACKFILL_FAMILIES, inspectCompletionArtifacts } from '../task/finalization-artifacts.ts';
|
|
5
|
-
import { parseArtifactName } from '../task/artifact-
|
|
5
|
+
import { parseArtifactName } from '../task/artifact-name.ts';
|
|
6
6
|
import { parseTaskFrontmatter } from '../task/frontmatter.ts';
|
|
7
7
|
import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
8
8
|
import { getOpenWorkflowWarnings } from '../task/workflow-warnings.ts';
|