@ours.network/fleet 1.2.0-nightly.3 → 1.2.0-nightly.5

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.
Files changed (50) hide show
  1. package/README.md +32 -19
  2. package/dist/agent-ours/bridge.d.ts +3 -0
  3. package/dist/agent-ours/bridge.js +169 -0
  4. package/dist/agent-ours/controller.d.ts +14 -0
  5. package/dist/agent-ours/controller.js +60 -0
  6. package/dist/agent-ours/file-rpc.d.ts +23 -0
  7. package/dist/agent-ours/file-rpc.js +123 -0
  8. package/dist/agent-ours/harness.d.ts +14 -0
  9. package/dist/agent-ours/harness.js +64 -0
  10. package/dist/agent-ours/mcp-endpoint.d.ts +15 -0
  11. package/dist/agent-ours/mcp-endpoint.js +111 -0
  12. package/dist/agent-ours/runtime.d.ts +53 -0
  13. package/dist/agent-ours/runtime.js +247 -0
  14. package/dist/agent-ours/service.d.ts +23 -0
  15. package/dist/agent-ours/service.js +352 -0
  16. package/dist/agent-ours/state.d.ts +45 -0
  17. package/dist/agent-ours/state.js +95 -0
  18. package/dist/agent-ours/wire.d.ts +14 -0
  19. package/dist/agent-ours/wire.js +55 -0
  20. package/dist/briefing.js +10 -59
  21. package/dist/build-info.json +5 -4
  22. package/dist/capabilities.d.ts +2 -1
  23. package/dist/capabilities.js +2 -0
  24. package/dist/cli.js +15 -2
  25. package/dist/client-profile.d.ts +1 -0
  26. package/dist/client-profile.js +18 -3
  27. package/dist/creation.d.ts +1 -1
  28. package/dist/creation.js +3 -14
  29. package/dist/docs.d.ts +1 -1
  30. package/dist/docs.js +15 -20
  31. package/dist/doctor.d.ts +1 -1
  32. package/dist/doctor.js +28 -9
  33. package/dist/fleet-command-audit.js +1 -1
  34. package/dist/harness/agent-session.d.ts +2 -0
  35. package/dist/harness/claude-code-session.js +4 -2
  36. package/dist/harness/codex-session.js +9 -2
  37. package/dist/harness/hermes-session.js +7 -1
  38. package/dist/rooms-tasks/cowork-adapter.js +9 -2
  39. package/dist/rooms-tasks/cowork-http.d.ts +3 -0
  40. package/dist/rooms-tasks/cowork-http.js +70 -0
  41. package/dist/rooms-tasks/provision.js +9 -2
  42. package/dist/runner.d.ts +4 -0
  43. package/dist/runner.js +522 -492
  44. package/dist/session/codex-app-server-transport.js +1 -1
  45. package/dist/spawn.js +12 -16
  46. package/dist/temp-supervisor-recovery.d.ts +3 -0
  47. package/dist/temp-supervisor-recovery.js +46 -0
  48. package/dist/watchdog/briefing.js +3 -16
  49. package/dist/watchdog/run.js +19 -5
  50. package/package.json +6 -5
package/dist/docs.js CHANGED
@@ -253,20 +253,15 @@ records receive a bounded grace so a not-yet-registered transient unit cannot be
253
253
  mistaken for a stopped one. Stale recorded supervisors are reclaimed in bounded
254
254
  batches by moving their state to the same recovery archive, never by blind deletion.
255
255
 
256
- Every temporary role creates a new session-owned identity by calling ours MCP
257
- \`create_temporary_identity\` with its exact assigned name. It never binds a
258
- pre-existing identity and never falls back to permanent \`create_identity\`.
259
- Fleet does not inspect, preserve, or provision an ours identity for temporary
260
- spawn; creation belongs exclusively to the launched temporary agent session.
261
- Collisions, missing tool support, and creation errors stop safely without
262
- force-adopting or deleting identity state. Permanent roles
263
- are provisioned by fleet before launch and never delegate normal identity
264
- creation to the harness.
265
-
266
- The temporary supervisor treats its first positive identity observation as the
267
- lifecycle readiness gate: a cold harness may take as long as needed to read its
268
- briefing and bind, without a fixed first-bind retirement timer. After readiness,
269
- only sustained authoritative absence closes the role. Unreachable, malformed, or
256
+ The Fleet supervisor creates and binds the assigned identity before the harness
257
+ starts. Temporary identities survive harness reconnects and are released only when
258
+ the logical run ends. Permanent identities remain available after the supervisor
259
+ releases ownership. Agents receive ready tools; no model-owned identity bootstrap
260
+ or onboarding skill is required.
261
+
262
+ The temporary supervisor provisions identity and verifies room readiness before
263
+ starting the harness. After readiness, only sustained authoritative absence
264
+ closes the role. Unreachable, malformed, or
270
265
  valid-but-empty daemon indexes are ambiguous and reset closure debounce rather
271
266
  than becoming cleanup authority.
272
267
 
@@ -454,10 +449,10 @@ tasks:
454
449
  \`\`\`
455
450
 
456
451
  Fleet launches each template member with a dedicated one-time Cowork invite.
457
- The generated temporary-agent briefing contains the exact identity name, invite,
458
- Cowork role, and task. The agent creates that identity itself with ours MCP
459
- \`create_temporary_identity\`, accepts the invite with \`add_contact\`, and starts
460
- work immediately. A room is ready only after the Cowork room (and its task, when
452
+ The supervisor accepts the startup invite and verifies the provisioned identity CID
453
+ in the authenticated room seat before starting the model. The generated briefing
454
+ contains the assigned identity, room, role and task, without invite material.
455
+ A room is ready only after the Cowork room (and its task, when
461
456
  task-bound) is durably active, every configured member seat is authenticated and active with its matching
462
457
  live Fleet launch, and the configured Owner seat is active when owner attachment is
463
458
  enabled. There is no briefing hash, startup ACK, or separate role-briefing readiness
@@ -480,8 +475,8 @@ Set \`room.anonymous: true\` on a room template, or pass \`--anonymous\` to
480
475
  \`task create\`, \`task start\`, \`task work\`, or \`room create\`, to create an
481
476
  anonymous Cowork room. \`--no-anonymous\` explicitly overrides an anonymous
482
477
  template. Fleet records the resolved value before room creation so retries keep
483
- the same choice. Temporary members of an anonymous room are instructed to call
484
- \`create_temporary_identity\` with \`expose_local=false\`. Their generated briefings
478
+ the same choice. The supervisor creates anonymous room members with local contact-book exposure
479
+ disabled. Their generated briefings
485
480
  do not disclose or compare an Owner participant CID. A participant-originated
486
481
  instruction has Owner authority only when the authenticated Cowork room envelope
487
482
  attributes that participant seat the exact \`Owner\` role. Literal text, display
package/dist/doctor.d.ts CHANGED
@@ -3,7 +3,7 @@ import { type Exec } from './exec.js';
3
3
  import type { YamlMode } from './config-yaml.js';
4
4
  import { type FetchLike } from './monitor.js';
5
5
  import type { PrereqReport } from './harness/types.js';
6
- type DoctorDaemonClient = Pick<OursClient, 'version'>;
6
+ type DoctorDaemonClient = Pick<OursClient, 'version' | 'close'>;
7
7
  type AttachDoctorDaemon = (options: AttachOursClientOptions) => Promise<DoctorDaemonClient>;
8
8
  /** Host-level + per-harness prerequisite report with actionable messages. */
9
9
  export declare function doctor(opts?: {
package/dist/doctor.js CHANGED
@@ -1,4 +1,5 @@
1
- import { userInfo } from 'node:os';
1
+ import { join } from 'node:path';
2
+ import { userInfo, homedir } from 'node:os';
2
3
  import { existsSync, readFileSync } from 'node:fs';
3
4
  import { attachOursClient, } from '@ours.network/sdk/client';
4
5
  import { realExec } from './exec.js';
@@ -272,21 +273,39 @@ export async function doctor(opts = {}, exec = realExec, platform = process.plat
272
273
  }
273
274
  catch { /* state dir may not exist yet */ }
274
275
  }
276
+ let managedSelection = false;
275
277
  try {
276
- const client = await attachDaemon({
277
- env: process.env,
278
+ // Mark explicit selection before validation so a damaged managed profile does
279
+ // not produce advice to start an unrelated local daemon.
280
+ managedSelection = process.env.OURS_CONFIG !== undefined
281
+ || existsSync(join(process.env.HOME || homedir(), '.ours-client', 'profile.json'));
282
+ const selected = readClientProfile(process.env);
283
+ managedSelection = selected !== undefined;
284
+ const client = await attachDaemon(selected ? {
285
+ endpoint: selected.endpoint, expectedInstanceId: selected.expectedInstanceId,
286
+ credentialPath: selected.credentialPath, env: {}, sessionMode: 'external',
278
287
  leaseToken: `ours-fleet-doctor-${process.pid}`,
279
- clientPid: process.pid,
288
+ } : {
289
+ env: process.env, leaseToken: `ours-fleet-doctor-${process.pid}`, clientPid: process.pid,
280
290
  });
281
- const info = await client.version();
282
- if (info.name !== 'ours' || typeof info.version !== 'string')
283
- throw new Error('the selected endpoint did not return a valid ours daemon identity');
284
- checks.push({ name: 'ours daemon', ok: true, detail: `running (${info.version})` });
291
+ let daemonVersion;
292
+ try {
293
+ const info = await client.version();
294
+ if (info.name !== 'ours' || typeof info.version !== 'string')
295
+ throw new Error('the selected endpoint did not return a valid ours daemon identity');
296
+ daemonVersion = info.version;
297
+ }
298
+ finally {
299
+ await client.close();
300
+ }
301
+ checks.push({ name: 'ours daemon', ok: true, detail: `running (${daemonVersion})` });
285
302
  }
286
303
  catch (error) {
287
304
  checks.push({
288
305
  name: 'ours daemon', ok: false,
289
- detail: `not reachable through the SDK — start it with: ours-daemon start `
306
+ detail: (managedSelection
307
+ ? 'selected client profile failed — check its endpoint, instance and issued credential '
308
+ : 'not reachable through the SDK — start it with: ours-daemon start ')
290
309
  + `[${error?.message ?? String(error)}]`,
291
310
  });
292
311
  }
@@ -132,7 +132,7 @@ export const fleetProxyTopLevelInventory = Object.freeze({
132
132
  'up', 'down', 'restart', 'force-restart', 'attach', 'send', 'loops', 'owner-channel',
133
133
  'watchdog-run', 'watchdog-report', 'rm', 'init', 'web',
134
134
  ], denied: [...DENIED],
135
- hidden: ['_run', '_run-temp', '_run-watchdog', '_run-watchdogs'], aliases: ['man'],
135
+ hidden: ['_run', '_run-temp', '_run-temp-worker', '_run-watchdog', '_run-watchdogs'], aliases: ['man'],
136
136
  });
137
137
  const globalValueOptions = new Set(['-c', '--configuration']);
138
138
  /** Classify before Commander parsing. Unknown and internal paths fail closed. */
@@ -1,3 +1,4 @@
1
+ import type { ManagedHarnessOurs } from '../agent-ours/harness.js';
1
2
  import type { CommonPermissions, FleetPermissionMode, ResolvedRole } from '../config.js';
2
3
  import type { AgentSession } from '../session/types.js';
3
4
  import type { Launch, SessionPrep } from './types.js';
@@ -22,6 +23,7 @@ export interface AgentSessionConfigSelection {
22
23
  value: string;
23
24
  }
24
25
  export interface AgentSessionStartOptions {
26
+ managedOurs?: ManagedHarnessOurs;
25
27
  role: ResolvedRole;
26
28
  prep: SessionPrep;
27
29
  launch: PreparedAgentSessionLaunch;
@@ -1,3 +1,4 @@
1
+ import { managedClaudeMeta } from '../agent-ours/harness.js';
1
2
  import { AcpSession } from '../session/acp.js';
2
3
  import { acpAdapterState, } from './acp-session-transport.js';
3
4
  /** Explicit Claude Code implementation of Fleet's shared agent-session factory. */
@@ -25,8 +26,9 @@ export class ClaudeCodeAgentSessionAdapter {
25
26
  argv: launch.argv, cwd: options.cwd, env: launch.env,
26
27
  stateDir: options.stateDir, mode: options.mode, permissions: options.permissions,
27
28
  modeId: this.strategy.permissionModeId(role),
28
- mcpServers: this.strategy.mcpServers(role),
29
- sessionMeta: this.strategy.sessionMeta(role, prep),
29
+ mcpServers: options.managedOurs ? [...(this.strategy.mcpServers(role) ?? []).filter(s => s.name !== 'ours'), options.managedOurs.server] : this.strategy.mcpServers(role),
30
+ inheritEnvironment: options.managedOurs ? false : undefined,
31
+ sessionMeta: options.managedOurs ? managedClaudeMeta(this.strategy.sessionMeta(role, prep)) : this.strategy.sessionMeta(role, prep),
30
32
  configSelections: this.strategy.sessionConfigSelections(role),
31
33
  permissionMode: options.permissionMode,
32
34
  permissionMetadataSource: acpAdapterState(launch.adapterState).permissionMetadataSource,
@@ -23,6 +23,12 @@ export class CodexAgentSessionAdapter {
23
23
  }
24
24
  start(options) {
25
25
  const { role, prep, launch } = options;
26
+ const original = this.strategy.nativeConfig(role) ?? {};
27
+ const managed = options.managedOurs?.native;
28
+ const config = managed ? { ...original, ...managed,
29
+ mcp_servers: { ...(original.mcp_servers ?? {}), ...(managed.mcp_servers ?? {}) },
30
+ plugins: { ...(original.plugins ?? {}), ...(managed.plugins ?? {}) },
31
+ } : original;
26
32
  if (role.session === 'codex-app-server')
27
33
  return this.nativeTransport({
28
34
  name: role.name, argv: launch.argv, cwd: options.cwd, env: launch.env,
@@ -31,7 +37,7 @@ export class CodexAgentSessionAdapter {
31
37
  model: role.model, effort: role.effort,
32
38
  approvalPolicy: this.strategy.approvalPolicy(role),
33
39
  sandbox: this.strategy.sandbox(role),
34
- config: this.strategy.nativeConfig(role),
40
+ config,
35
41
  addDirs: this.strategy.addDirs(role),
36
42
  log: options.log,
37
43
  });
@@ -40,7 +46,8 @@ export class CodexAgentSessionAdapter {
40
46
  argv: launch.argv, cwd: options.cwd, env: launch.env,
41
47
  stateDir: options.stateDir, mode: options.mode, permissions: options.permissions,
42
48
  modeId: this.strategy.permissionModeId(role),
43
- mcpServers: this.strategy.mcpServers(role),
49
+ mcpServers: options.managedOurs ? [...(this.strategy.mcpServers(role) ?? []).filter(s => s.name !== 'ours'), options.managedOurs.server] : this.strategy.mcpServers(role),
50
+ inheritEnvironment: options.managedOurs ? false : undefined,
44
51
  sessionMeta: this.strategy.sessionMeta(role, prep),
45
52
  configSelections: this.strategy.sessionConfigSelections(role),
46
53
  permissionMode: options.permissionMode,
@@ -53,6 +53,12 @@ export class HermesAgentSessionAdapter {
53
53
  // composition; no second ambient merge may reintroduce provider secrets.
54
54
  const env = hermesChildEnvironment(role, preparedHome(prep), launch.env, launch.env);
55
55
  delete env.OURS_AUTOSTART;
56
+ if (options.managedOurs) {
57
+ for (const key of Object.keys(env))
58
+ if (key.startsWith('OURS_') && !key.startsWith('OURS_FLEET_'))
59
+ delete env[key];
60
+ Object.assign(env, { FLEET_OURS_MANAGED: '1' });
61
+ }
56
62
  await this.checks.preflight?.(options, env);
57
63
  const nativeProvider = await this.checks.expectedProvider(role, prep);
58
64
  if (typeof nativeProvider !== 'string' || !nativeProvider.trim())
@@ -68,7 +74,7 @@ export class HermesAgentSessionAdapter {
68
74
  permissions: options.permissions, modeId, requireMode: true,
69
75
  permissionMode: { fleetMode: options.permissionMode.fleetMode, nativeMode: modeId },
70
76
  permissionTimeoutMs: 50_000,
71
- mcpServers: hermesMcpServers(role, env),
77
+ mcpServers: options.managedOurs ? [...hermesMcpServers(role, env).filter(s => s.name !== 'ours'), options.managedOurs.server] : hermesMcpServers(role, env),
72
78
  scrubObsoleteOursAutostart: true,
73
79
  validateStartupResponse: async (initialized, created) => {
74
80
  await this.checks.validateArtifact(initialized);
@@ -8,6 +8,8 @@ import { existsSync, readFileSync } from 'node:fs';
8
8
  import { homedir } from 'node:os';
9
9
  import { join, resolve } from 'node:path';
10
10
  import { createConnection } from 'node:net';
11
+ import { readClientProfile } from '../client-profile.js';
12
+ import { coworkHttpCall } from './cowork-http.js';
11
13
  const RPC_VERSION = 1;
12
14
  const DEFAULT_TIMEOUT_MS = 30_000;
13
15
  const MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
@@ -275,10 +277,15 @@ function rpcCall(socketPath, method, params, timeoutMs, connect) {
275
277
  });
276
278
  }
277
279
  export function createCoworkAdapter(options = {}) {
278
- const socketPath = resolveCoworkSocketPath(options);
280
+ const env = options.env ?? process.env;
281
+ const explicitLocal = options.socketPath !== undefined || options.configPath !== undefined
282
+ || env.OURS_COWORK_CONFIG !== undefined || !!env.OURS_COWORK_STATE_DIR;
283
+ const profile = explicitLocal ? undefined : readClientProfile({ ...env, HOME: options.home ?? env.HOME });
284
+ const remote = profile?.serverUrl ? profile : undefined;
285
+ const socketPath = remote ? undefined : resolveCoworkSocketPath(options);
279
286
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
280
287
  const connect = options.connect ?? ((path) => createConnection(path));
281
- const call = (method, params) => rpcCall(socketPath, method, params, timeoutMs, connect);
288
+ const call = (method, params) => remote ? coworkHttpCall(remote, method, params, timeoutMs) : rpcCall(socketPath, method, params, timeoutMs, connect);
282
289
  return {
283
290
  async available() {
284
291
  try {
@@ -0,0 +1,3 @@
1
+ import type { ExplicitClientProfile } from '../client-profile.js';
2
+ /** Same RPC protocol as the local socket; never replay a possibly delivered mutation. */
3
+ export declare function coworkHttpCall(profile: ExplicitClientProfile, method: string, params: Record<string, unknown>, timeoutMs: number): Promise<unknown>;
@@ -0,0 +1,70 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { attachOursClient } from '@ours.network/sdk/client';
3
+ import { readPrivateFile } from '@ours.network/sdk/connector';
4
+ import { CoworkProtocolError, CoworkUnavailableError } from './cowork-adapter.js';
5
+ /** Same RPC protocol as the local socket; never replay a possibly delivered mutation. */
6
+ export async function coworkHttpCall(profile, method, params, timeoutMs) {
7
+ const id = randomUUID();
8
+ const signal = AbortSignal.timeout(timeoutMs);
9
+ const body = JSON.stringify({ version: 1, id, method, params });
10
+ if (Buffer.byteLength(body) > 1024 * 1024)
11
+ throw new CoworkProtocolError(method, 'request exceeded 1 MiB');
12
+ try {
13
+ const client = await attachOursClient({ endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
14
+ credentialPath: profile.credentialPath, sessionMode: 'external', leaseToken: `fleet-cowork-check-${id}`, env: {}, requestSignal: signal });
15
+ await client.close();
16
+ const token = readPrivateFile(profile.credentialPath, 4096).toString('utf8').trim();
17
+ const response = await fetch(profile.serverUrl + '/cowork/management/rpc', {
18
+ method: 'POST', redirect: 'error', signal,
19
+ headers: { 'content-type': 'application/json', 'x-ours-api-token': token },
20
+ body,
21
+ });
22
+ if (response.status === 401 || response.status === 403) {
23
+ await response.body?.cancel();
24
+ throw new CoworkProtocolError(method, 'server credential was rejected', 'unauthorized');
25
+ }
26
+ const reader = response.body?.getReader();
27
+ if (!reader)
28
+ throw new CoworkProtocolError(method, 'empty HTTP response');
29
+ const chunks = [];
30
+ let size = 0;
31
+ try {
32
+ for (;;) {
33
+ const { done, value } = await reader.read();
34
+ if (done)
35
+ break;
36
+ size += value.length;
37
+ if (size > 4 * 1024 * 1024)
38
+ throw new CoworkProtocolError(method, 'response exceeded 4 MiB');
39
+ chunks.push(value);
40
+ }
41
+ }
42
+ finally {
43
+ await reader.cancel();
44
+ }
45
+ let rpc;
46
+ try {
47
+ rpc = JSON.parse(Buffer.concat(chunks).toString('utf8'));
48
+ }
49
+ catch {
50
+ throw new CoworkProtocolError(method, 'server returned malformed JSON');
51
+ }
52
+ if (!rpc || Array.isArray(rpc) || rpc.version !== 1 || rpc.id !== id
53
+ || Object.hasOwn(rpc, 'result') === Object.hasOwn(rpc, 'error'))
54
+ throw new CoworkProtocolError(method, 'server returned an invalid RPC response');
55
+ if (Object.hasOwn(rpc, 'error')) {
56
+ const error = rpc.error;
57
+ if (!error || typeof error.code !== 'string' || typeof error.message !== 'string')
58
+ throw new CoworkProtocolError(method, 'server returned an invalid RPC error');
59
+ throw new CoworkProtocolError(method, error.message, error.code);
60
+ }
61
+ if (!response.ok)
62
+ throw new CoworkProtocolError(method, `management answered HTTP ${response.status}`);
63
+ return rpc.result;
64
+ }
65
+ catch (error) {
66
+ if (error instanceof CoworkProtocolError)
67
+ throw error;
68
+ throw new CoworkUnavailableError('Cowork HTTP management failed; check the selected server and credential', { cause: error });
69
+ }
70
+ }
@@ -1,3 +1,4 @@
1
+ import { readRoomReadiness } from '../agent-ours/service.js';
1
2
  import { createHash, randomUUID } from 'node:crypto';
2
3
  import { existsSync, readFileSync, realpathSync } from 'node:fs';
3
4
  import { join } from 'node:path';
@@ -149,8 +150,7 @@ function launchMatches(dir, member, actionId, taskSha, roomId, roomIdentityCid,
149
150
  && (startup.anonymous ?? false) === anonymous
150
151
  && sha256Text(startup.task ?? '') === taskSha
151
152
  && (expectedInviteId === undefined || startup.invite_id === expectedInviteId)
152
- && typeof startup.invite === 'string'
153
- && startup.invite.length > 0;
153
+ && startup.invite === '';
154
154
  }
155
155
  catch {
156
156
  return false;
@@ -345,7 +345,14 @@ function reconcileMemberSeats(roomId, members, observed, ownerSeatCid) {
345
345
  && seat.seat_state === 'active');
346
346
  const seats = current.member_seats.map(seat => {
347
347
  const member = members.find(candidate => candidate.name === seat.role_name);
348
+ const readiness = readRoomReadiness(current.room_identity_cid, member.name);
349
+ if (!readiness || readiness.room !== roomId || readiness.invite !== seat.invite_id) {
350
+ complete = false;
351
+ return seat;
352
+ }
348
353
  const found = exactCoworkSeat(observed, member, seat.invite_id);
354
+ if (found && found.identity_cid !== readiness.cid)
355
+ throw new Error('Cowork seat CID does not match supervisor provisioned identity');
349
356
  if (!found || found.seat_state !== 'active') {
350
357
  complete = false;
351
358
  return seat;
package/dist/runner.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { prepareManagedAgent, releaseManagedAgent } from './agent-ours/service.js';
1
2
  import { type ResolvedRole } from './config.js';
2
3
  import { type MonitorHandle, type MonitorOpts, type FetchLike } from './monitor.js';
3
4
  import { type DaemonGenerationProbe } from './daemon-recovery.js';
@@ -9,6 +10,8 @@ import { type OwnerChannelHandle, type OwnerChannelOptions } from './owner-chann
9
10
  import { type OwnerBinderLease } from './owner-channel/binder.js';
10
11
  import { ScheduledLoopManager, type ScheduledLoopManagerHandle } from './loops/manager.js';
11
12
  export interface RunnerDeps {
13
+ prepareAgentOurs: typeof prepareManagedAgent;
14
+ releaseAgentOurs: typeof releaseManagedAgent;
12
15
  exec: Exec;
13
16
  cpuDelegated(): boolean;
14
17
  isAlive(pid: number): boolean;
@@ -168,6 +171,7 @@ export declare function runOnce(name: string, opts?: {
168
171
  temp?: boolean;
169
172
  configPath?: string;
170
173
  allowResumeRotation?: boolean;
174
+ identityLifetime?: 'permanent' | 'temporary';
171
175
  }, partialDeps?: Partial<RunnerDeps>): Promise<AttemptResult>;
172
176
  /**
173
177
  * The persistent supervisor for one permanent role: run child sessions in a