@ours.network/fleet 0.1.2 → 0.4.0
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 +18 -5
- package/dist/briefing.d.ts +1 -0
- package/dist/briefing.js +4 -0
- package/dist/cli.js +4 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +2 -1
- package/dist/harness/claude-code.js +16 -2
- package/dist/ops.js +2 -1
- package/dist/spawn.d.ts +1 -0
- package/dist/spawn.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,10 +49,19 @@ roles:
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
Each role gets a state dir (`~/.ours-fleet/agents/<Name>/`) holding its briefing,
|
|
52
|
-
|
|
53
|
-
publish bio/persona, arm a mail monitor (`ours-mcp watch`), announce to
|
|
54
|
-
coordinator, work. On crash the supervisor relaunches it and the harness
|
|
55
|
-
the same session.
|
|
52
|
+
logs, routines, and session markers. On boot the agent reads its briefing: bind
|
|
53
|
+
identity, publish bio/persona, arm a mail monitor (`ours-mcp watch`), announce to
|
|
54
|
+
its coordinator, work. On crash the supervisor relaunches it and the harness
|
|
55
|
+
resumes the same session.
|
|
56
|
+
|
|
57
|
+
The state dir contract:
|
|
58
|
+
|
|
59
|
+
| File | Owner | Lifecycle |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `briefing.md` | generated | rewritten on every `up`/`restart`; never hand-edit |
|
|
62
|
+
| `WORKLOG.md` | the agent | seeded empty, agent-appended; survives restarts |
|
|
63
|
+
| `ROUTINES.md` | operator / agent | **optional** recurring-work instructions; re-read at the start of every wake, hot-editable **without a restart**; absence means "no routines" |
|
|
64
|
+
| `.identity`, `.cwd`, `.session-id`, `.booted`, `.exit-status` | supervisor | dot-marker state — session resume and boot bookkeeping |
|
|
56
65
|
|
|
57
66
|
## Prerequisites
|
|
58
67
|
|
|
@@ -137,7 +146,7 @@ ours-fleet up|down|restart|force-restart [-c FILE] [Name...]
|
|
|
137
146
|
ours-fleet config [-c FILE] validate + print merged plan
|
|
138
147
|
ours-fleet ls | attach | peek | logs [-f] | status <Name>
|
|
139
148
|
ours-fleet send <Name> "text" | --key <K>
|
|
140
|
-
ours-fleet spawn [--temp] <Name> [--mission --bio-file --persona-file ...]
|
|
149
|
+
ours-fleet spawn [--temp] <Name> [--mission --model --bio-file --persona-file ...]
|
|
141
150
|
ours-fleet rm <Name>
|
|
142
151
|
ours-fleet doctor [--harness H]
|
|
143
152
|
ours-fleet init
|
|
@@ -149,6 +158,7 @@ ours-fleet init
|
|
|
149
158
|
vars: { work_root: /home/me/work } # ${var} substitution anywhere below
|
|
150
159
|
defaults:
|
|
151
160
|
harness: claude-code # for roles that don't set one
|
|
161
|
+
model: claude-fable-5 # default model for roles that don't set one (per-role model / --model wins)
|
|
152
162
|
max_tokens: 500000 # session cap (harness-interpreted)
|
|
153
163
|
roles:
|
|
154
164
|
Name: # [A-Za-z0-9_-]+
|
|
@@ -156,6 +166,7 @@ roles:
|
|
|
156
166
|
identity: "Display Name" # ours identity to bind (default: Name)
|
|
157
167
|
cwd: ${work_root}/repo # where the harness process runs
|
|
158
168
|
coordinator: FleetCoordinator # announce target on boot
|
|
169
|
+
model: claude-fable-5 # launch on a specific model (pass-through id; default: launcher default)
|
|
159
170
|
mission: one line
|
|
160
171
|
persona: | # operating contract (published as persona)
|
|
161
172
|
bio: | # public card (published as bio)
|
|
@@ -165,6 +176,8 @@ roles:
|
|
|
165
176
|
harness_options: # adapter-owned, adapter-validated
|
|
166
177
|
plugins: { "name@marketplace": false } # claude-code: plugin overrides
|
|
167
178
|
# mem_palace: false # claude-code: disable memory plugin
|
|
179
|
+
# permission_mode: dontAsk # claude-code: launch permission mode —
|
|
180
|
+
# one of default | acceptEdits | plan | dontAsk | bypassPermissions
|
|
168
181
|
```
|
|
169
182
|
|
|
170
183
|
Merge order: `fleet.yaml` ← `fleet.d/*.yaml`; a duplicate role name is a hard
|
package/dist/briefing.d.ts
CHANGED
package/dist/briefing.js
CHANGED
|
@@ -62,6 +62,10 @@ export function generateBriefing(role, v, opts) {
|
|
|
62
62
|
L.push('', '## Durable log');
|
|
63
63
|
L.push(`Append important commands / decisions / results to \`${opts.worklogPath}\` as you go —`);
|
|
64
64
|
L.push('it survives restarts.');
|
|
65
|
+
L.push('', '## Routines');
|
|
66
|
+
L.push(`If \`${opts.routinesPath}\` exists, re-read it at the START of every wake — before acting`);
|
|
67
|
+
L.push('on messages, timers, or prompts — and follow it for recurring or scheduled work. It may');
|
|
68
|
+
L.push('change between wakes without a restart; treat the file, not your memory of it, as current.');
|
|
65
69
|
L.push('', '## On restart (you run under a supervised launcher)');
|
|
66
70
|
L.push(`On restart, WITHOUT asking: re-bind (**${v.bindTool}** name "${id}" force=true),`);
|
|
67
71
|
L.push(`re-arm the \`${v.watchCommand(id)}\` monitor, then continue from your WORKLOG.`);
|
package/dist/cli.js
CHANGED
|
@@ -48,6 +48,8 @@ cOpt(program.command('config').description('validate + print the merged plan (no
|
|
|
48
48
|
console.log(` source: ${r.sourceFile}`);
|
|
49
49
|
if (r.cwd)
|
|
50
50
|
console.log(` cwd: ${r.cwd}`);
|
|
51
|
+
if (r.model)
|
|
52
|
+
console.log(` model: ${r.model}`);
|
|
51
53
|
if (r.coordinator)
|
|
52
54
|
console.log(` coordinator: ${r.coordinator}`);
|
|
53
55
|
if (r.mission)
|
|
@@ -148,12 +150,14 @@ cOpt(program.command('spawn <name>').description('spawn a new agent (permanent b
|
|
|
148
150
|
.option('--identity <name>', 'ours identity to bind (default: role name)')
|
|
149
151
|
.option('--cwd <dir>', 'working directory')
|
|
150
152
|
.option('--coordinator <name>', 'announce target')
|
|
153
|
+
.option('--model <id>', 'model id to launch on (e.g. claude-fable-5); default: launcher default')
|
|
151
154
|
.option('--bio-file <file>', 'public bio (file)')
|
|
152
155
|
.option('--persona-file <file>', 'persona / operating contract (file)')
|
|
153
156
|
.action(async (name, opts) => {
|
|
154
157
|
const o = {
|
|
155
158
|
name, temp: opts.temp, harness: opts.harness, mission: opts.mission,
|
|
156
159
|
identity: opts.identity, cwd: opts.cwd, coordinator: opts.coordinator,
|
|
160
|
+
model: opts.model,
|
|
157
161
|
bioFile: opts.bioFile, personaFile: opts.personaFile, configPath: opts.configuration,
|
|
158
162
|
};
|
|
159
163
|
try {
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -7,7 +7,7 @@ export class ConfigError extends Error {
|
|
|
7
7
|
const NAME_RE = /^[A-Za-z0-9_-]+$/;
|
|
8
8
|
const ROLE_KEYS = [
|
|
9
9
|
'harness', 'identity', 'cwd', 'coordinator', 'mission', 'persona', 'bio',
|
|
10
|
-
'briefing_file', 'max_tokens', 'autocompact_pct', 'env', 'oversee', 'harness_options',
|
|
10
|
+
'briefing_file', 'model', 'max_tokens', 'autocompact_pct', 'env', 'oversee', 'harness_options',
|
|
11
11
|
];
|
|
12
12
|
function deepSub(v, vars) {
|
|
13
13
|
if (typeof v === 'string')
|
|
@@ -65,6 +65,7 @@ export function loadConfig(configPath) {
|
|
|
65
65
|
sourceFile: file,
|
|
66
66
|
harness: r.harness ?? defaults.harness ?? 'claude-code',
|
|
67
67
|
identity: r.identity ?? name,
|
|
68
|
+
model: r.model ?? defaults.model,
|
|
68
69
|
max_tokens: r.max_tokens ?? defaults.max_tokens,
|
|
69
70
|
});
|
|
70
71
|
}
|
|
@@ -3,7 +3,18 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { home } from '../paths.js';
|
|
4
4
|
import { realExec } from '../exec.js';
|
|
5
5
|
import { registerAdapter } from './registry.js';
|
|
6
|
-
const OPTION_KEYS = ['plugins', 'mem_palace', 'mem_palace_midsession_autosave'];
|
|
6
|
+
const OPTION_KEYS = ['plugins', 'mem_palace', 'mem_palace_midsession_autosave', 'permission_mode'];
|
|
7
|
+
/** Claude Code's accepted --permission-mode values. */
|
|
8
|
+
const PERMISSION_MODES = ['default', 'acceptEdits', 'plan', 'dontAsk', 'bypassPermissions'];
|
|
9
|
+
/** Resolve & validate the per-role permission mode, throwing on an unknown value. */
|
|
10
|
+
function permissionMode(role) {
|
|
11
|
+
const pm = role.harness_options?.permission_mode;
|
|
12
|
+
if (pm == null)
|
|
13
|
+
return undefined;
|
|
14
|
+
if (!PERMISSION_MODES.includes(pm))
|
|
15
|
+
throw new Error(`invalid harness_options.permission_mode "${pm}"; allowed: ${PERMISSION_MODES.join(', ')}`);
|
|
16
|
+
return pm;
|
|
17
|
+
}
|
|
7
18
|
/** Context window of the fleet model (1M); max_tokens → % of this. */
|
|
8
19
|
const WINDOW = 1_000_000;
|
|
9
20
|
export function autocompactPct(role) {
|
|
@@ -78,7 +89,10 @@ export function makeClaudeCodeAdapter(exec = realExec) {
|
|
|
78
89
|
},
|
|
79
90
|
buildLaunch(role, mode, s, prep) {
|
|
80
91
|
const stateDir = roleStateDir(role);
|
|
81
|
-
const
|
|
92
|
+
const pm = permissionMode(role);
|
|
93
|
+
const base = ['claude', ...(role.model ? ['--model', role.model] : []),
|
|
94
|
+
...(pm ? ['--permission-mode', pm] : []),
|
|
95
|
+
...prep.argv, '--remote-control', role.name];
|
|
82
96
|
const argv = mode === 'fresh'
|
|
83
97
|
? [...base, '--session-id', s.sessionId, `Read and follow ${join(stateDir, 'briefing.md')} now.`]
|
|
84
98
|
: [...base, '--resume', s.sessionId,
|
package/dist/ops.js
CHANGED
|
@@ -23,7 +23,8 @@ export function applyRole(role, opts = {}) {
|
|
|
23
23
|
writeFileSync(join(dir, 'WORKLOG.md'), '');
|
|
24
24
|
const briefingBody = role.briefing_file ? readFileSync(role.briefing_file, 'utf8') : undefined;
|
|
25
25
|
writeFileSync(join(dir, 'briefing.md'), generateBriefing(role, adapter.vocabulary, {
|
|
26
|
-
stateDir: dir, worklogPath: join(dir, 'WORKLOG.md'),
|
|
26
|
+
stateDir: dir, worklogPath: join(dir, 'WORKLOG.md'),
|
|
27
|
+
routinesPath: join(dir, 'ROUTINES.md'), briefingBody,
|
|
27
28
|
}));
|
|
28
29
|
if (opts.fresh)
|
|
29
30
|
for (const f of ['.booted', '.session-id', '.exit-status'])
|
package/dist/spawn.d.ts
CHANGED
package/dist/spawn.js
CHANGED
|
@@ -17,6 +17,8 @@ function roleFromOpts(o) {
|
|
|
17
17
|
r.coordinator = o.coordinator;
|
|
18
18
|
if (o.mission)
|
|
19
19
|
r.mission = o.mission;
|
|
20
|
+
if (o.model?.trim())
|
|
21
|
+
r.model = o.model.trim();
|
|
20
22
|
if (o.bioFile)
|
|
21
23
|
r.bio = readFileSync(o.bioFile, 'utf8').trim();
|
|
22
24
|
if (o.personaFile)
|
|
@@ -57,6 +59,7 @@ export async function spawnTemp(o, binPath, launch = detachedSupervisor) {
|
|
|
57
59
|
name: o.name,
|
|
58
60
|
harness: o.harness ?? cfg.defaults.harness ?? 'claude-code',
|
|
59
61
|
identity: o.identity ?? o.name,
|
|
62
|
+
model: o.model?.trim() || cfg.defaults.model,
|
|
60
63
|
sourceFile: '(temp)',
|
|
61
64
|
};
|
|
62
65
|
const dir = applyRole(role, { temp: true });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ours.network/fleet",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Harness-agnostic fleet of persistent, identity-bound AI agents. Declarative fleet.yaml, tmux consoles, systemd/launchd supervision, ours.network messaging.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "FSL-1.1-Apache-2.0",
|