@fitlab-ai/agent-infra 0.11.2 → 0.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -8
- package/README.zh-CN.md +17 -8
- package/dist/bin/internal-cli.js +7 -2
- package/dist/lib/agent-clients/adapter.js +3 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +234 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +8 -35
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +1 -34
- package/dist/lib/agent-clients/adapters/codex.js +10 -0
- package/dist/lib/internal/cli-route-inventory.js +1 -1
- package/dist/lib/internal/manual-validation.js +22 -27
- package/dist/lib/internal/sandbox-control.js +1 -43
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-finalization.js +41 -1
- package/dist/lib/internal/task-ledger.js +13 -5
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +7 -2
- package/dist/lib/platform/completion-backfill.js +63 -17
- package/dist/lib/platform/issue-comments.js +2 -31
- package/dist/lib/platform/pr-summary.js +7 -2
- package/dist/lib/sandbox/control/client.js +3 -44
- package/dist/lib/sandbox/control/executor.js +14 -79
- package/dist/lib/sandbox/control/protocol.js +5 -64
- package/dist/lib/sandbox/control/server.js +49 -343
- package/dist/lib/sandbox/control/state.js +1 -7
- package/dist/lib/sandbox/control/task-view.js +1 -2
- package/dist/lib/sandbox/task-cleanup.js +13 -20
- package/dist/lib/task/artifact-command.js +11 -4
- package/dist/lib/task/artifact-lifecycle.js +217 -85
- package/dist/lib/task/artifact-operations.js +1 -26
- package/dist/lib/task/artifact-receipts.js +4 -1
- package/dist/lib/task/artifact-schema.js +1 -0
- package/dist/lib/task/artifact-selection.js +25 -0
- package/dist/lib/task/capabilities.js +158 -39
- package/dist/lib/task/commands/status.js +9 -2
- package/dist/lib/task/control-authority.js +37 -38
- package/dist/lib/task/control-recovery.js +3 -42
- package/dist/lib/task/decision-intents.js +12 -0
- package/dist/lib/task/delegation-receipts.js +2 -2
- package/dist/lib/task/delivery.js +1 -43
- package/dist/lib/task/events.js +80 -64
- package/dist/lib/task/finalization-artifacts.js +1 -3
- package/dist/lib/task/finalization-state.js +1 -1
- package/dist/lib/task/finalization.js +319 -234
- package/dist/lib/task/ledger-intents.js +157 -15
- package/dist/lib/task/lifecycle-path.js +75 -0
- package/dist/lib/task/lifecycle.js +11 -1
- package/dist/lib/task/local-artifact-finalization.js +1 -20
- package/dist/lib/task/manual-validation-receipt.js +8 -3
- package/dist/lib/task/orchestration.js +29 -13
- package/dist/lib/task/review-finalization.js +3 -16
- package/dist/lib/task/rework-intent.js +70 -4
- package/dist/lib/task/verification-engine.js +20 -30
- package/dist/lib/task/verification.js +1 -0
- package/dist/lib/task/workflow-warning-intents.js +21 -6
- package/dist/lib/task/workspace-snapshot.js +7 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +46 -0
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +297 -0
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +13 -33
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -44
- package/lib/agent-clients/adapters/codex.ts +10 -0
- package/lib/internal/cli-route-inventory.ts +1 -1
- package/lib/internal/manual-validation.ts +22 -26
- package/lib/internal/sandbox-control.ts +0 -42
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-finalization.ts +40 -1
- package/lib/internal/task-ledger.ts +12 -5
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-operation-registry.ts +8 -4
- package/lib/platform/completion-backfill.ts +78 -16
- package/lib/platform/issue-comments.ts +1 -35
- package/lib/platform/pr-summary.ts +10 -2
- package/lib/sandbox/control/client.ts +5 -68
- package/lib/sandbox/control/executor.ts +14 -81
- package/lib/sandbox/control/protocol.ts +7 -83
- package/lib/sandbox/control/server.ts +47 -362
- package/lib/sandbox/control/state.ts +1 -9
- package/lib/sandbox/control/task-view.ts +1 -2
- package/lib/sandbox/task-cleanup.ts +12 -20
- package/lib/task/artifact-command.ts +14 -4
- package/lib/task/artifact-lifecycle.ts +236 -85
- package/lib/task/artifact-operations.ts +1 -26
- package/lib/task/artifact-receipts.ts +4 -1
- package/lib/task/artifact-schema.ts +1 -0
- package/lib/task/artifact-selection.ts +53 -0
- package/lib/task/capabilities.ts +154 -40
- package/lib/task/commands/status.ts +11 -2
- package/lib/task/control-authority.ts +41 -48
- package/lib/task/control-recovery.ts +3 -43
- package/lib/task/decision-intents.ts +9 -0
- package/lib/task/delegation-receipts.ts +2 -2
- package/lib/task/delivery.ts +1 -40
- package/lib/task/events.ts +82 -64
- package/lib/task/finalization-artifacts.ts +1 -3
- package/lib/task/finalization-state.ts +1 -1
- package/lib/task/finalization.ts +310 -216
- package/lib/task/ledger-intents.ts +155 -15
- package/lib/task/lifecycle-path.ts +98 -0
- package/lib/task/lifecycle.ts +15 -2
- package/lib/task/local-artifact-finalization.ts +2 -25
- package/lib/task/manual-validation-receipt.ts +8 -2
- package/lib/task/orchestration.ts +56 -27
- package/lib/task/review-finalization.ts +3 -16
- package/lib/task/rework-intent.ts +75 -5
- package/lib/task/verification-engine.ts +19 -30
- package/lib/task/verification.ts +2 -1
- package/lib/task/workflow-warning-intents.ts +18 -6
- package/lib/task/workspace-snapshot.ts +3 -2
- package/package.json +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.en.md +19 -5
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +19 -5
- package/templates/.agents/rules/review-handshake.en.md +4 -4
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
- package/templates/.agents/skills/analyze-task/SKILL.en.md +11 -4
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -4
- package/templates/.agents/skills/code-task/SKILL.en.md +14 -8
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +18 -13
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +2 -2
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +4 -4
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/reference/report-template.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.en.md +7 -8
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +7 -8
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -10
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
- package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-code/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-plan/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +9 -8
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -8
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -2
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +11 -6
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/feature-development.en.yaml +11 -6
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/refactoring.en.yaml +11 -6
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +11 -6
- package/dist/lib/sandbox/control/finalization-response.js +0 -35
- package/dist/lib/sandbox/control/task-workflow.js +0 -74
- package/dist/lib/sandbox/control/workflow-executor.js +0 -142
- package/dist/lib/task/lifecycle-recovery.js +0 -549
- package/dist/lib/task/recovery-warning.js +0 -19
- package/dist/lib/task/workflow-command.js +0 -21
- package/dist/lib/task/workflow-dispatch.js +0 -213
- package/lib/sandbox/control/finalization-response.ts +0 -37
- package/lib/sandbox/control/task-workflow.ts +0 -95
- package/lib/sandbox/control/workflow-executor.ts +0 -156
- package/lib/task/lifecycle-recovery.ts +0 -691
- package/lib/task/recovery-warning.ts +0 -21
- package/lib/task/workflow-command.ts +0 -22
- package/lib/task/workflow-dispatch.ts +0 -214
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
const REWORK_INTENT_HEADINGS = ['返工意图', 'Rework Intent'];
|
|
2
2
|
const REWORK_INTENT_COLUMNS = [
|
|
3
|
+
'intent_id', 'finding_id', 'source_artifact', 'source_sha256', 'target',
|
|
4
|
+
'classification', 'evidence_digest', 'task_fact_digest', 'status', 'declared_at', 'consumed_at'
|
|
5
|
+
];
|
|
6
|
+
const LEGACY_REWORK_INTENT_COLUMNS = [
|
|
3
7
|
'intent_id', 'finding_id', 'source_artifact', 'source_sha256', 'target',
|
|
4
8
|
'status', 'declared_at', 'consumed_at'
|
|
5
9
|
];
|
|
@@ -55,12 +59,16 @@ function parseReworkIntentDocument(content) {
|
|
|
55
59
|
throw new Error(`invalid intent id '${row.intent_id}'`);
|
|
56
60
|
if (!/^(AN|PL|CD)-[1-9]\d*$/.test(row.finding_id))
|
|
57
61
|
throw new Error(`invalid finding id '${row.finding_id}'`);
|
|
58
|
-
if (!['analysis', 'plan', 'code'].includes(row.target))
|
|
62
|
+
if (!['analysis', 'plan', 'code', 'pause'].includes(row.target))
|
|
59
63
|
throw new Error(`invalid rework target '${row.target}'`);
|
|
64
|
+
if (!['scope-or-requirement', 'design', 'implementation', 'human-decision', 'insufficient-evidence'].includes(row.classification))
|
|
65
|
+
throw new Error(`invalid rework classification '${row.classification}'`);
|
|
60
66
|
if (!['pending', 'consumed', 'superseded'].includes(row.status))
|
|
61
67
|
throw new Error(`invalid rework status '${row.status}'`);
|
|
62
68
|
if (!/^[a-f0-9]{64}$/.test(row.source_sha256))
|
|
63
69
|
throw new Error(`invalid source hash for '${row.intent_id}'`);
|
|
70
|
+
if (!/^[a-f0-9]{64}$/.test(row.evidence_digest) || !/^[a-f0-9]{64}$/.test(row.task_fact_digest))
|
|
71
|
+
throw new Error(`invalid semantic digest for '${row.intent_id}'`);
|
|
64
72
|
if (!row.source_artifact || !row.declared_at)
|
|
65
73
|
throw new Error(`rework intent '${row.intent_id}' has invalid required fields`);
|
|
66
74
|
const terminal = row.status === 'consumed' || row.status === 'superseded';
|
|
@@ -69,6 +77,8 @@ function parseReworkIntentDocument(content) {
|
|
|
69
77
|
return {
|
|
70
78
|
intentId: row.intent_id, findingId: row.finding_id, sourceArtifact: row.source_artifact,
|
|
71
79
|
sourceSha256: row.source_sha256, target: row.target,
|
|
80
|
+
classification: row.classification,
|
|
81
|
+
evidenceDigest: row.evidence_digest, taskFactDigest: row.task_fact_digest,
|
|
72
82
|
status: row.status, declaredAt: row.declared_at, consumedAt: row.consumed_at
|
|
73
83
|
};
|
|
74
84
|
});
|
|
@@ -91,7 +101,10 @@ function renderReworkIntents(intents) {
|
|
|
91
101
|
`| ${REWORK_INTENT_COLUMNS.map(() => '---').join(' | ')} |`,
|
|
92
102
|
...intents.map((intent) => `| ${[
|
|
93
103
|
intent.intentId, intent.findingId, intent.sourceArtifact, intent.sourceSha256,
|
|
94
|
-
intent.target, intent.
|
|
104
|
+
intent.target, intent.classification ?? (intent.target === 'analysis' ? 'scope-or-requirement' : intent.target === 'plan' ? 'design' : intent.target === 'code' ? 'implementation' : 'insufficient-evidence'),
|
|
105
|
+
intent.evidenceDigest ?? requireDigest(`${intent.findingId}|${intent.sourceArtifact}|${intent.sourceSha256}`),
|
|
106
|
+
intent.taskFactDigest ?? requireDigest(`current|${intent.findingId}|${intent.target}`),
|
|
107
|
+
intent.status, intent.declaredAt, intent.consumedAt
|
|
95
108
|
].map(escape).join(' | ')} |`)
|
|
96
109
|
].join('\n');
|
|
97
110
|
}
|
|
@@ -99,11 +112,63 @@ function upsertReworkIntent(intents, intent) {
|
|
|
99
112
|
const existing = intents.find((candidate) => candidate.intentId === intent.intentId);
|
|
100
113
|
if (!existing)
|
|
101
114
|
return { changed: true, intents: [...intents, intent] };
|
|
102
|
-
const identity = (value) => [value.intentId, value.findingId, value.sourceArtifact, value.sourceSha256, value.target];
|
|
115
|
+
const identity = (value) => [value.intentId, value.findingId, value.sourceArtifact, value.sourceSha256, value.target, value.classification, value.evidenceDigest, value.taskFactDigest];
|
|
103
116
|
if (JSON.stringify(identity(existing)) !== JSON.stringify(identity(intent)))
|
|
104
117
|
throw new Error(`intent '${intent.intentId}' identity conflicts with existing state`);
|
|
105
118
|
return { changed: false, intents };
|
|
106
119
|
}
|
|
120
|
+
function consumeHumanDecisionReworkIntents(intents, findingId, consumedAt) {
|
|
121
|
+
let changed = false;
|
|
122
|
+
const next = intents.map((intent) => {
|
|
123
|
+
if (intent.status !== 'pending' || intent.target !== 'pause' || intent.classification !== 'human-decision' || intent.findingId !== findingId)
|
|
124
|
+
return intent;
|
|
125
|
+
changed = true;
|
|
126
|
+
return { ...intent, status: 'consumed', consumedAt };
|
|
127
|
+
});
|
|
128
|
+
return { changed, intents: next };
|
|
129
|
+
}
|
|
130
|
+
function parseLegacyReworkIntentDocument(content) {
|
|
131
|
+
// TODO(compat): Remove this explicit decoder once no task contains the eight-column rework-intent header.
|
|
132
|
+
const body = sectionBody(content);
|
|
133
|
+
if (body === null)
|
|
134
|
+
return { ok: false, code: 'TASK_REWORK_INTENT_INVALID', message: 'legacy rework intent section is missing' };
|
|
135
|
+
const lines = body.split(/\r?\n/).filter((line) => line.trim());
|
|
136
|
+
const header = lines[0] ? cells(lines[0]) : null;
|
|
137
|
+
const separator = lines[1] ? cells(lines[1]) : null;
|
|
138
|
+
if (!header || !separator || header.length !== LEGACY_REWORK_INTENT_COLUMNS.length || !header.every((value, index) => value === LEGACY_REWORK_INTENT_COLUMNS[index])) {
|
|
139
|
+
return { ok: false, code: 'TASK_REWORK_INTENT_INVALID', message: 'rework intent section is not the known legacy table' };
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const intents = lines.slice(2).map((line) => {
|
|
143
|
+
const values = cells(line);
|
|
144
|
+
if (!values || values.length !== LEGACY_REWORK_INTENT_COLUMNS.length)
|
|
145
|
+
throw new Error('legacy rework intent row has the wrong column count');
|
|
146
|
+
const row = Object.fromEntries(LEGACY_REWORK_INTENT_COLUMNS.map((column, index) => [column, values[index]]));
|
|
147
|
+
if (!/^RI-[1-9]\d*$/.test(row.intent_id) || !/^(AN|PL|CD)-[1-9]\d*$/.test(row.finding_id))
|
|
148
|
+
throw new Error('legacy rework intent identity is invalid');
|
|
149
|
+
if (!['analysis', 'plan', 'code'].includes(row.target) || !['pending', 'consumed', 'superseded'].includes(row.status))
|
|
150
|
+
throw new Error('legacy rework intent state is invalid');
|
|
151
|
+
if (!/^[a-f0-9]{64}$/.test(row.source_sha256))
|
|
152
|
+
throw new Error('legacy rework intent source hash is invalid');
|
|
153
|
+
const legacyDigest = (value) => requireDigest(value);
|
|
154
|
+
const classification = row.target === 'analysis' ? 'scope-or-requirement' : row.target === 'plan' ? 'design' : 'implementation';
|
|
155
|
+
return {
|
|
156
|
+
intentId: row.intent_id, findingId: row.finding_id, sourceArtifact: row.source_artifact, sourceSha256: row.source_sha256,
|
|
157
|
+
target: row.target, classification,
|
|
158
|
+
evidenceDigest: legacyDigest(`${row.finding_id}|${row.source_artifact}|${row.source_sha256}`),
|
|
159
|
+
taskFactDigest: legacyDigest(`legacy|${row.finding_id}|${row.target}`),
|
|
160
|
+
status: row.status, declaredAt: row.declared_at, consumedAt: row.consumed_at
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
return { ok: true, present: true, intents };
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
return { ok: false, code: 'TASK_REWORK_INTENT_INVALID', message: error instanceof Error ? error.message : String(error) };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function requireDigest(value) {
|
|
170
|
+
return createHash('sha256').update(value).digest('hex');
|
|
171
|
+
}
|
|
107
172
|
function consumeReworkIntents(intents, target, sourceHashes, consumedAt) {
|
|
108
173
|
let changed = false;
|
|
109
174
|
const next = intents.map((intent) => {
|
|
@@ -134,5 +199,6 @@ function reworkIntentMutation(content, intents) {
|
|
|
134
199
|
body: renderReworkIntents(intents)
|
|
135
200
|
};
|
|
136
201
|
}
|
|
137
|
-
export { REWORK_INTENT_COLUMNS, REWORK_INTENT_HEADINGS, parseReworkIntentDocument, renderReworkIntents, reworkIntentMutation, consumeReworkIntents, supersedeReworkIntents, upsertReworkIntent };
|
|
202
|
+
export { REWORK_INTENT_COLUMNS, REWORK_INTENT_HEADINGS, parseReworkIntentDocument, renderReworkIntents, reworkIntentMutation, consumeReworkIntents, supersedeReworkIntents, upsertReworkIntent, consumeHumanDecisionReworkIntents, parseLegacyReworkIntentDocument };
|
|
203
|
+
import { createHash } from 'node:crypto';
|
|
138
204
|
//# sourceMappingURL=rework-intent.js.map
|
|
@@ -26,7 +26,6 @@ import { OrchestrationStateError, readRun } from "./orchestration.js";
|
|
|
26
26
|
import { resolveDeliveryTarget } from "./delivery-target.js";
|
|
27
27
|
import { readPrDeliveryFact } from "./pr-delivery-fact.js";
|
|
28
28
|
import { validateLocalArtifact } from "./local-artifact-finalization.js";
|
|
29
|
-
import { validateQualificationAudit } from "./qualification-audit.js";
|
|
30
29
|
import { getArtifactSchema } from "./artifact-schema.js";
|
|
31
30
|
import { inspectArtifactContract } from "./artifact-operations.js";
|
|
32
31
|
import { normalizeVerificationRecord } from './gate-policy.js';
|
|
@@ -59,7 +58,6 @@ const ACTIVITY_LOG_STARTED_RE = /\s*\[started\]\s*$/;
|
|
|
59
58
|
const BRANCH_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
60
59
|
// Review disagreement ledger (see .agents/rules/review-handshake.md).
|
|
61
60
|
const LEDGER_SECTION_NAMES = ["审查分歧账本", "Review Disagreement Ledger"];
|
|
62
|
-
const DEFAULT_MAX_HANDSHAKE_ROUNDS = 3;
|
|
63
61
|
const POST_REVIEW_COMMIT_STAGE = "post-review-commit";
|
|
64
62
|
const SHA_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
65
63
|
const scriptPath = fileURLToPath(import.meta.url);
|
|
@@ -441,21 +439,6 @@ function checkArtifact({ taskDir, config, artifactFile, skillName }) {
|
|
|
441
439
|
return failResult("artifact", `Artifact is empty: ${path.basename(artifactPath)}`);
|
|
442
440
|
}
|
|
443
441
|
const content = fs.readFileSync(artifactPath, "utf8");
|
|
444
|
-
let taskContent = "";
|
|
445
|
-
const taskPath = path.join(taskDir, "task.md");
|
|
446
|
-
if (fs.existsSync(taskPath)) {
|
|
447
|
-
try {
|
|
448
|
-
taskContent = fs.readFileSync(taskPath, "utf8");
|
|
449
|
-
}
|
|
450
|
-
catch (error) {
|
|
451
|
-
return failResult("artifact", `Cannot read task qualification contract: ${error instanceof Error ? error.message : String(error)}`);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
if (taskContent && /^review-(?:analysis|plan|code)$/.test(skillName)) {
|
|
455
|
-
const qualification = validateQualificationAudit(taskContent, content, { artifact: path.basename(artifactPath) });
|
|
456
|
-
if (!qualification.ok)
|
|
457
|
-
return failResult("artifact", `${path.basename(artifactPath)} has qualification audit errors: ${qualification.code}: ${qualification.message}`);
|
|
458
|
-
}
|
|
459
442
|
const schemaFamily = skillName === "analyze-task"
|
|
460
443
|
? "analysis"
|
|
461
444
|
: skillName === "review-analysis"
|
|
@@ -479,9 +462,7 @@ function checkArtifact({ taskDir, config, artifactFile, skillName }) {
|
|
|
479
462
|
}
|
|
480
463
|
if (localFamily) {
|
|
481
464
|
const local = validateLocalArtifact(content, {
|
|
482
|
-
family: localFamily
|
|
483
|
-
...(taskContent ? { taskContent } : {}),
|
|
484
|
-
artifact: path.basename(artifactPath)
|
|
465
|
+
family: localFamily
|
|
485
466
|
});
|
|
486
467
|
if (!local.ok) {
|
|
487
468
|
return failResult("artifact", `${path.basename(artifactPath)} has local structural errors: ${local.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')}`);
|
|
@@ -715,11 +696,10 @@ function checkReviewLedger({ taskDir, config, repositoryRoot }) {
|
|
|
715
696
|
return passResult("review-ledger", "Disagreement ledger has no entries");
|
|
716
697
|
}
|
|
717
698
|
const stageScope = Array.isArray(config.stage_scope) ? config.stage_scope : null;
|
|
718
|
-
const maxRounds = Number(resolveReviewSetting(config, "maxHandshakeRounds", DEFAULT_MAX_HANDSHAKE_ROUNDS, repositoryRoot));
|
|
719
699
|
const problems = [];
|
|
720
700
|
let inScopeCount = 0;
|
|
721
701
|
for (const row of rows) {
|
|
722
|
-
const { id, stage,
|
|
702
|
+
const { id, stage, status, evidence } = row;
|
|
723
703
|
const stageScoped = stageScope ? stageScope.includes(stage) : true;
|
|
724
704
|
// post-review-commit exemption rows are consumed by the post-review-commit
|
|
725
705
|
// check, not enforced here.
|
|
@@ -738,13 +718,6 @@ function checkReviewLedger({ taskDir, config, repositoryRoot }) {
|
|
|
738
718
|
if (status !== "open" && evidence === "") {
|
|
739
719
|
problems.push(`${id}: status '${status}' requires evidence`);
|
|
740
720
|
}
|
|
741
|
-
const round = Number.parseInt(roundRaw, 10);
|
|
742
|
-
if (Number.isFinite(round) &&
|
|
743
|
-
round >= maxRounds &&
|
|
744
|
-
!LEDGER_TERMINAL.has(status) &&
|
|
745
|
-
status !== "needs-human-decision") {
|
|
746
|
-
problems.push(`${id}: round ${round} reached limit ${maxRounds} without convergence; escalate to needs-human-decision`);
|
|
747
|
-
}
|
|
748
721
|
if (!LEDGER_TERMINAL.has(status)) {
|
|
749
722
|
problems.push(`${id}: unresolved (status '${status}')`);
|
|
750
723
|
}
|
|
@@ -1051,7 +1024,8 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }) {
|
|
|
1051
1024
|
let targetHead;
|
|
1052
1025
|
let diffBase;
|
|
1053
1026
|
try {
|
|
1054
|
-
|
|
1027
|
+
const taskRepositoryRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
1028
|
+
gitRoot = reviewWorktreeForTask(taskRepositoryRoot, String(task.metadata.branch || "").trim()) ?? taskRepositoryRoot;
|
|
1055
1029
|
head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
|
1056
1030
|
const target = resolveDeliveryTarget(gitRoot, { remote: deliveryRemote, baseRef: deliveryBaseRef });
|
|
1057
1031
|
if (!target.ok)
|
|
@@ -1101,6 +1075,22 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }) {
|
|
|
1101
1075
|
}
|
|
1102
1076
|
return passResult("review-fact", `Review fact valid for ${path.basename(resolvedArtifact.path)} at ${baseline.slice(0, 8)}`);
|
|
1103
1077
|
}
|
|
1078
|
+
function reviewWorktreeForTask(repositoryRoot, branch) {
|
|
1079
|
+
if (!branch)
|
|
1080
|
+
return null;
|
|
1081
|
+
const expectedRef = `refs/heads/${branch}`;
|
|
1082
|
+
const records = execFileSync("git", ["-C", repositoryRoot, "worktree", "list", "--porcelain"], {
|
|
1083
|
+
encoding: "utf8",
|
|
1084
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1085
|
+
}).trim().split(/\r?\n\r?\n/);
|
|
1086
|
+
for (const record of records) {
|
|
1087
|
+
const worktree = /^worktree (.+)$/m.exec(record)?.[1];
|
|
1088
|
+
const branchRef = /^branch (.+)$/m.exec(record)?.[1];
|
|
1089
|
+
if (worktree && branchRef === expectedRef)
|
|
1090
|
+
return worktree;
|
|
1091
|
+
}
|
|
1092
|
+
throw new Error(`Task branch '${branch}' is not checked out in a registered worktree`);
|
|
1093
|
+
}
|
|
1104
1094
|
function resolvePostReviewBaseline({ gitRoot, lastReviewedCommit, reviewArtifact }) {
|
|
1105
1095
|
if (SHA_PATTERN.test(lastReviewedCommit) && gitCommitExists(gitRoot, lastReviewedCommit)) {
|
|
1106
1096
|
return { ok: true, sha: lastReviewedCommit };
|
|
@@ -23,6 +23,7 @@ const VERIFICATION_CATALOG = {
|
|
|
23
23
|
'commit.completed': gate('commit', 'active'),
|
|
24
24
|
'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
25
25
|
'complete-task.hard-preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
26
|
+
'complete-task.prepared': gate('complete-task', 'active'),
|
|
26
27
|
'complete-task.completed': gate('complete-task', 'completed'),
|
|
27
28
|
'create-pr.completed': gate('create-pr', 'active'),
|
|
28
29
|
'create-task.completed': gate('create-task', 'active'),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
3
|
+
import { parseTaskFrontmatter } from './frontmatter.js';
|
|
3
4
|
import { WORKFLOW_WARNING_COLUMNS, WORKFLOW_WARNING_HEADINGS, WORKFLOW_WARNING_SEVERITIES, WORKFLOW_WARNING_STATUSES, parseWorkflowWarnings } from './workflow-warnings.js';
|
|
4
5
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
5
6
|
import { allowsManualOverride } from './guard-override.js';
|
|
@@ -118,8 +119,18 @@ function projectFinalizationWarning(taskRef, warning, options = {}) {
|
|
|
118
119
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
119
120
|
if (!resolved.ok)
|
|
120
121
|
return { status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } };
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
let prepared = resolved.state === 'completed';
|
|
123
|
+
if (!prepared && resolved.state === 'active') {
|
|
124
|
+
try {
|
|
125
|
+
prepared = parseTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8')).status === 'completed';
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
prepared = false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (!prepared) {
|
|
132
|
+
return { status: 'failed', changed: false, error: { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected a prepared or completed task` } };
|
|
133
|
+
}
|
|
123
134
|
let content;
|
|
124
135
|
let rows;
|
|
125
136
|
try {
|
|
@@ -139,23 +150,27 @@ function projectFinalizationWarning(taskRef, warning, options = {}) {
|
|
|
139
150
|
message: warning.message,
|
|
140
151
|
action: retryHintForWarning(warning),
|
|
141
152
|
resolvedAt: warning.resolvedAt || (warning.status === 'resolved' ? metadata.timestamp : ''),
|
|
142
|
-
resolution: warning.status === 'resolved'
|
|
153
|
+
resolution: warning.status === 'resolved'
|
|
154
|
+
? options.resolution === 'superseded' ? 'Superseded by a later verification observation' : 'Resolved by a successful finalization retry'
|
|
155
|
+
: ''
|
|
143
156
|
}
|
|
144
157
|
: {
|
|
145
158
|
id: nextWarningId(rows), time: metadata.timestamp, step: warning.step,
|
|
146
159
|
severity: warning.severity, code: warning.code, status: warning.status,
|
|
147
160
|
target: warning.target, message: warning.message, action: retryHintForWarning(warning),
|
|
148
|
-
resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved'
|
|
161
|
+
resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved'
|
|
162
|
+
? options.resolution === 'superseded' ? 'Superseded by a later verification observation' : 'Resolved by a successful finalization retry'
|
|
163
|
+
: ''
|
|
149
164
|
};
|
|
150
165
|
if (existing && JSON.stringify(existing) === JSON.stringify(after))
|
|
151
166
|
return { status: 'no-op', changed: false, error: null };
|
|
152
167
|
const written = writeTask({
|
|
153
168
|
taskRef: resolved.taskId,
|
|
154
|
-
expectedState:
|
|
169
|
+
expectedState: resolved.state,
|
|
155
170
|
mutations: [...sectionMutation(content), rowMutation(after)]
|
|
156
171
|
}, {
|
|
157
172
|
...options,
|
|
158
|
-
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state:
|
|
173
|
+
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
|
|
159
174
|
metadataProvider: () => metadata
|
|
160
175
|
});
|
|
161
176
|
if (written.status === 'failed')
|
|
@@ -15,7 +15,12 @@ function captureWorktreeTree(repoRoot, forcedPath) {
|
|
|
15
15
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-infra-orchestration-index-'));
|
|
16
16
|
const indexFile = path.join(tempDir, 'index');
|
|
17
17
|
try {
|
|
18
|
-
|
|
18
|
+
try {
|
|
19
|
+
git(repoRoot, indexFile, ['read-tree', 'HEAD']);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
git(repoRoot, indexFile, ['read-tree', '--empty']);
|
|
23
|
+
}
|
|
19
24
|
git(repoRoot, indexFile, ['add', '-A', '--', ':/']);
|
|
20
25
|
if (forcedPath && fs.existsSync(path.join(repoRoot, forcedPath))) {
|
|
21
26
|
git(repoRoot, indexFile, ['add', '-f', '--', forcedPath]);
|
|
@@ -111,5 +116,5 @@ function diffWorkspaceSnapshots(repoRoot, before, after) {
|
|
|
111
116
|
}
|
|
112
117
|
return [...changed].sort();
|
|
113
118
|
}
|
|
114
|
-
export { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots };
|
|
119
|
+
export { captureRepositorySnapshot, captureWorkspaceSnapshot, captureWorktreeTree, diffWorkspaceSnapshots };
|
|
115
120
|
//# sourceMappingURL=workspace-snapshot.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"protocolVersion": 3,
|
|
3
|
-
"packageVersion": "0.11.
|
|
4
|
-
"sourceInputHash": "
|
|
5
|
-
"internalExecutableBuildHash": "
|
|
3
|
+
"packageVersion": "0.11.4",
|
|
4
|
+
"sourceInputHash": "46c0a39f354bff8c80fe19354cdda89a91a4acad060b1f134b6d599bc0ba6806",
|
|
5
|
+
"internalExecutableBuildHash": "1bc9e5755c857ee9f13a702452b2ce7c85da94444dc5a8d1305ae80d5899b001"
|
|
6
6
|
}
|
|
@@ -7,6 +7,10 @@ import type {
|
|
|
7
7
|
AgentClientCapabilityMap,
|
|
8
8
|
AgentClientId
|
|
9
9
|
} from './types.ts';
|
|
10
|
+
import type {
|
|
11
|
+
OrchestrationOptions,
|
|
12
|
+
OrchestrationResult
|
|
13
|
+
} from '../task/orchestration.ts';
|
|
10
14
|
import {
|
|
11
15
|
SANDBOX_HOOK_PHASES
|
|
12
16
|
} from '../sandbox/tool-types.ts';
|
|
@@ -43,6 +47,41 @@ type AgentClientDelegationEvidence = Readonly<{
|
|
|
43
47
|
actualReasoningEffort: 'host-event' | 'app-server' | 'spawn-ack' | 'unavailable';
|
|
44
48
|
}>;
|
|
45
49
|
|
|
50
|
+
type AgentClientLifecycleRecoveryRequest = Readonly<{
|
|
51
|
+
taskRef: string;
|
|
52
|
+
intent: 'recover-started';
|
|
53
|
+
agent: string;
|
|
54
|
+
auto: true;
|
|
55
|
+
}>;
|
|
56
|
+
|
|
57
|
+
type AgentClientLifecycleRecoveryResult = Readonly<{
|
|
58
|
+
status: 'applied' | 'no-op' | 'owner-unknown' | 'conflict';
|
|
59
|
+
changed: boolean;
|
|
60
|
+
targetState: 'active';
|
|
61
|
+
requestRef: string;
|
|
62
|
+
intent: 'recover-started';
|
|
63
|
+
taskId: string | null;
|
|
64
|
+
receiptId: string | null;
|
|
65
|
+
childId: string | null;
|
|
66
|
+
error: Readonly<{ code: string; message: string }> | null;
|
|
67
|
+
}>;
|
|
68
|
+
|
|
69
|
+
type AgentClientOrchestrationAdapter = Readonly<{
|
|
70
|
+
prepareDelegation?: (
|
|
71
|
+
taskRef: string,
|
|
72
|
+
input: Readonly<{
|
|
73
|
+
client: AgentClientId;
|
|
74
|
+
requestedModel?: string;
|
|
75
|
+
requestedReasoningEffort?: string;
|
|
76
|
+
}>,
|
|
77
|
+
options?: Readonly<{ repoRoot?: string; orchestrationOptions?: OrchestrationOptions }>
|
|
78
|
+
) => Promise<OrchestrationResult>;
|
|
79
|
+
recoverStarted?: (
|
|
80
|
+
request: AgentClientLifecycleRecoveryRequest,
|
|
81
|
+
options: Readonly<{ repoRoot: string }>
|
|
82
|
+
) => AgentClientLifecycleRecoveryResult | Promise<AgentClientLifecycleRecoveryResult>;
|
|
83
|
+
}>;
|
|
84
|
+
|
|
46
85
|
type AgentClientSeedCommand = Readonly<{
|
|
47
86
|
templates: Readonly<{
|
|
48
87
|
en: string;
|
|
@@ -75,6 +114,7 @@ type AgentClientAdapter = Readonly<{
|
|
|
75
114
|
capabilities: AgentClientCapabilities;
|
|
76
115
|
modelSelection: AgentClientModelSelectionContext;
|
|
77
116
|
delegationEvidence: AgentClientDelegationEvidence;
|
|
117
|
+
orchestrationAdapter?: AgentClientOrchestrationAdapter;
|
|
78
118
|
project: AgentClientProjectDescriptor;
|
|
79
119
|
sandbox: AgentClientSandboxDescriptor;
|
|
80
120
|
}>;
|
|
@@ -628,6 +668,9 @@ function defineAgentClientAdapter(
|
|
|
628
668
|
capabilities: Object.freeze(capabilities),
|
|
629
669
|
modelSelection: frozenModelSelection,
|
|
630
670
|
delegationEvidence: Object.freeze({ ...evidence }),
|
|
671
|
+
...(candidate.orchestrationAdapter === undefined
|
|
672
|
+
? {}
|
|
673
|
+
: { orchestrationAdapter: Object.freeze({ ...candidate.orchestrationAdapter }) }),
|
|
631
674
|
project: Object.freeze({
|
|
632
675
|
ownedPathPrefixes: Object.freeze(paths),
|
|
633
676
|
...projectAssets,
|
|
@@ -650,6 +693,9 @@ export type {
|
|
|
650
693
|
AgentClientCapabilities,
|
|
651
694
|
AgentClientCustomCommandDescriptor,
|
|
652
695
|
AgentClientDelegationEvidence,
|
|
696
|
+
AgentClientLifecycleRecoveryRequest,
|
|
697
|
+
AgentClientLifecycleRecoveryResult,
|
|
698
|
+
AgentClientOrchestrationAdapter,
|
|
653
699
|
AgentClientManifestEntry,
|
|
654
700
|
AgentClientModelSelectionContext,
|
|
655
701
|
AgentClientProjectDescriptor,
|