@ours.network/fleet 1.2.0-nightly.48 → 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.
@@ -1,10 +1,11 @@
1
1
  {
2
- "version": "1.2.0-nightly.48",
3
- "buildId": "3f9ee2279814",
4
- "commit": "c319f766cc541c22bd6327fe6cf7b9e3fdfc1945",
2
+ "version": "1.2.0-nightly.49",
3
+ "buildId": "992b454dab6f",
4
+ "commit": "007bb41d5f822b22ebc9c708882f6cd7e70ae537",
5
5
  "dirty": true,
6
- "builtAt": "2026-10-06T07:13:59.670Z",
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",
@@ -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
- export declare const CAPABILITIES: readonly ["managed-cli.setup-v1", "workspace.enroll.preserve-profile-v1", "cowork.http-management-v1", "monitor.interrupt.after_tool"];
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;
@@ -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
+ }
@@ -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
- const levels = ['low', 'medium', 'high', 'xhigh', 'max', 'ultra'];
322
- if (brain.effort != null
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
- ...(typeof role.effort === 'string'
347
- ? [{ configId: 'reasoning_effort', value: role.effort }] : []),
352
+ ...(reasoningEffort(role)
353
+ ? [{ configId: 'reasoning_effort', value: reasoningEffort(role) }] : []),
348
354
  ],
349
355
  permissionModeId: role => acpAgentMode(role),
350
356
  mcpServers: () => undefined,
@@ -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 && typeof native.config === 'object' && Object.keys(native.config).length)))
337
- reasons.push('harness_native.config or harness_native.profile changes Codex\'s native sandbox in ways Fleet does not inspect (for example extra writable roots), so it cannot tell whether the agent could replace the pinned files; drop them or keep this role sandboxed');
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))
@@ -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.once('error', retry);
27
+ child.on('error', retry);
27
28
  child.once('exit', retry);
28
29
  };
29
30
  launch();
30
- return async () => { stopped = true; clearTimeout(timer); const current = child; if (!current || current.exitCode !== null)
31
- return; await new Promise(resolve => { const timeout = setTimeout(() => { current.kill('SIGKILL'); resolve(); }, 5000); current.once('exit', () => { clearTimeout(timeout); resolve(); }); current.kill('SIGTERM'); }); };
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.config` or `harness_options.profile` are unsupported too:
131
- those change the native sandbox (for example extra writable roots) in ways Fleet
132
- does not inspect. Native user settings Fleet cannot read are outside this check.
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.48",
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",