@ours.network/fleet 1.0.5 → 1.1.0-nightly.1
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 +28 -41
- package/dist/application/capabilities.d.ts +0 -1
- package/dist/application/capabilities.js +3 -13
- package/dist/application/fleet-query-service.d.ts +0 -3
- package/dist/application/fleet-query-service.js +1 -24
- package/dist/application/role-creation-service.d.ts +5 -5
- package/dist/application/role-creation-service.js +5 -7
- package/dist/application/role-repository.d.ts +1 -2
- package/dist/application/role-repository.js +7 -7
- package/dist/application/session-control.d.ts +0 -12
- package/dist/application/session-control.js +0 -42
- package/dist/application/session-mutations.d.ts +5 -5
- package/dist/application/types.d.ts +0 -7
- package/dist/briefing.js +1 -4
- package/dist/build-info.json +5 -5
- package/dist/cli.js +9 -23
- package/dist/config.d.ts +1 -4
- package/dist/config.js +13 -17
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +20 -26
- package/dist/doctor.js +0 -7
- package/dist/fleet-proxy.d.ts +1 -1
- package/dist/harness/acp-session-transport.d.ts +8 -0
- package/dist/harness/acp-session-transport.js +3 -0
- package/dist/harness/agent-session.d.ts +33 -0
- package/dist/harness/agent-session.js +1 -0
- package/dist/harness/claude-code-session.d.ts +18 -0
- package/dist/harness/claude-code-session.js +28 -0
- package/dist/harness/claude-code.d.ts +2 -1
- package/dist/harness/claude-code.js +29 -81
- package/dist/harness/codex-session.d.ts +18 -0
- package/dist/harness/codex-session.js +28 -0
- package/dist/harness/codex.d.ts +2 -1
- package/dist/harness/codex.js +35 -45
- package/dist/harness/types.d.ts +6 -38
- package/dist/index.d.ts +3 -5
- package/dist/index.js +1 -3
- package/dist/isolation/resources.d.ts +3 -7
- package/dist/isolation/resources.js +3 -7
- package/dist/model-env.d.ts +2 -3
- package/dist/model-env.js +2 -3
- package/dist/monitor.d.ts +1 -24
- package/dist/monitor.js +5 -140
- package/dist/owner-channel/channel.d.ts +2 -2
- package/dist/runner.d.ts +5 -15
- package/dist/runner.js +26 -102
- package/dist/session/acp.d.ts +2 -2
- package/dist/session/acp.js +2 -3
- package/dist/session/activity.d.ts +2 -3
- package/dist/session/activity.js +2 -3
- package/dist/session/arbiter.d.ts +4 -4
- package/dist/session/control.d.ts +3 -3
- package/dist/session/types.d.ts +11 -5
- package/dist/spawn.js +8 -9
- package/dist/supervisor/none.d.ts +1 -1
- package/dist/supervisor/none.js +2 -2
- package/dist/tmux.d.ts +1 -14
- package/dist/tmux.js +1 -51
- package/dist/watchdog/config.js +6 -4
- package/dist/watchdog/run.js +8 -23
- package/dist/web/auth.d.ts +1 -1
- package/dist/web/runtime.js +4 -15
- package/dist/web/server.d.ts +1 -3
- package/dist/web/server.js +3 -14
- package/dist/web/topology-promote.js +2 -5
- package/dist/web-app/assets/index-59GF-gsZ.css +1 -0
- package/dist/web-app/assets/{index-BCBK78hw.js → index-DhMDVRU1.js} +6 -6
- package/dist/web-app/index.html +2 -2
- package/package.json +3 -8
- package/dist/session/tmux.d.ts +0 -28
- package/dist/session/tmux.js +0 -80
- package/dist/web/terminal/bridge.d.ts +0 -27
- package/dist/web/terminal/bridge.js +0 -317
- package/dist/web-app/assets/TerminalView-C_G1ID2P.js +0 -9
- package/dist/web-app/assets/index-DuC-xnX4.css +0 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ResolvedRole } from '../config.js';
|
|
2
|
+
import type { AcpMcpServer, SessionPrep } from './types.js';
|
|
3
|
+
import type { AgentSessionAdapter, AgentSessionStartOptions, PreparedAgentSessionLaunch } from './agent-session.js';
|
|
4
|
+
import { type AcpSessionTransport } from './acp-session-transport.js';
|
|
5
|
+
export interface ClaudeCodeSessionStrategy {
|
|
6
|
+
prepareLaunch(role: ResolvedRole, prep: SessionPrep): PreparedAgentSessionLaunch;
|
|
7
|
+
permissionModeId(role: ResolvedRole): string | undefined;
|
|
8
|
+
mcpServers(role: ResolvedRole): AcpMcpServer[] | undefined;
|
|
9
|
+
sessionMeta(role: ResolvedRole, prep: SessionPrep): Record<string, unknown> | undefined;
|
|
10
|
+
}
|
|
11
|
+
/** Explicit Claude Code implementation of Fleet's shared agent-session factory. */
|
|
12
|
+
export declare class ClaudeCodeAgentSessionAdapter implements AgentSessionAdapter {
|
|
13
|
+
private readonly strategy;
|
|
14
|
+
private readonly transport;
|
|
15
|
+
constructor(strategy: ClaudeCodeSessionStrategy, transport?: AcpSessionTransport);
|
|
16
|
+
prepareLaunch(role: ResolvedRole, prep: SessionPrep): PreparedAgentSessionLaunch;
|
|
17
|
+
start(options: AgentSessionStartOptions): Promise<import("../index.js").AgentSession>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AcpSession } from '../session/acp.js';
|
|
2
|
+
import { acpAdapterState, } from './acp-session-transport.js';
|
|
3
|
+
/** Explicit Claude Code implementation of Fleet's shared agent-session factory. */
|
|
4
|
+
export class ClaudeCodeAgentSessionAdapter {
|
|
5
|
+
strategy;
|
|
6
|
+
transport;
|
|
7
|
+
constructor(strategy, transport = AcpSession.start) {
|
|
8
|
+
this.strategy = strategy;
|
|
9
|
+
this.transport = transport;
|
|
10
|
+
}
|
|
11
|
+
prepareLaunch(role, prep) {
|
|
12
|
+
return this.strategy.prepareLaunch(role, prep);
|
|
13
|
+
}
|
|
14
|
+
start(options) {
|
|
15
|
+
const { role, prep, launch } = options;
|
|
16
|
+
return this.transport({
|
|
17
|
+
name: role.name, argv: launch.argv, cwd: options.cwd, env: launch.env,
|
|
18
|
+
stateDir: options.stateDir, mode: options.mode, permissions: options.permissions,
|
|
19
|
+
modeId: this.strategy.permissionModeId(role),
|
|
20
|
+
mcpServers: this.strategy.mcpServers(role),
|
|
21
|
+
sessionMeta: this.strategy.sessionMeta(role, prep),
|
|
22
|
+
permissionMode: options.permissionMode,
|
|
23
|
+
permissionMetadataSource: acpAdapterState(launch.adapterState).permissionMetadataSource,
|
|
24
|
+
scrubObsoleteOursAutostart: true,
|
|
25
|
+
log: options.log,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -2,6 +2,7 @@ import { type Exec } from '../exec.js';
|
|
|
2
2
|
import type { ResolvedRole } from '../config.js';
|
|
3
3
|
import type { HarnessAdapter, UnattendedCapability } from './types.js';
|
|
4
4
|
import { type LockDeps } from '../atomic-file.js';
|
|
5
|
+
import type { AcpSessionTransport } from './acp-session-transport.js';
|
|
5
6
|
/**
|
|
6
7
|
* Neutral approval → native Claude mode. The ONE definition, shared by launch
|
|
7
8
|
* and by translation so the two can never disagree about what a role will run
|
|
@@ -40,5 +41,5 @@ export declare function pretrust(dir: string, deps?: {
|
|
|
40
41
|
log?(line: string): void;
|
|
41
42
|
lock?: LockDeps;
|
|
42
43
|
}): Promise<void>;
|
|
43
|
-
export declare function makeClaudeCodeAdapter(exec?: Exec): HarnessAdapter;
|
|
44
|
+
export declare function makeClaudeCodeAdapter(exec?: Exec, transport?: AcpSessionTransport): HarnessAdapter;
|
|
44
45
|
export declare const claudeCodeAdapter: HarnessAdapter;
|
|
@@ -6,6 +6,7 @@ import { registerAdapter } from './registry.js';
|
|
|
6
6
|
import { replaceFileAtomically, withFileLock } from '../atomic-file.js';
|
|
7
7
|
import { harnessRuntimeDir } from '../isolation/policy.js';
|
|
8
8
|
import { bundledAcpAgent } from './acp-agent.js';
|
|
9
|
+
import { ClaudeCodeAgentSessionAdapter } from './claude-code-session.js';
|
|
9
10
|
const OPTION_KEYS = [
|
|
10
11
|
'plugins', 'mem_palace', 'mem_palace_midsession_autosave', 'permission_mode', 'effort',
|
|
11
12
|
'mcp_servers', 'mcp_servers_only',
|
|
@@ -223,9 +224,32 @@ const watchCommand = (id) => 'ours api watch-notifications --input '
|
|
|
223
224
|
const armMonitor = (id) => 'arm a **persistent Monitor** (the Monitor TOOL — NOT a background Bash command; a ' +
|
|
224
225
|
`background Bash task never wakes you on output) running \`${watchCommand(id)}\` ` +
|
|
225
226
|
'so inbound ours mail wakes you';
|
|
226
|
-
export function makeClaudeCodeAdapter(exec = realExec) {
|
|
227
|
+
export function makeClaudeCodeAdapter(exec = realExec, transport) {
|
|
227
228
|
return {
|
|
228
229
|
id: 'claude-code',
|
|
230
|
+
agentSession: new ClaudeCodeAgentSessionAdapter({
|
|
231
|
+
prepareLaunch(role, prep) {
|
|
232
|
+
const configured = role.session_options?.acp?.command;
|
|
233
|
+
const argv = Array.isArray(configured)
|
|
234
|
+
? [...configured]
|
|
235
|
+
: typeof configured === 'string'
|
|
236
|
+
? ['sh', '-c', configured]
|
|
237
|
+
: bundledAcpAgent('@agentclientprotocol/claude-agent-acp', 'claude-agent-acp', 'claude-agent-acp');
|
|
238
|
+
return { argv, env: prep.env };
|
|
239
|
+
},
|
|
240
|
+
permissionModeId: role => permissionMode(role),
|
|
241
|
+
mcpServers: role => acpMcpServersFor(role.harness_options?.mcp_servers),
|
|
242
|
+
sessionMeta(role, prep) {
|
|
243
|
+
if (customAcpCommand(role))
|
|
244
|
+
return undefined;
|
|
245
|
+
const options = {};
|
|
246
|
+
if (prep.settingsOverlay)
|
|
247
|
+
options.settings = prep.settingsOverlay;
|
|
248
|
+
if (role.harness_options?.mcp_servers_only === true)
|
|
249
|
+
options.strictMcpConfig = true;
|
|
250
|
+
return Object.keys(options).length ? { claudeCode: { options } } : undefined;
|
|
251
|
+
},
|
|
252
|
+
}, transport),
|
|
229
253
|
supportsResume: true,
|
|
230
254
|
async checkPrereqs() {
|
|
231
255
|
const r = await exec('claude', ['--version']);
|
|
@@ -305,7 +329,7 @@ export function makeClaudeCodeAdapter(exec = realExec) {
|
|
|
305
329
|
MEMPALACE_MIDSESSION_AUTOSAVE: o.mem_palace_midsession_autosave ? 'true' : 'false',
|
|
306
330
|
// The role's identity, for the ours connector to bind at startup instead of
|
|
307
331
|
// the briefing telling the MODEL to call choose_identity. Both launches
|
|
308
|
-
//
|
|
332
|
+
// reaches the adapter-owned ACP child through `prep.env`.
|
|
309
333
|
//
|
|
310
334
|
// The bind the connector performs is PLAIN and fail-closed: it can never
|
|
311
335
|
// evict a live session, and a role whose identity does not exist yet simply
|
|
@@ -321,99 +345,23 @@ export function makeClaudeCodeAdapter(exec = realExec) {
|
|
|
321
345
|
// sandbox needs this directory to exist before entry.
|
|
322
346
|
if (role.isolation)
|
|
323
347
|
mkdirSync(harnessRuntimeDir(dirs.stateDir, 'claude'), { recursive: true });
|
|
324
|
-
const argv = [];
|
|
325
348
|
let settingsOverlay;
|
|
326
349
|
if (Object.keys(enabledPlugins).length) {
|
|
327
350
|
settingsOverlay = join(dirs.stateDir, '.settings-overlay.json');
|
|
328
351
|
writeFileSync(settingsOverlay, JSON.stringify({ enabledPlugins }, null, 2));
|
|
329
|
-
argv.push('--settings', settingsOverlay);
|
|
330
352
|
}
|
|
331
|
-
//
|
|
332
|
-
// file's servers; `--strict-mcp-config` is what makes the set exclusive, and
|
|
333
|
-
// it is opt-in per role because it drops everything else the user has,
|
|
334
|
-
// plugins included (see `declaresOursConnector`).
|
|
353
|
+
// Keep the MCP config artifact for diagnostics and adapter metadata.
|
|
335
354
|
let mcpConfigFile;
|
|
336
355
|
if (o.mcp_servers) {
|
|
337
356
|
mcpConfigFile = join(dirs.stateDir, '.mcp-config.json');
|
|
338
357
|
writeFileSync(mcpConfigFile, JSON.stringify({ mcpServers: o.mcp_servers }, null, 2), { mode: 0o600 });
|
|
339
|
-
argv.push('--mcp-config', mcpConfigFile);
|
|
340
|
-
if (o.mcp_servers_only === true)
|
|
341
|
-
argv.push('--strict-mcp-config');
|
|
342
358
|
}
|
|
343
359
|
return {
|
|
344
|
-
|
|
360
|
+
env,
|
|
345
361
|
...(settingsOverlay ? { settingsOverlay } : {}),
|
|
346
362
|
...(mcpConfigFile ? { mcpConfigFile } : {}),
|
|
347
363
|
};
|
|
348
364
|
},
|
|
349
|
-
buildLaunch(role, mode, s, prep) {
|
|
350
|
-
const stateDir = roleStateDir(role);
|
|
351
|
-
const pm = permissionMode(role);
|
|
352
|
-
const o = role.harness_options;
|
|
353
|
-
const base = ['claude', ...(role.model ? ['--model', role.model] : []),
|
|
354
|
-
...(o?.effort ? ['--effort', o.effort] : []),
|
|
355
|
-
...(pm ? ['--permission-mode', pm] : []),
|
|
356
|
-
...prep.argv, '--remote-control', role.name];
|
|
357
|
-
const argv = mode === 'fresh'
|
|
358
|
-
? [...base, '--session-id', s.sessionId, `Read and follow ${join(stateDir, 'briefing.md')} now.`]
|
|
359
|
-
: [...base, '--resume', s.sessionId,
|
|
360
|
-
this.vocabulary.restartPrompt(role.identity, join(stateDir, 'WORKLOG.md'), role)];
|
|
361
|
-
return { argv, env: prep.env };
|
|
362
|
-
},
|
|
363
|
-
buildAcpLaunch(role, prep) {
|
|
364
|
-
const configured = role.session_options?.acp?.command;
|
|
365
|
-
const argv = Array.isArray(configured)
|
|
366
|
-
? [...configured]
|
|
367
|
-
: typeof configured === 'string'
|
|
368
|
-
? ['sh', '-c', configured]
|
|
369
|
-
: bundledAcpAgent('@agentclientprotocol/claude-agent-acp', 'claude-agent-acp', 'claude-agent-acp');
|
|
370
|
-
return { argv, env: prep.env };
|
|
371
|
-
},
|
|
372
|
-
// Same source as buildLaunch's --permission-mode flag, so the tmux and ACP
|
|
373
|
-
// backends cannot disagree about what a role's permissions translate to.
|
|
374
|
-
acpPermissionModeId(role) {
|
|
375
|
-
return permissionMode(role);
|
|
376
|
-
},
|
|
377
|
-
/**
|
|
378
|
-
* Deliver, over ACP, the two things the tmux launch delivers as flags.
|
|
379
|
-
*
|
|
380
|
-
* `buildAcpLaunch` builds its own argv and cannot carry `prep.argv`: the
|
|
381
|
-
* process it launches is the ACP agent, not `claude`, and it takes none of
|
|
382
|
-
* claude's flags. That is why `harness_options.plugins` did nothing at all on
|
|
383
|
-
* an ACP role — the overlay was written and then dropped, and the mem-palace
|
|
384
|
-
* toggle rode `prep.env` and survived, so the failure was silent AND
|
|
385
|
-
* selective.
|
|
386
|
-
*
|
|
387
|
-
* `_meta.claudeCode.options` is the bundled agent's own passthrough into the
|
|
388
|
-
* Claude Agent SDK (@agentclientprotocol/claude-agent-acp, acp-agent.js:4092
|
|
389
|
-
* → the `options` object at :4144). `settings` takes the same overlay path
|
|
390
|
-
* `--settings` takes; `strictMcpConfig` is the SDK's spelling of
|
|
391
|
-
* `--strict-mcp-config`. Both are spread BEFORE the fields the agent forces,
|
|
392
|
-
* so neither is overwritten.
|
|
393
|
-
*
|
|
394
|
-
* ⚠ RETURNS NOTHING FOR A ROLE THAT NAMES ITS OWN ACP COMMAND. That process
|
|
395
|
-
* is not the bundled agent and has no reason to read this vocabulary; sending
|
|
396
|
-
* it anyway would be the silent drop again, one level down. `validateOptions`
|
|
397
|
-
* refuses those roles instead.
|
|
398
|
-
*/
|
|
399
|
-
acpSessionMeta(role, prep) {
|
|
400
|
-
if (customAcpCommand(role))
|
|
401
|
-
return undefined;
|
|
402
|
-
const options = {};
|
|
403
|
-
if (prep.settingsOverlay)
|
|
404
|
-
options.settings = prep.settingsOverlay;
|
|
405
|
-
if (role.harness_options?.mcp_servers_only === true)
|
|
406
|
-
options.strictMcpConfig = true;
|
|
407
|
-
return Object.keys(options).length ? { claudeCode: { options } } : undefined;
|
|
408
|
-
},
|
|
409
|
-
/**
|
|
410
|
-
* The declared servers, in ACP's array shape. Sent on `session/new` and on
|
|
411
|
-
* resume/load, because the SDK builds its server set once per session and a
|
|
412
|
-
* resumed session that dropped them would quietly lose its tools.
|
|
413
|
-
*/
|
|
414
|
-
acpMcpServers(role) {
|
|
415
|
-
return acpMcpServersFor(role.harness_options?.mcp_servers);
|
|
416
|
-
},
|
|
417
365
|
isolationPaths(_role, _dirs) {
|
|
418
366
|
const claudeHome = join(home(), '.claude');
|
|
419
367
|
return {
|
|
@@ -501,7 +449,7 @@ export function makeClaudeCodeAdapter(exec = realExec) {
|
|
|
501
449
|
}
|
|
502
450
|
// The adapter needs the state dir for briefing/worklog paths in launch prompts.
|
|
503
451
|
// Roles' state dirs are canonical: agentDir(name) — temp roles carry their dir in cwd handling
|
|
504
|
-
// by the runner, which passes dirs to prepareSession
|
|
452
|
+
// by the runner, which passes dirs to prepareSession.
|
|
505
453
|
import { agentDir } from '../paths.js';
|
|
506
454
|
function roleStateDir(role) {
|
|
507
455
|
// Temp roles are marked by the runner via a private field to keep the interface small.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ResolvedRole } from '../config.js';
|
|
2
|
+
import type { AcpMcpServer, SessionPrep } from './types.js';
|
|
3
|
+
import type { AgentSessionAdapter, AgentSessionStartOptions, PreparedAgentSessionLaunch } from './agent-session.js';
|
|
4
|
+
import { type AcpSessionTransport } from './acp-session-transport.js';
|
|
5
|
+
export interface CodexSessionStrategy {
|
|
6
|
+
prepareLaunch(role: ResolvedRole, prep: SessionPrep): PreparedAgentSessionLaunch;
|
|
7
|
+
permissionModeId(role: ResolvedRole): string | undefined;
|
|
8
|
+
mcpServers(role: ResolvedRole): AcpMcpServer[] | undefined;
|
|
9
|
+
sessionMeta(role: ResolvedRole, prep: SessionPrep): Record<string, unknown> | undefined;
|
|
10
|
+
}
|
|
11
|
+
/** Explicit Codex implementation of Fleet's shared agent-session factory. */
|
|
12
|
+
export declare class CodexAgentSessionAdapter implements AgentSessionAdapter {
|
|
13
|
+
private readonly strategy;
|
|
14
|
+
private readonly transport;
|
|
15
|
+
constructor(strategy: CodexSessionStrategy, transport?: AcpSessionTransport);
|
|
16
|
+
prepareLaunch(role: ResolvedRole, prep: SessionPrep): PreparedAgentSessionLaunch;
|
|
17
|
+
start(options: AgentSessionStartOptions): Promise<import("../index.js").AgentSession>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AcpSession } from '../session/acp.js';
|
|
2
|
+
import { acpAdapterState, } from './acp-session-transport.js';
|
|
3
|
+
/** Explicit Codex implementation of Fleet's shared agent-session factory. */
|
|
4
|
+
export class CodexAgentSessionAdapter {
|
|
5
|
+
strategy;
|
|
6
|
+
transport;
|
|
7
|
+
constructor(strategy, transport = AcpSession.start) {
|
|
8
|
+
this.strategy = strategy;
|
|
9
|
+
this.transport = transport;
|
|
10
|
+
}
|
|
11
|
+
prepareLaunch(role, prep) {
|
|
12
|
+
return this.strategy.prepareLaunch(role, prep);
|
|
13
|
+
}
|
|
14
|
+
start(options) {
|
|
15
|
+
const { role, prep, launch } = options;
|
|
16
|
+
return this.transport({
|
|
17
|
+
name: role.name, argv: launch.argv, cwd: options.cwd, env: launch.env,
|
|
18
|
+
stateDir: options.stateDir, mode: options.mode, permissions: options.permissions,
|
|
19
|
+
modeId: this.strategy.permissionModeId(role),
|
|
20
|
+
mcpServers: this.strategy.mcpServers(role),
|
|
21
|
+
sessionMeta: this.strategy.sessionMeta(role, prep),
|
|
22
|
+
permissionMode: options.permissionMode,
|
|
23
|
+
permissionMetadataSource: acpAdapterState(launch.adapterState).permissionMetadataSource,
|
|
24
|
+
scrubObsoleteOursAutostart: true,
|
|
25
|
+
log: options.log,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
package/dist/harness/codex.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Exec } from '../exec.js';
|
|
2
2
|
import type { AcpLaunch, HarnessAdapter, UnattendedCapability } from './types.js';
|
|
3
3
|
import { type AcpAgentResolution } from './acp-agent.js';
|
|
4
|
+
import type { AcpSessionTransport } from './acp-session-transport.js';
|
|
4
5
|
/**
|
|
5
6
|
* What an unattended role can actually do under Codex's native settings.
|
|
6
7
|
* `on-request` and `untrusted` stop to ask, and with no console attached that
|
|
@@ -9,5 +10,5 @@ import { type AcpAgentResolution } from './acp-agent.js';
|
|
|
9
10
|
export declare function codexCapabilities(approval: string, sandbox: string): UnattendedCapability[];
|
|
10
11
|
/** Bind launch argv and metadata provenance to one already-completed resolution. */
|
|
11
12
|
export declare function codexAcpLaunchForResolution(resolution: AcpAgentResolution): Pick<AcpLaunch, 'argv' | 'permissionMetadataSource'>;
|
|
12
|
-
export declare function makeCodexAdapter(exec?: Exec): HarnessAdapter;
|
|
13
|
+
export declare function makeCodexAdapter(exec?: Exec, transport?: AcpSessionTransport): HarnessAdapter;
|
|
13
14
|
export declare const codexAdapter: HarnessAdapter;
|
package/dist/harness/codex.js
CHANGED
|
@@ -6,6 +6,7 @@ import { realExec } from '../exec.js';
|
|
|
6
6
|
import { registerAdapter } from './registry.js';
|
|
7
7
|
import { harnessRuntimeDir } from '../isolation/policy.js';
|
|
8
8
|
import { resolveBundledAcpAgent, } from './acp-agent.js';
|
|
9
|
+
import { CodexAgentSessionAdapter } from './codex-session.js';
|
|
9
10
|
const OPTION_KEYS = [
|
|
10
11
|
'launcher', 'sandbox', 'approval', 'permission_mode', 'search', 'profile', 'config', 'add_dirs',
|
|
11
12
|
'monitor',
|
|
@@ -124,6 +125,24 @@ function launcherMode(role) {
|
|
|
124
125
|
function bundledCodexAcp() {
|
|
125
126
|
return resolveBundledAcpAgent(CODEX_ACP_PACKAGE, 'codex-acp', 'codex-acp');
|
|
126
127
|
}
|
|
128
|
+
function codexAgentLaunch(role, prep) {
|
|
129
|
+
const configured = role.session_options?.acp?.command;
|
|
130
|
+
const resolved = configured == null
|
|
131
|
+
? codexAcpLaunchForResolution(bundledCodexAcp())
|
|
132
|
+
: undefined;
|
|
133
|
+
const argv = Array.isArray(configured)
|
|
134
|
+
? [...configured]
|
|
135
|
+
: typeof configured === 'string'
|
|
136
|
+
? ['sh', '-c', configured]
|
|
137
|
+
: resolved.argv;
|
|
138
|
+
const initialMode = acpAgentMode(role);
|
|
139
|
+
return {
|
|
140
|
+
argv,
|
|
141
|
+
env: initialMode ? { ...prep.env, INITIAL_AGENT_MODE: initialMode } : prep.env,
|
|
142
|
+
...(resolved?.permissionMetadataSource
|
|
143
|
+
? { permissionMetadataSource: resolved.permissionMetadataSource } : {}),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
127
146
|
/** Bind launch argv and metadata provenance to one already-completed resolution. */
|
|
128
147
|
export function codexAcpLaunchForResolution(resolution) {
|
|
129
148
|
const permissionMetadataSource = resolution.bundled
|
|
@@ -228,9 +247,23 @@ function hasInstalledOursPlugin(output) {
|
|
|
228
247
|
return false;
|
|
229
248
|
}
|
|
230
249
|
}
|
|
231
|
-
export function makeCodexAdapter(exec = realExec) {
|
|
250
|
+
export function makeCodexAdapter(exec = realExec, transport) {
|
|
232
251
|
return {
|
|
233
252
|
id: 'codex',
|
|
253
|
+
agentSession: new CodexAgentSessionAdapter({
|
|
254
|
+
prepareLaunch(role, prep) {
|
|
255
|
+
const launch = codexAgentLaunch(role, prep);
|
|
256
|
+
const { permissionMetadataSource, ...neutral } = launch;
|
|
257
|
+
return {
|
|
258
|
+
...neutral,
|
|
259
|
+
...(permissionMetadataSource
|
|
260
|
+
? { adapterState: { permissionMetadataSource } } : {}),
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
permissionModeId: role => acpAgentMode(role),
|
|
264
|
+
mcpServers: () => undefined,
|
|
265
|
+
sessionMeta: () => undefined,
|
|
266
|
+
}, transport),
|
|
234
267
|
supportsResume: true,
|
|
235
268
|
async checkPrereqs() {
|
|
236
269
|
const [r, hasOursCodex, plugins] = await Promise.all([
|
|
@@ -314,58 +347,15 @@ export function makeCodexAdapter(exec = realExec) {
|
|
|
314
347
|
// sandbox needs this directory to exist before entry.
|
|
315
348
|
if (role.isolation)
|
|
316
349
|
mkdirSync(harnessRuntimeDir(dirs.stateDir, 'codex'), { recursive: true });
|
|
317
|
-
const requested = launcherMode(role);
|
|
318
|
-
const hasOursCodex = await commandAvailable('ours-codex', exec);
|
|
319
|
-
if (requested === 'ours-codex' && !hasOursCodex)
|
|
320
|
-
throw new Error('harness_options.launcher is ours-codex, but ours-codex is not on PATH; install @ours.network/codex or use launcher: auto');
|
|
321
|
-
const command = requested === 'codex' ? 'codex' : hasOursCodex ? 'ours-codex' : 'codex';
|
|
322
350
|
return {
|
|
323
|
-
argv: [],
|
|
324
351
|
// OURS_BIND_IDENTITY is the connector's startup bind seed — see the note in
|
|
325
352
|
// claude-code.ts's prepareSession. It belongs on EVERY harness that runs a
|
|
326
353
|
// role with an ours identity, not just claude-code: a seed that works on one
|
|
327
354
|
// harness and silently does nothing on the other is the same class of defect
|
|
328
355
|
// as a config key that only works on one session type.
|
|
329
356
|
env: { OURS_BIND_IDENTITY: role.identity, ...codexAcpEnvironment(role, dirs) },
|
|
330
|
-
command,
|
|
331
|
-
};
|
|
332
|
-
},
|
|
333
|
-
buildLaunch(role, mode, _s, prep) {
|
|
334
|
-
const stateDir = roleStateDir(role);
|
|
335
|
-
const flags = commonFlags(role);
|
|
336
|
-
const command = prep.command ?? 'codex';
|
|
337
|
-
const argv = mode === 'fresh'
|
|
338
|
-
? [command, ...flags, ...prep.argv, `Read and follow ${join(stateDir, 'briefing.md')} now.`]
|
|
339
|
-
: [command, 'resume', '--last', ...flags, ...prep.argv,
|
|
340
|
-
this.vocabulary.restartPrompt(role.identity, join(stateDir, 'WORKLOG.md'), role)];
|
|
341
|
-
return { argv, env: prep.env };
|
|
342
|
-
},
|
|
343
|
-
buildAcpLaunch(role, prep) {
|
|
344
|
-
const configured = role.session_options?.acp?.command;
|
|
345
|
-
// Resolve once: both argv and permission-metadata provenance must describe
|
|
346
|
-
// the same artifact. A bare PATH fallback is launchable for compatibility,
|
|
347
|
-
// but is never authenticated for protected-MCP auto-approval.
|
|
348
|
-
const resolved = configured == null
|
|
349
|
-
? codexAcpLaunchForResolution(bundledCodexAcp())
|
|
350
|
-
: undefined;
|
|
351
|
-
const argv = Array.isArray(configured)
|
|
352
|
-
? [...configured]
|
|
353
|
-
: typeof configured === 'string'
|
|
354
|
-
? ['sh', '-c', configured]
|
|
355
|
-
: resolved.argv;
|
|
356
|
-
const initialMode = acpAgentMode(role);
|
|
357
|
-
return {
|
|
358
|
-
argv,
|
|
359
|
-
env: initialMode ? { ...prep.env, INITIAL_AGENT_MODE: initialMode } : prep.env,
|
|
360
|
-
...(resolved?.permissionMetadataSource
|
|
361
|
-
? { permissionMetadataSource: resolved.permissionMetadataSource } : {}),
|
|
362
357
|
};
|
|
363
358
|
},
|
|
364
|
-
// INITIAL_AGENT_MODE covers session/new in codex-acp; session/set_mode
|
|
365
|
-
// keeps resumed/loaded sessions and live status on the identical mode.
|
|
366
|
-
acpPermissionModeId(role) {
|
|
367
|
-
return acpAgentMode(role);
|
|
368
|
-
},
|
|
369
359
|
isolationPaths(role, _dirs) {
|
|
370
360
|
const codexHome = join(home(), '.codex');
|
|
371
361
|
const profile = role.harness_options?.profile;
|
|
@@ -508,7 +498,7 @@ export function makeCodexAdapter(exec = realExec) {
|
|
|
508
498
|
}
|
|
509
499
|
// The adapter needs the state dir for briefing/worklog paths in launch prompts.
|
|
510
500
|
// Roles' state dirs are canonical: agentDir(name) — temp roles carry their dir in cwd handling
|
|
511
|
-
// by the runner, which passes dirs to prepareSession
|
|
501
|
+
// by the runner, which passes dirs to prepareSession.
|
|
512
502
|
function roleStateDir(role) {
|
|
513
503
|
// Temp roles are marked by the runner via a private field to keep the interface small.
|
|
514
504
|
const temp = role.__temp === true;
|
package/dist/harness/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from '@agentclientprotocol/sdk';
|
|
2
2
|
import type { CommonPermissions, FleetPermissionMode, ResolvedRole } from '../config.js';
|
|
3
|
+
import type { AgentSessionAdapter } from './agent-session.js';
|
|
3
4
|
export interface PrereqCheck {
|
|
4
5
|
name: string;
|
|
5
6
|
ok: boolean;
|
|
@@ -13,27 +14,19 @@ export interface RoleDirs {
|
|
|
13
14
|
stateDir: string;
|
|
14
15
|
runCwd: string;
|
|
15
16
|
}
|
|
16
|
-
export interface SessionState {
|
|
17
|
-
sessionId: string;
|
|
18
|
-
}
|
|
19
17
|
/** Extra command/argv/env contributed by prepareSession (overlays, trust, limits). */
|
|
20
18
|
export interface SessionPrep {
|
|
21
|
-
argv: string[];
|
|
22
19
|
env: Record<string, string>;
|
|
23
|
-
/** Optional launcher selected after runtime prerequisite probing. */
|
|
24
|
-
command?: string;
|
|
25
20
|
/**
|
|
26
21
|
* The settings overlay prepareSession wrote, if it wrote one.
|
|
27
22
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* the two deliveries read one value instead of each re-deriving the filename.
|
|
23
|
+
* Claude's ACP agent takes no settings flag, so it needs the path in session
|
|
24
|
+
* metadata. Record it here rather than re-deriving the filename.
|
|
31
25
|
*/
|
|
32
26
|
settingsOverlay?: string;
|
|
33
27
|
/**
|
|
34
28
|
* The MCP config file prepareSession wrote for `harness_options.mcp_servers`,
|
|
35
|
-
* if the role declared any.
|
|
36
|
-
* passes the file, the ACP launch has to send the servers themselves.
|
|
29
|
+
* if the role declared any. The ACP launch sends the parsed servers themselves.
|
|
37
30
|
*/
|
|
38
31
|
mcpConfigFile?: string;
|
|
39
32
|
}
|
|
@@ -121,6 +114,8 @@ export interface ValidationError {
|
|
|
121
114
|
}
|
|
122
115
|
export interface HarnessAdapter {
|
|
123
116
|
id: string;
|
|
117
|
+
/** Harness-specific construction behind Fleet's shared live-session contract. */
|
|
118
|
+
agentSession: AgentSessionAdapter;
|
|
124
119
|
supportsResume: boolean;
|
|
125
120
|
checkPrereqs(): Promise<PrereqReport>;
|
|
126
121
|
/**
|
|
@@ -131,33 +126,6 @@ export interface HarnessAdapter {
|
|
|
131
126
|
*/
|
|
132
127
|
validateOptions(opts: unknown, role?: ResolvedRole): ValidationError[];
|
|
133
128
|
prepareSession(role: ResolvedRole, dirs: RoleDirs): Promise<SessionPrep>;
|
|
134
|
-
buildLaunch(role: ResolvedRole, mode: 'fresh' | 'resume', s: SessionState, prep: SessionPrep): Launch;
|
|
135
|
-
buildAcpLaunch?(role: ResolvedRole, prep: SessionPrep): AcpLaunch;
|
|
136
|
-
/**
|
|
137
|
-
* The native permission-mode id an ACP session should run at, from the same
|
|
138
|
-
* translation `buildLaunch` uses for its CLI flag. `undefined` keeps the
|
|
139
|
-
* agent's default. Omit for a harness whose ACP agent has no modes.
|
|
140
|
-
*/
|
|
141
|
-
acpPermissionModeId?(role: ResolvedRole): string | undefined;
|
|
142
|
-
/**
|
|
143
|
-
* The MCP servers this role declares, for the `mcpServers` array of ACP's
|
|
144
|
-
* `session/new` / `resume` / `load`. ACP requires an array on the wire, so
|
|
145
|
-
* `undefined` is encoded as `[]` while the authenticated bundled adapter is
|
|
146
|
-
* left to preserve its inherited configuration. An explicit empty array uses
|
|
147
|
-
* that adapter's compatibility path to disable all inherited servers.
|
|
148
|
-
*/
|
|
149
|
-
acpMcpServers?(role: ResolvedRole): AcpMcpServer[] | undefined;
|
|
150
|
-
/**
|
|
151
|
-
* Agent-specific `_meta` for `session/new` — how a capability the CLI takes as
|
|
152
|
-
* a flag reaches an ACP agent that accepts no flags.
|
|
153
|
-
*
|
|
154
|
-
* ⚠ THIS IS A PER-AGENT VOCABULARY, NOT PROTOCOL. `_meta` is free-form in ACP,
|
|
155
|
-
* so what an adapter puts here is only honoured by the agent it was written
|
|
156
|
-
* for. An adapter must therefore return nothing for an ACP command it did not
|
|
157
|
-
* choose, and the options that depend on it must be refused at validation for
|
|
158
|
-
* such a role rather than sent and silently ignored.
|
|
159
|
-
*/
|
|
160
|
-
acpSessionMeta?(role: ResolvedRole, prep: SessionPrep): Record<string, unknown> | undefined;
|
|
161
129
|
/** Effective portable policy and harness-native approval mode after native overrides win. */
|
|
162
130
|
effectivePermissionMode?(role: ResolvedRole): {
|
|
163
131
|
fleetMode: FleetPermissionMode;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
export { loadConfig, findRole, resolvePermissions, ConfigError } from './config.js';
|
|
2
2
|
export type { FleetConfig, ResolvedRole, RoleConfig, OverseeEntry, SessionBackendId, CommonPermissions, FleetPermissionMode, ApprovalMode, SessionOptions, OwnerChannelConfig, } from './config.js';
|
|
3
|
-
export type { HarnessAdapter, BriefingVocab, ExitPolicy, PrereqReport, PrereqCheck, SessionPrep,
|
|
4
|
-
export type { SessionHandle, SessionSnapshot, SessionEvent, TurnResult, InterruptOutcome, InterruptResult, TurnCancellationSource, QueuedPrompt, ExitRecord, } from './session/types.js';
|
|
3
|
+
export type { HarnessAdapter, BriefingVocab, ExitPolicy, PrereqReport, PrereqCheck, SessionPrep, Launch, AcpLaunch, PermissionTranslation, RoleDirs, ValidationError, } from './harness/types.js';
|
|
4
|
+
export type { AgentSession, SessionHandle, SessionSnapshot, SessionEvent, TurnResult, InterruptOutcome, InterruptResult, TurnCancellationSource, QueuedPrompt, ExitRecord, } from './session/types.js';
|
|
5
5
|
export { interruptOutcome } from './session/types.js';
|
|
6
6
|
export { AcpSession } from './session/acp.js';
|
|
7
7
|
export { OwnerChannel } from './owner-channel/channel.js';
|
|
8
|
-
export { TmuxSession } from './session/tmux.js';
|
|
9
8
|
export { registerAdapter, getAdapter, knownAdapters } from './harness/registry.js';
|
|
10
9
|
export { claudeCodeAdapter, makeClaudeCodeAdapter } from './harness/claude-code.js';
|
|
11
10
|
export { codexAdapter, makeCodexAdapter } from './harness/codex.js';
|
|
@@ -17,7 +16,7 @@ export { up, down, restartRoles, rmRole, applyRole } from './ops.js';
|
|
|
17
16
|
export { spawnPermanent, spawnTemp, buildRoleConfig, profileValues, validateSpawnOpts, } from './spawn.js';
|
|
18
17
|
export { RoleRepository } from './application/role-repository.js';
|
|
19
18
|
export { FleetQueryService } from './application/fleet-query-service.js';
|
|
20
|
-
export { AcpRoleSessionAdapter
|
|
19
|
+
export { AcpRoleSessionAdapter } from './application/session-control.js';
|
|
21
20
|
export { RoleCreationService } from './application/role-creation-service.js';
|
|
22
21
|
export { RoleCommandService, RoleLifecycleService } from './application/role-command-service.js';
|
|
23
22
|
export { StructuredLogService } from './application/log-service.js';
|
|
@@ -26,5 +25,4 @@ export { FleetError, normalizeError } from './application/errors.js';
|
|
|
26
25
|
export type * from './application/types.js';
|
|
27
26
|
export { doctor } from './doctor.js';
|
|
28
27
|
export { runOnce, runTemp } from './runner.js';
|
|
29
|
-
export { Tmux } from './tmux.js';
|
|
30
28
|
export { VERSION } from './version.js';
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,6 @@ export { loadConfig, findRole, resolvePermissions, ConfigError } from './config.
|
|
|
2
2
|
export { interruptOutcome } from './session/types.js';
|
|
3
3
|
export { AcpSession } from './session/acp.js';
|
|
4
4
|
export { OwnerChannel } from './owner-channel/channel.js';
|
|
5
|
-
export { TmuxSession } from './session/tmux.js';
|
|
6
5
|
export { registerAdapter, getAdapter, knownAdapters } from './harness/registry.js';
|
|
7
6
|
export { claudeCodeAdapter, makeClaudeCodeAdapter } from './harness/claude-code.js';
|
|
8
7
|
export { codexAdapter, makeCodexAdapter } from './harness/codex.js';
|
|
@@ -13,7 +12,7 @@ export { up, down, restartRoles, rmRole, applyRole } from './ops.js';
|
|
|
13
12
|
export { spawnPermanent, spawnTemp, buildRoleConfig, profileValues, validateSpawnOpts, } from './spawn.js';
|
|
14
13
|
export { RoleRepository } from './application/role-repository.js';
|
|
15
14
|
export { FleetQueryService } from './application/fleet-query-service.js';
|
|
16
|
-
export { AcpRoleSessionAdapter
|
|
15
|
+
export { AcpRoleSessionAdapter } from './application/session-control.js';
|
|
17
16
|
export { RoleCreationService } from './application/role-creation-service.js';
|
|
18
17
|
export { RoleCommandService, RoleLifecycleService } from './application/role-command-service.js';
|
|
19
18
|
export { StructuredLogService } from './application/log-service.js';
|
|
@@ -21,5 +20,4 @@ export { roleCapabilities } from './application/capabilities.js';
|
|
|
21
20
|
export { FleetError, normalizeError } from './application/errors.js';
|
|
22
21
|
export { doctor } from './doctor.js';
|
|
23
22
|
export { runOnce, runTemp } from './runner.js';
|
|
24
|
-
export { Tmux } from './tmux.js';
|
|
25
23
|
export { VERSION } from './version.js';
|
|
@@ -4,13 +4,9 @@ export interface ResourceArgs {
|
|
|
4
4
|
warnings: string[];
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* Build the `systemd-run --user --scope -p … --` prefix that caps the
|
|
8
|
-
* cgroup-v2 scope.
|
|
9
|
-
*
|
|
10
|
-
* only reliable per-agent limit is a transient scope at the pane itself. (Since
|
|
11
|
-
* #32 that server is per role rather than fleet-wide, which is what keeps one
|
|
12
|
-
* role's `stop` off every other role's pane — the limit still belongs on the
|
|
13
|
-
* pane.)
|
|
7
|
+
* Build the `systemd-run --user --scope -p … --` prefix that caps the agent
|
|
8
|
+
* process's cgroup-v2 scope. It composes outside the sandbox wrapper so both
|
|
9
|
+
* the adapter and its sandboxed child remain inside the transient scope.
|
|
14
10
|
*
|
|
15
11
|
* mem/pids are always enforced (their controllers are delegated to `--user` by
|
|
16
12
|
* default). cpu degrades to a warning when the cpu controller is not delegated.
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
/**
|
|
3
|
-
* Build the `systemd-run --user --scope -p … --` prefix that caps the
|
|
4
|
-
* cgroup-v2 scope.
|
|
5
|
-
*
|
|
6
|
-
* only reliable per-agent limit is a transient scope at the pane itself. (Since
|
|
7
|
-
* #32 that server is per role rather than fleet-wide, which is what keeps one
|
|
8
|
-
* role's `stop` off every other role's pane — the limit still belongs on the
|
|
9
|
-
* pane.)
|
|
3
|
+
* Build the `systemd-run --user --scope -p … --` prefix that caps the agent
|
|
4
|
+
* process's cgroup-v2 scope. It composes outside the sandbox wrapper so both
|
|
5
|
+
* the adapter and its sandboxed child remain inside the transient scope.
|
|
10
6
|
*
|
|
11
7
|
* mem/pids are always enforced (their controllers are delegated to `--user` by
|
|
12
8
|
* default). cpu degrades to a warning when the cpu controller is not delegated.
|
package/dist/model-env.d.ts
CHANGED
|
@@ -3,9 +3,8 @@ import type { ResolvedRole } from './config.js';
|
|
|
3
3
|
* Which environment variable a harness reads to pin the model it RUNS.
|
|
4
4
|
*
|
|
5
5
|
* This is not a convenience: for `claude-code` it is the only channel that
|
|
6
|
-
* reaches the
|
|
7
|
-
*
|
|
8
|
-
* adapter resolves its model in this order — ANTHROPIC_MODEL, then
|
|
6
|
+
* reaches the Claude Code ACP adapter. The adapter resolves its model in this
|
|
7
|
+
* order — ANTHROPIC_MODEL, then
|
|
9
8
|
* `settings.model`, then a resumed session's live model, then its first
|
|
10
9
|
* catalogue entry. A role's declared model was therefore invisible to every
|
|
11
10
|
* ACP role, and a fleet-wide `defaults.env.ANTHROPIC_MODEL` silently outranked
|
package/dist/model-env.js
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
* Which environment variable a harness reads to pin the model it RUNS.
|
|
3
3
|
*
|
|
4
4
|
* This is not a convenience: for `claude-code` it is the only channel that
|
|
5
|
-
* reaches the
|
|
6
|
-
*
|
|
7
|
-
* adapter resolves its model in this order — ANTHROPIC_MODEL, then
|
|
5
|
+
* reaches the Claude Code ACP adapter. The adapter resolves its model in this
|
|
6
|
+
* order — ANTHROPIC_MODEL, then
|
|
8
7
|
* `settings.model`, then a resumed session's live model, then its first
|
|
9
8
|
* catalogue entry. A role's declared model was therefore invisible to every
|
|
10
9
|
* ACP role, and a fleet-wide `defaults.env.ANTHROPIC_MODEL` silently outranked
|