@fitlab-ai/agent-infra 0.9.6 → 0.9.8
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 +10 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +47 -5
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +162 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +245 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +398 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +23 -6
- package/dist/lib/decide.js +68 -14
- package/dist/lib/defaults.json +10 -2
- package/dist/lib/git/workflow.js +30 -3
- package/dist/lib/init.js +1 -1
- package/dist/lib/internal/codex-lifecycle.js +69 -1
- package/dist/lib/internal/codex-sandbox-controller.js +106 -0
- package/dist/lib/internal/release-workflow.js +1 -3
- package/dist/lib/internal/task-activity.js +57 -7
- package/dist/lib/internal/task-event.js +57 -4
- package/dist/lib/internal/task-ledger.js +59 -5
- package/dist/lib/internal/task-lifecycle.js +59 -8
- package/dist/lib/internal/task-orchestration.js +48 -8
- package/dist/lib/internal/task-override.js +131 -0
- package/dist/lib/internal/task-review.js +64 -6
- package/dist/lib/internal/task-verify.js +65 -3
- package/dist/lib/internal/task-warning.js +57 -4
- package/dist/lib/platform/context.js +6 -4
- package/dist/lib/platform/github-client.js +55 -2
- package/dist/lib/platform/pr-summary.js +5 -3
- package/dist/lib/process-data/checkpoint.js +319 -0
- package/dist/lib/process-data/index.js +128 -60
- package/dist/lib/process-data/materialize.js +84 -0
- package/dist/lib/process-data/normalize.js +34 -1
- package/dist/lib/process-data/reconcile.js +55 -1
- package/dist/lib/process-data/sources.js +422 -1
- package/dist/lib/process-data/store.js +341 -11
- package/dist/lib/runtime/agent-runtime.js +56 -0
- package/dist/lib/sandbox/commands/create.js +394 -264
- package/dist/lib/sandbox/commands/rm.js +64 -15
- package/dist/lib/sandbox/commands/start.js +4 -3
- package/dist/lib/sandbox/control/container-identity.js +64 -0
- package/dist/lib/sandbox/control/executor.js +23 -9
- package/dist/lib/sandbox/control/lifecycle.js +590 -61
- package/dist/lib/sandbox/control/protocol.js +7 -0
- package/dist/lib/sandbox/control/server.js +202 -26
- package/dist/lib/sandbox/control/state.js +51 -25
- package/dist/lib/sandbox/control/timing.js +8 -0
- package/dist/lib/sandbox/engine.js +14 -7
- package/dist/lib/sandbox/mounts.js +7 -0
- package/dist/lib/sandbox/recovery.js +135 -50
- package/dist/lib/sandbox/workspace-view.js +59 -20
- package/dist/lib/server/daemon.js +1 -1
- package/dist/lib/server/process-control.js +11 -1
- package/dist/lib/server/process-state.js +107 -20
- package/dist/lib/task/activity-intent.js +4 -1
- package/dist/lib/task/artifact-lifecycle.js +82 -11
- package/dist/lib/task/artifact-receipts.js +128 -0
- package/dist/lib/task/codex-orchestration.js +81 -6
- package/dist/lib/task/commands/validate.js +1 -1
- package/dist/lib/task/create.js +10 -2
- package/dist/lib/task/decision-intents.js +4 -2
- package/dist/lib/task/delegation-receipts.js +120 -17
- package/dist/lib/task/events.js +148 -7
- package/dist/lib/task/guard-override.js +31 -0
- package/dist/lib/task/human-override.js +1240 -0
- package/dist/lib/task/ledger-intents.js +6 -3
- package/dist/lib/task/ledger.js +13 -1
- package/dist/lib/task/lifecycle.js +69 -14
- package/dist/lib/task/orchestration.js +293 -18
- package/dist/lib/task/review-finalization.js +3 -2
- package/dist/lib/task/sections.js +5 -1
- package/dist/lib/task/task-execution-lock.js +5 -4
- package/dist/lib/task/verification-engine.js +80 -22
- package/dist/lib/task/verification.js +13 -1
- package/dist/lib/task/workflow-warning-intents.js +4 -2
- package/dist/lib/task/write.js +2 -1
- package/dist/lifecycle-build-manifest.json +6 -0
- package/lib/agent-clients/adapters/claude-code.ts +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +62 -4
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +227 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +322 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +26 -0
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +480 -0
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +35 -5
- package/lib/decide.ts +59 -14
- package/lib/defaults.json +10 -2
- package/lib/git/workflow.ts +25 -2
- package/lib/init.ts +1 -1
- package/lib/internal/codex-lifecycle.ts +68 -1
- package/lib/internal/codex-sandbox-controller.ts +109 -0
- package/lib/internal/release-workflow.ts +1 -3
- package/lib/internal/task-activity.ts +46 -6
- package/lib/internal/task-event.ts +53 -4
- package/lib/internal/task-ledger.ts +48 -4
- package/lib/internal/task-lifecycle.ts +56 -12
- package/lib/internal/task-orchestration.ts +43 -8
- package/lib/internal/task-override.ts +138 -0
- package/lib/internal/task-review.ts +51 -6
- package/lib/internal/task-verify.ts +52 -3
- package/lib/internal/task-warning.ts +44 -3
- package/lib/platform/context.ts +8 -5
- package/lib/platform/github-client.ts +61 -3
- package/lib/platform/pr-summary.ts +10 -3
- package/lib/process-data/checkpoint.ts +335 -0
- package/lib/process-data/index.ts +112 -60
- package/lib/process-data/materialize.ts +83 -0
- package/lib/process-data/normalize.ts +29 -1
- package/lib/process-data/reconcile.ts +64 -2
- package/lib/process-data/sources.ts +449 -2
- package/lib/process-data/store.ts +304 -11
- package/lib/process-data/types.ts +124 -2
- package/lib/runtime/agent-runtime.ts +76 -0
- package/lib/sandbox/commands/create.ts +139 -6
- package/lib/sandbox/commands/rm.ts +64 -14
- package/lib/sandbox/commands/start.ts +4 -3
- package/lib/sandbox/config.ts +2 -1
- package/lib/sandbox/control/container-identity.ts +93 -0
- package/lib/sandbox/control/executor.ts +24 -12
- package/lib/sandbox/control/lifecycle.ts +685 -67
- package/lib/sandbox/control/protocol.ts +35 -6
- package/lib/sandbox/control/server.ts +184 -24
- package/lib/sandbox/control/state.ts +67 -33
- package/lib/sandbox/control/timing.ts +12 -0
- package/lib/sandbox/engine.ts +20 -9
- package/lib/sandbox/mounts.ts +8 -0
- package/lib/sandbox/recovery.ts +159 -49
- package/lib/sandbox/workspace-view.ts +73 -22
- package/lib/server/daemon.ts +1 -1
- package/lib/server/process-control.ts +13 -3
- package/lib/server/process-state.ts +129 -27
- package/lib/task/activity-intent.ts +4 -2
- package/lib/task/artifact-lifecycle.ts +73 -10
- package/lib/task/artifact-receipts.ts +161 -0
- package/lib/task/codex-orchestration.ts +91 -5
- package/lib/task/commands/validate.ts +1 -1
- package/lib/task/create.ts +11 -2
- package/lib/task/decision-intents.ts +4 -2
- package/lib/task/delegation-receipts.ts +205 -25
- package/lib/task/events.ts +132 -6
- package/lib/task/guard-override.ts +50 -0
- package/lib/task/human-override.ts +1503 -0
- package/lib/task/ledger-intents.ts +6 -3
- package/lib/task/ledger.ts +16 -1
- package/lib/task/lifecycle.ts +71 -14
- package/lib/task/orchestration.ts +443 -22
- package/lib/task/review-finalization.ts +6 -2
- package/lib/task/sections.ts +6 -2
- package/lib/task/task-execution-lock.ts +11 -9
- package/lib/task/verification-engine.ts +93 -32
- package/lib/task/verification.ts +15 -1
- package/lib/task/workflow-warning-intents.ts +4 -2
- package/lib/task/write.ts +4 -1
- package/package.json +1 -1
- package/templates/.agents/README.en.md +7 -0
- package/templates/.agents/README.zh-CN.md +5 -0
- package/templates/.agents/hooks/lifecycle-delegation.js +33 -4
- package/templates/.agents/rules/lifecycle-orchestration.en.md +10 -3
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +10 -3
- package/templates/.agents/rules/pr-checks-commands.github.en.md +1 -1
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +1 -1
- package/templates/.agents/rules/review-handshake.en.md +3 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +3 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +3 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +4 -0
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +4 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +6 -6
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +14 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +14 -3
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +67 -0
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +67 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +25 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +25 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +9 -7
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -7
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +5 -1
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +5 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +75 -3
- package/templates/.agents/workspace/README.en.md +0 -26
- package/templates/.agents/workspace/README.zh-CN.md +0 -26
|
@@ -11,6 +11,8 @@ import { resolveTaskRef } from './resolve-ref.ts';
|
|
|
11
11
|
import { validateLifecycleExecution } from './lifecycle-execution.ts';
|
|
12
12
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
13
13
|
import type { ResolveTaskRefErrorCode } from './resolve-ref.ts';
|
|
14
|
+
import { allowsManualOverride } from './guard-override.ts';
|
|
15
|
+
import type { ManualOverrideCapability } from './guard-override.ts';
|
|
14
16
|
|
|
15
17
|
type ReviewFinalizationErrorCode =
|
|
16
18
|
| ResolveTaskRefErrorCode
|
|
@@ -62,6 +64,8 @@ type ReviewFinalizationOptions = {
|
|
|
62
64
|
repoRoot?: string;
|
|
63
65
|
randomSuffix?: () => string;
|
|
64
66
|
fileSystem?: Partial<ReviewFileSystem>;
|
|
67
|
+
manualOverride?: ManualOverrideCapability;
|
|
68
|
+
lockAlreadyHeld?: boolean;
|
|
65
69
|
};
|
|
66
70
|
|
|
67
71
|
const STAGES: Record<ReviewStage, { family: 'review-analysis' | 'review-plan' | 'review-code'; action: string }> = {
|
|
@@ -127,7 +131,7 @@ function finalizeReviewSummaryUnlocked(
|
|
|
127
131
|
}
|
|
128
132
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
129
133
|
if (!resolved.ok) return failed(request, resolved.code, resolved.message, resolved.taskId);
|
|
130
|
-
if (resolved.state !== 'active') {
|
|
134
|
+
if (resolved.state !== 'active' && !allowsManualOverride(options.manualOverride, 'review-finalization', 'TASK_STATE_MISMATCH')) {
|
|
131
135
|
return failed(request, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
132
136
|
}
|
|
133
137
|
const parsedArtifact = parseArtifactName(request.artifact);
|
|
@@ -275,7 +279,7 @@ function finalizeReviewSummary(
|
|
|
275
279
|
request: ReviewFinalizationRequest,
|
|
276
280
|
options: ReviewFinalizationOptions = {}
|
|
277
281
|
): ReviewFinalizationResult {
|
|
278
|
-
if (request.dryRun) return finalizeReviewSummaryUnlocked(request, options);
|
|
282
|
+
if (request.dryRun || options.lockAlreadyHeld) return finalizeReviewSummaryUnlocked(request, options);
|
|
279
283
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
280
284
|
if (!resolved.ok) return finalizeReviewSummaryUnlocked(request, options);
|
|
281
285
|
try {
|
package/lib/task/sections.ts
CHANGED
|
@@ -73,7 +73,7 @@ class DocumentMutationError extends Error {
|
|
|
73
73
|
|
|
74
74
|
function parseTable(
|
|
75
75
|
content: string,
|
|
76
|
-
input: { sectionAliases: readonly string[]; columns: readonly string[] }
|
|
76
|
+
input: { sectionAliases: readonly string[]; columns: readonly string[]; keyColumn?: string }
|
|
77
77
|
): ParsedTable | null {
|
|
78
78
|
validateAliases(input.sectionAliases, 'table section aliases');
|
|
79
79
|
if (
|
|
@@ -83,6 +83,10 @@ function parseTable(
|
|
|
83
83
|
) {
|
|
84
84
|
throw new DocumentMutationError('MUTATION_INVALID', 'table columns are invalid');
|
|
85
85
|
}
|
|
86
|
+
const keyColumn = input.keyColumn ?? input.columns[0]!;
|
|
87
|
+
if (!input.columns.includes(keyColumn)) {
|
|
88
|
+
throw new DocumentMutationError('MUTATION_INVALID', 'table key column is invalid');
|
|
89
|
+
}
|
|
86
90
|
const sections = matchingSections(content, input.sectionAliases);
|
|
87
91
|
if (sections.length === 0) return null;
|
|
88
92
|
if (sections.length > 1) {
|
|
@@ -125,7 +129,7 @@ function parseTable(
|
|
|
125
129
|
column,
|
|
126
130
|
decodeCell(parsed.cells[columnIndex]!).trim()
|
|
127
131
|
]));
|
|
128
|
-
const key = values[
|
|
132
|
+
const key = values[keyColumn]!;
|
|
129
133
|
if (seen.has(key)) {
|
|
130
134
|
throw new DocumentMutationError('TABLE_DUPLICATE_KEY', `duplicate table key '${key}'`);
|
|
131
135
|
}
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { createHash, randomUUID } from 'node:crypto';
|
|
5
5
|
|
|
6
6
|
import { getProcessStartTime, processIdentityMatches } from '../server/process-state.ts';
|
|
7
|
+
import type { ProcessIdentity } from '../server/process-state.ts';
|
|
7
8
|
|
|
8
9
|
type TaskExecutionLockErrorCode =
|
|
9
10
|
| 'ORCHESTRATION_LOCK_BUSY'
|
|
@@ -11,9 +12,9 @@ type TaskExecutionLockErrorCode =
|
|
|
11
12
|
| 'ORCHESTRATION_LOCK_FAILED';
|
|
12
13
|
|
|
13
14
|
type TaskExecutionLockOwner = Readonly<{
|
|
14
|
-
version:
|
|
15
|
+
version: 2;
|
|
15
16
|
pid: number;
|
|
16
|
-
startTime:
|
|
17
|
+
startTime: number;
|
|
17
18
|
token: string;
|
|
18
19
|
owner: string;
|
|
19
20
|
canonicalRepoRoot: string;
|
|
@@ -28,8 +29,8 @@ type TaskExecutionLockOptions = Readonly<{
|
|
|
28
29
|
lockRoot?: string;
|
|
29
30
|
token?: () => string;
|
|
30
31
|
now?: () => string;
|
|
31
|
-
getStartTime?: (pid: number) =>
|
|
32
|
-
identityMatches?: (
|
|
32
|
+
getStartTime?: (pid: number) => number | null;
|
|
33
|
+
identityMatches?: (identity: ProcessIdentity) => boolean;
|
|
33
34
|
linkSync?: (existingPath: string, newPath: string) => void;
|
|
34
35
|
}>;
|
|
35
36
|
|
|
@@ -84,11 +85,12 @@ function parseOwner(raw: string, key: string): TaskExecutionLockOwner {
|
|
|
84
85
|
const value = JSON.parse(raw) as Partial<TaskExecutionLockOwner> | null;
|
|
85
86
|
if (
|
|
86
87
|
value !== null
|
|
87
|
-
&& value.version ===
|
|
88
|
+
&& value.version === 2
|
|
88
89
|
&& Number.isSafeInteger(value.pid)
|
|
89
90
|
&& (value.pid ?? 0) > 0
|
|
90
|
-
&& typeof value.startTime === '
|
|
91
|
-
&& value.startTime
|
|
91
|
+
&& typeof value.startTime === 'number'
|
|
92
|
+
&& Number.isSafeInteger(value.startTime)
|
|
93
|
+
&& value.startTime >= 0
|
|
92
94
|
&& typeof value.token === 'string'
|
|
93
95
|
&& value.token.length > 0
|
|
94
96
|
&& typeof value.owner === 'string'
|
|
@@ -164,7 +166,7 @@ function reclaimStaleLock(
|
|
|
164
166
|
function cleanStaleCandidates(
|
|
165
167
|
lockRoot: string,
|
|
166
168
|
key: string,
|
|
167
|
-
identityMatches: (
|
|
169
|
+
identityMatches: (identity: ProcessIdentity) => boolean
|
|
168
170
|
): void {
|
|
169
171
|
const prefix = `${key}.candidate.`;
|
|
170
172
|
for (const name of fs.readdirSync(lockRoot)) {
|
|
@@ -211,7 +213,7 @@ function withTaskExecutionLock<T>(
|
|
|
211
213
|
);
|
|
212
214
|
}
|
|
213
215
|
const owner: TaskExecutionLockOwner = {
|
|
214
|
-
version:
|
|
216
|
+
version: 2,
|
|
215
217
|
pid: process.pid,
|
|
216
218
|
startTime,
|
|
217
219
|
token,
|
|
@@ -23,6 +23,7 @@ import { resolveReviewedHeadRelation } from "../platform/merged-pr-equivalence.t
|
|
|
23
23
|
import { resolveLocalReviewedCommitRelation } from "../git/reviewed-commit-equivalence.ts";
|
|
24
24
|
import { parseTypedTaskFrontmatter } from "./frontmatter.ts";
|
|
25
25
|
import { parseLedger, summarizeLedgerStage, validateLedgerRows } from "./ledger.ts";
|
|
26
|
+
import type { LedgerRow } from "./ledger.ts";
|
|
26
27
|
import { equalCounts, parseReviewSummary } from "./review-artifacts.ts";
|
|
27
28
|
import { loadVerificationConfig } from "./verification-config.ts";
|
|
28
29
|
import { snapshotReview } from "../git/review-snapshot.ts";
|
|
@@ -173,7 +174,7 @@ function checkOrchestrationState({ taskDir }: any): any {
|
|
|
173
174
|
if (isFailClosedLegacyPause(run)) {
|
|
174
175
|
return passResult('orchestration-state', 'Legacy orchestration run is safely paused');
|
|
175
176
|
}
|
|
176
|
-
if (run.schemaVersion
|
|
177
|
+
if (![2, 3].includes(run.schemaVersion) || !['paused', 'completed'].includes(run.status)) {
|
|
177
178
|
return failResult('orchestration-state', `Expected paused or completed run, received '${run.status}'`);
|
|
178
179
|
}
|
|
179
180
|
if (run.status === 'paused' && (!run.pause?.code || !run.pause?.message)) {
|
|
@@ -290,7 +291,22 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
|
|
|
290
291
|
&& guards?.commitAuthorizationUnused === true
|
|
291
292
|
&& guards?.completionEvidenceAbsent === true
|
|
292
293
|
&& guards?.commitIntentAbsent === true;
|
|
293
|
-
|
|
294
|
+
const validClaudeCodeRecovery = recovery.code === 'CLIENT_CAPABILITY_ENABLED_NO_MIGRATION'
|
|
295
|
+
&& recovery.previousSchemaVersion === 3
|
|
296
|
+
&& recovery.previousStatus === 'paused'
|
|
297
|
+
&& recovery.previousPause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
|
|
298
|
+
&& recovery.client === 'claude-code'
|
|
299
|
+
&& recovery.resultingStatus === 'running'
|
|
300
|
+
&& exactText(recovery.recoveredAt)
|
|
301
|
+
&& guards?.stepCount === 0
|
|
302
|
+
&& guards?.nextStage === null
|
|
303
|
+
&& guards?.baselineEmpty === true
|
|
304
|
+
&& guards?.receiptCount === 0
|
|
305
|
+
&& guards?.pendingDelegation === false
|
|
306
|
+
&& guards?.commitAuthorizationUnused === true
|
|
307
|
+
&& guards?.completionEvidenceAbsent === true
|
|
308
|
+
&& guards?.commitIntentAbsent === true;
|
|
309
|
+
if (!validModelRecovery && !validCapabilityRecovery && !validClaudeCodeRecovery) {
|
|
294
310
|
return failResult('orchestration-evidence', 'Run recovery history contains invalid provenance');
|
|
295
311
|
}
|
|
296
312
|
}
|
|
@@ -321,29 +337,40 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
|
|
|
321
337
|
}
|
|
322
338
|
const activated = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
|
|
323
339
|
if (activated) {
|
|
324
|
-
|
|
325
|
-
|
|
340
|
+
const isClaudeCode = receipt.client === 'claude-code';
|
|
341
|
+
if (!isClaudeCode) {
|
|
342
|
+
if (!exactText(receipt.actualModel)) {
|
|
343
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual model`);
|
|
344
|
+
}
|
|
345
|
+
if (!exactText(receipt.actualReasoningEffort)) {
|
|
346
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual reasoning effort`);
|
|
347
|
+
}
|
|
326
348
|
}
|
|
327
|
-
|
|
328
|
-
|
|
349
|
+
// parentId/childId 校验对所有 client 保持统一(不新增分支);spawnMode 检查按 client 判断
|
|
350
|
+
if (!exactText(receipt.parentId) || !exactText(receipt.childId) || receipt.parentId === receipt.childId) {
|
|
351
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid delegation identity`);
|
|
329
352
|
}
|
|
330
|
-
if (!
|
|
353
|
+
if (!isClaudeCode && receipt.spawnMode !== 'fresh') {
|
|
331
354
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid fresh delegation identity`);
|
|
332
355
|
}
|
|
333
|
-
if (receipt.actualModel
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated model fallback reason`);
|
|
356
|
+
if (receipt.actualModel != null && receipt.actualModel !== receipt.requestedModel) {
|
|
357
|
+
if (!isClaudeCode && !exactText(receipt.modelFallbackReason)) {
|
|
358
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' model fallback is not justified`);
|
|
359
|
+
}
|
|
338
360
|
}
|
|
339
361
|
if (
|
|
340
|
-
receipt.
|
|
341
|
-
&&
|
|
362
|
+
(receipt.actualModel == null || receipt.actualModel === receipt.requestedModel)
|
|
363
|
+
&& receipt.modelFallbackReason !== null
|
|
342
364
|
) {
|
|
343
|
-
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}'
|
|
365
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated model fallback reason`);
|
|
366
|
+
}
|
|
367
|
+
if (receipt.actualReasoningEffort != null && receipt.actualReasoningEffort !== receipt.requestedReasoningEffort) {
|
|
368
|
+
if (!isClaudeCode && !exactText(receipt.reasoningEffortFallbackReason)) {
|
|
369
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' reasoning-effort fallback is not justified`);
|
|
370
|
+
}
|
|
344
371
|
}
|
|
345
372
|
if (
|
|
346
|
-
receipt.actualReasoningEffort === receipt.requestedReasoningEffort
|
|
373
|
+
(receipt.actualReasoningEffort == null || receipt.actualReasoningEffort === receipt.requestedReasoningEffort)
|
|
347
374
|
&& receipt.reasoningEffortFallbackReason !== null
|
|
348
375
|
) {
|
|
349
376
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated reasoning-effort fallback reason`);
|
|
@@ -901,7 +928,7 @@ function checkReviewLedger({ taskDir, config }: any): any {
|
|
|
901
928
|
}
|
|
902
929
|
|
|
903
930
|
const stageScope = Array.isArray(config.stage_scope) ? config.stage_scope : null;
|
|
904
|
-
const maxRounds = Number(resolveReviewSetting(config, "
|
|
931
|
+
const maxRounds = Number(resolveReviewSetting(config, "maxHandshakeRounds", DEFAULT_MAX_HANDSHAKE_ROUNDS));
|
|
905
932
|
const problems = [];
|
|
906
933
|
let inScopeCount = 0;
|
|
907
934
|
|
|
@@ -1070,11 +1097,13 @@ function checkPostReviewCommit({ taskDir, config }: any): any {
|
|
|
1070
1097
|
return blockedResult("post-review-commit", relation.message, relation.code);
|
|
1071
1098
|
}
|
|
1072
1099
|
if (relation.status === "strict") {
|
|
1073
|
-
return
|
|
1074
|
-
"
|
|
1075
|
-
"Merged PR evidence did not establish squash merge equivalence"
|
|
1076
|
-
|
|
1077
|
-
|
|
1100
|
+
return resolvePostReviewIdentityFailure(task.content, {
|
|
1101
|
+
code: "PR_MERGE_IDENTITY_INVALID",
|
|
1102
|
+
message: "Merged PR evidence did not establish squash merge equivalence"
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
if (relation.code === "PR_MERGE_IDENTITY_INVALID") {
|
|
1106
|
+
return resolvePostReviewIdentityFailure(task.content, relation);
|
|
1078
1107
|
}
|
|
1079
1108
|
return failResult("post-review-commit", relation.message, relation.code);
|
|
1080
1109
|
}
|
|
@@ -1144,11 +1173,11 @@ function checkPostReviewCommit({ taskDir, config }: any): any {
|
|
|
1144
1173
|
}
|
|
1145
1174
|
|
|
1146
1175
|
const exemption = resolvePostReviewExemption(task.content);
|
|
1147
|
-
if (!exemption.ok) return failResult("post-review-commit", exemption.message);
|
|
1148
|
-
if (exemption.
|
|
1176
|
+
if (!exemption.ok) return failResult("post-review-commit", exemption.message, exemption.code);
|
|
1177
|
+
if (exemption.exemptions.length > 0) {
|
|
1149
1178
|
return passResult(
|
|
1150
1179
|
"post-review-commit",
|
|
1151
|
-
`${commits.length} post-review commit(s) covered by a human-decided exemption`
|
|
1180
|
+
`${commits.length} post-review commit(s) covered by a human-decided exemption: ${formatPostReviewExemptions(exemption.exemptions)}`
|
|
1152
1181
|
);
|
|
1153
1182
|
}
|
|
1154
1183
|
|
|
@@ -1158,18 +1187,50 @@ function checkPostReviewCommit({ taskDir, config }: any): any {
|
|
|
1158
1187
|
);
|
|
1159
1188
|
}
|
|
1160
1189
|
|
|
1190
|
+
function resolvePostReviewIdentityFailure(
|
|
1191
|
+
content: string,
|
|
1192
|
+
failure: { code: string; message: string }
|
|
1193
|
+
): any {
|
|
1194
|
+
const exemption = resolvePostReviewExemption(content);
|
|
1195
|
+
if (!exemption.ok) {
|
|
1196
|
+
return failResult("post-review-commit", exemption.message, exemption.code);
|
|
1197
|
+
}
|
|
1198
|
+
if (exemption.exemptions.length === 0) {
|
|
1199
|
+
return failResult("post-review-commit", failure.message, failure.code);
|
|
1200
|
+
}
|
|
1201
|
+
return passResult(
|
|
1202
|
+
"post-review-commit",
|
|
1203
|
+
`Human-decided post-review exemption overrode ${failure.code}: ${failure.message}; ${formatPostReviewExemptions(exemption.exemptions)}`
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
function formatPostReviewExemptions(exemptions: readonly LedgerRow[]): string {
|
|
1208
|
+
return exemptions.map((row) => `${row.id}: ${row.evidence}`).join("; ");
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1161
1211
|
function resolvePostReviewExemption(content: string):
|
|
1162
|
-
| { ok: true;
|
|
1163
|
-
| { ok: false; message: string } {
|
|
1212
|
+
| { ok: true; exemptions: readonly LedgerRow[] }
|
|
1213
|
+
| { ok: false; code: "POST_REVIEW_EXEMPTION_INVALID"; message: string } {
|
|
1164
1214
|
try {
|
|
1215
|
+
const rows = parseLedger(content);
|
|
1216
|
+
const invalid = validateLedgerRows(rows);
|
|
1217
|
+
if (invalid) {
|
|
1218
|
+
return {
|
|
1219
|
+
ok: false,
|
|
1220
|
+
code: "POST_REVIEW_EXEMPTION_INVALID",
|
|
1221
|
+
message: `${invalid.code}: ${invalid.message}`
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1165
1224
|
return {
|
|
1166
1225
|
ok: true,
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1226
|
+
exemptions: rows.filter((row) => row.stage === POST_REVIEW_COMMIT_STAGE)
|
|
1227
|
+
};
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
return {
|
|
1230
|
+
ok: false,
|
|
1231
|
+
code: "POST_REVIEW_EXEMPTION_INVALID",
|
|
1232
|
+
message: `Invalid disagreement ledger: ${error instanceof Error ? error.message : String(error)}`
|
|
1170
1233
|
};
|
|
1171
|
-
} catch {
|
|
1172
|
-
return { ok: false, message: "Invalid disagreement ledger" };
|
|
1173
1234
|
}
|
|
1174
1235
|
}
|
|
1175
1236
|
|
package/lib/task/verification.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { parseArtifactName } from './artifact-lifecycle.ts';
|
|
|
7
7
|
import type { ArtifactFamily } from './artifact-lifecycle.ts';
|
|
8
8
|
import { resolveTaskRef } from './resolve-ref.ts';
|
|
9
9
|
import type { TaskWorkspaceState } from './resolve-ref.ts';
|
|
10
|
+
import { allowsManualOverride } from './guard-override.ts';
|
|
11
|
+
import type { ManualOverrideCapability } from './guard-override.ts';
|
|
10
12
|
|
|
11
13
|
type VerificationEvent =
|
|
12
14
|
| 'analyze.awaiting-input' | 'analyze.completed'
|
|
@@ -48,6 +50,7 @@ type TaskVerificationResult = {
|
|
|
48
50
|
type VerificationOptions = {
|
|
49
51
|
repoRoot?: string;
|
|
50
52
|
engine?: typeof verifyInProcess;
|
|
53
|
+
manualOverride?: ManualOverrideCapability;
|
|
51
54
|
};
|
|
52
55
|
|
|
53
56
|
const gate = (skill: string, expectedState: VerificationSpec['expectedState'], artifactFamily?: ArtifactFamily): VerificationSpec => ({
|
|
@@ -93,7 +96,7 @@ function verifyTaskEvent(request: { taskRef: string; event: string; artifact?: s
|
|
|
93
96
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
94
97
|
if (!resolved.ok) return failure(request, resolved.code, resolved.message, { taskId: resolved.taskId, skill: spec.skill, mode: spec.mode });
|
|
95
98
|
const identity = { taskId: resolved.taskId, taskDir: resolved.taskDir, taskState: resolved.state, skill: spec.skill, mode: spec.mode } as const;
|
|
96
|
-
if (resolved.state !== spec.expectedState) {
|
|
99
|
+
if (resolved.state !== spec.expectedState && !allowsManualOverride(options.manualOverride, 'task-verify', 'VERIFY_TASK_STATE_MISMATCH')) {
|
|
97
100
|
return failure(request, 'VERIFY_TASK_STATE_MISMATCH', `event '${request.event}' requires workspace '${spec.expectedState}', received '${resolved.state}'`, identity);
|
|
98
101
|
}
|
|
99
102
|
if (spec.artifactFamily) {
|
|
@@ -111,11 +114,22 @@ function verifyTaskEvent(request: { taskRef: string; event: string; artifact?: s
|
|
|
111
114
|
if (spec.mode === 'gate') {
|
|
112
115
|
const payload = engine({ mode: 'gate', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [], repositoryRoot: resolved.repoRoot }) as Record<string, unknown>;
|
|
113
116
|
const status = payload.gate as 'pass' | 'fail' | 'blocked';
|
|
117
|
+
if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
|
|
118
|
+
invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, gate: 'pass', humanOverride: 'human-approved', originalGate: status } });
|
|
119
|
+
return {
|
|
120
|
+
status: 'pass', changed: false, event: request.event, requestRef: request.taskRef,
|
|
121
|
+
...identity, artifact: request.artifact ?? null, invocations, error: null
|
|
122
|
+
};
|
|
123
|
+
}
|
|
114
124
|
invocations.push({ status, exitCode: ({ pass: 0, fail: 1, blocked: 2 } as const)[status], payload });
|
|
115
125
|
} else {
|
|
116
126
|
for (const check of spec.checks ?? []) {
|
|
117
127
|
const payload = engine({ mode: 'checks', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [check], repositoryRoot: resolved.repoRoot }) as Record<string, unknown>;
|
|
118
128
|
const status = payload.status as 'pass' | 'fail' | 'blocked';
|
|
129
|
+
if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
|
|
130
|
+
invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, status: 'pass', humanOverride: 'human-approved', originalStatus: status } });
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
119
133
|
invocations.push({ status, exitCode: ({ pass: 0, fail: 1, blocked: 2 } as const)[status], payload });
|
|
120
134
|
if (status !== 'pass') break;
|
|
121
135
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import type { WorkflowWarning } from './workflow-warnings.ts';
|
|
12
12
|
import { captureTaskWriteMetadata, writeTask } from './write.ts';
|
|
13
13
|
import type { TaskMutation, TaskOperationSummary, TaskWriteOptions } from './write.ts';
|
|
14
|
+
import { allowsManualOverride } from './guard-override.ts';
|
|
14
15
|
|
|
15
16
|
type WarningSeverity = 'IMPORTANT' | 'ACTION_REQUIRED';
|
|
16
17
|
type WarningStatus = 'open' | 'resolved' | 'ignored';
|
|
@@ -79,7 +80,8 @@ function rowMutation(row: WorkflowWarning): TaskMutation {
|
|
|
79
80
|
function applyWorkflowWarningIntent(intent: WorkflowWarningIntent, options: TaskWriteOptions = {}): WorkflowWarningIntentResult {
|
|
80
81
|
const resolved = resolveTaskRef(intent.taskRef, { repoRoot: options.repoRoot });
|
|
81
82
|
if (!resolved.ok) return failed(intent, resolved.code, resolved.message, resolved.taskId);
|
|
82
|
-
|
|
83
|
+
const stateOverride = allowsManualOverride(options.manualOverride, 'workflow-warning', 'TASK_STATE_MISMATCH');
|
|
84
|
+
if (resolved.state !== 'active' && !stateOverride) return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
83
85
|
let content: string;
|
|
84
86
|
let rows: WorkflowWarning[];
|
|
85
87
|
try {
|
|
@@ -125,7 +127,7 @@ function applyWorkflowWarningIntent(intent: WorkflowWarningIntent, options: Task
|
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
const writeResult = writeTask({
|
|
128
|
-
taskRef: intent.taskRef, expectedState: 'active', mutations: [...sectionMutation(content), rowMutation(after)], dryRun: intent.dryRun
|
|
130
|
+
taskRef: intent.taskRef, expectedState: stateOverride ? resolved.state : 'active', mutations: [...sectionMutation(content), rowMutation(after)], dryRun: intent.dryRun
|
|
129
131
|
}, {
|
|
130
132
|
...options,
|
|
131
133
|
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
|
package/lib/task/write.ts
CHANGED
|
@@ -12,6 +12,8 @@ import type {
|
|
|
12
12
|
ResolveTaskRefErrorCode,
|
|
13
13
|
TaskWorkspaceState
|
|
14
14
|
} from './resolve-ref.ts';
|
|
15
|
+
import { allowsManualOverride } from './guard-override.ts';
|
|
16
|
+
import type { ManualOverrideCapability } from './guard-override.ts';
|
|
15
17
|
import { mutateTableRow, upsertSection } from './sections.ts';
|
|
16
18
|
import type {
|
|
17
19
|
TableRowDeleteMutation,
|
|
@@ -156,6 +158,7 @@ type TaskWriteOptions = {
|
|
|
156
158
|
metadataProvider?: () => TaskWriteMetadata;
|
|
157
159
|
randomSuffix?: () => string;
|
|
158
160
|
fileSystem?: Partial<TaskFileSystem>;
|
|
161
|
+
manualOverride?: ManualOverrideCapability;
|
|
159
162
|
};
|
|
160
163
|
|
|
161
164
|
const DEFAULT_FILE_SYSTEM: TaskFileSystem = {
|
|
@@ -241,7 +244,7 @@ function writeTask(request: TaskWriteRequest, options: TaskWriteOptions = {}): T
|
|
|
241
244
|
taskMdPath: resolved.taskMdPath,
|
|
242
245
|
actualState: resolved.state
|
|
243
246
|
};
|
|
244
|
-
if (resolved.state !== request.expectedState) {
|
|
247
|
+
if (resolved.state !== request.expectedState && !allowsManualOverride(options.manualOverride, 'task.write', 'TASK_STATE_MISMATCH')) {
|
|
245
248
|
return failure(
|
|
246
249
|
request,
|
|
247
250
|
identity,
|
package/package.json
CHANGED
|
@@ -47,6 +47,13 @@ This dual-config approach ensures every AI tool receives appropriate project con
|
|
|
47
47
|
settings.json # Claude settings
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
The whole `.agents/workspace/` tree is runtime-only and git-ignored. `active/`
|
|
51
|
+
contains current tasks, `blocked/` contains paused tasks, `completed/` contains
|
|
52
|
+
finished tasks, and `archive/` stores archived history. Runtime logs and process
|
|
53
|
+
evidence may also be created below this root. Do not place content here that must
|
|
54
|
+
be shared through Git; collaboration rules, templates, and workflows belong in
|
|
55
|
+
the version-controlled `.agents/` tree.
|
|
56
|
+
|
|
50
57
|
## Collaboration Model
|
|
51
58
|
|
|
52
59
|
The multi-AI collaboration follows a structured workflow:
|
|
@@ -47,6 +47,11 @@
|
|
|
47
47
|
settings.json # Claude 设置
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
整个 `.agents/workspace/` 目录树都只保存运行时数据,并被 git ignore。`active/`
|
|
51
|
+
保存当前任务,`blocked/` 保存暂停任务,`completed/` 保存已完成任务,`archive/`
|
|
52
|
+
保存归档历史;运行时日志和过程证据也可能出现在该根目录下。不要在这里存放必须
|
|
53
|
+
通过 Git 共享的内容;协作规则、模板和工作流应放在受版本控制的 `.agents/` 目录中。
|
|
54
|
+
|
|
50
55
|
## 协作模型
|
|
51
56
|
|
|
52
57
|
多 AI 协作遵循结构化工作流:
|
|
@@ -36,6 +36,28 @@ function internalCliInvocation(args) {
|
|
|
36
36
|
return { command: 'agent-infra-internal', commandArgs: args, shell: process.platform === 'win32' };
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function findCapabilityToken(value, depth = 0) {
|
|
40
|
+
if (depth > 5 || value == null) return '';
|
|
41
|
+
if (typeof value === 'string') {
|
|
42
|
+
const match = /agent-infra-codex-capability:([A-Za-z0-9_-]{20,})/.exec(value);
|
|
43
|
+
return match?.[1] || '';
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
for (const entry of value) {
|
|
47
|
+
const found = findCapabilityToken(entry, depth + 1);
|
|
48
|
+
if (found) return found;
|
|
49
|
+
}
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
if (typeof value === 'object') {
|
|
53
|
+
for (const entry of Object.values(value)) {
|
|
54
|
+
const found = findCapabilityToken(entry, depth + 1);
|
|
55
|
+
if (found) return found;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
|
|
39
61
|
let input = '';
|
|
40
62
|
process.stdin.setEncoding('utf8');
|
|
41
63
|
process.stdin.on('data', (chunk) => {
|
|
@@ -75,10 +97,11 @@ process.stdin.on('end', () => {
|
|
|
75
97
|
process.exit(1);
|
|
76
98
|
}
|
|
77
99
|
const managedAgent = /^agent-infra-lifecycle-(executor|reviewer)$/.test(String(nativeAgent || ''));
|
|
100
|
+
const capabilityToken = hook === 'post-tool' ? findCapabilityToken(toolResponse) : '';
|
|
78
101
|
const parentReconcile = hook === 'post-tool'
|
|
79
102
|
&& toolName === 'collaborationwait_agent'
|
|
80
103
|
&& toolResponse.timed_out !== true;
|
|
81
|
-
if (!managedAgent && !parentReconcile) process.exit(0);
|
|
104
|
+
if (!managedAgent && !parentReconcile && !capabilityToken) process.exit(0);
|
|
82
105
|
const hookDefinitionHash = createHash('sha256').update(readFileSync(codexHooks)).digest('hex');
|
|
83
106
|
const normalized = {
|
|
84
107
|
sessionId: String(event.session_id || ''),
|
|
@@ -93,6 +116,7 @@ process.stdin.on('end', () => {
|
|
|
93
116
|
taskName: String(toolInput.task_name || ''),
|
|
94
117
|
transcriptPath: String(event.transcript_path || '')
|
|
95
118
|
};
|
|
119
|
+
if (capabilityToken) normalized.capabilityToken = capabilityToken;
|
|
96
120
|
const args = ['codex-lifecycle', 'hook-event', '--event', hook, '--bridge', 'true'];
|
|
97
121
|
try {
|
|
98
122
|
const { command, commandArgs, shell } = internalCliInvocation(args);
|
|
@@ -123,11 +147,11 @@ process.stdin.on('end', () => {
|
|
|
123
147
|
args.push(
|
|
124
148
|
'--native-agent', String(nativeAgent),
|
|
125
149
|
'--child-id', String(event.child_id || event.agent_id || event.thread_id || ''),
|
|
126
|
-
'--parent-id', String(event.parent_id || event.parent_session_id || event.session_id || '')
|
|
127
|
-
'--spawn-mode', String(event.spawn_mode || (client === 'claude-code' ? 'fresh' : ''))
|
|
150
|
+
'--parent-id', String(event.parent_id || event.parent_session_id || event.session_id || '')
|
|
128
151
|
);
|
|
152
|
+
if (event.spawn_mode) args.push('--spawn-mode', String(event.spawn_mode));
|
|
129
153
|
if (event.model) args.push('--actual-model', String(event.model));
|
|
130
|
-
const actualEffort = event.actual_reasoning_effort || event.reasoning_effort || event.model_reasoning_effort;
|
|
154
|
+
const actualEffort = event.effort?.level || event.actual_reasoning_effort || event.reasoning_effort || event.model_reasoning_effort;
|
|
131
155
|
if (actualEffort) args.push('--actual-reasoning-effort', String(actualEffort));
|
|
132
156
|
if (event.model_fallback_reason) args.push('--model-fallback-reason', String(event.model_fallback_reason));
|
|
133
157
|
if (event.reasoning_effort_fallback_reason) {
|
|
@@ -138,6 +162,11 @@ process.stdin.on('end', () => {
|
|
|
138
162
|
'--native-agent', String(nativeAgent),
|
|
139
163
|
'--child-id', String(event.child_id || event.agent_id || event.thread_id || '')
|
|
140
164
|
);
|
|
165
|
+
if (client === 'claude-code') {
|
|
166
|
+
if (event.model) args.push('--actual-model', String(event.model));
|
|
167
|
+
const actualEffort = event.effort?.level || event.actual_reasoning_effort || event.reasoning_effort;
|
|
168
|
+
if (actualEffort) args.push('--actual-reasoning-effort', String(actualEffort));
|
|
169
|
+
}
|
|
141
170
|
}
|
|
142
171
|
try {
|
|
143
172
|
const { command, commandArgs, shell } = internalCliInvocation(args);
|
|
@@ -5,19 +5,26 @@
|
|
|
5
5
|
- The orchestrator only routes and delegates. Stage skills remain the single source of truth for behavior and artifacts.
|
|
6
6
|
- Every stage and rework round uses a fresh executor. Every review uses a fresh reviewer; follow-up reuse is forbidden.
|
|
7
7
|
- A reviewer may only write its review artifact and core-generated task metadata. Business-code, HEAD, or index changes invalidate the receipt.
|
|
8
|
-
- An active run has one pending delegation. Missing, mismatched, forked, replayed, hook-less, or drifted evidence pauses the run.
|
|
8
|
+
- An active run has one pending delegation. The child must pass the activation barrier before any stage side effect. Missing, late, mismatched, forked, replayed, hook-less, or drifted evidence pauses the run (on the claude-code path, missing or mismatched model/effort evidence, and fork/spawn-mode evidence this host structurally does not provide, follow the recording rules in `.agents/skills/run-task/reference/host-validation.md` instead and are exempt from this; missing or mismatched `parentId`/`childId` still pauses).
|
|
9
9
|
- The first release ends after one existing safely gated `commit`; it does not create a PR, monitor checks, or complete the task.
|
|
10
10
|
|
|
11
11
|
## Recovery
|
|
12
12
|
|
|
13
|
-
`orchestration.json` is the detailed state source. A
|
|
13
|
+
`orchestration.json` is the detailed state source. A v3 run persists complete policy, append-only recovery, build/contract/hook-source/controller provenance, and activation monotonic timestamps. Historical v2 state is read-compatible, but new Codex delegations require v3. An expired prepared orphan may be explicitly recovered only with the exact task fingerprint, no commit intent or consumed authorization, and no matching unconsumed active lifecycle evidence. Unknown schemas and incomplete active v3 state fail closed.
|
|
14
|
+
|
|
15
|
+
## Codex Host and Capability
|
|
16
|
+
|
|
17
|
+
- Direct-host accepts only trusted project or managed lifecycle hooks. A task-bound sandbox requires a controlled nested controller and isolated `CODEX_HOME`; only user hooks bound to that controller context are accepted. Ordinary user/plugin hooks are not evidence.
|
|
18
|
+
- The controller starts a nested loop only after control generation, task binding, Codex version, closed bundle, build/contract manifest, profiles, and hook discovery pass. Both bypass flags are restricted to this task-bound launch path.
|
|
19
|
+
- Every prepare arms a one-use capability attested by the current loop's real PostToolUse. Atomic consumption binds task/session/build/controller and retains only a redacted tombstone.
|
|
14
20
|
|
|
15
21
|
## Model Policy
|
|
16
22
|
|
|
17
23
|
- A new run persists model and reasoning effort for both roles. Explicit policy is atomic across all four role fields; only a fully absent explicit policy may fall back to the current client's `agentClients[].orchestration`.
|
|
18
24
|
- Route resolves requested model/effort by role, and prepare matches both before snapshotting. Native spawn must not inherit session defaults.
|
|
19
|
-
- Native start records host-observed actual model/effort. Each mismatch needs its own fallback reason, and requested values must never be fabricated as actual evidence.
|
|
25
|
+
- Native start records host-observed actual model/effort. Each mismatch needs its own fallback reason (the claude-code path may leave this blank and follow the recording rules instead), and requested values must never be fabricated as actual evidence.
|
|
20
26
|
- Model selection is labeled as a complete catalog, partial catalog, or interactive-only guidance; a local override enum must not be presented as complete.
|
|
27
|
+
- claude-code's requested reasoning effort does not yet support per-role dispatch (concurrent tasks would race on a shared file); `delegationEvidence.actualReasoningEffort` is declared `spawn-ack`, meaning it is only recorded when honestly observed in a native spawn lifecycle event (Start/Stop) — it is not a promise of per-role dispatch and does not gate activation.
|
|
21
28
|
|
|
22
29
|
## Stable Pause Conditions
|
|
23
30
|
|
|
@@ -5,19 +5,26 @@
|
|
|
5
5
|
- 总控只路由和委派;阶段技能仍是业务规则与产物格式的单一事实源。
|
|
6
6
|
- 每个阶段和每轮返工都创建 fresh executor;每轮审查创建 fresh reviewer,禁止 follow-up 复用。
|
|
7
7
|
- reviewer 只能写当前审查产物和核心生成的任务元数据。业务代码、HEAD 或暂存区变化会使 receipt 失效。
|
|
8
|
-
- active run 中只有一个 pending delegation
|
|
8
|
+
- active run 中只有一个 pending delegation。child 必须在任何阶段副作用前通过 activation barrier;缺失、超时、错配、fork、重放、hook 不可用或工作区漂移一律暂停(claude-code 路径下 model/effort 证据的缺失或错配、以及该宿主结构上不提供的 fork/spawn-mode 证据,按 `.agents/skills/run-task/reference/host-validation.md` 的记录规则处理,不在此列;`parentId`/`childId` 的缺失或错配仍然暂停)。
|
|
9
9
|
- 首版成功终点是一次通过既有安全门禁的 `commit`;不创建 PR、不监控 checks、不执行 `complete-task`。
|
|
10
10
|
|
|
11
11
|
## 恢复语义
|
|
12
12
|
|
|
13
|
-
`orchestration.json` 是详细状态源。
|
|
13
|
+
`orchestration.json` 是详细状态源。v3 run 保存完整策略、append-only 恢复历史、build/contract/hook source/controller provenance 与 activation 单调时钟。只有无 pending delegation 且零历史 receipt 的 v1 run 可在补齐完整策略后升级;历史 v2 状态仅兼容读取,新的 Codex delegation 必须写 v3。prepared orphan 只允许在 deadline 已过、task fingerprint 精确不变、无 commit intent/authorization 消耗且无匹配的未消费 active lifecycle evidence 时显式恢复。迁移是 forward-only,未知 schema 或缺 provenance 的 active v3 一律失败关闭。
|
|
14
|
+
|
|
15
|
+
## Codex 宿主与 capability
|
|
16
|
+
|
|
17
|
+
- direct-host 只接受 trusted project 或 managed lifecycle hooks。task-bound sandbox 必须由受控 nested controller 建立隔离 `CODEX_HOME`,仅接受绑定 controller context 的 user hooks;普通 user/plugin hook 不得成为证据源。
|
|
18
|
+
- controller 只有在 control generation、task binding、Codex 版本、closed bundle、build/contract manifest、profile 与 hook discovery 全部通过后才能启动 nested loop。两个 bypass 参数只允许出现在该 task-bound 启动路径。
|
|
19
|
+
- 每次 prepare 前必须 arm 一次性 capability,并由同一当前 loop 的真实 PostToolUse attestation;token 原子消费后保留去敏 tombstone,不能重放或跨 task/session/build/controller 使用。
|
|
14
20
|
|
|
15
21
|
## 模型策略
|
|
16
22
|
|
|
17
23
|
- 新 run 必须固化 executor/reviewer 各自的 model 与 reasoning effort;显式策略必须四字段原子完整,完全没有显式字段时才读取当前 client 的 `agentClients[].orchestration`。重入不得静默改写策略。
|
|
18
24
|
- route 按 role 返回 requested model/effort,prepare 必须在工作区快照前精确匹配两者;原生 spawn 不能继承会话默认值。
|
|
19
|
-
- 原生 start 必须记录宿主观察到的 actual model/effort。任一字段与 requested 不同时必须记录独立 fallback reason
|
|
25
|
+
- 原生 start 必须记录宿主观察到的 actual model/effort。任一字段与 requested 不同时必须记录独立 fallback reason(claude-code 路径下允许留空,按记录规则处理);requested 值不得补造 actual 证据。
|
|
20
26
|
- 模型选择能力必须标记 complete catalog、partial catalog 或 interactive-only guidance;局部 override 枚举不得冒充完整目录。
|
|
27
|
+
- claude-code 的 requested reasoning effort 暂不支持按角色下发(跨任务共享文件存在写入竞态);`delegationEvidence.actualReasoningEffort` 声明为 `spawn-ack`,语义为"仅能在宿主原生 spawn 生命周期事件(Start/Stop)里如实观察到时记录,不构成按角色下发的承诺,也不构成放行门禁"。
|
|
21
28
|
|
|
22
29
|
## 稳定暂停条件
|
|
23
30
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GitHub PR Readiness Intents
|
|
2
2
|
|
|
3
|
-
The GitHub adapter requires `gh >= 2.
|
|
3
|
+
The GitHub adapter requires `gh >= 2.72.0`. Platform context checks this dependency before any GitHub API, PR, or checks operation. Projects that do not select GitHub neither probe nor depend on `gh`.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
agent-infra-internal platform-checks inspect {task-id}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
PR 全部 checks 的获取、轮询 deadline、失败 run/job 定位和日志获取统一由 typed internal intent 执行。SKILL/模型只负责失败分类、根因分析、最小修复和授权后的提交推送。
|
|
4
4
|
|
|
5
|
-
GitHub adapter 要求 `gh >= 2.
|
|
5
|
+
GitHub adapter 要求 `gh >= 2.72.0`。平台上下文会在任何 GitHub API、PR 或 checks 操作前检查该依赖;未选择 GitHub 平台时不探测也不依赖 `gh`。
|
|
6
6
|
|
|
7
7
|
## 快照与监控
|
|
8
8
|
|