@ours.network/fleet 1.0.5 → 1.1.0-nightly.2
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 +90 -112
- 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 +14 -9
- package/dist/application/role-removal-service.js +6 -3
- 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 +1 -8
- package/dist/briefing.js +3 -6
- package/dist/build-info.json +5 -5
- package/dist/cli.js +17 -28
- package/dist/config.d.ts +29 -6
- package/dist/config.js +320 -40
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +2 -2
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +51 -95
- package/dist/doctor.js +1 -8
- package/dist/fleet-proxy.d.ts +1 -1
- package/dist/generated-agent-source.d.ts +9 -0
- package/dist/generated-agent-source.js +53 -0
- 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 +46 -0
- package/dist/harness/agent-session.js +1 -0
- package/dist/harness/claude-code-session.d.ts +22 -0
- package/dist/harness/claude-code-session.js +32 -0
- package/dist/harness/claude-code.d.ts +2 -1
- package/dist/harness/claude-code.js +39 -81
- package/dist/harness/codex-session.d.ts +22 -0
- package/dist/harness/codex-session.js +32 -0
- package/dist/harness/codex.d.ts +2 -1
- package/dist/harness/codex.js +49 -45
- package/dist/harness/registry.js +4 -0
- package/dist/harness/types.d.ts +6 -38
- package/dist/index.d.ts +3 -5
- package/dist/index.js +1 -3
- package/dist/init-guidance.d.ts +1 -0
- package/dist/init-guidance.js +5 -0
- package/dist/isolation/resources.d.ts +3 -7
- package/dist/isolation/resources.js +3 -7
- package/dist/model-env.d.ts +2 -4
- package/dist/model-env.js +4 -7
- package/dist/monitor.d.ts +1 -24
- package/dist/monitor.js +5 -140
- package/dist/ops.js +4 -2
- package/dist/owner-channel/channel.d.ts +2 -2
- package/dist/resolved-plan.d.ts +3 -2
- package/dist/resolved-plan.js +21 -5
- package/dist/runner.d.ts +5 -15
- package/dist/runner.js +26 -102
- package/dist/sensitive-config.d.ts +2 -0
- package/dist/sensitive-config.js +4 -0
- 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.d.ts +4 -4
- package/dist/spawn.js +60 -32
- 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/fleet-config-service.d.ts +11 -8
- package/dist/web/fleet-config-service.js +383 -183
- 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 +18 -18
- package/dist/web/topology.js +2 -2
- package/dist/web/yaml-document-edit.js +2 -0
- package/dist/web-app/assets/index-59GF-gsZ.css +1 -0
- package/dist/web-app/assets/index-BbE9EX6n.js +10 -0
- package/dist/web-app/index.html +2 -2
- package/examples/fleet/agents/Alice.yaml +11 -0
- package/examples/fleet/agents/FleetCoordinator.yaml +7 -0
- package/examples/fleet/brains/claude-default.yaml +5 -0
- package/examples/fleet/roles/coordinator.yaml +5 -0
- package/examples/fleet/roles/developer.yaml +3 -0
- package/examples/fleet.yaml +74 -0
- package/package.json +4 -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-BCBK78hw.js +0 -10
- package/dist/web-app/assets/index-DuC-xnX4.css +0 -1
|
@@ -4,6 +4,7 @@ import { loadConfig, ROLE_NAME_RE } from '../config.js';
|
|
|
4
4
|
import { agentDir, stateRoot } from '../paths.js';
|
|
5
5
|
import { rmRole } from '../ops.js';
|
|
6
6
|
import { FleetError } from './errors.js';
|
|
7
|
+
import { provesGeneratedAgentSource } from '../generated-agent-source.js';
|
|
7
8
|
export class RoleRemovalService {
|
|
8
9
|
options;
|
|
9
10
|
constructor(options) {
|
|
@@ -45,8 +46,9 @@ export class RoleRemovalService {
|
|
|
45
46
|
`Stop and uninstall the exact backend registration for '${requested}'.`,
|
|
46
47
|
`Archive then remove ${lifetime === 'temporary' ? temporaryState : permanentState}.`,
|
|
47
48
|
];
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
const generatedSource = role && provesGeneratedAgentSource(agentDir(requested), cfg.files[0], role.sourceFile);
|
|
50
|
+
if (generatedSource)
|
|
51
|
+
effects.push(`Remove generated Agent configuration ${role.sourceFile}.`);
|
|
50
52
|
else if (role)
|
|
51
53
|
effects.push(`Keep hand-written configuration ${role.sourceFile}; the role can be recreated from it.`);
|
|
52
54
|
else
|
|
@@ -76,7 +78,8 @@ export class RoleRemovalService {
|
|
|
76
78
|
mkdirSync(recoveryPath, { recursive: true, mode: 0o700 });
|
|
77
79
|
if (existsSync(sourceState))
|
|
78
80
|
cpSync(sourceState, join(recoveryPath, 'state'), { recursive: true });
|
|
79
|
-
if (role
|
|
81
|
+
if (role && provesGeneratedAgentSource(agentDir(preview.role), cfg.files[0], role.sourceFile)
|
|
82
|
+
&& existsSync(role.sourceFile))
|
|
80
83
|
cpSync(role.sourceFile, join(recoveryPath, basename(role.sourceFile)));
|
|
81
84
|
if (role || preview.lifetime === 'temporary')
|
|
82
85
|
await rmRole(cfg, preview.role, this.options.ops);
|
|
@@ -3,9 +3,8 @@ export interface RoleRepositoryOptions {
|
|
|
3
3
|
configPath?: string;
|
|
4
4
|
permanentRoot?: string;
|
|
5
5
|
temporaryRoot?: string;
|
|
6
|
-
probeBackend?: (name: string, intended?: 'acp'
|
|
6
|
+
probeBackend?: (name: string, intended?: 'acp') => Promise<{
|
|
7
7
|
acp: boolean;
|
|
8
|
-
tmux: boolean;
|
|
9
8
|
}>;
|
|
10
9
|
concurrency?: number;
|
|
11
10
|
timeoutMs?: number;
|
|
@@ -42,7 +42,11 @@ function snapshot(path, name) {
|
|
|
42
42
|
const raw = parse(readFileSync(path, 'utf8'));
|
|
43
43
|
if (!raw || typeof raw !== 'object')
|
|
44
44
|
throw new Error('snapshot must be a mapping');
|
|
45
|
-
|
|
45
|
+
if (raw.session === 'tmux')
|
|
46
|
+
throw new Error('session: tmux is no longer supported; use session: acp with the Codex or Claude Code adapter');
|
|
47
|
+
if (raw.session !== undefined && raw.session !== 'acp')
|
|
48
|
+
throw new Error(`unsupported session '${String(raw.session)}'; expected session: acp`);
|
|
49
|
+
const session = 'acp';
|
|
46
50
|
return {
|
|
47
51
|
role: {
|
|
48
52
|
...raw, name, harness: typeof raw.harness === 'string' ? raw.harness : 'claude-code',
|
|
@@ -167,14 +171,10 @@ export class RoleRepository {
|
|
|
167
171
|
const timeoutMs = this.options.timeoutMs ?? 2_000;
|
|
168
172
|
const result = await Promise.race([
|
|
169
173
|
this.options.probeBackend(name, intended),
|
|
170
|
-
new Promise(resolve => setTimeout(() => resolve({ acp: false
|
|
171
|
-
]).catch(() => ({ acp: false
|
|
172
|
-
if (result.acp && result.tmux)
|
|
173
|
-
return 'ambiguous';
|
|
174
|
+
new Promise(resolve => setTimeout(() => resolve({ acp: false }), timeoutMs)),
|
|
175
|
+
]).catch(() => ({ acp: false }));
|
|
174
176
|
if (result.acp)
|
|
175
177
|
return 'acp';
|
|
176
|
-
if (result.tmux)
|
|
177
|
-
return 'tmux';
|
|
178
178
|
return 'none';
|
|
179
179
|
}
|
|
180
180
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { controlRequest } from '../session/control.js';
|
|
2
2
|
import type { ConversationEventV1, ConversationSnapshot, PromptReceipt } from '../session/conversation-types.js';
|
|
3
3
|
import type { SessionSnapshot } from '../session/types.js';
|
|
4
|
-
import { Tmux } from '../tmux.js';
|
|
5
4
|
import type { OutputPage, SendReceipt, SessionDescriptor } from './types.js';
|
|
6
5
|
export interface ConversationPageView {
|
|
7
6
|
events: ConversationEventV1[];
|
|
@@ -120,14 +119,3 @@ export declare class AcpRoleSessionAdapter implements RoleSessionControl {
|
|
|
120
119
|
}>;
|
|
121
120
|
private call;
|
|
122
121
|
}
|
|
123
|
-
export declare class TmuxRoleSessionAdapter implements RoleSessionControl {
|
|
124
|
-
private readonly roleId;
|
|
125
|
-
private readonly tmux;
|
|
126
|
-
constructor(roleId: string, tmux?: Tmux);
|
|
127
|
-
describe(): Promise<SessionDescriptor>;
|
|
128
|
-
snapshot(): Promise<SessionSnapshot>;
|
|
129
|
-
recentOutput(request?: {
|
|
130
|
-
limit?: number;
|
|
131
|
-
}): Promise<OutputPage>;
|
|
132
|
-
sendText(text: string): Promise<SendReceipt>;
|
|
133
|
-
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
1
|
import { controlRequest, followConversation } from '../session/control.js';
|
|
3
|
-
import { Tmux } from '../tmux.js';
|
|
4
2
|
import { FleetError, normalizeError } from './errors.js';
|
|
5
3
|
const visibleEvents = (events) => events.filter(event => event.kind !== 'thought');
|
|
6
4
|
export class AcpRoleSessionAdapter {
|
|
@@ -149,43 +147,3 @@ export class AcpRoleSessionAdapter {
|
|
|
149
147
|
}
|
|
150
148
|
}
|
|
151
149
|
}
|
|
152
|
-
export class TmuxRoleSessionAdapter {
|
|
153
|
-
roleId;
|
|
154
|
-
tmux;
|
|
155
|
-
constructor(roleId, tmux = new Tmux()) {
|
|
156
|
-
this.roleId = roleId;
|
|
157
|
-
this.tmux = tmux;
|
|
158
|
-
}
|
|
159
|
-
async describe() {
|
|
160
|
-
return { backend: 'tmux', protocolVersion: 1, features: ['text', 'capture'] };
|
|
161
|
-
}
|
|
162
|
-
async snapshot() {
|
|
163
|
-
const alive = await this.tmux.has(this.roleId);
|
|
164
|
-
return { backend: 'tmux', alive, readiness: alive ? 'idle' : 'failed' };
|
|
165
|
-
}
|
|
166
|
-
async recentOutput(request = {}) {
|
|
167
|
-
try {
|
|
168
|
-
const text = await this.tmux.capture(this.roleId, Math.min(request.limit ?? 100, 500));
|
|
169
|
-
return { events: [], text, truncated: false };
|
|
170
|
-
}
|
|
171
|
-
catch (error) {
|
|
172
|
-
throw normalizeError(error);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
async sendText(text) {
|
|
176
|
-
if (!text.trim())
|
|
177
|
-
throw new FleetError('invalid_request', 'text is required');
|
|
178
|
-
if (Buffer.byteLength(text) > 32 * 1024)
|
|
179
|
-
throw new FleetError('invalid_request', 'text exceeds 32 KiB');
|
|
180
|
-
try {
|
|
181
|
-
await this.tmux.sendText(this.roleId, text);
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
throw normalizeError(error);
|
|
185
|
-
}
|
|
186
|
-
return {
|
|
187
|
-
accepted: true, promptId: randomUUID(), queuedBehind: 0, terminalOutcomeKnown: false,
|
|
188
|
-
detail: 'literal text and Enter sent; terminal outcome is unknown',
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { InterruptOutcome, QueuedPrompt,
|
|
1
|
+
import type { AgentSession, InterruptOutcome, QueuedPrompt, SubmitPromptOptions, TurnCancellationSource } from '../session/types.js';
|
|
2
2
|
/** Thin in-process session mutation kernel. Callers retain every policy decision. */
|
|
3
|
-
export declare const queueSessionPrompt: (session:
|
|
4
|
-
export declare const interruptSession: (session:
|
|
5
|
-
export declare const respondSessionPermission: (session:
|
|
3
|
+
export declare const queueSessionPrompt: (session: AgentSession, text: string, options?: SubmitPromptOptions) => Promise<QueuedPrompt>;
|
|
4
|
+
export declare const interruptSession: (session: AgentSession, source: TurnCancellationSource) => Promise<InterruptOutcome>;
|
|
5
|
+
export declare const respondSessionPermission: (session: AgentSession, permissionId: string, optionId: string) => boolean;
|
|
6
6
|
export type GenerationPermissionResult = 'accepted' | 'stale' | 'unavailable';
|
|
7
|
-
export declare const respondSessionPermissionV2: (session:
|
|
7
|
+
export declare const respondSessionPermissionV2: (session: AgentSession, permissionId: string, optionId: string, sessionGeneration: string) => GenerationPermissionResult;
|
|
@@ -18,7 +18,7 @@ export interface ResolvedRoleView {
|
|
|
18
18
|
sandbox?: string;
|
|
19
19
|
};
|
|
20
20
|
oversee?: Array<{
|
|
21
|
-
|
|
21
|
+
agent: string;
|
|
22
22
|
interval: string;
|
|
23
23
|
}>;
|
|
24
24
|
}
|
|
@@ -117,7 +117,6 @@ export interface RoleCapabilities {
|
|
|
117
117
|
};
|
|
118
118
|
input: {
|
|
119
119
|
text: boolean;
|
|
120
|
-
rawKeys: boolean;
|
|
121
120
|
interrupt: boolean;
|
|
122
121
|
steering: boolean;
|
|
123
122
|
};
|
|
@@ -125,12 +124,6 @@ export interface RoleCapabilities {
|
|
|
125
124
|
observe: boolean;
|
|
126
125
|
respond: boolean;
|
|
127
126
|
};
|
|
128
|
-
terminal: {
|
|
129
|
-
available: boolean;
|
|
130
|
-
reason?: 'wrong_backend' | 'pty_unavailable' | 'offline' | 'incompatible_runner';
|
|
131
|
-
multiViewer: boolean;
|
|
132
|
-
writerLease: boolean;
|
|
133
|
-
};
|
|
134
127
|
lifecycle: {
|
|
135
128
|
start: boolean;
|
|
136
129
|
stop: boolean;
|
package/dist/briefing.js
CHANGED
|
@@ -190,21 +190,18 @@ export function generateBriefing(role, v, opts) {
|
|
|
190
190
|
L.push(`7. Await messages. When the monitor wakes you (or the owner requests a manual check),`);
|
|
191
191
|
L.push(` call **${v.getMessagesTool}**, act on them,`);
|
|
192
192
|
L.push(` and reply with ${v.sendTool}. No coordinator is configured — the owner drives you`);
|
|
193
|
-
// NOT `tmux attach -t <name>`: each role's pane lives on its own tmux
|
|
194
|
-
// socket (#32), so a bare attach finds no server. `ours-fleet attach`
|
|
195
|
-
// addresses the right one.
|
|
196
193
|
L.push(` via \`ours-fleet attach ${role.name}\` or by messaging "${id}".`);
|
|
197
194
|
}
|
|
198
195
|
if (role.oversee?.length) {
|
|
199
196
|
L.push('', '## Oversight assignments');
|
|
200
197
|
L.push('These agents are your wards — you keep them unstuck:');
|
|
201
198
|
for (const o of role.oversee)
|
|
202
|
-
L.push(`- **${o.
|
|
199
|
+
L.push(`- **${o.agent}** — check every ${o.interval}`);
|
|
203
200
|
L.push('');
|
|
204
201
|
L.push('Procedure (see also the oversee-agents skill if available). On each tick, for each ward');
|
|
205
202
|
L.push('run BOTH — they answer different questions:');
|
|
206
203
|
for (const o of role.oversee)
|
|
207
|
-
L.push(`\`ours-fleet status ${o.
|
|
204
|
+
L.push(`\`ours-fleet status ${o.agent}\` then \`ours-fleet peek ${o.agent}\``);
|
|
208
205
|
L.push('');
|
|
209
206
|
L.push('**One console command is not a liveness verdict.** A `peek` or `send` that fails tells');
|
|
210
207
|
L.push('you what happened to YOUR REQUEST, and only one of its outcomes says the agent is gone.');
|
|
@@ -221,7 +218,7 @@ export function generateBriefing(role, v, opts) {
|
|
|
221
218
|
L.push('idle or stalled from `readiness=idle` alone.');
|
|
222
219
|
L.push('');
|
|
223
220
|
L.push('Then judge the console content: stuck on a prompt/menu/trust dialog → answer it directly');
|
|
224
|
-
L.push('with `ours-fleet send <Name> "<text>"
|
|
221
|
+
L.push('with `ours-fleet send <Name> "<text>"`; idle with work');
|
|
225
222
|
L.push('assigned → nudge; actively working → do nothing, and do not mistake a long turn for a');
|
|
226
223
|
L.push('stall. Escalate over ours messaging only when you cannot resolve it yourself.');
|
|
227
224
|
}
|
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.0.
|
|
3
|
-
"buildId": "
|
|
4
|
-
"commit": "
|
|
5
|
-
"dirty":
|
|
6
|
-
"builtAt": "2026-08-
|
|
2
|
+
"version": "1.1.0-nightly.2",
|
|
3
|
+
"buildId": "da774e6be6b1",
|
|
4
|
+
"commit": "f8890b0449f4142a0c27a195e1351555fe599186",
|
|
5
|
+
"dirty": true,
|
|
6
|
+
"builtAt": "2026-08-30T15:08:00.155Z",
|
|
7
7
|
"capabilities": [
|
|
8
8
|
"monitor.interrupt.after_tool"
|
|
9
9
|
]
|
package/dist/cli.js
CHANGED
|
@@ -6,12 +6,12 @@ import { join as joinPath, resolve as resolvePath } from 'node:path';
|
|
|
6
6
|
import { createInterface } from 'node:readline';
|
|
7
7
|
import { Command } from 'commander';
|
|
8
8
|
import { VERSION } from './version.js';
|
|
9
|
+
import { INIT_COMPLETION_GUIDANCE } from './init-guidance.js';
|
|
9
10
|
import { analyzeInstalls, buildInfo, buildLabel, discoverInstalls, runningLabel, } from './provenance.js';
|
|
10
11
|
import { agentDir, agentsRoot, tmpRoot, logsRoot, deriveXdgRuntimeDir } from './paths.js';
|
|
11
12
|
import { findRole, loadConfig } from './config.js';
|
|
12
13
|
import { formatDuration } from './duration.js';
|
|
13
|
-
import { resolvedPlan } from './resolved-plan.js';
|
|
14
|
-
import { Tmux, tmuxArgs } from './tmux.js';
|
|
14
|
+
import { redactSensitive, resolvedPlan } from './resolved-plan.js';
|
|
15
15
|
import { pickBackend } from './supervisor/index.js';
|
|
16
16
|
import { up, down } from './ops.js';
|
|
17
17
|
import { readRestartLedger, runSupervised, runTemp } from './runner.js';
|
|
@@ -74,10 +74,10 @@ const passthrough = (cmd, args) => new Promise(resolve => {
|
|
|
74
74
|
});
|
|
75
75
|
const program = new Command()
|
|
76
76
|
.name('ours-fleet')
|
|
77
|
-
.description('Fleet of persistent, identity-bound AI agents — selectable
|
|
77
|
+
.description('Fleet of persistent, identity-bound AI agents — selectable harnesses over ACP sessions.')
|
|
78
78
|
.enablePositionalOptions()
|
|
79
79
|
.version(VERSION);
|
|
80
|
-
const cOpt = (cmd) => cmd.option('-c, --configuration <file>', '
|
|
80
|
+
const cOpt = (cmd) => cmd.option('-c, --configuration <file>', 'manifest (default: ~/fleet.yaml; documents under ~/fleet/)');
|
|
81
81
|
const collect = (value, previous) => [...previous, value];
|
|
82
82
|
program.command('docs')
|
|
83
83
|
.alias('man')
|
|
@@ -261,14 +261,16 @@ cOpt(program.command('config').description('validate + print the merged plan (no
|
|
|
261
261
|
console.log(` cwd: ${r.cwd}`);
|
|
262
262
|
if (r.model)
|
|
263
263
|
console.log(` model: ${r.model}`);
|
|
264
|
+
if (r.effort)
|
|
265
|
+
console.log(` effort: ${r.effort}`);
|
|
264
266
|
if (r.harness_options && Object.keys(r.harness_options).length)
|
|
265
|
-
console.log(` options: ${JSON.stringify(r.harness_options)}`);
|
|
267
|
+
console.log(` options: ${JSON.stringify(redactSensitive(r.harness_options))}`);
|
|
266
268
|
if (r.coordinator)
|
|
267
269
|
console.log(` coordinator: ${r.coordinator}`);
|
|
268
270
|
if (r.mission)
|
|
269
271
|
console.log(` mission: ${r.mission.split('\n')[0]}`);
|
|
270
272
|
if (r.oversee?.length)
|
|
271
|
-
console.log(` oversees: ${r.oversee.map(o => `${o.
|
|
273
|
+
console.log(` oversees: ${r.oversee.map(o => `${o.agent}@${o.interval}`).join(', ')}`);
|
|
272
274
|
if (r.isolation) {
|
|
273
275
|
const iso = r.isolation;
|
|
274
276
|
const caps = [
|
|
@@ -369,15 +371,11 @@ cOpt(program.command('force-restart [names...]').description('re-sync + bounce F
|
|
|
369
371
|
});
|
|
370
372
|
program.command('ls').description('list running fleet sessions')
|
|
371
373
|
.action(async () => {
|
|
372
|
-
// Each session has its own tmux server (#32), so there is no single server
|
|
373
|
-
// to ask: the known role names ARE the list of servers to poll.
|
|
374
|
-
const names = [];
|
|
375
374
|
const acp = [];
|
|
376
375
|
for (const root of [agentsRoot(), tmpRoot()]) {
|
|
377
376
|
if (!existsSync(root))
|
|
378
377
|
continue;
|
|
379
378
|
for (const name of readdirSync(root)) {
|
|
380
|
-
names.push(name);
|
|
381
379
|
const stateDir = joinPath(root, name);
|
|
382
380
|
if (!existsSync(controlSocketPath(stateDir)))
|
|
383
381
|
continue;
|
|
@@ -395,14 +393,13 @@ program.command('ls').description('list running fleet sessions')
|
|
|
395
393
|
catch { /* ignore stale sockets */ }
|
|
396
394
|
}
|
|
397
395
|
}
|
|
398
|
-
|
|
399
|
-
console.log([tmux, ...acp].filter(Boolean).join('\n') || '(none)');
|
|
396
|
+
console.log(acp.join('\n') || '(none)');
|
|
400
397
|
});
|
|
401
398
|
program.command('attach <name>').description('open the live console (Ctrl-b d to leave)')
|
|
402
399
|
.action(async (name) => {
|
|
403
400
|
const stateDir = acpStateDir(name);
|
|
404
401
|
if (!stateDir)
|
|
405
|
-
|
|
402
|
+
throw new SessionControlError('offline', 'agent session is offline');
|
|
406
403
|
try {
|
|
407
404
|
const { socket, send } = await followControl(stateDir, message => {
|
|
408
405
|
if ('event' in message)
|
|
@@ -439,7 +436,7 @@ program.command('attach <name>').description('open the live console (Ctrl-b d to
|
|
|
439
436
|
die(e);
|
|
440
437
|
}
|
|
441
438
|
});
|
|
442
|
-
/**
|
|
439
|
+
/** Preserve typed control failures and classify only definite missing-session errors as offline. */
|
|
443
440
|
const asControlError = (e) => {
|
|
444
441
|
if (e instanceof SessionControlError)
|
|
445
442
|
return e;
|
|
@@ -467,23 +464,17 @@ program.command('peek <name> [lines]').description('pane snapshot without attach
|
|
|
467
464
|
for (const event of events.slice(-(lines ? Number(lines) : 40)))
|
|
468
465
|
renderSessionEvent(event);
|
|
469
466
|
}
|
|
470
|
-
else
|
|
471
|
-
|
|
472
|
-
}
|
|
467
|
+
else
|
|
468
|
+
throw new SessionControlError('offline', 'agent session is offline');
|
|
473
469
|
}
|
|
474
470
|
catch (e) {
|
|
475
471
|
die(controlFailure(name, 'peek', e));
|
|
476
472
|
}
|
|
477
473
|
});
|
|
478
474
|
program.command('send <name> [text...]').description("type into the agent's console")
|
|
479
|
-
.option('--key <key>', 'send a raw key instead (Escape, Up, C-c, ...)')
|
|
480
475
|
.action(async (name, text, opts) => {
|
|
481
476
|
const stateDir = acpStateDir(name);
|
|
482
|
-
if (
|
|
483
|
-
die('--key is available only for tmux sessions');
|
|
484
|
-
if (!stateDir && !opts.key && !text?.length)
|
|
485
|
-
die('nothing to send: give text or --key');
|
|
486
|
-
if (stateDir && !text?.length)
|
|
477
|
+
if (!text?.length)
|
|
487
478
|
die('nothing to send: give text');
|
|
488
479
|
try {
|
|
489
480
|
if (stateDir) {
|
|
@@ -496,10 +487,8 @@ program.command('send <name> [text...]').description("type into the agent's cons
|
|
|
496
487
|
? `queued for ${name} behind ${queued.queuedBehind} running turn(s)`
|
|
497
488
|
: `queued for ${name}`);
|
|
498
489
|
}
|
|
499
|
-
else if (opts.key)
|
|
500
|
-
await new Tmux().sendKey(name, opts.key);
|
|
501
490
|
else
|
|
502
|
-
|
|
491
|
+
throw new SessionControlError('offline', 'agent session is offline');
|
|
503
492
|
}
|
|
504
493
|
catch (e) {
|
|
505
494
|
die(controlFailure(name, 'send', e, asControlError(e).kind === 'timeout'
|
|
@@ -1126,7 +1115,7 @@ cOpt(program.command('spawn [name]').description('spawn a new agent (permanent b
|
|
|
1126
1115
|
.option('--role <name>', 'role name (alternative to the positional name)')
|
|
1127
1116
|
.option('--temp', 'temporary: independent transient supervisor, archived on retirement, gone on reboot')
|
|
1128
1117
|
.option('--harness <id>', 'harness adapter (default: defaults.harness)')
|
|
1129
|
-
.option('--session <backend>', 'session backend:
|
|
1118
|
+
.option('--session <backend>', 'session backend: acp (default: acp)')
|
|
1130
1119
|
.option('--mission <text>', 'one-line mission')
|
|
1131
1120
|
.option('--mission-file <path>', 'UTF-8 mission text (mutually exclusive with --mission)')
|
|
1132
1121
|
.option('--identity <name>', 'ours identity to bind (default: role name)')
|
|
@@ -1267,7 +1256,7 @@ program.command('init').description('one-time host setup (units, dirs, linger)')
|
|
|
1267
1256
|
mkdirSync(d, { recursive: true });
|
|
1268
1257
|
for (const m of await pickBackend().init(binPath))
|
|
1269
1258
|
console.log(m);
|
|
1270
|
-
console.log(
|
|
1259
|
+
console.log(INIT_COMPLETION_GUIDANCE);
|
|
1271
1260
|
});
|
|
1272
1261
|
const webCommand = cOpt(program.command('web').description('start or open the secure localhost fleet web console'))
|
|
1273
1262
|
.enablePositionalOptions()
|
package/dist/config.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { IsolationConfig, WrapContext } from './isolation/types.js';
|
|
|
3
3
|
import type { ResolvedWatchdog } from './watchdog/config.js';
|
|
4
4
|
import type { ResolvedLoop, ResolvedRoleLoop } from './loops/config.js';
|
|
5
5
|
export interface OverseeEntry {
|
|
6
|
-
|
|
6
|
+
agent: string;
|
|
7
7
|
interval: string;
|
|
8
8
|
}
|
|
9
9
|
export interface WorklogPolicy {
|
|
@@ -25,7 +25,7 @@ export declare const NOTIFY_EVENT_TYPES: readonly ["message_received", "file_rec
|
|
|
25
25
|
export type NotifyEventType = (typeof NOTIFY_EVENT_TYPES)[number];
|
|
26
26
|
export type InjectMode = 'notification' | 'full';
|
|
27
27
|
export type MonitorMode = 'fleet' | 'native';
|
|
28
|
-
export type SessionBackendId = '
|
|
28
|
+
export type SessionBackendId = 'acp';
|
|
29
29
|
/** Public, harness-neutral permission policy. */
|
|
30
30
|
export type FleetPermissionMode = 'ask' | 'auto' | 'allow';
|
|
31
31
|
/** `deny` is a deprecated, fail-closed compatibility alias retained for old fleet files. */
|
|
@@ -44,9 +44,6 @@ export interface SessionOptions {
|
|
|
44
44
|
/** ACP agent command and arguments. Defaults are supplied by the harness adapter. */
|
|
45
45
|
command?: string | string[];
|
|
46
46
|
};
|
|
47
|
-
tmux?: {
|
|
48
|
-
boot_grace_ms?: number;
|
|
49
|
-
};
|
|
50
47
|
}
|
|
51
48
|
/** Resolved per-role supervisor-monitor config. */
|
|
52
49
|
export interface MonitorConfig {
|
|
@@ -126,6 +123,8 @@ export interface RoleConfig {
|
|
|
126
123
|
briefing_file?: string;
|
|
127
124
|
/** Explicit null means use the selected harness's own default, bypassing fleet defaults. */
|
|
128
125
|
model?: string | null;
|
|
126
|
+
/** Neutral Brain reasoning effort, retained for inspection after adapter translation. */
|
|
127
|
+
effort?: string;
|
|
129
128
|
model_chain?: string[];
|
|
130
129
|
max_tokens?: number;
|
|
131
130
|
autocompact_pct?: number;
|
|
@@ -169,14 +168,37 @@ export interface ResolvedRole extends Omit<RoleConfig, 'model' | 'owner_channel'
|
|
|
169
168
|
worklog?: WorklogPolicy;
|
|
170
169
|
auth_proxy?: AuthProxyConfig;
|
|
171
170
|
loops?: ResolvedRoleLoop[];
|
|
171
|
+
provenance?: Record<string, FieldProvenance>;
|
|
172
172
|
/** Internal/transient: never accepted as a user-authored RoleConfig key. */
|
|
173
173
|
roomMemberStartup?: RoomMemberStartup;
|
|
174
174
|
}
|
|
175
|
+
export interface FieldProvenance {
|
|
176
|
+
sourceFile: string;
|
|
177
|
+
sourcePointer: string;
|
|
178
|
+
sourceKind: 'Agent' | 'Role' | 'Brain' | 'Manifest' | 'built-in';
|
|
179
|
+
sourceId?: string;
|
|
180
|
+
origin: 'explicit' | 'typed-default' | 'built-in';
|
|
181
|
+
viaReference?: {
|
|
182
|
+
sourcePointer: string;
|
|
183
|
+
kind: 'Role' | 'Brain';
|
|
184
|
+
id: string;
|
|
185
|
+
};
|
|
186
|
+
transforms: Array<{
|
|
187
|
+
kind: 'substitution' | 'adapter-normalization' | 'validation-default';
|
|
188
|
+
detail: string;
|
|
189
|
+
}>;
|
|
190
|
+
}
|
|
175
191
|
export interface FleetConfig {
|
|
176
192
|
roles: ResolvedRole[];
|
|
177
193
|
vars: Record<string, string>;
|
|
178
194
|
defaults: Record<string, unknown>;
|
|
179
195
|
files: string[];
|
|
196
|
+
configMode?: 'split-v2';
|
|
197
|
+
sourceDocuments?: Array<{
|
|
198
|
+
kind: 'Manifest' | 'Agent' | 'Role' | 'Brain';
|
|
199
|
+
id?: string;
|
|
200
|
+
path: string;
|
|
201
|
+
}>;
|
|
180
202
|
/** Fleet-wide delay (ms) enforced between agent launches to avoid boot bursts (0 = none). */
|
|
181
203
|
startStaggerMs: number;
|
|
182
204
|
/** Warning-first non-plain YAML migration diagnostics, in source order. */
|
|
@@ -207,7 +229,8 @@ export declare function resolveRoleModel(model: string | null | undefined, harne
|
|
|
207
229
|
*/
|
|
208
230
|
export declare function isolationContextFor(role: ResolvedRole): WrapContext;
|
|
209
231
|
export declare const ROLE_NAME_RE: RegExp;
|
|
210
|
-
|
|
232
|
+
export declare function splitRootFor(base: string): string;
|
|
233
|
+
/** Load a v2 manifest and bare Agent/Role/Brain documents from its stem directory. */
|
|
211
234
|
export declare function loadConfig(configPath?: string, options?: {
|
|
212
235
|
yamlMode?: YamlMode;
|
|
213
236
|
}): FleetConfig;
|