@ours.network/fleet 1.2.0-nightly.12 → 1.2.0-nightly.14
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 +2 -0
- package/dist/application/model-catalog.d.ts +1 -1
- package/dist/application/model-catalog.js +5 -2
- package/dist/application/task-room-service.d.ts +3 -0
- package/dist/application/task-room-service.js +1 -1
- package/dist/atomic-file.d.ts +5 -0
- package/dist/atomic-file.js +62 -1
- package/dist/build-info.json +4 -4
- package/dist/erased-resources.d.ts +9 -0
- package/dist/erased-resources.js +87 -0
- package/dist/fleet-command-audit.d.ts +1 -0
- package/dist/fleet-command-audit.js +9 -5
- package/dist/init-wizard.js +10 -3
- package/dist/owner-channel/lifecycle-outbox.js +4 -3
- package/dist/rooms-tasks/close.d.ts +2 -2
- package/dist/rooms-tasks/close.js +128 -22
- package/dist/rooms-tasks/deletion.d.ts +1 -1
- package/dist/rooms-tasks/deletion.js +41 -9
- package/dist/rooms-tasks/erasure.d.ts +6 -0
- package/dist/rooms-tasks/erasure.js +169 -0
- package/dist/rooms-tasks/room-state.d.ts +3 -1
- package/dist/rooms-tasks/room-state.js +37 -7
- package/dist/rooms-tasks/task-state.d.ts +10 -4
- package/dist/rooms-tasks/task-state.js +54 -12
- package/dist/rooms-tasks/types.d.ts +2 -0
- package/dist/temp-lifecycle.d.ts +5 -0
- package/dist/temp-lifecycle.js +28 -12
- package/docs/task-workspaces.md +34 -2
- package/package.json +1 -1
- package/presets/README.md +66 -0
- package/presets/brain-catalog.json +7 -2
- package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
- package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
- package/presets/manifest.json +2 -2
package/README.md
CHANGED
|
@@ -1767,3 +1767,5 @@ A `task_not_ready` result distinguishes confirmed degradation from unknown healt
|
|
|
1767
1767
|
and member status and choose supported recovery. These checks do not restart agents,
|
|
1768
1768
|
recreate identities, redeem invites, or rewrite lifecycle state. Temporary room agents
|
|
1769
1769
|
remain transient across host reboot; retained launch records do not restore them.
|
|
1770
|
+
|
|
1771
|
+
See [predefined Brain models and runtime requirements](presets/README.md) for the verified model/effort matrix and regeneration instructions.
|
|
@@ -14,7 +14,7 @@ export interface HarnessModelCatalog {
|
|
|
14
14
|
export declare function codexModelCatalog(path?: string): HarnessModelCatalog;
|
|
15
15
|
/**
|
|
16
16
|
* Claude Code 2.1 has no typed model-list endpoint. These exact IDs are the
|
|
17
|
-
* versioned adapter contract
|
|
17
|
+
* versioned adapter contract verified with CLI 2.1.281, not aliases and
|
|
18
18
|
* not a claim about account entitlement (which Claude validates at launch).
|
|
19
19
|
*/
|
|
20
20
|
export declare function claudeModelCatalog(): HarnessModelCatalog;
|
|
@@ -41,17 +41,20 @@ export function codexModelCatalog(path = join(homedir(), '.codex', 'models_cache
|
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* Claude Code 2.1 has no typed model-list endpoint. These exact IDs are the
|
|
44
|
-
* versioned adapter contract
|
|
44
|
+
* versioned adapter contract verified with CLI 2.1.281, not aliases and
|
|
45
45
|
* not a claim about account entitlement (which Claude validates at launch).
|
|
46
46
|
*/
|
|
47
47
|
export function claudeModelCatalog() {
|
|
48
48
|
const reasoningEfforts = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
49
49
|
return {
|
|
50
50
|
models: [
|
|
51
|
+
{ id: 'claude-fable-5-1', label: 'Claude Fable 5.1', reasoningEfforts, source: 'claude-adapter-2.1' },
|
|
52
|
+
{ id: 'claude-opus-5-5', label: 'Claude Opus 5.5', reasoningEfforts, source: 'claude-adapter-2.1' },
|
|
53
|
+
{ id: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5', reasoningEfforts: [], source: 'claude-adapter-2.1' },
|
|
51
54
|
{ id: 'claude-fable-5', label: 'Claude Fable 5', reasoningEfforts, source: 'claude-adapter-2.1' },
|
|
52
55
|
{ id: 'claude-opus-5', label: 'Claude Opus 5', reasoningEfforts, source: 'claude-adapter-2.1' },
|
|
53
56
|
{ id: 'claude-sonnet-5', label: 'Claude Sonnet 5', reasoningEfforts, source: 'claude-adapter-2.1' },
|
|
54
57
|
],
|
|
55
|
-
warnings: ['Claude Code exposes no typed model-list endpoint; choices
|
|
58
|
+
warnings: ['Claude Code exposes no typed model-list endpoint; choices require a current Claude runtime (verified with 2.1.281); older bundled SDKs need CLAUDE_CODE_EXECUTABLE. Entitlement is validated at launch.'],
|
|
56
59
|
};
|
|
57
60
|
}
|
|
@@ -638,7 +638,7 @@ export class TaskRoomApplicationService {
|
|
|
638
638
|
createCoworkAdapter({ configPath: cfg.rooms.cowork?.config });
|
|
639
639
|
const room = getRoomRecord(input.roomId);
|
|
640
640
|
if (!room) {
|
|
641
|
-
|
|
641
|
+
return { room: undefined, settlementRequired: true };
|
|
642
642
|
}
|
|
643
643
|
return { room: await acceptManagedRoomClose(input.roomId), settlementRequired: true };
|
|
644
644
|
}
|
package/dist/atomic-file.d.ts
CHANGED
|
@@ -36,3 +36,8 @@ export declare function withFileLock<T>(lockPath: string, fn: () => T | Promise<
|
|
|
36
36
|
* intact.
|
|
37
37
|
*/
|
|
38
38
|
export declare function replaceFileAtomically(path: string, contents: string, mode?: number, deps?: WriteDeps): void;
|
|
39
|
+
/** Synchronous Lamport bakery lock: unique claims are never reused, so stale
|
|
40
|
+
* reclamation cannot remove a successor's lock. Atomic claim replacement keeps
|
|
41
|
+
* choosing/ticket reads complete. Dead claims can be removed by any contender.
|
|
42
|
+
*/
|
|
43
|
+
export declare function withSynchronousFileLock<T>(path: string, work: () => T): T;
|
package/dist/atomic-file.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { closeSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeFileSync, writeSync, } from 'node:fs';
|
|
1
|
+
import { closeSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, writeSync, } from 'node:fs';
|
|
2
2
|
import { basename, dirname, join } from 'node:path';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
const DEFAULT_STALE_MS = 10_000;
|
|
@@ -136,3 +136,64 @@ export function replaceFileAtomically(path, contents, mode = 0o600, deps = {}) {
|
|
|
136
136
|
}
|
|
137
137
|
catch { /* platform does not allow fsync on a directory */ }
|
|
138
138
|
}
|
|
139
|
+
/** Synchronous Lamport bakery lock: unique claims are never reused, so stale
|
|
140
|
+
* reclamation cannot remove a successor's lock. Atomic claim replacement keeps
|
|
141
|
+
* choosing/ticket reads complete. Dead claims can be removed by any contender.
|
|
142
|
+
*/
|
|
143
|
+
export function withSynchronousFileLock(path, work) {
|
|
144
|
+
mkdirSync(path, { recursive: true, mode: 0o700 });
|
|
145
|
+
const token = `${process.pid}-${randomUUID()}.json`;
|
|
146
|
+
const own = join(path, token);
|
|
147
|
+
const deadline = Date.now() + 10_000;
|
|
148
|
+
const claims = () => {
|
|
149
|
+
const result = [];
|
|
150
|
+
for (const name of readdirSync(path).filter(name => /^[0-9]+-[a-f0-9-]{36}\.json$/.test(name))) {
|
|
151
|
+
const file = join(path, name);
|
|
152
|
+
let value;
|
|
153
|
+
try {
|
|
154
|
+
value = JSON.parse(readFileSync(file, 'utf8'));
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
if (error.code === 'ENOENT')
|
|
158
|
+
continue;
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
if (!Number.isSafeInteger(value.pid) || value.pid <= 0 || !Number.isSafeInteger(value.ticket) || value.ticket < 0)
|
|
162
|
+
throw new Error('Invalid synchronous lock claim');
|
|
163
|
+
if (name !== token) {
|
|
164
|
+
try {
|
|
165
|
+
process.kill(value.pid, 0);
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
if (error.code === 'ESRCH') {
|
|
169
|
+
// This dead contender alone owned this UUID path. Never remove
|
|
170
|
+
// the namespace or a successor's differently named claim.
|
|
171
|
+
rmSync(file, { force: true });
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
result.push({ name, value });
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
};
|
|
180
|
+
replaceFileAtomically(own, JSON.stringify({ pid: process.pid, choosing: true, ticket: 0 }));
|
|
181
|
+
try {
|
|
182
|
+
const ticket = Math.max(0, ...claims().map(claim => claim.value.ticket)) + 1;
|
|
183
|
+
if (!Number.isSafeInteger(ticket))
|
|
184
|
+
throw new Error('Synchronous lock ticket overflow');
|
|
185
|
+
replaceFileAtomically(own, JSON.stringify({ pid: process.pid, choosing: false, ticket }));
|
|
186
|
+
for (;;) {
|
|
187
|
+
const preceding = claims().some(claim => claim.name !== token && (claim.value.choosing
|
|
188
|
+
|| claim.value.ticket < ticket || (claim.value.ticket === ticket && claim.name < token)));
|
|
189
|
+
if (!preceding)
|
|
190
|
+
return work();
|
|
191
|
+
if (Date.now() >= deadline)
|
|
192
|
+
throw new Error('Timed out acquiring synchronous journal lock');
|
|
193
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
rmSync(own, { force: true });
|
|
198
|
+
}
|
|
199
|
+
}
|
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.2.0-nightly.
|
|
3
|
-
"buildId": "
|
|
4
|
-
"commit": "
|
|
2
|
+
"version": "1.2.0-nightly.14",
|
|
3
|
+
"buildId": "b88d0d158cc7",
|
|
4
|
+
"commit": "717f6d8a09e6173b9b8a017fc5efb08d71927524",
|
|
5
5
|
"dirty": true,
|
|
6
|
-
"builtAt": "2026-09-
|
|
6
|
+
"builtAt": "2026-09-24T14:15:57.206Z",
|
|
7
7
|
"capabilities": [
|
|
8
8
|
"cowork.http-management-v1",
|
|
9
9
|
"monitor.interrupt.after_tool"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const erasedArg: (value: string) => string;
|
|
2
|
+
export declare function redactErasedContent<T>(value: T): T;
|
|
3
|
+
/** Every cooperative writer filters stale in-memory snapshots under the same lock as erasure. */
|
|
4
|
+
export declare function writePrivacyFilteredLedger(path: string, value: unknown): void;
|
|
5
|
+
/** Content-free resource IDs persist to prevent active writers resurrecting erased labels. */
|
|
6
|
+
export declare function eraseResourcePresentations(resources: ReadonlyArray<{
|
|
7
|
+
kind: 'task' | 'room' | 'agent';
|
|
8
|
+
id: string;
|
|
9
|
+
}>): void;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, lstatSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { replaceFileAtomically, withSynchronousFileLock } from './atomic-file.js';
|
|
5
|
+
import { stateRoot } from './paths.js';
|
|
6
|
+
const registryPath = () => join(stateRoot(), 'erased-resources.json');
|
|
7
|
+
const lockPath = () => join(stateRoot(), 'locks', 'audit-erasure');
|
|
8
|
+
const ledgerNames = ['.fleet-command-audit.json', '.owner-channel-command-audit.json', '.owner-channel-lifecycle-outbox.json'];
|
|
9
|
+
export const erasedArg = (value) => `[erased:${createHash('sha256').update(value).digest('hex')}]`;
|
|
10
|
+
function registry() {
|
|
11
|
+
if (!existsSync(registryPath()))
|
|
12
|
+
return new Set();
|
|
13
|
+
const value = JSON.parse(readFileSync(registryPath(), 'utf8'));
|
|
14
|
+
if (!Array.isArray(value) || value.some(v => typeof v !== 'string'))
|
|
15
|
+
throw new Error('Invalid erasure registry');
|
|
16
|
+
return new Set(value);
|
|
17
|
+
}
|
|
18
|
+
/** Preserve delivery/deduplication metadata while removing owned presentation content. */
|
|
19
|
+
function redact(value, erased) {
|
|
20
|
+
if (Array.isArray(value))
|
|
21
|
+
return value.map(item => redact(item, erased));
|
|
22
|
+
if (!value || typeof value !== 'object')
|
|
23
|
+
return value;
|
|
24
|
+
const next = { ...value };
|
|
25
|
+
const kind = value.kind === 'lifecycle_failure' ? value.resource?.toLowerCase() : value.kind === 'agent_started' ? 'agent' : value.kind;
|
|
26
|
+
if (typeof value.id === 'string' && erased.has(`${kind}:${value.id}`)) {
|
|
27
|
+
for (const key of ['title', 'reason', 'roomName', 'label', 'template', 'configuration', 'model', 'permissions'])
|
|
28
|
+
delete next[key];
|
|
29
|
+
if (kind === 'task')
|
|
30
|
+
next.agents = [];
|
|
31
|
+
if (kind === 'room') {
|
|
32
|
+
delete next.name;
|
|
33
|
+
next.participants = [];
|
|
34
|
+
}
|
|
35
|
+
if (kind === 'agent') {
|
|
36
|
+
next.name = 'erased';
|
|
37
|
+
next.brain = '[erased]';
|
|
38
|
+
next.role = '[erased]';
|
|
39
|
+
next.inherited = [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (typeof value.roomId === 'string' && erased.has(`room:${value.roomId}`)) {
|
|
43
|
+
delete next.roomName;
|
|
44
|
+
if (kind === 'task')
|
|
45
|
+
next.agents = [];
|
|
46
|
+
}
|
|
47
|
+
const resources = value.outcome?.resourceIds;
|
|
48
|
+
const ownsAttempt = resources && Object.entries(resources).some(([key, id]) => erased.has(`${key}:${id}`));
|
|
49
|
+
const ownsPresentation = value.outcome?.presentations?.some((p) => erased.has(`${p.kind === 'agent_started' ? 'agent' : p.kind}:${p.id}`));
|
|
50
|
+
if ((ownsAttempt || ownsPresentation) && Array.isArray(value.argv) && !value.erased) {
|
|
51
|
+
next.argv = value.argv.map(erasedArg);
|
|
52
|
+
next.erased = true;
|
|
53
|
+
}
|
|
54
|
+
for (const key of Object.keys(next))
|
|
55
|
+
if (typeof next[key] === 'object')
|
|
56
|
+
next[key] = redact(next[key], erased);
|
|
57
|
+
return next;
|
|
58
|
+
}
|
|
59
|
+
export function redactErasedContent(value) { return redact(value, registry()); }
|
|
60
|
+
/** Every cooperative writer filters stale in-memory snapshots under the same lock as erasure. */
|
|
61
|
+
export function writePrivacyFilteredLedger(path, value) {
|
|
62
|
+
withSynchronousFileLock(lockPath(), () => replaceFileAtomically(path, JSON.stringify(redact(value, registry())) + '\n'));
|
|
63
|
+
}
|
|
64
|
+
/** Content-free resource IDs persist to prevent active writers resurrecting erased labels. */
|
|
65
|
+
export function eraseResourcePresentations(resources) {
|
|
66
|
+
withSynchronousFileLock(lockPath(), () => {
|
|
67
|
+
const erased = registry();
|
|
68
|
+
for (const resource of resources)
|
|
69
|
+
erased.add(`${resource.kind}:${resource.id}`);
|
|
70
|
+
replaceFileAtomically(registryPath(), JSON.stringify([...erased]));
|
|
71
|
+
for (const root of [join(stateRoot(), 'agents'), join(stateRoot(), 'tmp'), join(stateRoot(), 'recovery', 'temporary')]) {
|
|
72
|
+
if (!existsSync(root))
|
|
73
|
+
continue;
|
|
74
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
75
|
+
if (!entry.isDirectory() || entry.isSymbolicLink())
|
|
76
|
+
continue;
|
|
77
|
+
for (const name of ledgerNames) {
|
|
78
|
+
const path = join(root, entry.name, name);
|
|
79
|
+
if (!existsSync(path) || !lstatSync(path).isFile() || lstatSync(path).isSymbolicLink())
|
|
80
|
+
continue;
|
|
81
|
+
const old = JSON.parse(readFileSync(path, 'utf8'));
|
|
82
|
+
replaceFileAtomically(path, JSON.stringify(redact(old, erased)) + '\n');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { erasedArg, redactErasedContent, writePrivacyFilteredLedger } from './erased-resources.js';
|
|
1
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
3
|
import { randomUUID } from 'node:crypto';
|
|
3
4
|
import { Buffer } from 'node:buffer';
|
|
4
|
-
import { replaceFileAtomically } from './atomic-file.js';
|
|
5
5
|
import { isSensitiveConfigKey } from './sensitive-config.js';
|
|
6
6
|
import { mandatoryConfigurationFits } from './lifecycle-summary.js';
|
|
7
7
|
import { MARKDOWN_MAX_BYTES, MARKDOWN_MAX_CODE_POINTS, markdownCode, markdownProse, } from './rooms-tasks/markdown.js';
|
|
@@ -91,7 +91,7 @@ export function recordFleetAuditPresentation(value) {
|
|
|
91
91
|
: value.kind === 'lifecycle_failure'
|
|
92
92
|
? { label: fleetPresentationLabel(value.label) }
|
|
93
93
|
: {};
|
|
94
|
-
const presentation = structuredClone({ ...value, ...labels, eventId: basis });
|
|
94
|
+
const presentation = redactErasedContent(structuredClone({ ...value, ...labels, eventId: basis }));
|
|
95
95
|
const digest = lifecycleEventDigestBasis(presentation);
|
|
96
96
|
if (!collection.presentations.some(existing => lifecycleEventDigestBasis(existing) === digest))
|
|
97
97
|
collection.presentations.push(presentation);
|
|
@@ -305,11 +305,12 @@ export class FleetCommandAuditStore {
|
|
|
305
305
|
if (recovered)
|
|
306
306
|
this.persist();
|
|
307
307
|
}
|
|
308
|
-
list() { return this.attempts.map(item => structuredClone(item)); }
|
|
308
|
+
list() { return redactErasedContent(this.attempts).map(item => structuredClone(item)); }
|
|
309
309
|
begin(requestId, caller, argv) {
|
|
310
|
+
this.attempts = redactErasedContent(this.attempts);
|
|
310
311
|
const existing = this.attempts.find(item => item.caller === caller && item.requestId === requestId);
|
|
311
312
|
if (existing) {
|
|
312
|
-
if (JSON.stringify(existing.argv) !== JSON.stringify(redactFleetArgv(argv)))
|
|
313
|
+
if (JSON.stringify(existing.argv) !== JSON.stringify(existing.erased ? redactFleetArgv(argv).map(erasedArg) : redactFleetArgv(argv)))
|
|
313
314
|
throw new Error('fleet command request ID was reused with different argv');
|
|
314
315
|
return structuredClone(existing);
|
|
315
316
|
}
|
|
@@ -329,6 +330,8 @@ export class FleetCommandAuditStore {
|
|
|
329
330
|
return structuredClone(attempt);
|
|
330
331
|
}
|
|
331
332
|
finish(correlationId, caller, outcome) {
|
|
333
|
+
this.attempts = redactErasedContent(this.attempts);
|
|
334
|
+
outcome = redactErasedContent({ outcome }).outcome;
|
|
332
335
|
const attempt = this.owned(correlationId, caller);
|
|
333
336
|
if (!attempt.outcome)
|
|
334
337
|
attempt.outcome = { ...outcome, completedAt: this.deps.now().toISOString(), delivery: 'sending' };
|
|
@@ -360,7 +363,8 @@ export class FleetCommandAuditStore {
|
|
|
360
363
|
return attempt;
|
|
361
364
|
}
|
|
362
365
|
persist() {
|
|
363
|
-
|
|
366
|
+
this.attempts = redactErasedContent(this.attempts);
|
|
367
|
+
writePrivacyFilteredLedger(this.path, { version: 1, attempts: this.attempts });
|
|
364
368
|
}
|
|
365
369
|
}
|
|
366
370
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
package/dist/init-wizard.js
CHANGED
|
@@ -14,6 +14,11 @@ const REASONING_EFFORT = {
|
|
|
14
14
|
quick: 'low', balanced: 'medium', thorough: 'high',
|
|
15
15
|
};
|
|
16
16
|
const MODEL_LABELS = {
|
|
17
|
+
'gpt-6-sol': 'GPT-6 Sol',
|
|
18
|
+
'gpt-6-luna': 'GPT-6 Luna',
|
|
19
|
+
'claude-fable-5-1': 'Claude Fable 5.1',
|
|
20
|
+
'claude-opus-5-5': 'Claude Opus 5.5',
|
|
21
|
+
'claude-haiku-4-5-20251001': 'Claude Haiku 4.5',
|
|
17
22
|
'gpt-6-astra': 'GPT-6 Astra',
|
|
18
23
|
'gpt-5.6-sol': 'GPT-5.6 Sol',
|
|
19
24
|
'gpt-5.6-terra': 'GPT-5.6 Terra',
|
|
@@ -33,10 +38,10 @@ export function validateCatalog(value, path = 'supported model catalog') {
|
|
|
33
38
|
for (const model of value.models) {
|
|
34
39
|
if (!['codex', 'claude-code'].includes(model.harness) || model.session !== 'acp'
|
|
35
40
|
|| typeof model.model !== 'string' || !MODEL_LABELS[model.model]
|
|
36
|
-
|| !Array.isArray(model.efforts)
|
|
41
|
+
|| !Array.isArray(model.efforts)
|
|
37
42
|
|| new Set(model.efforts).size !== model.efforts.length
|
|
38
43
|
|| model.efforts.some(effort => !['low', 'medium', 'high', 'xhigh', 'max', 'ultra'].includes(effort))
|
|
39
|
-
|| !['low', 'medium', 'high'].every(effort => model.efforts.includes(effort)))
|
|
44
|
+
|| (model.efforts.length > 0 && !['low', 'medium', 'high'].every(effort => model.efforts.includes(effort))))
|
|
40
45
|
throw new Error(`unsupported model state in ${path}`);
|
|
41
46
|
const tuple = `${model.harness}\0${model.session}\0${model.model}`;
|
|
42
47
|
if (tuples.has(tuple))
|
|
@@ -90,7 +95,9 @@ export async function askInitQuestions(prompter, configuration) {
|
|
|
90
95
|
return undefined;
|
|
91
96
|
if (subscriptions.length === 0)
|
|
92
97
|
throw new Error('Select at least one subscription.');
|
|
93
|
-
|
|
98
|
+
// The wizard assigns low/medium/high; no-effort models remain available as named presets.
|
|
99
|
+
const available = catalog().models.filter(model => subscriptions.includes(subscriptionFor(model))
|
|
100
|
+
&& model.efforts.length > 0);
|
|
94
101
|
if (!available.length)
|
|
95
102
|
throw new Error('None of the selected subscriptions has a supported model.');
|
|
96
103
|
prompter.note([
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { redactErasedContent, writePrivacyFilteredLedger } from '../erased-resources.js';
|
|
1
2
|
import { createHash } from 'node:crypto';
|
|
2
3
|
import { chmodSync, existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { replaceFileAtomically } from '../atomic-file.js';
|
|
4
4
|
import { lifecycleEventDigestBasis, validateFleetAuditPresentations, } from '../fleet-command-audit.js';
|
|
5
5
|
const DIGEST = /^[a-f0-9]{64}$/u;
|
|
6
6
|
const LIMIT = 5_000;
|
|
@@ -63,7 +63,7 @@ export class FleetLifecycleOutbox {
|
|
|
63
63
|
}
|
|
64
64
|
pending() {
|
|
65
65
|
this.assertHealthy();
|
|
66
|
-
return this.entries.filter(entry => entry.delivery === 'pending').map(entry => structuredClone(entry));
|
|
66
|
+
return redactErasedContent(this.entries).filter(entry => entry.delivery === 'pending').map(entry => structuredClone(entry));
|
|
67
67
|
}
|
|
68
68
|
finish(digest, delivery) {
|
|
69
69
|
this.assertHealthy();
|
|
@@ -82,6 +82,7 @@ export class FleetLifecycleOutbox {
|
|
|
82
82
|
throw new Error(this.corruptReason);
|
|
83
83
|
}
|
|
84
84
|
persist() {
|
|
85
|
-
|
|
85
|
+
this.entries = redactErasedContent(this.entries);
|
|
86
|
+
writePrivacyFilteredLedger(this.path, { version: 1, entries: this.entries });
|
|
86
87
|
}
|
|
87
88
|
}
|
|
@@ -27,7 +27,7 @@ export declare function acceptManagedRoomClose(roomId: string): Promise<RoomOrch
|
|
|
27
27
|
export declare function recordManagedRoomCloseError(roomId: string, error: string, recoveryHint: string): Promise<RoomOrchestrationRecord>;
|
|
28
28
|
export declare function closeManagedRoom(input: {
|
|
29
29
|
roomId: string;
|
|
30
|
-
cowork: Pick<CoworkAdapter, 'closeRoom'
|
|
30
|
+
cowork: Pick<CoworkAdapter, 'closeRoom'> & Partial<Pick<CoworkAdapter, 'getRoom'>>;
|
|
31
31
|
deps?: RoomCloseDeps;
|
|
32
32
|
}): Promise<RoomOrchestrationRecord>;
|
|
33
33
|
export interface ManagedRoomDeleteResult {
|
|
@@ -41,6 +41,6 @@ export declare function deleteLegacyClosedRooms(input: {
|
|
|
41
41
|
/** Retire live resources through the existing cursor, then delete retained state. */
|
|
42
42
|
export declare function deleteManagedRoom(input: {
|
|
43
43
|
roomId: string;
|
|
44
|
-
cowork: Pick<CoworkAdapter, 'closeRoom' | 'deleteRoom'
|
|
44
|
+
cowork: Pick<CoworkAdapter, 'closeRoom' | 'deleteRoom'> & Partial<Pick<CoworkAdapter, 'getRoom'>>;
|
|
45
45
|
deps?: RoomCloseDeps;
|
|
46
46
|
}): Promise<ManagedRoomDeleteResult>;
|