@fitlab-ai/agent-infra 0.9.12 → 0.9.13
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 +11 -11
- package/README.zh-CN.md +11 -11
- package/dist/bin/internal-cli.js +24 -19
- package/dist/lib/agent-clients/next-steps.js +6 -1
- package/dist/lib/decide.js +3 -3
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +9 -9
- package/dist/lib/internal/platform-context.js +2 -2
- package/dist/lib/internal/platform-issue.js +8 -9
- package/dist/lib/internal/platform-pr-review.js +10 -10
- package/dist/lib/internal/platform-pr.js +63 -24
- package/dist/lib/internal/platform-release-notes.js +3 -3
- package/dist/lib/internal/pr-review-grade.js +11 -9
- package/dist/lib/internal/release-workflow.js +4 -4
- package/dist/lib/internal/sandbox-control.js +30 -4
- package/dist/lib/internal/task-activity.js +3 -3
- package/dist/lib/internal/task-artifact.js +76 -24
- package/dist/lib/internal/task-create.js +2 -2
- package/dist/lib/internal/task-event.js +6 -3
- package/dist/lib/internal/task-finalization.js +2 -2
- package/dist/lib/internal/task-invalidation.js +60 -0
- package/dist/lib/internal/task-ledger.js +9 -6
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-override.js +4 -4
- package/dist/lib/internal/task-review.js +3 -3
- package/dist/lib/internal/task-verify.js +6 -6
- package/dist/lib/internal/task-warning.js +3 -3
- package/dist/lib/platform/adapters.js +1 -3
- package/dist/lib/platform/change-request-git-evidence.js +27 -7
- package/dist/lib/platform/completion-backfill.js +6 -5
- package/dist/lib/platform/context.js +70 -130
- package/dist/lib/platform/github-client.js +1 -1
- package/dist/lib/platform/github-provider.js +903 -0
- package/dist/lib/platform/in-label-sync.js +143 -0
- package/dist/lib/platform/issue-comments.js +94 -41
- package/dist/lib/platform/issues.js +335 -189
- package/dist/lib/platform/metadata-labels.js +6 -2
- package/dist/lib/platform/none-provider.js +28 -0
- package/dist/lib/platform/pr-change-report.js +551 -0
- package/dist/lib/platform/pr-checks.js +119 -104
- package/dist/lib/platform/pr-review.js +88 -72
- package/dist/lib/platform/pr-summary.js +520 -50
- package/dist/lib/platform/provider-bridge.js +45 -0
- package/dist/lib/platform/provider-contract.d.ts +487 -0
- package/dist/lib/platform/provider-contract.js +95 -0
- package/dist/lib/platform/provider-loader.js +251 -0
- package/dist/lib/platform/provider-validation.js +504 -0
- package/dist/lib/platform/pull-request-metadata.js +8 -6
- package/dist/lib/platform/pull-requests.js +783 -537
- package/dist/lib/platform/release-notes.js +78 -55
- package/dist/lib/platform/releases.js +79 -50
- package/dist/lib/platform/resource-identity.d.ts +26 -0
- package/dist/lib/platform/resource-identity.js +92 -0
- package/dist/lib/platform/task-identities.js +31 -0
- package/dist/lib/platform/verification-required.js +11 -12
- package/dist/lib/platform/verification-sync.js +112 -238
- package/dist/lib/pr-review/evidence-grading.js +19 -6
- package/dist/lib/run/index.js +63 -23
- package/dist/lib/sandbox/control/client.js +96 -15
- package/dist/lib/sandbox/control/executor.js +3 -3
- package/dist/lib/sandbox/control/protocol.js +77 -0
- package/dist/lib/sandbox/control/server.js +398 -25
- package/dist/lib/sandbox/control/state.js +204 -1
- package/dist/lib/server/protocol.js +38 -1
- package/dist/lib/task/activity-log.js +8 -1
- package/dist/lib/task/artifact-lifecycle.js +33 -5
- package/dist/lib/task/capabilities.js +239 -0
- package/dist/lib/task/commands/status.js +32 -0
- package/dist/lib/task/commit-operation.js +12 -1
- package/dist/lib/task/control-authority.js +8 -5
- package/dist/lib/task/create-service.js +8 -8
- package/dist/lib/task/current-contract.js +5 -1
- package/dist/lib/task/decision-details.js +1 -1
- package/dist/lib/task/events.js +297 -17
- package/dist/lib/task/finalization.js +37 -13
- package/dist/lib/task/human-override.js +58 -36
- package/dist/lib/task/invalidation-command.js +86 -0
- package/dist/lib/task/invalidation.js +260 -0
- package/dist/lib/task/ledger-intents.js +37 -0
- package/dist/lib/task/local-artifact-finalization.js +374 -0
- package/dist/lib/task/orchestration.js +49 -93
- package/dist/lib/task/pr-delivery-fact.js +102 -101
- package/dist/lib/task/review-fingerprint.js +24 -2
- package/dist/lib/task/rework-intent.js +138 -0
- package/dist/lib/task/skill-scope.js +23 -0
- package/dist/lib/task/task-execution-lock.js +27 -17
- package/dist/lib/task/verification-engine.js +43 -16
- package/dist/lib/task/verification.js +3 -3
- package/dist/lib/task/workflow-warning-intents.js +1 -1
- package/dist/lib/task/write.js +11 -0
- package/dist/lib/version.js +11 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/next-steps.ts +6 -1
- package/lib/decide.ts +3 -3
- package/lib/init.ts +1 -1
- package/lib/internal/platform-checks.ts +3 -3
- package/lib/internal/platform-comment.ts +8 -8
- package/lib/internal/platform-context.ts +2 -2
- package/lib/internal/platform-issue.ts +7 -8
- package/lib/internal/platform-pr-review.ts +9 -9
- package/lib/internal/platform-pr.ts +47 -22
- package/lib/internal/platform-release-notes.ts +3 -3
- package/lib/internal/pr-review-grade.ts +10 -8
- package/lib/internal/release-workflow.ts +4 -4
- package/lib/internal/sandbox-control.ts +27 -3
- package/lib/internal/task-activity.ts +3 -3
- package/lib/internal/task-artifact.ts +65 -22
- package/lib/internal/task-create.ts +2 -2
- package/lib/internal/task-event.ts +6 -3
- package/lib/internal/task-finalization.ts +3 -3
- package/lib/internal/task-invalidation.ts +39 -0
- package/lib/internal/task-ledger.ts +11 -8
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-override.ts +6 -6
- package/lib/internal/task-review.ts +3 -3
- package/lib/internal/task-verify.ts +6 -6
- package/lib/internal/task-warning.ts +3 -3
- package/lib/platform/adapters.ts +3 -2
- package/lib/platform/change-request-git-evidence.ts +36 -9
- package/lib/platform/completion-backfill.ts +9 -8
- package/lib/platform/context.ts +97 -134
- package/lib/platform/github-client.ts +2 -2
- package/lib/platform/github-provider.ts +910 -0
- package/lib/platform/in-label-sync.ts +208 -0
- package/lib/platform/issue-comments.ts +97 -47
- package/lib/platform/issues.ts +344 -174
- package/lib/platform/metadata-labels.ts +5 -2
- package/lib/platform/none-provider.ts +35 -0
- package/lib/platform/pr-change-report.ts +645 -0
- package/lib/platform/pr-checks.ts +126 -106
- package/lib/platform/pr-review.ts +88 -85
- package/lib/platform/pr-summary.ts +560 -53
- package/lib/platform/provider-bridge.ts +82 -0
- package/lib/platform/provider-contract.ts +536 -0
- package/lib/platform/provider-loader.ts +307 -0
- package/lib/platform/provider-validation.ts +529 -0
- package/lib/platform/pull-request-metadata.ts +10 -7
- package/lib/platform/pull-requests.ts +826 -543
- package/lib/platform/release-notes.ts +76 -59
- package/lib/platform/releases.ts +77 -43
- package/lib/platform/resource-identity.ts +121 -0
- package/lib/platform/task-identities.ts +40 -0
- package/lib/platform/types.ts +4 -2
- package/lib/platform/verification-required.ts +11 -13
- package/lib/platform/verification-sync.ts +112 -292
- package/lib/pr-review/evidence-grading.ts +25 -8
- package/lib/run/index.ts +57 -22
- package/lib/sandbox/control/client.ts +105 -15
- package/lib/sandbox/control/executor.ts +4 -4
- package/lib/sandbox/control/protocol.ts +113 -0
- package/lib/sandbox/control/server.ts +431 -23
- package/lib/sandbox/control/state.ts +251 -1
- package/lib/server/protocol.ts +36 -1
- package/lib/task/activity-log.ts +8 -0
- package/lib/task/artifact-lifecycle.ts +32 -6
- package/lib/task/capabilities.ts +262 -0
- package/lib/task/commands/status.ts +38 -0
- package/lib/task/commit-operation.ts +12 -1
- package/lib/task/control-authority.ts +11 -8
- package/lib/task/create-service.ts +8 -8
- package/lib/task/current-contract.ts +6 -1
- package/lib/task/decision-details.ts +6 -2
- package/lib/task/events.ts +296 -16
- package/lib/task/finalization.ts +50 -14
- package/lib/task/human-override.ts +70 -46
- package/lib/task/invalidation-command.ts +86 -0
- package/lib/task/invalidation.ts +332 -0
- package/lib/task/ledger-intents.ts +35 -1
- package/lib/task/local-artifact-finalization.ts +563 -0
- package/lib/task/orchestration.ts +52 -85
- package/lib/task/pr-delivery-fact.ts +131 -148
- package/lib/task/review-fingerprint.ts +23 -2
- package/lib/task/rework-intent.ts +165 -0
- package/lib/task/skill-scope.ts +24 -0
- package/lib/task/task-execution-lock.ts +23 -14
- package/lib/task/verification-engine.ts +52 -17
- package/lib/task/verification.ts +4 -4
- package/lib/task/workflow-warning-intents.ts +1 -1
- package/lib/task/write.ts +19 -0
- package/lib/update.ts +1 -1
- package/lib/version.ts +15 -1
- package/package.json +7 -1
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +2 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
- package/templates/.agents/rules/local-artifact-repair.en.md +9 -2
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +9 -2
- package/templates/.agents/rules/pr-sync.en.md +10 -4
- package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
- package/templates/.agents/rules/task-short-id.en.md +1 -4
- package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
- package/templates/.agents/skills/analyze-task/SKILL.en.md +13 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -3
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/code-task/SKILL.en.md +27 -8
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +29 -10
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +21 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +10 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.en.md +4 -4
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +4 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +4 -2
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
- package/templates/.agents/templates/task.en.md +18 -1
- package/templates/.agents/templates/task.zh-CN.md +18 -1
- package/templates/.github/workflows/pr-label.yml +7 -26
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
5
5
|
import { buildProcessTreeStopCommand } from '../../server/process-control.ts';
|
|
6
6
|
import { getProcessIdentityState } from '../../server/process-state.ts';
|
|
7
7
|
import type { ProcessIdentity, ProcessIdentityProbe } from '../../server/process-state.ts';
|
|
@@ -9,8 +9,20 @@ import type {
|
|
|
9
9
|
SandboxControlExecution,
|
|
10
10
|
SandboxControlLease,
|
|
11
11
|
SandboxControlManifest,
|
|
12
|
+
SandboxControlPayload,
|
|
13
|
+
SandboxControlReservation,
|
|
14
|
+
SandboxControlResultEvidence,
|
|
12
15
|
SandboxControlStatus
|
|
13
16
|
} from './protocol.ts';
|
|
17
|
+
import {
|
|
18
|
+
parseSandboxControlResultEvidence,
|
|
19
|
+
parseSandboxControlPayload,
|
|
20
|
+
parseSandboxControlReservation,
|
|
21
|
+
SANDBOX_CONTROL_MAX_RESPONSE_BYTES,
|
|
22
|
+
SANDBOX_CONTROL_MAX_LOGICAL_RECORDS,
|
|
23
|
+
SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES,
|
|
24
|
+
SANDBOX_CONTROL_RESERVATION_BYTES
|
|
25
|
+
} from './protocol.ts';
|
|
14
26
|
|
|
15
27
|
export const SANDBOX_CONTROL_AUDIT_MAX_BYTES = 1024 * 1024;
|
|
16
28
|
export const SANDBOX_CONTROL_EXECUTION_STOP_MS = 2_000;
|
|
@@ -31,11 +43,249 @@ export function atomicWriteJson(filePath: string, value: unknown, mode = 0o600,
|
|
|
31
43
|
}
|
|
32
44
|
}
|
|
33
45
|
|
|
46
|
+
export function sandboxControlEncodedJsonBytes(value: unknown): number {
|
|
47
|
+
return Buffer.byteLength(`${JSON.stringify(value)}\n`, 'utf8');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function atomicWriteJsonNoReplace(
|
|
51
|
+
filePath: string,
|
|
52
|
+
value: unknown,
|
|
53
|
+
mode = 0o600,
|
|
54
|
+
createParent = true
|
|
55
|
+
): void {
|
|
56
|
+
atomicCreateJson(filePath, value, 'SANDBOX_CONTROL_TERMINAL_ALREADY_EXISTS', mode, createParent);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function atomicCreateJson(
|
|
60
|
+
filePath: string,
|
|
61
|
+
value: unknown,
|
|
62
|
+
duplicateCode: string,
|
|
63
|
+
mode = 0o600,
|
|
64
|
+
createParent = true
|
|
65
|
+
): void {
|
|
66
|
+
if (createParent) fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
67
|
+
const temporary = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`);
|
|
68
|
+
try {
|
|
69
|
+
fs.writeFileSync(temporary, `${JSON.stringify(value)}\n`, { mode, flag: 'wx' });
|
|
70
|
+
try {
|
|
71
|
+
fs.linkSync(temporary, filePath);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if ((error as NodeJS.ErrnoException).code === 'EEXIST') {
|
|
74
|
+
throw new Error(duplicateCode);
|
|
75
|
+
}
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
} finally {
|
|
79
|
+
fs.rmSync(temporary, { force: true });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
34
83
|
export function readJsonFile(filePath: string): unknown {
|
|
35
84
|
assertRegularFile(filePath);
|
|
36
85
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
37
86
|
}
|
|
38
87
|
|
|
88
|
+
export function resultEvidencePath(manifest: SandboxControlManifest, requestId: string): string {
|
|
89
|
+
if (!/^[a-f0-9-]{16,64}$/u.test(requestId)) throw new Error('SANDBOX_CONTROL_RESULT_EVIDENCE_INVALID');
|
|
90
|
+
return path.join(manifest.processingDir, requestId, 'result.json');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function payloadPath(manifest: SandboxControlManifest, requestId: string): string {
|
|
94
|
+
if (!/^[a-f0-9-]{16,64}$/u.test(requestId)) throw new Error('SANDBOX_CONTROL_PAYLOAD_INVALID');
|
|
95
|
+
return path.join(manifest.channelDir, 'responses', `${requestId}.payload.json`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function reservationPath(manifest: SandboxControlManifest, requestId: string): string {
|
|
99
|
+
if (!/^[a-f0-9-]{16,64}$/u.test(requestId)) throw new Error('SANDBOX_CONTROL_RESERVATION_INVALID');
|
|
100
|
+
return path.join(manifest.processingDir, requestId, 'reservation.json');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function readSandboxControlResultEvidence(filePath: string): SandboxControlResultEvidence {
|
|
104
|
+
assertRegularFile(filePath);
|
|
105
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
106
|
+
if (Buffer.byteLength(raw, 'utf8') > SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
|
|
107
|
+
throw new Error('SANDBOX_CONTROL_RESULT_EVIDENCE_TOO_LARGE');
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
return parseSandboxControlResultEvidence(JSON.parse(raw));
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (error instanceof Error && error.message === 'SANDBOX_CONTROL_RESULT_EVIDENCE_INVALID') throw error;
|
|
113
|
+
throw new Error('SANDBOX_CONTROL_RESULT_EVIDENCE_INVALID');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function sanitizeSandboxControlOutput(manifest: SandboxControlManifest, output: string): string {
|
|
118
|
+
return manifest.token.length === 0 ? output : output.split(manifest.token).join('[REDACTED]');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function sanitizeSandboxControlResult(
|
|
122
|
+
manifest: SandboxControlManifest,
|
|
123
|
+
result: Readonly<{ exitCode: number; stdout: string; stderr: string }>
|
|
124
|
+
): { exitCode: number; stdout: string; stderr: string } {
|
|
125
|
+
return {
|
|
126
|
+
exitCode: result.exitCode,
|
|
127
|
+
stdout: sanitizeSandboxControlOutput(manifest, result.stdout),
|
|
128
|
+
stderr: sanitizeSandboxControlOutput(manifest, result.stderr)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function writeSandboxControlResultEvidence(
|
|
133
|
+
manifest: SandboxControlManifest,
|
|
134
|
+
requestId: string,
|
|
135
|
+
result: Readonly<{ exitCode: number; stdout: string; stderr: string }>
|
|
136
|
+
): SandboxControlResultEvidence {
|
|
137
|
+
const normalized = sanitizeSandboxControlResult(manifest, result);
|
|
138
|
+
const stdout = normalized.stdout;
|
|
139
|
+
const stderr = normalized.stderr;
|
|
140
|
+
if (!/^[a-f0-9-]{16,64}$/u.test(requestId)
|
|
141
|
+
|| !Number.isSafeInteger(result.exitCode)
|
|
142
|
+
|| Buffer.byteLength(stdout, 'utf8') + Buffer.byteLength(stderr, 'utf8') > SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
|
|
143
|
+
throw new Error('SANDBOX_CONTROL_RESULT_EVIDENCE_INVALID');
|
|
144
|
+
}
|
|
145
|
+
const evidence: SandboxControlResultEvidence = {
|
|
146
|
+
version: 1,
|
|
147
|
+
id: requestId,
|
|
148
|
+
generation: manifest.generation,
|
|
149
|
+
exitCode: result.exitCode,
|
|
150
|
+
stdoutBytes: Buffer.byteLength(stdout, 'utf8'),
|
|
151
|
+
stderrBytes: Buffer.byteLength(stderr, 'utf8'),
|
|
152
|
+
stdoutSha256: createHash('sha256').update(stdout, 'utf8').digest('hex'),
|
|
153
|
+
stderrSha256: createHash('sha256').update(stderr, 'utf8').digest('hex'),
|
|
154
|
+
captureState: 'metadata-only'
|
|
155
|
+
};
|
|
156
|
+
if (sandboxControlEncodedJsonBytes(evidence) > SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
|
|
157
|
+
throw new Error('SANDBOX_CONTROL_RESULT_EVIDENCE_TOO_LARGE');
|
|
158
|
+
}
|
|
159
|
+
atomicCreateJson(
|
|
160
|
+
resultEvidencePath(manifest, requestId), evidence,
|
|
161
|
+
'SANDBOX_CONTROL_RESULT_EVIDENCE_ALREADY_EXISTS'
|
|
162
|
+
);
|
|
163
|
+
return readSandboxControlResultEvidence(resultEvidencePath(manifest, requestId));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function writeSandboxControlPayload(
|
|
167
|
+
manifest: SandboxControlManifest,
|
|
168
|
+
requestId: string,
|
|
169
|
+
result: Readonly<{ stdout: string; stderr: string }>
|
|
170
|
+
): SandboxControlPayload {
|
|
171
|
+
const payload = createSandboxControlPayload(manifest, requestId, result);
|
|
172
|
+
atomicCreateJson(payloadPath(manifest, requestId), payload, 'SANDBOX_CONTROL_PAYLOAD_ALREADY_EXISTS');
|
|
173
|
+
return readSandboxControlPayload(payloadPath(manifest, requestId));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function createSandboxControlPayload(
|
|
177
|
+
manifest: SandboxControlManifest,
|
|
178
|
+
requestId: string,
|
|
179
|
+
result: Readonly<{ stdout: string; stderr: string }>
|
|
180
|
+
): SandboxControlPayload {
|
|
181
|
+
const stdout = sanitizeSandboxControlOutput(manifest, result.stdout);
|
|
182
|
+
const stderr = sanitizeSandboxControlOutput(manifest, result.stderr);
|
|
183
|
+
const payload: SandboxControlPayload = {
|
|
184
|
+
version: 1,
|
|
185
|
+
id: requestId,
|
|
186
|
+
generation: manifest.generation,
|
|
187
|
+
stdout,
|
|
188
|
+
stderr,
|
|
189
|
+
stdoutBytes: Buffer.byteLength(stdout, 'utf8'),
|
|
190
|
+
stderrBytes: Buffer.byteLength(stderr, 'utf8'),
|
|
191
|
+
stdoutSha256: createHash('sha256').update(stdout, 'utf8').digest('hex'),
|
|
192
|
+
stderrSha256: createHash('sha256').update(stderr, 'utf8').digest('hex')
|
|
193
|
+
};
|
|
194
|
+
if (payload.stdoutBytes + payload.stderrBytes > SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
|
|
195
|
+
throw new Error('SANDBOX_CONTROL_PAYLOAD_TOO_LARGE');
|
|
196
|
+
}
|
|
197
|
+
if (sandboxControlEncodedJsonBytes(payload) > SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
|
|
198
|
+
throw new Error('SANDBOX_CONTROL_PAYLOAD_TOO_LARGE');
|
|
199
|
+
}
|
|
200
|
+
return payload;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function readSandboxControlPayload(filePath: string): SandboxControlPayload {
|
|
204
|
+
assertRegularFile(filePath);
|
|
205
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
206
|
+
if (Buffer.byteLength(raw, 'utf8') > SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
|
|
207
|
+
throw new Error('SANDBOX_CONTROL_PAYLOAD_TOO_LARGE');
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
return parseSandboxControlPayload(JSON.parse(raw));
|
|
211
|
+
} catch {
|
|
212
|
+
throw new Error('SANDBOX_CONTROL_PAYLOAD_INVALID');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function writeSandboxControlReservation(
|
|
217
|
+
manifest: SandboxControlManifest,
|
|
218
|
+
requestId: string,
|
|
219
|
+
usage: Readonly<{ logicalRecords: number; bytes: number }>,
|
|
220
|
+
now = Date.now()
|
|
221
|
+
): SandboxControlReservation {
|
|
222
|
+
if (usage.logicalRecords >= SANDBOX_CONTROL_MAX_LOGICAL_RECORDS
|
|
223
|
+
|| usage.bytes + SANDBOX_CONTROL_RESERVATION_BYTES > SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
|
|
224
|
+
throw new Error('SANDBOX_CONTROL_CAPACITY_EXHAUSTED');
|
|
225
|
+
}
|
|
226
|
+
const reservation: SandboxControlReservation = {
|
|
227
|
+
version: 1,
|
|
228
|
+
id: requestId,
|
|
229
|
+
generation: manifest.generation,
|
|
230
|
+
logicalRecords: 1,
|
|
231
|
+
bytes: SANDBOX_CONTROL_RESERVATION_BYTES,
|
|
232
|
+
createdAt: now
|
|
233
|
+
};
|
|
234
|
+
atomicCreateJson(reservationPath(manifest, requestId), reservation, 'SANDBOX_CONTROL_RESERVATION_ALREADY_EXISTS');
|
|
235
|
+
return parseSandboxControlReservation(readJsonFile(reservationPath(manifest, requestId)));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function readSandboxControlReservation(filePath: string): SandboxControlReservation {
|
|
239
|
+
return parseSandboxControlReservation(readJsonFile(filePath));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function sandboxControlGenerationUsage(manifest: SandboxControlManifest): { logicalRecords: number; bytes: number } {
|
|
243
|
+
const records = new Map<string, { baseBytes: number; payloadBytes: number; logical: boolean }>();
|
|
244
|
+
const responsesDir = path.join(manifest.channelDir, 'responses');
|
|
245
|
+
if (fs.existsSync(responsesDir)) {
|
|
246
|
+
for (const entry of fs.readdirSync(responsesDir, { withFileTypes: true })) {
|
|
247
|
+
if (!entry.isFile()) continue;
|
|
248
|
+
const terminal = entry.name.match(/^([a-f0-9-]{16,64})\.json$/u);
|
|
249
|
+
const accepted = entry.name.match(/^([a-f0-9-]{16,64})\.accepted\.json$/u);
|
|
250
|
+
const payload = entry.name.match(/^([a-f0-9-]{16,64})\.payload\.json$/u);
|
|
251
|
+
if (!terminal && !accepted && !payload) continue;
|
|
252
|
+
const id = (terminal ?? accepted ?? payload)![1]!;
|
|
253
|
+
const previous = records.get(id) ?? { baseBytes: 0, payloadBytes: 0, logical: false };
|
|
254
|
+
const fileBytes = fs.statSync(path.join(responsesDir, entry.name)).size;
|
|
255
|
+
if (payload) previous.payloadBytes += fileBytes;
|
|
256
|
+
else previous.baseBytes += fileBytes;
|
|
257
|
+
previous.logical ||= Boolean(terminal || accepted);
|
|
258
|
+
records.set(id, previous);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (fs.existsSync(manifest.processingDir)) {
|
|
262
|
+
for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
|
|
263
|
+
if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/u.test(entry.name)) continue;
|
|
264
|
+
const resultPath = resultEvidencePath(manifest, entry.name);
|
|
265
|
+
const filePath = reservationPath(manifest, entry.name);
|
|
266
|
+
const previous = records.get(entry.name) ?? { baseBytes: 0, payloadBytes: 0, logical: false };
|
|
267
|
+
if (fs.existsSync(resultPath)) {
|
|
268
|
+
const result = readSandboxControlResultEvidence(resultPath);
|
|
269
|
+
if (result.id !== entry.name || result.generation !== manifest.generation) {
|
|
270
|
+
throw new Error('SANDBOX_CONTROL_RESULT_EVIDENCE_INVALID');
|
|
271
|
+
}
|
|
272
|
+
previous.baseBytes += fs.statSync(resultPath).size;
|
|
273
|
+
previous.logical = true;
|
|
274
|
+
}
|
|
275
|
+
if (fs.existsSync(filePath)) {
|
|
276
|
+
const reservation = readSandboxControlReservation(filePath);
|
|
277
|
+
previous.baseBytes = Math.max(previous.baseBytes, reservation.bytes);
|
|
278
|
+
previous.logical = true;
|
|
279
|
+
}
|
|
280
|
+
records.set(entry.name, previous);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
logicalRecords: [...records.values()].filter((record) => record.logical).length,
|
|
285
|
+
bytes: [...records.values()].reduce((total, record) => total + record.baseBytes + record.payloadBytes, 0)
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
39
289
|
export function statusPath(manifest: SandboxControlManifest): string {
|
|
40
290
|
return path.join(manifest.publicStatusDir, 'status.json');
|
|
41
291
|
}
|
package/lib/server/protocol.ts
CHANGED
|
@@ -20,6 +20,27 @@ function taskSubcommand(subcommand: string): string {
|
|
|
20
20
|
return subcommand === 'list' ? 'ls' : subcommand;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function splitRunArguments(args: readonly string[]): { hostArgs: string[]; skillArgs: string[] } {
|
|
24
|
+
const hostArgs: string[] = [];
|
|
25
|
+
const skillArgs: string[] = [];
|
|
26
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
27
|
+
const arg = args[i] as string;
|
|
28
|
+
if (arg === '--tui') {
|
|
29
|
+
hostArgs.push(arg);
|
|
30
|
+
const value = args[i + 1];
|
|
31
|
+
if (value !== undefined) {
|
|
32
|
+
hostArgs.push(value);
|
|
33
|
+
i += 1;
|
|
34
|
+
}
|
|
35
|
+
} else if (arg === '--recreate') {
|
|
36
|
+
hostArgs.push(arg);
|
|
37
|
+
} else {
|
|
38
|
+
skillArgs.push(arg);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { hostArgs, skillArgs };
|
|
42
|
+
}
|
|
43
|
+
|
|
23
44
|
export function commandHelp(): string {
|
|
24
45
|
return [
|
|
25
46
|
`agent-infra ${VERSION}`,
|
|
@@ -88,7 +109,21 @@ export function parseCommand(text: string): CommandPlan {
|
|
|
88
109
|
if (command === '/run') {
|
|
89
110
|
const spec = getSkillRunSpec(subcommand);
|
|
90
111
|
if (!spec) return { kind: 'error', message: `Unknown skill: ${subcommand}` };
|
|
91
|
-
|
|
112
|
+
if (spec.kind === 'create') {
|
|
113
|
+
return { kind: 'ai', role: spec.role, argv: ['run', '--skill', subcommand, ...rest] };
|
|
114
|
+
}
|
|
115
|
+
const [taskRef, ...skillArgs] = rest;
|
|
116
|
+
if (!taskRef) return { kind: 'error', message: '/run task skills require a task-ref' };
|
|
117
|
+
const { hostArgs, skillArgs: forwardedSkillArgs } = splitRunArguments(skillArgs);
|
|
118
|
+
return {
|
|
119
|
+
kind: 'ai',
|
|
120
|
+
role: spec.role,
|
|
121
|
+
argv: [
|
|
122
|
+
'run', '--skill', subcommand, '--task', taskRef,
|
|
123
|
+
...hostArgs,
|
|
124
|
+
...(forwardedSkillArgs.length > 0 ? ['--', ...forwardedSkillArgs] : [])
|
|
125
|
+
]
|
|
126
|
+
};
|
|
92
127
|
}
|
|
93
128
|
|
|
94
129
|
return { kind: 'error', message: `Unknown command: ${command}` };
|
package/lib/task/activity-log.ts
CHANGED
|
@@ -5,6 +5,7 @@ const STARTED_SUFFIX_RE = /\s*\[started\]\s*$/;
|
|
|
5
5
|
const ABORTED_SUFFIX_RE = /\s*\[aborted\]\s*$/;
|
|
6
6
|
const ATTEMPT_RE = /(?:^|;\s*)attempt=([A-Za-z0-9-]{8,64})(?:;|$)/;
|
|
7
7
|
const COMMIT_STARTED_RE = /^started; attempt=([A-Za-z0-9-]{8,64}); baseline=([a-f0-9]{40,64}); agent=([a-z-]+)$/;
|
|
8
|
+
const LIFECYCLE_STEP_RE = /^(?:Complete Manual Validation|(?:Analyze Task|Review Analysis|Plan Task|Review Plan|Code Task|Review Code|Run Manual Validation) \(Round [1-9]\d*(?:(?:, fix for review-code(?:-r[2-9]|-r[1-9]\d+)?\.md)|(?:, decision II-[1-9]\d*))?\))$/;
|
|
8
9
|
|
|
9
10
|
type LogEntry = { time: string; step: string; agent: string; note: string };
|
|
10
11
|
type StepRow = { step: string; agent: string; started: string; done: string; note: string; attempt?: string };
|
|
@@ -93,9 +94,16 @@ function startedBackedRows(rows: StepRow[]): StepRow[] {
|
|
|
93
94
|
return rows.filter((row) => row.started !== '');
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
function hasOpenLifecycleExecution(content: string): boolean {
|
|
98
|
+
const section = locateActivityLog(content);
|
|
99
|
+
if (!section) return false;
|
|
100
|
+
return pairEntries(section.entries).some((row) => row.done === '' && LIFECYCLE_STEP_RE.test(row.step));
|
|
101
|
+
}
|
|
102
|
+
|
|
96
103
|
export {
|
|
97
104
|
activityAttempt,
|
|
98
105
|
appendActivityEntry,
|
|
106
|
+
hasOpenLifecycleExecution,
|
|
99
107
|
commitAttemptStartedNote,
|
|
100
108
|
locateActivityLog,
|
|
101
109
|
pairEntries,
|
|
@@ -8,6 +8,8 @@ import { parseImplementationInputs, selectPendingImplementationInput } from './i
|
|
|
8
8
|
import { parseVerdict } from './review-artifacts.ts';
|
|
9
9
|
import { extractSection, findSectionHeading } from './sections.ts';
|
|
10
10
|
import { receiptForOutput, sha256File } from './artifact-receipts.ts';
|
|
11
|
+
import { isArtifactInvalidated, parseInvalidationDocument } from './invalidation.ts';
|
|
12
|
+
import type { InvalidationDocument } from './invalidation.ts';
|
|
11
13
|
|
|
12
14
|
const artifactFamilyCatalog = [
|
|
13
15
|
{ family: 'analysis', sectionAliases: ['分析', 'Analysis'], heading: '分析', labels: ['需求分析报告', 'Requirements Analysis'] },
|
|
@@ -47,7 +49,7 @@ type ArtifactErrorCode =
|
|
|
47
49
|
| 'ARTIFACT_REFERENCE_INVALID' | 'ARTIFACT_PATH_INVALID'
|
|
48
50
|
| 'ARTIFACT_IDENTITY_INVALID' | 'ARTIFACT_NOT_FOUND'
|
|
49
51
|
| 'ARTIFACT_NOT_REGULAR' | 'ARTIFACT_NOT_READABLE' | 'ARTIFACT_VERDICT_INVALID'
|
|
50
|
-
| 'ARTIFACT_MODE_REFUSED';
|
|
52
|
+
| 'ARTIFACT_MODE_REFUSED' | 'ARTIFACT_INVALIDATION_INVALID';
|
|
51
53
|
type ArtifactError = { code: ArtifactErrorCode; message: string };
|
|
52
54
|
type ArtifactInventoryResult = {
|
|
53
55
|
status: 'ready' | 'failed';
|
|
@@ -154,7 +156,23 @@ function inspectArtifactDirectory(
|
|
|
154
156
|
taskId: identity.taskId ?? null, taskDir, taskState: identity.taskState ?? null
|
|
155
157
|
});
|
|
156
158
|
}
|
|
159
|
+
let invalidation: InvalidationDocument = { operations: [], targets: [] };
|
|
160
|
+
try {
|
|
161
|
+
const taskContent = fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8');
|
|
162
|
+
const parsed = parseInvalidationDocument(taskContent);
|
|
163
|
+
if (!parsed.ok) {
|
|
164
|
+
return failure(requestRef, family, { code: 'ARTIFACT_INVALIDATION_INVALID', message: parsed.message }, {
|
|
165
|
+
taskId: identity.taskId ?? null, taskDir, taskState: identity.taskState ?? null
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
invalidation = parsed.document;
|
|
169
|
+
} catch (error) {
|
|
170
|
+
return failure(requestRef, family, { code: 'ARTIFACT_INVALIDATION_INVALID', message: String(error) }, {
|
|
171
|
+
taskId: identity.taskId ?? null, taskDir, taskState: identity.taskState ?? null
|
|
172
|
+
});
|
|
173
|
+
}
|
|
157
174
|
const artifacts: ArtifactIdentity[] = [];
|
|
175
|
+
const knownArtifacts: ArtifactIdentity[] = [];
|
|
158
176
|
const diagnostics: ArtifactDiagnostic[] = [];
|
|
159
177
|
for (const entry of entries) {
|
|
160
178
|
const parsed = parseArtifactName(entry.name);
|
|
@@ -184,17 +202,20 @@ function inspectArtifactDirectory(
|
|
|
184
202
|
diagnostics.push(diagnostic('FILESYSTEM_RACE', family, entry.name, `artifact is not readable: ${entry.name}`));
|
|
185
203
|
continue;
|
|
186
204
|
}
|
|
187
|
-
|
|
205
|
+
const identity = { ...parsed, path: abs, size: stat.size, mtimeMs: stat.mtimeMs };
|
|
206
|
+
knownArtifacts.push(identity);
|
|
207
|
+
if (!isArtifactInvalidated(invalidation, family, entry.name)) artifacts.push(identity);
|
|
188
208
|
} catch (error) {
|
|
189
209
|
diagnostics.push(diagnostic('FILESYSTEM_RACE', family, entry.name, String(error)));
|
|
190
210
|
}
|
|
191
211
|
}
|
|
192
212
|
artifacts.sort((left, right) => left.round - right.round);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
213
|
+
knownArtifacts.sort((left, right) => left.round - right.round);
|
|
214
|
+
const knownRounds = new Set(knownArtifacts.map((item) => item.round));
|
|
215
|
+
if (knownArtifacts.length > 0 && !knownRounds.has(1)) diagnostics.push(diagnostic('MISSING_BASE', family, null, `${family}.md is missing`));
|
|
216
|
+
const max = knownArtifacts.at(-1)?.round ?? 0;
|
|
196
217
|
for (let round = 1; round <= max; round += 1) {
|
|
197
|
-
if (!
|
|
218
|
+
if (!knownRounds.has(round)) diagnostics.push(diagnostic('ROUND_GAP', family, null, `${artifactName(family, round)} is missing`));
|
|
198
219
|
}
|
|
199
220
|
const latest = artifacts.at(-1) ?? null;
|
|
200
221
|
const reviewedInput = family.startsWith('review-') && latest
|
|
@@ -230,6 +251,11 @@ function resolveReviewedInput(
|
|
|
230
251
|
const stat = fs.lstatSync(abs);
|
|
231
252
|
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error('referenced input is not a regular file');
|
|
232
253
|
const taskContent = fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8');
|
|
254
|
+
const invalidation = parseInvalidationDocument(taskContent);
|
|
255
|
+
if (!invalidation.ok) throw new Error(invalidation.message);
|
|
256
|
+
if (isArtifactInvalidated(invalidation.document, expectedFamily, parsed.name)) {
|
|
257
|
+
throw new Error(`${parsed.name} is invalidated`);
|
|
258
|
+
}
|
|
233
259
|
const receipt = receiptForOutput(taskContent, review.name);
|
|
234
260
|
if (!receipt) throw new Error(`receipt for ${review.name} is missing`);
|
|
235
261
|
const expectedEvent = reviewEventName(review.family);
|