@ours.network/fleet 1.2.0-nightly.47 → 1.2.0-nightly.49
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/build-info.json +5 -4
- package/dist/capabilities.d.ts +3 -1
- package/dist/capabilities.js +3 -0
- package/dist/harness/codex-reasoning.d.ts +5 -0
- package/dist/harness/codex-reasoning.js +12 -0
- package/dist/harness/codex.js +12 -6
- package/dist/managed-cli.js +3 -2
- package/dist/session/conversation-store.d.ts +1 -1
- package/dist/session/conversation-store.js +7 -11
- package/dist/web/workspace-tunnel.js +5 -3
- package/docs/validation/managed-cli-permissions.md +8 -3
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.2.0-nightly.
|
|
3
|
-
"buildId": "
|
|
4
|
-
"commit": "
|
|
2
|
+
"version": "1.2.0-nightly.49",
|
|
3
|
+
"buildId": "992b454dab6f",
|
|
4
|
+
"commit": "007bb41d5f822b22ebc9c708882f6cd7e70ae537",
|
|
5
5
|
"dirty": true,
|
|
6
|
-
"builtAt": "2026-10-
|
|
6
|
+
"builtAt": "2026-10-06T08:03:31.440Z",
|
|
7
7
|
"capabilities": [
|
|
8
|
+
"managed-cli.codex-reasoning-effort-v1",
|
|
8
9
|
"managed-cli.setup-v1",
|
|
9
10
|
"workspace.enroll.preserve-profile-v1",
|
|
10
11
|
"cowork.http-management-v1",
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -18,7 +18,9 @@ export declare const CAP_HTTP_COWORK = "cowork.http-management-v1";
|
|
|
18
18
|
export declare const CAP_WORKSPACE_PRESERVE_PROFILE = "workspace.enroll.preserve-profile-v1";
|
|
19
19
|
/** `ours-fleet managed-cli setup|status` and the `managed_cli` Agent key (#233). */
|
|
20
20
|
export declare const CAP_MANAGED_CLI_SETUP = "managed-cli.setup-v1";
|
|
21
|
-
|
|
21
|
+
/** Managed CLI accepts validated Codex Brain reasoning effort without dropping it (#235). */
|
|
22
|
+
export declare const CAP_MANAGED_CLI_CODEX_EFFORT = "managed-cli.codex-reasoning-effort-v1";
|
|
23
|
+
export declare const CAPABILITIES: readonly ["managed-cli.codex-reasoning-effort-v1", "managed-cli.setup-v1", "workspace.enroll.preserve-profile-v1", "cowork.http-management-v1", "monitor.interrupt.after_tool"];
|
|
22
24
|
export type Capability = (typeof CAPABILITIES)[number];
|
|
23
25
|
/** Does this build declare `token`? */
|
|
24
26
|
export declare const hasCapability: (token: string) => boolean;
|
package/dist/capabilities.js
CHANGED
|
@@ -18,7 +18,10 @@ export const CAP_HTTP_COWORK = 'cowork.http-management-v1';
|
|
|
18
18
|
export const CAP_WORKSPACE_PRESERVE_PROFILE = 'workspace.enroll.preserve-profile-v1';
|
|
19
19
|
/** `ours-fleet managed-cli setup|status` and the `managed_cli` Agent key (#233). */
|
|
20
20
|
export const CAP_MANAGED_CLI_SETUP = 'managed-cli.setup-v1';
|
|
21
|
+
/** Managed CLI accepts validated Codex Brain reasoning effort without dropping it (#235). */
|
|
22
|
+
export const CAP_MANAGED_CLI_CODEX_EFFORT = 'managed-cli.codex-reasoning-effort-v1';
|
|
21
23
|
export const CAPABILITIES = [
|
|
24
|
+
CAP_MANAGED_CLI_CODEX_EFFORT,
|
|
22
25
|
CAP_MANAGED_CLI_SETUP,
|
|
23
26
|
CAP_WORKSPACE_PRESERVE_PROFILE,
|
|
24
27
|
CAP_HTTP_COWORK,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Efforts supported by Fleet's Codex Brain contract. Keep native eligibility in sync. */
|
|
2
|
+
export declare const CODEX_REASONING_EFFORTS: readonly ["low", "medium", "high", "xhigh", "max", "ultra"];
|
|
3
|
+
export declare function isCodexReasoningEffort(value: unknown): value is string;
|
|
4
|
+
/** Only inspected model tuning may bypass the managed CLI native-config guard. */
|
|
5
|
+
export declare function isManagedCliCodexConfig(config: unknown): boolean;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Efforts supported by Fleet's Codex Brain contract. Keep native eligibility in sync. */
|
|
2
|
+
export const CODEX_REASONING_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max', 'ultra'];
|
|
3
|
+
export function isCodexReasoningEffort(value) {
|
|
4
|
+
return typeof value === 'string' && CODEX_REASONING_EFFORTS.includes(value);
|
|
5
|
+
}
|
|
6
|
+
/** Only inspected model tuning may bypass the managed CLI native-config guard. */
|
|
7
|
+
export function isManagedCliCodexConfig(config) {
|
|
8
|
+
if (config == null)
|
|
9
|
+
return true;
|
|
10
|
+
return typeof config === 'object' && !Array.isArray(config)
|
|
11
|
+
&& Object.entries(config).every(([key, value]) => key === 'model_reasoning_effort' && isCodexReasoningEffort(value));
|
|
12
|
+
}
|
package/dist/harness/codex.js
CHANGED
|
@@ -8,6 +8,7 @@ import { harnessRuntimeDir } from '../isolation/policy.js';
|
|
|
8
8
|
import { resolveBundledAcpAgent, } from './acp-agent.js';
|
|
9
9
|
import { VERIFIED_CODEX_ACP_VERSIONS, probeCodexRuntime, codexVersionAtLeast, hostCodex } from './codex-runtime.js';
|
|
10
10
|
import { CodexAgentSessionAdapter } from './codex-session.js';
|
|
11
|
+
import { CODEX_REASONING_EFFORTS, isCodexReasoningEffort } from './codex-reasoning.js';
|
|
11
12
|
const OPTION_KEYS = [
|
|
12
13
|
'launcher', 'sandbox', 'approval', 'permission_mode', 'search', 'profile', 'config', 'add_dirs',
|
|
13
14
|
'monitor',
|
|
@@ -141,6 +142,13 @@ function neutralSandboxMode(role) {
|
|
|
141
142
|
function isNativeConfigKeyAllowed(key) {
|
|
142
143
|
return NATIVE_CONFIG_ALLOWLIST.has(key);
|
|
143
144
|
}
|
|
145
|
+
/** Brain wins; a validated native effort also needs delivery through ACP's selection API. */
|
|
146
|
+
function reasoningEffort(role) {
|
|
147
|
+
if (typeof role.effort === 'string')
|
|
148
|
+
return role.effort;
|
|
149
|
+
const value = role.harness_options?.config?.model_reasoning_effort;
|
|
150
|
+
return isCodexReasoningEffort(value) ? value : undefined;
|
|
151
|
+
}
|
|
144
152
|
/** Defense in depth for callers which launch a role after validation was bypassed. */
|
|
145
153
|
export function nativeCodexConfig(role) {
|
|
146
154
|
const options = role.harness_options;
|
|
@@ -318,10 +326,8 @@ export function makeCodexAdapter(exec = realExec, transport, nativeTransport) {
|
|
|
318
326
|
id: 'codex',
|
|
319
327
|
agentSession: new CodexAgentSessionAdapter({
|
|
320
328
|
resolveBrain(brain) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
&& (typeof brain.effort !== 'string' || !levels.includes(brain.effort)))
|
|
324
|
-
throw new Error(`Codex Brain effort must be one of: ${levels.join(', ')}`);
|
|
329
|
+
if (brain.effort != null && !isCodexReasoningEffort(brain.effort))
|
|
330
|
+
throw new Error(`Codex Brain effort must be one of: ${CODEX_REASONING_EFFORTS.join(', ')}`);
|
|
325
331
|
const harnessOptions = { ...(brain.harnessOptions ?? {}) };
|
|
326
332
|
if (brain.effort)
|
|
327
333
|
harnessOptions.config = {
|
|
@@ -343,8 +349,8 @@ export function makeCodexAdapter(exec = realExec, transport, nativeTransport) {
|
|
|
343
349
|
sessionConfigSelections: role => [
|
|
344
350
|
...(typeof role.model === 'string'
|
|
345
351
|
? [{ configId: 'model', value: role.model }] : []),
|
|
346
|
-
...(
|
|
347
|
-
? [{ configId: 'reasoning_effort', value: role
|
|
352
|
+
...(reasoningEffort(role)
|
|
353
|
+
? [{ configId: 'reasoning_effort', value: reasoningEffort(role) }] : []),
|
|
348
354
|
],
|
|
349
355
|
permissionModeId: role => acpAgentMode(role),
|
|
350
356
|
mcpServers: () => undefined,
|
package/dist/managed-cli.js
CHANGED
|
@@ -26,6 +26,7 @@ import { managedPinMarker } from './fleet-command-audit.js';
|
|
|
26
26
|
import { MANAGED_CLI_WORKFLOW_IDS, splitRootFor, } from './config.js';
|
|
27
27
|
import { resolveBundledAcpAgent } from './harness/acp-agent.js';
|
|
28
28
|
import { executableOnPath, hostCodex } from './harness/codex-runtime.js';
|
|
29
|
+
import { isManagedCliCodexConfig } from './harness/codex-reasoning.js';
|
|
29
30
|
import { getAdapter } from './harness/registry.js';
|
|
30
31
|
import { agentDir, defaultConfigPath, home, stateRoot } from './paths.js';
|
|
31
32
|
/** Capability-gated by the installer; see src/capabilities.ts. */
|
|
@@ -333,8 +334,8 @@ export function analyzeManagedCli(role, configPath, options = {}) {
|
|
|
333
334
|
// directories the role adds. Native settings Fleet cannot read may add more.
|
|
334
335
|
const native = (role.harness_options ?? {});
|
|
335
336
|
const writable = [workspace, ...(Array.isArray(native.add_dirs) ? native.add_dirs.filter((dir) => typeof dir === 'string').map(dir => resolve(workspace, dir)) : [])];
|
|
336
|
-
if (role.harness === 'codex' && (native.profile != null || (native.config
|
|
337
|
-
reasons.push('harness_native.config or harness_native.profile
|
|
337
|
+
if (role.harness === 'codex' && (native.profile != null || !isManagedCliCodexConfig(native.config)))
|
|
338
|
+
reasons.push('harness_native.config or harness_native.profile contains invalid or uninspected Codex settings (for example extra writable roots), so Fleet does not inspect enough to tell whether the agent could replace the pinned files; only validated model_reasoning_effort is supported; drop the other settings or keep this role sandboxed');
|
|
338
339
|
if (role.isolation)
|
|
339
340
|
reasons.push('the role declares isolation:, and Fleet\'s outer sandbox is kept as-is; the workflow is not qualified inside it');
|
|
340
341
|
if (!plainExecutable(paths.node))
|
|
@@ -29,6 +29,7 @@ type EventDraft = Omit<ConversationEventV1, 'schemaVersion' | 'roleId' | 'eventI
|
|
|
29
29
|
export declare class ConversationEventStore {
|
|
30
30
|
private readonly dir;
|
|
31
31
|
private nextSeq;
|
|
32
|
+
private firstAvailableSeq?;
|
|
32
33
|
private segments;
|
|
33
34
|
private tail;
|
|
34
35
|
private readonly listeners;
|
|
@@ -81,7 +82,6 @@ export declare class ConversationEventStore {
|
|
|
81
82
|
private trackPromptState;
|
|
82
83
|
private segmentFd;
|
|
83
84
|
private writeManifest;
|
|
84
|
-
private firstStoredSeq;
|
|
85
85
|
private eventsAfter;
|
|
86
86
|
private markDegraded;
|
|
87
87
|
}
|
|
@@ -24,6 +24,7 @@ export class IdempotencyConflictError extends Error {
|
|
|
24
24
|
export class ConversationEventStore {
|
|
25
25
|
dir;
|
|
26
26
|
nextSeq = 1;
|
|
27
|
+
firstAvailableSeq;
|
|
27
28
|
segments = [];
|
|
28
29
|
tail = [];
|
|
29
30
|
listeners = new Set();
|
|
@@ -72,6 +73,7 @@ export class ConversationEventStore {
|
|
|
72
73
|
const fd = this.segmentFd(Buffer.byteLength(line));
|
|
73
74
|
writeSync(fd, line);
|
|
74
75
|
fsyncSync(fd);
|
|
76
|
+
this.firstAvailableSeq ??= event.seq;
|
|
75
77
|
this.nextSeq++;
|
|
76
78
|
this.activeBytes += Buffer.byteLength(line);
|
|
77
79
|
this.tail.push(event);
|
|
@@ -101,7 +103,10 @@ export class ConversationEventStore {
|
|
|
101
103
|
const events = this.eventsAfter(after, limit + 1);
|
|
102
104
|
const hasMore = events.length > limit;
|
|
103
105
|
const pageEvents = hasMore ? events.slice(0, limit) : events;
|
|
104
|
-
|
|
106
|
+
// This single-writer store never removes retained segments. Recovery and
|
|
107
|
+
// append already know the first readable sequence; paging the memory tail
|
|
108
|
+
// must not parse the first segment again, especially while skipping replay.
|
|
109
|
+
const firstStored = this.firstAvailableSeq;
|
|
105
110
|
return {
|
|
106
111
|
events: pageEvents,
|
|
107
112
|
...(firstStored !== undefined ? { firstAvailableCursor: String(firstStored) } : {}),
|
|
@@ -159,6 +164,7 @@ export class ConversationEventStore {
|
|
|
159
164
|
let maxSeq = 0;
|
|
160
165
|
for (const segment of this.segments) {
|
|
161
166
|
for (const event of this.readSegment(segment)) {
|
|
167
|
+
this.firstAvailableSeq ??= event.seq;
|
|
162
168
|
maxSeq = Math.max(maxSeq, event.seq);
|
|
163
169
|
this.tail.push(event);
|
|
164
170
|
if (this.tail.length > TAIL_EVENTS)
|
|
@@ -308,16 +314,6 @@ export class ConversationEventStore {
|
|
|
308
314
|
}
|
|
309
315
|
}
|
|
310
316
|
// ── reads ──────────────────────────────────────────────────────────────────
|
|
311
|
-
firstStoredSeq() {
|
|
312
|
-
if (this.tail.length && this.tail[0].seq === 1)
|
|
313
|
-
return 1;
|
|
314
|
-
for (const segment of this.segments) {
|
|
315
|
-
const events = this.readSegment(segment);
|
|
316
|
-
if (events.length)
|
|
317
|
-
return events[0].seq;
|
|
318
|
-
}
|
|
319
|
-
return this.tail[0]?.seq;
|
|
320
|
-
}
|
|
321
317
|
eventsAfter(after, limit) {
|
|
322
318
|
// Serve from the in-memory tail whenever the range allows it.
|
|
323
319
|
if (this.tail.length && after >= this.tail[0].seq - 1)
|
|
@@ -21,12 +21,14 @@ export function startWorkspaceTunnel(origin) {
|
|
|
21
21
|
return;
|
|
22
22
|
child = spawn('cloudflared', ['tunnel', '--no-autoupdate', 'run', '--token-file', tokenFile], { stdio: 'ignore' });
|
|
23
23
|
let scheduled = false;
|
|
24
|
+
// Keep an error listener for the child's whole lifetime, including shutdown.
|
|
24
25
|
const retry = () => { if (scheduled || stopped)
|
|
25
26
|
return; scheduled = true; timer = setTimeout(launch, backoff); backoff = Math.min(30000, backoff * 2); };
|
|
26
|
-
child.
|
|
27
|
+
child.on('error', retry);
|
|
27
28
|
child.once('exit', retry);
|
|
28
29
|
};
|
|
29
30
|
launch();
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
// A failed spawn has no pid and reports error/close rather than exit.
|
|
32
|
+
return async () => { stopped = true; clearTimeout(timer); const current = child; if (!current?.pid || current.exitCode !== null || current.signalCode !== null)
|
|
33
|
+
return; await new Promise(resolve => { const timeout = setTimeout(() => { current.kill('SIGKILL'); resolve(); }, 5000); current.once('close', () => { clearTimeout(timeout); resolve(); }); current.kill('SIGTERM'); }); };
|
|
32
34
|
}
|
|
@@ -127,9 +127,14 @@ commands; roles declaring `isolation:`; unspellable paths; a Node path needing
|
|
|
127
127
|
quotes; Codex older than 0.160.0 with quoted paths; and any pinned file the
|
|
128
128
|
agent could replace — inside its workspace or a `harness_options.add_dirs`
|
|
129
129
|
directory, lexically or through a symlink. Codex roles with
|
|
130
|
-
`harness_options.
|
|
131
|
-
those change the native sandbox (for example extra writable roots) in ways
|
|
132
|
-
does not inspect.
|
|
130
|
+
`harness_options.profile` or uninspected `harness_options.config` remain unsupported:
|
|
131
|
+
those can change the native sandbox (for example extra writable roots) in ways
|
|
132
|
+
Fleet does not inspect. The only accepted nonempty config is
|
|
133
|
+
`model_reasoning_effort` with a supported value (`low`, `medium`, `high`, `xhigh`,
|
|
134
|
+
`max`, `ultra`). This includes the config generated by Codex Brain `effort` (#235).
|
|
135
|
+
The chosen effort remains in native launch config and ACP reasoning selection;
|
|
136
|
+
adding any unknown or security-bearing config key still prevents policy setup.
|
|
137
|
+
Native user settings Fleet cannot read are outside this check.
|
|
133
138
|
|
|
134
139
|
### Qualification
|
|
135
140
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ours.network/fleet",
|
|
3
|
-
"version": "1.2.0-nightly.
|
|
3
|
+
"version": "1.2.0-nightly.49",
|
|
4
4
|
"description": "Harness-agnostic fleet of persistent, identity-bound AI agents. Declarative fleet.yaml, managed native/ACP sessions, supervision, and ours.network messaging.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "FSL-1.1-Apache-2.0",
|