@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
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { verifyLifecycleBuildIdentity } from './build-identity.ts';
|
|
6
|
+
import type { LifecycleBuildIdentity } from './build-identity.ts';
|
|
7
|
+
import { resolveAgentRuntimeStoreRoot } from '../../../runtime/agent-runtime.ts';
|
|
8
|
+
|
|
9
|
+
type CapabilityStatus = 'armed' | 'attested' | 'consumed' | 'expired';
|
|
10
|
+
type CodexControllerBinding = Readonly<{
|
|
11
|
+
instanceDigest: string;
|
|
12
|
+
controlGeneration: string;
|
|
13
|
+
}>;
|
|
14
|
+
type CodexCapabilityRecord = Readonly<{
|
|
15
|
+
schemaVersion: 1;
|
|
16
|
+
revision: number;
|
|
17
|
+
tokenDigest: string;
|
|
18
|
+
taskId: string;
|
|
19
|
+
status: CapabilityStatus;
|
|
20
|
+
buildIdentity: LifecycleBuildIdentity;
|
|
21
|
+
controller: CodexControllerBinding | null;
|
|
22
|
+
sessionId: string | null;
|
|
23
|
+
turnId: string | null;
|
|
24
|
+
toolUseId: string | null;
|
|
25
|
+
hookDefinitionHash: string | null;
|
|
26
|
+
armedAt: number;
|
|
27
|
+
expiresAt: number;
|
|
28
|
+
attestedAt: number | null;
|
|
29
|
+
consumedAt: number | null;
|
|
30
|
+
}>;
|
|
31
|
+
|
|
32
|
+
type CodexCapabilityStoreOptions = Readonly<{
|
|
33
|
+
root?: string;
|
|
34
|
+
now?: () => number;
|
|
35
|
+
token?: () => string;
|
|
36
|
+
ttlMs?: number;
|
|
37
|
+
tombstoneMs?: number;
|
|
38
|
+
maxRecords?: number;
|
|
39
|
+
beforeCompareAndSwap?: (input: Readonly<{ path: string; expectedRevision: number }>) => void;
|
|
40
|
+
}>;
|
|
41
|
+
|
|
42
|
+
const DEFAULT_TTL_MS = 30_000;
|
|
43
|
+
const DEFAULT_TOMBSTONE_MS = 86_400_000;
|
|
44
|
+
const DEFAULT_MAX_RECORDS = 128;
|
|
45
|
+
const LOCK_RETRY_MS = 10;
|
|
46
|
+
const LOCK_TIMEOUT_MS = 1_000;
|
|
47
|
+
const LOCK_STALE_MS = 30_000;
|
|
48
|
+
|
|
49
|
+
function digest(value: string): string {
|
|
50
|
+
return crypto.createHash('sha256').update(value).digest('hex');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function capabilityError(code: string, message: string): Error {
|
|
54
|
+
const error = new Error(`${code}: ${message}`);
|
|
55
|
+
error.name = code;
|
|
56
|
+
return error;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function sameController(
|
|
60
|
+
left: CodexControllerBinding | null,
|
|
61
|
+
right: CodexControllerBinding | null
|
|
62
|
+
): boolean {
|
|
63
|
+
return left?.instanceDigest === right?.instanceDigest
|
|
64
|
+
&& left?.controlGeneration === right?.controlGeneration;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createCodexCapabilityStore(options: CodexCapabilityStoreOptions = {}) {
|
|
68
|
+
const root = options.root
|
|
69
|
+
?? resolveAgentRuntimeStoreRoot({ store: 'capabilities' });
|
|
70
|
+
const now = options.now ?? Date.now;
|
|
71
|
+
const createToken = options.token ?? (() => crypto.randomBytes(32).toString('base64url'));
|
|
72
|
+
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
73
|
+
const tombstoneMs = options.tombstoneMs ?? DEFAULT_TOMBSTONE_MS;
|
|
74
|
+
const maxRecords = options.maxRecords ?? DEFAULT_MAX_RECORDS;
|
|
75
|
+
fs.mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
76
|
+
fs.chmodSync(root, 0o700);
|
|
77
|
+
|
|
78
|
+
function fileFor(token: string): string {
|
|
79
|
+
return path.join(root, `${digest(token)}.json`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function read(file: string): CodexCapabilityRecord {
|
|
83
|
+
const value = JSON.parse(fs.readFileSync(file, 'utf8')) as CodexCapabilityRecord;
|
|
84
|
+
if (value.schemaVersion !== 1 || !Number.isSafeInteger(value.revision) || value.revision < 1) {
|
|
85
|
+
throw capabilityError('CODEX_CAPABILITY_STATE_INVALID', 'capability record schema is invalid');
|
|
86
|
+
}
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function withWriteLock<T>(operation: () => T): T {
|
|
91
|
+
const lock = path.join(root, '.write.lock');
|
|
92
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
93
|
+
let descriptor: number | null = null;
|
|
94
|
+
while (descriptor === null) {
|
|
95
|
+
try {
|
|
96
|
+
descriptor = fs.openSync(lock, 'wx', 0o600);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
99
|
+
try {
|
|
100
|
+
if (Date.now() - fs.statSync(lock).mtimeMs > LOCK_STALE_MS) {
|
|
101
|
+
fs.unlinkSync(lock);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
} catch (lockError) {
|
|
105
|
+
if ((lockError as NodeJS.ErrnoException).code === 'ENOENT') continue;
|
|
106
|
+
throw lockError;
|
|
107
|
+
}
|
|
108
|
+
if (Date.now() >= deadline) {
|
|
109
|
+
throw capabilityError('CODEX_CAPABILITY_STORE_BUSY', 'capability store is busy');
|
|
110
|
+
}
|
|
111
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, LOCK_RETRY_MS);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
return operation();
|
|
116
|
+
} finally {
|
|
117
|
+
fs.closeSync(descriptor);
|
|
118
|
+
fs.unlinkSync(lock);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function write(file: string, value: CodexCapabilityRecord, expectedRevision: number): void {
|
|
123
|
+
const temporary = `${file}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
124
|
+
fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, {
|
|
125
|
+
encoding: 'utf8',
|
|
126
|
+
flag: 'wx',
|
|
127
|
+
mode: 0o600
|
|
128
|
+
});
|
|
129
|
+
try {
|
|
130
|
+
withWriteLock(() => {
|
|
131
|
+
options.beforeCompareAndSwap?.({ path: file, expectedRevision });
|
|
132
|
+
const actualRevision = fs.existsSync(file) ? read(file).revision : 0;
|
|
133
|
+
if (actualRevision !== expectedRevision) {
|
|
134
|
+
throw capabilityError(
|
|
135
|
+
'CODEX_CAPABILITY_REVISION_CONFLICT',
|
|
136
|
+
`capability revision changed from ${expectedRevision} to ${actualRevision}`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
fs.renameSync(temporary, file);
|
|
140
|
+
fs.chmodSync(file, 0o600);
|
|
141
|
+
});
|
|
142
|
+
} finally {
|
|
143
|
+
if (fs.existsSync(temporary)) fs.unlinkSync(temporary);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function files(): string[] {
|
|
148
|
+
return fs.readdirSync(root)
|
|
149
|
+
.filter((name) => /^[a-f0-9]{64}\.json$/u.test(name))
|
|
150
|
+
.map((name) => path.join(root, name));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function expire(record: CodexCapabilityRecord, file: string, timestamp: number): CodexCapabilityRecord {
|
|
154
|
+
if ((record.status === 'armed' || record.status === 'attested') && timestamp > record.expiresAt) {
|
|
155
|
+
const expired = Object.freeze({
|
|
156
|
+
...record,
|
|
157
|
+
revision: record.revision + 1,
|
|
158
|
+
status: 'expired' as const
|
|
159
|
+
});
|
|
160
|
+
write(file, expired, record.revision);
|
|
161
|
+
return expired;
|
|
162
|
+
}
|
|
163
|
+
return record;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function sweep(): void {
|
|
167
|
+
const timestamp = now();
|
|
168
|
+
const records = files().map((file) => ({ file, record: expire(read(file), file, timestamp) }));
|
|
169
|
+
for (const entry of records) {
|
|
170
|
+
const terminalAt = entry.record.consumedAt ?? entry.record.expiresAt;
|
|
171
|
+
if (['consumed', 'expired'].includes(entry.record.status)
|
|
172
|
+
&& timestamp - terminalAt > tombstoneMs) {
|
|
173
|
+
fs.unlinkSync(entry.file);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const remaining = files().map((file) => ({ file, record: read(file) }));
|
|
177
|
+
if (remaining.length <= maxRecords) return;
|
|
178
|
+
const removable = remaining
|
|
179
|
+
.filter(({ record }) => ['consumed', 'expired'].includes(record.status))
|
|
180
|
+
.sort((left, right) =>
|
|
181
|
+
(left.record.consumedAt ?? left.record.expiresAt)
|
|
182
|
+
- (right.record.consumedAt ?? right.record.expiresAt));
|
|
183
|
+
while (files().length > maxRecords && removable.length > 0) {
|
|
184
|
+
fs.unlinkSync(removable.shift()!.file);
|
|
185
|
+
}
|
|
186
|
+
if (files().length > maxRecords) {
|
|
187
|
+
throw capabilityError('CODEX_CAPABILITY_CAPACITY_EXCEEDED', 'capability store has too many active records');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function arm(input: Readonly<{
|
|
192
|
+
taskId: string;
|
|
193
|
+
buildIdentity: LifecycleBuildIdentity;
|
|
194
|
+
controller?: CodexControllerBinding;
|
|
195
|
+
}>): CodexCapabilityRecord & Readonly<{ token: string; path: string; marker: string }> {
|
|
196
|
+
sweep();
|
|
197
|
+
const token = createToken();
|
|
198
|
+
const file = fileFor(token);
|
|
199
|
+
if (fs.existsSync(file)) throw capabilityError('CODEX_CAPABILITY_TOKEN_COLLISION', 'capability token already exists');
|
|
200
|
+
const timestamp = now();
|
|
201
|
+
const record: CodexCapabilityRecord = Object.freeze({
|
|
202
|
+
schemaVersion: 1,
|
|
203
|
+
revision: 1,
|
|
204
|
+
tokenDigest: digest(token),
|
|
205
|
+
taskId: input.taskId,
|
|
206
|
+
status: 'armed',
|
|
207
|
+
buildIdentity: input.buildIdentity,
|
|
208
|
+
controller: input.controller ?? null,
|
|
209
|
+
sessionId: null,
|
|
210
|
+
turnId: null,
|
|
211
|
+
toolUseId: null,
|
|
212
|
+
hookDefinitionHash: null,
|
|
213
|
+
armedAt: timestamp,
|
|
214
|
+
expiresAt: timestamp + ttlMs,
|
|
215
|
+
attestedAt: null,
|
|
216
|
+
consumedAt: null
|
|
217
|
+
});
|
|
218
|
+
write(file, record, 0);
|
|
219
|
+
return Object.freeze({
|
|
220
|
+
...record,
|
|
221
|
+
token,
|
|
222
|
+
path: file,
|
|
223
|
+
marker: `agent-infra-codex-capability:${token}`
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function loadActive(token: string): Readonly<{ file: string; record: CodexCapabilityRecord }> {
|
|
228
|
+
const file = fileFor(token);
|
|
229
|
+
if (!fs.existsSync(file)) {
|
|
230
|
+
throw capabilityError('CODEX_CAPABILITY_MISSING', 'capability token was not found');
|
|
231
|
+
}
|
|
232
|
+
const record = expire(read(file), file, now());
|
|
233
|
+
if (record.status === 'expired') {
|
|
234
|
+
throw capabilityError('CODEX_CAPABILITY_EXPIRED', 'capability token expired');
|
|
235
|
+
}
|
|
236
|
+
return { file, record };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function attest(input: Readonly<{
|
|
240
|
+
token: string;
|
|
241
|
+
sessionId: string;
|
|
242
|
+
turnId: string;
|
|
243
|
+
toolUseId: string;
|
|
244
|
+
hookDefinitionHash: string;
|
|
245
|
+
buildIdentity: LifecycleBuildIdentity;
|
|
246
|
+
controller?: CodexControllerBinding;
|
|
247
|
+
}>): CodexCapabilityRecord {
|
|
248
|
+
const { file, record } = loadActive(input.token);
|
|
249
|
+
if (record.status !== 'armed') {
|
|
250
|
+
throw capabilityError(
|
|
251
|
+
record.status === 'consumed' ? 'CODEX_CAPABILITY_REPLAY' : 'CODEX_CAPABILITY_STATE_INVALID',
|
|
252
|
+
`capability is ${record.status}, expected armed`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
const identity = verifyLifecycleBuildIdentity(record.buildIdentity, input.buildIdentity);
|
|
256
|
+
if (!identity.ok || !sameController(record.controller, input.controller ?? null)) {
|
|
257
|
+
throw capabilityError('CODEX_CAPABILITY_PROVENANCE_MISMATCH', 'capability build or controller identity does not match');
|
|
258
|
+
}
|
|
259
|
+
if (![input.sessionId, input.turnId, input.toolUseId, input.hookDefinitionHash]
|
|
260
|
+
.every((value) => value.trim().length > 0)) {
|
|
261
|
+
throw capabilityError('CODEX_CAPABILITY_IDENTITY_INVALID', 'hook identity is incomplete');
|
|
262
|
+
}
|
|
263
|
+
const timestamp = now();
|
|
264
|
+
const next: CodexCapabilityRecord = Object.freeze({
|
|
265
|
+
...record,
|
|
266
|
+
revision: record.revision + 1,
|
|
267
|
+
status: 'attested',
|
|
268
|
+
sessionId: input.sessionId,
|
|
269
|
+
turnId: input.turnId,
|
|
270
|
+
toolUseId: input.toolUseId,
|
|
271
|
+
hookDefinitionHash: input.hookDefinitionHash,
|
|
272
|
+
attestedAt: timestamp
|
|
273
|
+
});
|
|
274
|
+
write(file, next, record.revision);
|
|
275
|
+
return next;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function consume(token: string, expected: Readonly<{
|
|
279
|
+
taskId: string;
|
|
280
|
+
hookDefinitionHash: string;
|
|
281
|
+
buildIdentity: LifecycleBuildIdentity;
|
|
282
|
+
controller?: CodexControllerBinding;
|
|
283
|
+
}>): CodexCapabilityRecord {
|
|
284
|
+
sweep();
|
|
285
|
+
const { file, record } = loadActive(token);
|
|
286
|
+
if (record.status === 'consumed') {
|
|
287
|
+
throw capabilityError('CODEX_CAPABILITY_REPLAY', 'capability token was already consumed');
|
|
288
|
+
}
|
|
289
|
+
if (record.status !== 'attested') {
|
|
290
|
+
throw capabilityError('CODEX_CAPABILITY_NOT_ATTESTED', 'capability token has no current-session hook attestation');
|
|
291
|
+
}
|
|
292
|
+
const identity = verifyLifecycleBuildIdentity(record.buildIdentity, expected.buildIdentity);
|
|
293
|
+
if (!identity.ok
|
|
294
|
+
|| record.taskId !== expected.taskId
|
|
295
|
+
|| record.hookDefinitionHash !== expected.hookDefinitionHash
|
|
296
|
+
|| !sameController(record.controller, expected.controller ?? null)) {
|
|
297
|
+
throw capabilityError('CODEX_CAPABILITY_PROVENANCE_MISMATCH', 'capability consumption identity does not match');
|
|
298
|
+
}
|
|
299
|
+
const next: CodexCapabilityRecord = Object.freeze({
|
|
300
|
+
...record,
|
|
301
|
+
revision: record.revision + 1,
|
|
302
|
+
status: 'consumed',
|
|
303
|
+
consumedAt: now()
|
|
304
|
+
});
|
|
305
|
+
write(file, next, record.revision);
|
|
306
|
+
return next;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function inspect(token: string): CodexCapabilityRecord {
|
|
310
|
+
return loadActive(token).record;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return Object.freeze({ arm, attest, consume, inspect, sweep });
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export { createCodexCapabilityStore };
|
|
317
|
+
export type {
|
|
318
|
+
CapabilityStatus,
|
|
319
|
+
CodexCapabilityRecord,
|
|
320
|
+
CodexCapabilityStoreOptions,
|
|
321
|
+
CodexControllerBinding
|
|
322
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"executableFiles": [
|
|
3
|
+
"bin/internal-cli.ts",
|
|
4
|
+
"lib/internal/codex-lifecycle.ts",
|
|
5
|
+
"lib/internal/codex-sandbox-controller.ts",
|
|
6
|
+
"lib/internal/task-orchestration.ts",
|
|
7
|
+
"lib/task/codex-orchestration.ts",
|
|
8
|
+
"lib/task/delegation-receipts.ts",
|
|
9
|
+
"lib/task/orchestration.ts",
|
|
10
|
+
"lib/agent-clients/adapters/codex-lifecycle/build-identity.ts",
|
|
11
|
+
"lib/agent-clients/adapters/codex-lifecycle/capability-store.ts",
|
|
12
|
+
"lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts",
|
|
13
|
+
"lib/agent-clients/adapters/codex-lifecycle/app-server.ts",
|
|
14
|
+
"lib/agent-clients/adapters/codex-lifecycle/evidence.ts",
|
|
15
|
+
"lib/agent-clients/adapters/codex-lifecycle/store.ts",
|
|
16
|
+
"lib/agent-clients/adapters/codex-lifecycle/manifest-files.json"
|
|
17
|
+
],
|
|
18
|
+
"contractFiles": [
|
|
19
|
+
".codex/hooks.json",
|
|
20
|
+
".codex/agents/agent-infra-lifecycle-executor.toml",
|
|
21
|
+
".codex/agents/agent-infra-lifecycle-reviewer.toml",
|
|
22
|
+
".agents/hooks/lifecycle-delegation.js",
|
|
23
|
+
".agents/skills/run-task/SKILL.md",
|
|
24
|
+
".agents/rules/lifecycle-orchestration.md"
|
|
25
|
+
]
|
|
26
|
+
}
|