@ours.network/fleet 2.3.0 → 2.3.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 +1 -1
- package/dist/application/harness-capabilities.d.ts +60 -0
- package/dist/application/harness-capabilities.js +96 -0
- package/dist/application/model-catalog.d.ts +17 -0
- package/dist/application/model-catalog.js +32 -9
- package/dist/application/preset-provenance.d.ts +1 -1
- package/dist/application/preset-provenance.js +18 -3
- package/dist/application/role-creation-service.d.ts +3 -1
- package/dist/application/role-creation-service.js +4 -1
- package/dist/application/task-room-service.d.ts +1 -1
- package/dist/application/task-room-service.js +2 -2
- package/dist/build-info.json +4 -4
- package/dist/harness/claude-code.js +7 -0
- package/dist/harness/codex.js +7 -0
- package/dist/harness/hermes.js +2 -0
- package/dist/harness/types.d.ts +20 -0
- package/dist/init-wizard.d.ts +19 -12
- package/dist/init-wizard.js +122 -104
- package/dist/rooms-tasks/add-member.d.ts +15 -1
- package/dist/rooms-tasks/add-member.js +27 -4
- package/dist/rooms-tasks/task-state.js +7 -2
- package/dist/web/fleet-config-service.d.ts +8 -0
- package/dist/web/fleet-config-service.js +23 -8
- package/dist/web/resource-views.d.ts +2 -0
- package/dist/web/resource-views.js +26 -15
- package/dist/web/server.js +58 -24
- package/package.json +1 -1
- package/presets/README.md +38 -18
- package/presets/brain-catalog.json +19 -19
- package/presets/legacy/generated-brains-rev4.json +497 -0
- package/presets/manifest.json +1 -1
- package/presets/fleet/brains/claude-fable-5-1-high.yaml +0 -5
- package/presets/fleet/brains/claude-fable-5-1-low.yaml +0 -5
- package/presets/fleet/brains/claude-fable-5-1-max.yaml +0 -5
- package/presets/fleet/brains/claude-fable-5-1-medium.yaml +0 -5
- package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +0 -5
- package/presets/fleet/brains/claude-fable-5-high.yaml +0 -5
- package/presets/fleet/brains/claude-fable-5-low.yaml +0 -5
- package/presets/fleet/brains/claude-fable-5-max.yaml +0 -5
- package/presets/fleet/brains/claude-fable-5-medium.yaml +0 -5
- package/presets/fleet/brains/claude-fable-5-xhigh.yaml +0 -5
- package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +0 -4
- package/presets/fleet/brains/claude-opus-5-5-high.yaml +0 -5
- package/presets/fleet/brains/claude-opus-5-5-low.yaml +0 -5
- package/presets/fleet/brains/claude-opus-5-5-max.yaml +0 -5
- package/presets/fleet/brains/claude-opus-5-5-medium.yaml +0 -5
- package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +0 -5
- package/presets/fleet/brains/claude-opus-5-high.yaml +0 -5
- package/presets/fleet/brains/claude-opus-5-low.yaml +0 -5
- package/presets/fleet/brains/claude-opus-5-max.yaml +0 -5
- package/presets/fleet/brains/claude-opus-5-medium.yaml +0 -5
- package/presets/fleet/brains/claude-opus-5-xhigh.yaml +0 -5
- package/presets/fleet/brains/claude-sonnet-5-high.yaml +0 -5
- package/presets/fleet/brains/claude-sonnet-5-low.yaml +0 -5
- package/presets/fleet/brains/claude-sonnet-5-max.yaml +0 -5
- package/presets/fleet/brains/claude-sonnet-5-medium.yaml +0 -5
- package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-4-high.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-4-low.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-5-high.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-5-low.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +0 -5
- package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +0 -5
package/README.md
CHANGED
|
@@ -1853,7 +1853,7 @@ and member status and choose supported recovery. These checks do not restart age
|
|
|
1853
1853
|
recreate identities, redeem invites, or rewrite lifecycle state. Temporary room agents
|
|
1854
1854
|
remain transient across host reboot; retained launch records do not restore them.
|
|
1855
1855
|
|
|
1856
|
-
See [
|
|
1856
|
+
See [models, efforts and Brains](presets/README.md) for the adapter model catalog, the three purpose Brains setup creates, and runtime requirements.
|
|
1857
1857
|
|
|
1858
1858
|
### YAML room layouts
|
|
1859
1859
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { PrereqCheck } from '../harness/types.js';
|
|
2
|
+
import type { OfferedModels } from '../subscriptions/cli.js';
|
|
3
|
+
/**
|
|
4
|
+
* A selectable model. `efforts: null` means the efforts are unknown (a model the
|
|
5
|
+
* adapter does not maintain); `[]` means the model is known to have no effort setting.
|
|
6
|
+
*/
|
|
7
|
+
export interface CapabilityModel {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
efforts: string[] | null;
|
|
11
|
+
available: boolean;
|
|
12
|
+
reason?: string;
|
|
13
|
+
source: 'adapter' | 'runtime-discovery';
|
|
14
|
+
}
|
|
15
|
+
export interface HarnessCapability {
|
|
16
|
+
harness: string;
|
|
17
|
+
ready: boolean;
|
|
18
|
+
checks: PrereqCheck[];
|
|
19
|
+
sessions: string[];
|
|
20
|
+
modelRequired: boolean;
|
|
21
|
+
customModels: boolean;
|
|
22
|
+
models: CapabilityModel[];
|
|
23
|
+
discovery?: {
|
|
24
|
+
state: 'known';
|
|
25
|
+
} | {
|
|
26
|
+
state: 'unknown';
|
|
27
|
+
reason: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface HarnessCapabilitiesReport {
|
|
31
|
+
harnesses: HarnessCapability[];
|
|
32
|
+
}
|
|
33
|
+
export interface HarnessCapabilityDeps {
|
|
34
|
+
harnesses?: string[];
|
|
35
|
+
codexModels?: () => Promise<OfferedModels>;
|
|
36
|
+
/** Skip executable checks when only model facts are needed. */
|
|
37
|
+
checkReadiness?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/** Each production adapter's maintained models, narrowed by what the active Codex account offers. */
|
|
40
|
+
export declare function harnessCapabilities(deps?: HarnessCapabilityDeps): Promise<HarnessCapabilitiesReport>;
|
|
41
|
+
export interface BrainChoice {
|
|
42
|
+
harness: string;
|
|
43
|
+
session?: string;
|
|
44
|
+
model?: string | null;
|
|
45
|
+
effort?: string | null;
|
|
46
|
+
}
|
|
47
|
+
export type BrainChoiceCheck = {
|
|
48
|
+
ok: true;
|
|
49
|
+
model?: CapabilityModel;
|
|
50
|
+
} | {
|
|
51
|
+
ok: false;
|
|
52
|
+
code: string;
|
|
53
|
+
message: string;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Strict check of a new harness/model/effort choice. Nothing is substituted: an
|
|
57
|
+
* effort is kept only when the chosen model supports it; an omitted effort keeps
|
|
58
|
+
* the harness default. Existing Brains are resolved by their adapter as before.
|
|
59
|
+
*/
|
|
60
|
+
export declare function validateBrainChoice(report: HarnessCapabilitiesReport, choice: BrainChoice): BrainChoiceCheck;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { getAdapter, knownAdapters, productionAdapters } from '../harness/registry.js';
|
|
2
|
+
/** Each production adapter's maintained models, narrowed by what the active Codex account offers. */
|
|
3
|
+
export async function harnessCapabilities(deps = {}) {
|
|
4
|
+
// Only harnesses whose adapter is registered in this process can report facts.
|
|
5
|
+
const ids = (deps.harnesses ?? productionAdapters()).filter(id => knownAdapters().includes(id));
|
|
6
|
+
const harnesses = await Promise.all(ids.map(async (id) => {
|
|
7
|
+
const adapter = getAdapter(id);
|
|
8
|
+
const capabilities = adapter.modelCapabilities?.();
|
|
9
|
+
if (!capabilities)
|
|
10
|
+
return undefined;
|
|
11
|
+
let ready = true, checks = [];
|
|
12
|
+
if (deps.checkReadiness !== false) {
|
|
13
|
+
try {
|
|
14
|
+
({ ok: ready, checks } = await adapter.checkPrereqs());
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
ready = false;
|
|
18
|
+
checks = [{ name: 'availability', ok: false, detail: 'Harness checks unavailable' }];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
let models = capabilities.models.map(model => ({
|
|
22
|
+
id: model.id, label: model.label, efforts: [...model.efforts], available: true, source: 'adapter',
|
|
23
|
+
}));
|
|
24
|
+
let discovery;
|
|
25
|
+
if (id === 'codex' && deps.codexModels) {
|
|
26
|
+
const offered = await deps.codexModels();
|
|
27
|
+
discovery = offered.state === 'known' ? { state: 'known' } : { state: 'unknown', reason: offered.reason };
|
|
28
|
+
if (offered.state === 'known') {
|
|
29
|
+
models = models.map(model => offered.models.includes(model.id) ? model
|
|
30
|
+
: { ...model, available: false, reason: 'The active Codex account does not offer this model.' });
|
|
31
|
+
// Offered models the adapter does not maintain: selectable, but their efforts are not known.
|
|
32
|
+
for (const extra of offered.models.filter(model => !models.some(known => known.id === model)))
|
|
33
|
+
models.push({ id: extra, label: extra, efforts: null, available: true, source: 'runtime-discovery' });
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
models = models.map(model => ({ ...model, available: false,
|
|
37
|
+
reason: `Codex could not report the models offered to the active account: ${offered.reason}` }));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { harness: id, ready, checks, sessions: [...capabilities.sessions], modelRequired: capabilities.modelRequired,
|
|
41
|
+
customModels: capabilities.customModels, models, ...(discovery ? { discovery } : {}) };
|
|
42
|
+
}));
|
|
43
|
+
return { harnesses: harnesses.filter((item) => item !== undefined) };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Strict check of a new harness/model/effort choice. Nothing is substituted: an
|
|
47
|
+
* effort is kept only when the chosen model supports it; an omitted effort keeps
|
|
48
|
+
* the harness default. Existing Brains are resolved by their adapter as before.
|
|
49
|
+
*/
|
|
50
|
+
export function validateBrainChoice(report, choice) {
|
|
51
|
+
const harness = report.harnesses.find(item => item.harness === choice.harness);
|
|
52
|
+
if (!harness)
|
|
53
|
+
return { ok: false, code: 'harness_unsupported',
|
|
54
|
+
message: `Harness '${String(choice.harness)}' is not available; choose one of: ${report.harnesses.map(item => item.harness).join(', ')}` };
|
|
55
|
+
if (choice.session !== undefined && !harness.sessions.includes(choice.session))
|
|
56
|
+
return { ok: false, code: 'session_unsupported',
|
|
57
|
+
message: `${harness.harness} does not support session '${choice.session}'; choose one of: ${harness.sessions.join(', ')}` };
|
|
58
|
+
const effort = choice.effort ?? undefined;
|
|
59
|
+
if (effort !== undefined && (typeof effort !== 'string' || !effort))
|
|
60
|
+
return { ok: false, code: 'effort_invalid', message: 'Effort must be a non-empty string or omitted' };
|
|
61
|
+
const modelId = choice.model ?? undefined;
|
|
62
|
+
if (modelId === undefined) {
|
|
63
|
+
if (harness.modelRequired)
|
|
64
|
+
return { ok: false, code: 'model_required', message: `${harness.harness} requires an explicit model` };
|
|
65
|
+
if (effort !== undefined)
|
|
66
|
+
return { ok: false, code: 'effort_unknown',
|
|
67
|
+
message: `The ${harness.harness} default model has no known effort list; choose a model to set effort, or leave effort at the harness default` };
|
|
68
|
+
return { ok: true };
|
|
69
|
+
}
|
|
70
|
+
if (typeof modelId !== 'string' || !modelId.trim())
|
|
71
|
+
return { ok: false, code: 'model_invalid', message: 'Model must be a non-empty string' };
|
|
72
|
+
const model = harness.models.find(item => item.id === modelId);
|
|
73
|
+
if (!model) {
|
|
74
|
+
if (!harness.customModels)
|
|
75
|
+
return { ok: false, code: 'model_unsupported',
|
|
76
|
+
message: `${harness.harness} does not support model '${modelId}'` };
|
|
77
|
+
if (effort !== undefined)
|
|
78
|
+
return { ok: false, code: 'effort_unknown',
|
|
79
|
+
message: `Efforts for '${modelId}' are unknown to ${harness.harness}; leave effort at the harness default or choose a listed model` };
|
|
80
|
+
return { ok: true };
|
|
81
|
+
}
|
|
82
|
+
if (!model.available)
|
|
83
|
+
return { ok: false, code: 'model_unavailable', message: `${model.label}: ${model.reason ?? 'unavailable'}` };
|
|
84
|
+
if (effort === undefined)
|
|
85
|
+
return { ok: true, model };
|
|
86
|
+
if (model.efforts === null)
|
|
87
|
+
return { ok: false, code: 'effort_unknown',
|
|
88
|
+
message: `Efforts for ${model.label} are unknown; leave effort at the harness default` };
|
|
89
|
+
if (!model.efforts.length)
|
|
90
|
+
return { ok: false, code: 'effort_unsupported',
|
|
91
|
+
message: `${model.label} has no effort setting; remove the effort` };
|
|
92
|
+
if (!model.efforts.includes(effort))
|
|
93
|
+
return { ok: false, code: 'effort_unsupported',
|
|
94
|
+
message: `${model.label} does not support effort '${effort}'; choose one of: ${model.efforts.join(', ')}` };
|
|
95
|
+
return { ok: true, model };
|
|
96
|
+
}
|
|
@@ -10,6 +10,23 @@ export interface HarnessModelCatalog {
|
|
|
10
10
|
models: HarnessModelOption[];
|
|
11
11
|
warnings: string[];
|
|
12
12
|
}
|
|
13
|
+
/** One model a harness adapter knows, with the exact efforts it supports ([] = no effort setting). */
|
|
14
|
+
export interface PackagedModel {
|
|
15
|
+
harness: 'codex' | 'claude-code';
|
|
16
|
+
session: 'acp';
|
|
17
|
+
model: string;
|
|
18
|
+
label: string;
|
|
19
|
+
efforts: string[];
|
|
20
|
+
}
|
|
21
|
+
export interface PackagedModelCatalog {
|
|
22
|
+
schema_version: 2;
|
|
23
|
+
catalog_revision: number;
|
|
24
|
+
models: PackagedModel[];
|
|
25
|
+
}
|
|
26
|
+
/** Validate the packaged model catalog maintained with the Claude Code and Codex adapters. */
|
|
27
|
+
export declare function validatePackagedModelCatalog(value: unknown, path?: string): PackagedModelCatalog;
|
|
28
|
+
/** The packaged catalog, optionally limited to one harness. */
|
|
29
|
+
export declare function packagedModelCatalog(harness?: PackagedModel['harness']): PackagedModel[];
|
|
13
30
|
/** Read Codex's locally materialized `model/list` result; never invent fallback aliases. */
|
|
14
31
|
export declare function codexModelCatalog(path?: string): HarnessModelCatalog;
|
|
15
32
|
/**
|
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import { readFileSync, statSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
+
import { packagedPresetRoot } from '../preset-bootstrap.js';
|
|
5
|
+
const KNOWN_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max', 'ultra'];
|
|
6
|
+
/** Validate the packaged model catalog maintained with the Claude Code and Codex adapters. */
|
|
7
|
+
export function validatePackagedModelCatalog(value, path = 'packaged model catalog') {
|
|
8
|
+
const catalog = value;
|
|
9
|
+
if (!catalog || catalog.schema_version !== 2 || !Number.isInteger(catalog.catalog_revision)
|
|
10
|
+
|| !Array.isArray(catalog.models) || catalog.models.length === 0)
|
|
11
|
+
throw new Error(`invalid ${path}`);
|
|
12
|
+
const tuples = new Set();
|
|
13
|
+
for (const model of catalog.models) {
|
|
14
|
+
if (!model || !['codex', 'claude-code'].includes(model.harness) || model.session !== 'acp'
|
|
15
|
+
|| typeof model.model !== 'string' || !model.model
|
|
16
|
+
|| typeof model.label !== 'string' || !model.label.trim()
|
|
17
|
+
|| !Array.isArray(model.efforts) || new Set(model.efforts).size !== model.efforts.length
|
|
18
|
+
|| model.efforts.some(effort => !KNOWN_EFFORTS.includes(effort)))
|
|
19
|
+
throw new Error(`unsupported model state in ${path}: ${String(model?.model)}`);
|
|
20
|
+
const tuple = `${model.harness}\0${model.session}\0${model.model}`;
|
|
21
|
+
if (tuples.has(tuple))
|
|
22
|
+
throw new Error(`duplicate supported model in ${path}: ${model.model}`);
|
|
23
|
+
tuples.add(tuple);
|
|
24
|
+
}
|
|
25
|
+
return catalog;
|
|
26
|
+
}
|
|
27
|
+
/** The packaged catalog, optionally limited to one harness. */
|
|
28
|
+
export function packagedModelCatalog(harness) {
|
|
29
|
+
const path = join(packagedPresetRoot(), 'brain-catalog.json');
|
|
30
|
+
const models = validatePackagedModelCatalog(JSON.parse(readFileSync(path, 'utf8')), path).models;
|
|
31
|
+
return harness ? models.filter(model => model.harness === harness) : models;
|
|
32
|
+
}
|
|
4
33
|
const validId = (value) => typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9._[\]-]{0,127}$/.test(value);
|
|
5
34
|
/** Read Codex's locally materialized `model/list` result; never invent fallback aliases. */
|
|
6
35
|
export function codexModelCatalog(path = join(homedir(), '.codex', 'models_cache.json')) {
|
|
@@ -45,16 +74,10 @@ export function codexModelCatalog(path = join(homedir(), '.codex', 'models_cache
|
|
|
45
74
|
* not a claim about account entitlement (which Claude validates at launch).
|
|
46
75
|
*/
|
|
47
76
|
export function claudeModelCatalog() {
|
|
48
|
-
const reasoningEfforts = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
49
77
|
return {
|
|
50
|
-
models:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
{ id: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5', reasoningEfforts: [], source: 'claude-adapter-2.1' },
|
|
54
|
-
{ id: 'claude-fable-5', label: 'Claude Fable 5', reasoningEfforts, source: 'claude-adapter-2.1' },
|
|
55
|
-
{ id: 'claude-opus-5', label: 'Claude Opus 5', reasoningEfforts, source: 'claude-adapter-2.1' },
|
|
56
|
-
{ id: 'claude-sonnet-5', label: 'Claude Sonnet 5', reasoningEfforts, source: 'claude-adapter-2.1' },
|
|
57
|
-
],
|
|
78
|
+
models: packagedModelCatalog('claude-code').map(model => ({
|
|
79
|
+
id: model.model, label: model.label, reasoningEfforts: model.efforts, source: 'claude-adapter-2.1',
|
|
80
|
+
})),
|
|
58
81
|
warnings: ['Claude Code exposes no typed model-list endpoint; choices require a current Claude runtime (verified with 2.1.281); older bundled SDKs need CLAUDE_CODE_EXECUTABLE. Entitlement is validated at launch.'],
|
|
59
82
|
};
|
|
60
83
|
}
|
|
@@ -5,7 +5,7 @@ export declare const PROVENANCE_KINDS: {
|
|
|
5
5
|
readonly layout: "room_layouts";
|
|
6
6
|
};
|
|
7
7
|
export type ProvenanceKind = keyof typeof PROVENANCE_KINDS;
|
|
8
|
-
export type ProvenanceStatus = 'predefined' | 'changed' | 'custom';
|
|
8
|
+
export type ProvenanceStatus = 'predefined' | 'changed' | 'custom' | 'legacy';
|
|
9
9
|
export interface ProvenanceEntry {
|
|
10
10
|
status: ProvenanceStatus;
|
|
11
11
|
preset?: string;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/** Where each configured definition came from, compared with the packaged presets.
|
|
2
2
|
* A definition is `predefined` when it has the same value as the shipped preset of
|
|
3
3
|
* the same name, `changed` when a preset of that name exists but the value differs,
|
|
4
|
-
* and `custom` otherwise.
|
|
4
|
+
* and `custom` otherwise. A Brain whose name and value match a preset that earlier
|
|
5
|
+
* releases generated for every model and effort is `legacy`; one of such a name
|
|
6
|
+
* with another value is `changed`. Values are compared after parsing, so the formatting the
|
|
5
7
|
* configuration editor writes does not count as a change. Preset text is returned
|
|
6
8
|
* so users can see exactly what ships; it contains no host data.
|
|
7
9
|
*/
|
|
@@ -29,6 +31,14 @@ function yamlFiles(dir) {
|
|
|
29
31
|
}
|
|
30
32
|
return files;
|
|
31
33
|
}
|
|
34
|
+
/** Brain presets earlier releases generated per model and effort; no longer shipped or installed. */
|
|
35
|
+
function legacyBrains(presetRoot) {
|
|
36
|
+
const path = join(presetRoot, '..', 'legacy', 'generated-brains-rev4.json');
|
|
37
|
+
if (!existsSync(path))
|
|
38
|
+
return new Map();
|
|
39
|
+
const { brains } = JSON.parse(readFileSync(path, 'utf8'));
|
|
40
|
+
return new Map(Object.entries(brains).map(([name, value]) => [name, Object.entries(value).map(([key, item]) => `${key}: ${item}`).join('\n') + '\n']));
|
|
41
|
+
}
|
|
32
42
|
const parsed = (text) => { try {
|
|
33
43
|
return parse(text);
|
|
34
44
|
}
|
|
@@ -47,10 +57,15 @@ export class PresetProvenance {
|
|
|
47
57
|
const result = {};
|
|
48
58
|
for (const [kind, dir] of Object.entries(PROVENANCE_KINDS)) {
|
|
49
59
|
const presets = yamlFiles(join(this.presetRoot, dir)), installed = yamlFiles(join(split, dir));
|
|
60
|
+
const legacy = kind === 'brain' ? legacyBrains(this.presetRoot) : new Map();
|
|
50
61
|
result[kind] = Object.fromEntries([...installed].map(([name, text]) => {
|
|
51
62
|
const preset = presets.get(name);
|
|
52
|
-
if (preset === undefined)
|
|
53
|
-
|
|
63
|
+
if (preset === undefined) {
|
|
64
|
+
const old = legacy.get(name);
|
|
65
|
+
if (old === undefined)
|
|
66
|
+
return [name, { status: 'custom' }];
|
|
67
|
+
return [name, { status: isDeepStrictEqual(parsed(text), parsed(old)) ? 'legacy' : 'changed', preset: old }];
|
|
68
|
+
}
|
|
54
69
|
return [name, { status: isDeepStrictEqual(parsed(text), parsed(preset)) ? 'predefined' : 'changed', preset }];
|
|
55
70
|
}));
|
|
56
71
|
}
|
|
@@ -145,7 +145,9 @@ export declare class RoleCreationService {
|
|
|
145
145
|
private launchSync;
|
|
146
146
|
capabilities(): Promise<CreationCapabilities>;
|
|
147
147
|
preview(input: CreateRoleSessionRequest): Promise<CreationPreview>;
|
|
148
|
-
create(input: CreateRoleSessionRequest, previewHash: string, idempotencyKey: string, browserSession: string
|
|
148
|
+
create(input: CreateRoleSessionRequest, previewHash: string, idempotencyKey: string, browserSession: string,
|
|
149
|
+
/** Checks a new request only; a retry of an accepted request returns its action. */
|
|
150
|
+
checkNewRequest?: (input: CreateRoleSessionRequest) => Promise<void>): Promise<CreationAction>;
|
|
149
151
|
get(actionId: string): CreationAction | undefined;
|
|
150
152
|
private run;
|
|
151
153
|
private waitForReady;
|
|
@@ -184,7 +184,9 @@ export class RoleCreationService {
|
|
|
184
184
|
identityBootstrap, previewHash,
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
|
-
async create(input, previewHash, idempotencyKey, browserSession
|
|
187
|
+
async create(input, previewHash, idempotencyKey, browserSession,
|
|
188
|
+
/** Checks a new request only; a retry of an accepted request returns its action. */
|
|
189
|
+
checkNewRequest) {
|
|
188
190
|
if (!/^[A-Za-z0-9_-]{22,256}$/.test(idempotencyKey))
|
|
189
191
|
throw new FleetError('invalid_request', 'Idempotency-Key must contain at least 128 bits');
|
|
190
192
|
const requestHash = hash(this.validate(input));
|
|
@@ -195,6 +197,7 @@ export class RoleCreationService {
|
|
|
195
197
|
throw new FleetError('idempotency_conflict', 'Idempotency-Key was already used for another request');
|
|
196
198
|
return this.actions.get(existing.actionId);
|
|
197
199
|
}
|
|
200
|
+
await checkNewRequest?.(input);
|
|
198
201
|
const preview = await this.preview(input);
|
|
199
202
|
if (preview.previewHash !== previewHash)
|
|
200
203
|
throw new FleetError('stale_state', 'preview is stale; review the effective plan again');
|
|
@@ -114,7 +114,7 @@ export declare class TaskRoomApplicationService {
|
|
|
114
114
|
private readonly configurationPath?;
|
|
115
115
|
private readonly deps;
|
|
116
116
|
constructor(configurationPath?: string | undefined, deps?: TaskRoomServiceDeps);
|
|
117
|
-
addMember(taskId: string, request: AddMemberRequest): Promise<{
|
|
117
|
+
addMember(taskId: string, request: AddMemberRequest, checkNewRequest?: (request: AddMemberRequest) => Promise<void>): Promise<{
|
|
118
118
|
requestId: string;
|
|
119
119
|
taskId: string;
|
|
120
120
|
roleId: string;
|
|
@@ -76,11 +76,11 @@ export class TaskRoomApplicationService {
|
|
|
76
76
|
this.configurationPath = configurationPath;
|
|
77
77
|
this.deps = deps;
|
|
78
78
|
}
|
|
79
|
-
async addMember(taskId, request) {
|
|
79
|
+
async addMember(taskId, request, checkNewRequest) {
|
|
80
80
|
const { requestMemberAddition } = await import('../rooms-tasks/add-member.js');
|
|
81
81
|
const cfg = (this.deps.loadConfiguration ?? loadConfig)(this.configurationPath);
|
|
82
82
|
return requestMemberAddition({ taskId, request, cfg, configPath: this.configurationPath,
|
|
83
|
-
cowork: readTask(taskId).layout ? undefined : (this.deps.cowork ?? createCoworkAdapter)(cfg), binPath: (this.deps.binPath ?? getBinPath)(), provision: this.deps.provisionMembers, layouts: this.layouts() });
|
|
83
|
+
cowork: readTask(taskId).layout ? undefined : (this.deps.cowork ?? createCoworkAdapter)(cfg), binPath: (this.deps.binPath ?? getBinPath)(), provision: this.deps.provisionMembers, layouts: this.layouts(), checkNewRequest });
|
|
84
84
|
}
|
|
85
85
|
async memberAddition(taskId, requestId) { const { memberAddition } = await import('../rooms-tasks/add-member.js'); return memberAddition(taskId, requestId); }
|
|
86
86
|
layouts() {
|
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.3.
|
|
3
|
-
"buildId": "
|
|
4
|
-
"commit": "
|
|
2
|
+
"version": "2.3.2",
|
|
3
|
+
"buildId": "597fdbc5f69f",
|
|
4
|
+
"commit": "31b6aae8610ef1e4d74212cb06d9b03379a8eb40",
|
|
5
5
|
"dirty": false,
|
|
6
|
-
"builtAt": "2026-10-
|
|
6
|
+
"builtAt": "2026-10-31T06:49:15.117Z",
|
|
7
7
|
"capabilities": [
|
|
8
8
|
"service.refresh-v1",
|
|
9
9
|
"managed-cli.codex-reasoning-effort-v1",
|
|
@@ -9,6 +9,7 @@ import { harnessRuntimeDir } from '../isolation/policy.js';
|
|
|
9
9
|
import { bundledAcpAgent, resolveBundledAcpAgent } from './acp-agent.js';
|
|
10
10
|
import { hostClaude } from './claude-runtime.js';
|
|
11
11
|
import { ClaudeCodeAgentSessionAdapter } from './claude-code-session.js';
|
|
12
|
+
import { packagedModelCatalog } from '../application/model-catalog.js';
|
|
12
13
|
const OPTION_KEYS = [
|
|
13
14
|
'plugins', 'mem_palace', 'mem_palace_midsession_autosave', 'permission_mode', 'effort',
|
|
14
15
|
'mcp_servers', 'mcp_servers_only',
|
|
@@ -196,6 +197,12 @@ export function makeClaudeCodeAdapter(exec = realExec, transport) {
|
|
|
196
197
|
},
|
|
197
198
|
}, transport),
|
|
198
199
|
supportsResume: true,
|
|
200
|
+
modelCapabilities: () => ({
|
|
201
|
+
sessions: ['acp'],
|
|
202
|
+
models: packagedModelCatalog('claude-code').map(model => ({ id: model.model, label: model.label, efforts: model.efforts })),
|
|
203
|
+
modelRequired: false,
|
|
204
|
+
customModels: true,
|
|
205
|
+
}),
|
|
199
206
|
async checkPrereqs() {
|
|
200
207
|
const r = await exec('claude', ['--version']);
|
|
201
208
|
const ok = r.code === 0;
|
package/dist/harness/codex.js
CHANGED
|
@@ -9,6 +9,7 @@ import { resolveBundledAcpAgent, } from './acp-agent.js';
|
|
|
9
9
|
import { VERIFIED_CODEX_ACP_VERSIONS, probeCodexRuntime, codexVersionAtLeast, hostCodex } from './codex-runtime.js';
|
|
10
10
|
import { CodexAgentSessionAdapter } from './codex-session.js';
|
|
11
11
|
import { CODEX_REASONING_EFFORTS, isCodexReasoningEffort } from './codex-reasoning.js';
|
|
12
|
+
import { packagedModelCatalog } from '../application/model-catalog.js';
|
|
12
13
|
const OPTION_KEYS = [
|
|
13
14
|
'launcher', 'sandbox', 'approval', 'permission_mode', 'search', 'profile', 'config', 'add_dirs',
|
|
14
15
|
'monitor',
|
|
@@ -361,6 +362,12 @@ export function makeCodexAdapter(exec = realExec, transport, nativeTransport) {
|
|
|
361
362
|
addDirs: role => role.harness_options?.add_dirs,
|
|
362
363
|
}, transport, nativeTransport),
|
|
363
364
|
supportsResume: true,
|
|
365
|
+
modelCapabilities: () => ({
|
|
366
|
+
sessions: ['acp', 'codex-app-server'],
|
|
367
|
+
models: packagedModelCatalog('codex').map(model => ({ id: model.model, label: model.label, efforts: model.efforts })),
|
|
368
|
+
modelRequired: false,
|
|
369
|
+
customModels: true,
|
|
370
|
+
}),
|
|
364
371
|
async checkPrereqs() {
|
|
365
372
|
const [r, hasOursCodex, plugins] = await Promise.all([
|
|
366
373
|
exec('codex', ['--version']),
|
package/dist/harness/hermes.js
CHANGED
|
@@ -21,6 +21,8 @@ export function makeHermesAdapter(exec = realExec, transport) {
|
|
|
21
21
|
},
|
|
22
22
|
}),
|
|
23
23
|
supportsResume: false,
|
|
24
|
+
// Hermes models come from the provider configured in the role home; Fleet knows none of them and sets no effort.
|
|
25
|
+
modelCapabilities: () => ({ sessions: ['acp'], models: [], modelRequired: true, customModels: true }),
|
|
24
26
|
async checkPrereqs() {
|
|
25
27
|
const result = await exec('hermes-acp', ['--help'], { timeout: 10_000 });
|
|
26
28
|
const ok = result.code === 0;
|
package/dist/harness/types.d.ts
CHANGED
|
@@ -112,12 +112,32 @@ export interface ValidationError {
|
|
|
112
112
|
path: string;
|
|
113
113
|
message: string;
|
|
114
114
|
}
|
|
115
|
+
/** A model the adapter knows, with the exact efforts it supports; [] means the model has no effort setting. */
|
|
116
|
+
export interface HarnessKnownModel {
|
|
117
|
+
id: string;
|
|
118
|
+
label: string;
|
|
119
|
+
efforts: string[];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Models and efforts a harness adapter maintains. Brain choices made in onboarding,
|
|
123
|
+
* Settings and launch flows are validated against this; existing Brains are not.
|
|
124
|
+
*/
|
|
125
|
+
export interface HarnessModelCapabilities {
|
|
126
|
+
sessions: string[];
|
|
127
|
+
models: HarnessKnownModel[];
|
|
128
|
+
/** A Brain must name a model; otherwise `model: null` selects the harness default. */
|
|
129
|
+
modelRequired: boolean;
|
|
130
|
+
/** Models outside `models` may be named; their efforts are unknown. */
|
|
131
|
+
customModels: boolean;
|
|
132
|
+
}
|
|
115
133
|
export interface HarnessAdapter {
|
|
116
134
|
id: string;
|
|
117
135
|
/** Harness-specific construction behind Fleet's shared live-session contract. */
|
|
118
136
|
agentSession: AgentSessionAdapter;
|
|
119
137
|
supportsResume: boolean;
|
|
120
138
|
checkPrereqs(): Promise<PrereqReport>;
|
|
139
|
+
/** Models and efforts this adapter supports. Optional for test adapters. */
|
|
140
|
+
modelCapabilities?(): HarnessModelCapabilities;
|
|
121
141
|
/**
|
|
122
142
|
* `role` is the SESSION-AWARE half: some harness options can only be honoured
|
|
123
143
|
* on some session types, and an option that is silently dropped is worse than
|
package/dist/init-wizard.d.ts
CHANGED
|
@@ -2,19 +2,18 @@ import type { Stats } from 'node:fs';
|
|
|
2
2
|
import type { ReadStream, WriteStream } from 'node:tty';
|
|
3
3
|
export type Subscription = 'codex' | 'claude';
|
|
4
4
|
export type WorkKind = 'development' | 'review' | 'coordination';
|
|
5
|
-
export type ReasoningPreference = 'quick' | 'balanced' | 'thorough';
|
|
6
5
|
export type AssignmentStrategy = 'one-model' | 'per-job';
|
|
7
|
-
|
|
6
|
+
/** The exact harness, model and effort chosen for one purpose; `effort: null` only for a model without effort. */
|
|
7
|
+
export interface PurposeChoice {
|
|
8
8
|
harness: 'codex' | 'claude-code';
|
|
9
9
|
session: 'acp';
|
|
10
10
|
model: string;
|
|
11
|
-
|
|
11
|
+
effort: string | null;
|
|
12
12
|
}
|
|
13
13
|
export interface InitAnswers {
|
|
14
14
|
subscriptions: Subscription[];
|
|
15
15
|
assignmentStrategy: AssignmentStrategy;
|
|
16
|
-
models: Record<WorkKind,
|
|
17
|
-
reasoning: ReasoningPreference;
|
|
16
|
+
models: Record<WorkKind, PurposeChoice>;
|
|
18
17
|
}
|
|
19
18
|
export interface Choice<T> {
|
|
20
19
|
label: string;
|
|
@@ -26,10 +25,6 @@ export interface InitPrompter {
|
|
|
26
25
|
select<T>(message: string, choices: Choice<T>[], initial?: number): Promise<T | undefined>;
|
|
27
26
|
note(message: string): void;
|
|
28
27
|
}
|
|
29
|
-
export interface BrainCatalog {
|
|
30
|
-
schema_version: number;
|
|
31
|
-
models: CatalogModel[];
|
|
32
|
-
}
|
|
33
28
|
export interface GeneratedSetup {
|
|
34
29
|
files: Map<string, string>;
|
|
35
30
|
answers: InitAnswers;
|
|
@@ -48,15 +43,27 @@ export interface InitExecutionDeps {
|
|
|
48
43
|
generate?(answers: InitAnswers): GeneratedSetup;
|
|
49
44
|
preflight?(configuration: string): InitPathState;
|
|
50
45
|
}
|
|
51
|
-
|
|
46
|
+
/** The three editable purpose Brains setup creates, by kind of work. */
|
|
47
|
+
export declare const PURPOSE_BRAINS: Record<WorkKind, {
|
|
48
|
+
id: string;
|
|
49
|
+
label: string;
|
|
50
|
+
}>;
|
|
52
51
|
/** Read and validate the complete public InitAnswers JSON shape without side effects. */
|
|
53
52
|
export declare function readInitSettings(path: string): InitAnswers;
|
|
54
53
|
export declare function askInitQuestions(prompter: InitPrompter, configuration: string): Promise<InitAnswers | undefined>;
|
|
55
54
|
export declare function formatSetupSummary(answers: InitAnswers, configuration: string): string;
|
|
56
|
-
/** The
|
|
57
|
-
export declare function
|
|
55
|
+
/** The YAML of one purpose Brain holding exactly the chosen triple. */
|
|
56
|
+
export declare function purposeBrainYaml(work: WorkKind, choice: PurposeChoice): string;
|
|
58
57
|
/** Build the exact, deterministic default experience without writing to disk. */
|
|
59
58
|
export declare function generateSetup(answers: InitAnswers): GeneratedSetup;
|
|
59
|
+
/** Setup stopped because an existing purpose Brain holds other settings than the chosen ones. */
|
|
60
|
+
export declare class PurposeBrainConflictError extends Error {
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Setup preserves existing files. A purpose Brain that already exists with a different
|
|
64
|
+
* harness, session, model or effort is never overwritten; setup stops and says so.
|
|
65
|
+
*/
|
|
66
|
+
export declare function purposeBrainConflicts(configuration: string, answers: InitAnswers): string[];
|
|
60
67
|
/** Run the mutation boundary only after the complete questionnaire is accepted. */
|
|
61
68
|
export declare function executeInitWizard(prompter: InitPrompter, configuration: string, deps: InitExecutionDeps): Promise<InitPublishResult | undefined>;
|
|
62
69
|
/** Execute already-collected answers through the same validation and mutation boundary. */
|