@ours.network/fleet 1.1.4 → 1.2.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 +137 -1
- package/dist/briefing.js +4 -3
- package/dist/build-info.json +5 -5
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +13 -6
- package/dist/client-profile.d.ts +17 -0
- package/dist/client-profile.js +115 -0
- package/dist/creation.js +10 -7
- package/dist/daemon-recovery.d.ts +2 -0
- package/dist/daemon-recovery.js +53 -2
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +23 -2
- package/dist/doctor.js +62 -6
- package/dist/harness/acp-mcp.d.ts +14 -0
- package/dist/harness/acp-mcp.js +68 -0
- package/dist/harness/claude-code.js +1 -69
- package/dist/harness/codex.js +4 -0
- package/dist/harness/hermes-compatibility.d.ts +24 -0
- package/dist/harness/hermes-compatibility.js +191 -0
- package/dist/harness/hermes-config.d.ts +12 -0
- package/dist/harness/hermes-config.js +367 -0
- package/dist/harness/hermes-permissions.d.ts +4 -0
- package/dist/harness/hermes-permissions.js +36 -0
- package/dist/harness/hermes-session.d.ts +24 -0
- package/dist/harness/hermes-session.js +85 -0
- package/dist/harness/hermes-startup.d.ts +3 -0
- package/dist/harness/hermes-startup.js +21 -0
- package/dist/harness/hermes.d.ts +5 -0
- package/dist/harness/hermes.js +62 -0
- package/dist/harness/registry.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/init-wizard.d.ts +11 -6
- package/dist/init-wizard.js +33 -0
- package/dist/monitor.d.ts +34 -3
- package/dist/monitor.js +174 -74
- package/dist/owner-channel/channel.js +4 -2
- package/dist/owner-channel/ours-client.d.ts +12 -5
- package/dist/owner-channel/ours-client.js +48 -12
- package/dist/runner.js +21 -7
- package/dist/session/acp.d.ts +15 -0
- package/dist/session/acp.js +22 -7
- package/dist/session/codex-app-server.js +31 -5
- package/dist/spawn.d.ts +1 -0
- package/dist/spawn.js +1 -0
- package/dist/supervisor/launchd.js +8 -1
- package/examples/fleet/brains/hermes.yaml +5 -0
- package/package.json +5 -4
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type * as acp from '@agentclientprotocol/sdk';
|
|
2
|
+
import type { ResolvedRole } from '../config.js';
|
|
3
|
+
import type { SessionPrep } from './types.js';
|
|
4
|
+
import type { AgentSessionAdapter, AgentSessionStartOptions, BrainSelection } from './agent-session.js';
|
|
5
|
+
import type { AcpSessionTransport } from './acp-session-transport.js';
|
|
6
|
+
/** Native provider resolution and executable compatibility remain harness-owned. */
|
|
7
|
+
export interface HermesStartupChecks {
|
|
8
|
+
/** Independently resolved native provider identity, never copied from the ACP report. */
|
|
9
|
+
expectedProvider(role: ResolvedRole, prep: SessionPrep): string | Promise<string>;
|
|
10
|
+
validateArtifact(initialized: acp.InitializeResponse): void | Promise<void>;
|
|
11
|
+
/** Inspect the executable using the completed child environment before spawning it. */
|
|
12
|
+
preflight?(options: AgentSessionStartOptions, env: Record<string, string>): void | Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/** Fresh-only Hermes implementation of Fleet's existing live-session factory. */
|
|
15
|
+
export declare class HermesAgentSessionAdapter implements AgentSessionAdapter {
|
|
16
|
+
private readonly transport;
|
|
17
|
+
private readonly checks?;
|
|
18
|
+
constructor(transport?: AcpSessionTransport, checks?: HermesStartupChecks | undefined);
|
|
19
|
+
resolveBrain(brain: BrainSelection): ReturnType<AgentSessionAdapter['resolveBrain']>;
|
|
20
|
+
modelEnvironmentVariable(): string | undefined;
|
|
21
|
+
prepareLaunch(role: ResolvedRole, prep: SessionPrep): ReturnType<AgentSessionAdapter['prepareLaunch']>;
|
|
22
|
+
sessionConfigSelections(role: ResolvedRole): ReturnType<AgentSessionAdapter['sessionConfigSelections']>;
|
|
23
|
+
start(options: AgentSessionStartOptions): ReturnType<AgentSessionAdapter['start']>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { AcpSession } from '../session/acp.js';
|
|
2
|
+
import { hermesChildEnvironment, hermesMcpServers, validateHermesOptions, validateHermesRole } from './hermes-config.js';
|
|
3
|
+
import { hermesPermissionMode } from './hermes-permissions.js';
|
|
4
|
+
function requireValid(errors) {
|
|
5
|
+
if (errors.length)
|
|
6
|
+
throw new Error(errors.map(error => `${error.path}: ${error.message}`).join('; '));
|
|
7
|
+
}
|
|
8
|
+
function requireModel(model) {
|
|
9
|
+
if (typeof model !== 'string' || !model.trim())
|
|
10
|
+
throw new Error('Hermes requires an explicit non-empty Brain model');
|
|
11
|
+
return model.trim();
|
|
12
|
+
}
|
|
13
|
+
function preparedHome(prep) {
|
|
14
|
+
if (!prep.env.HERMES_HOME?.trim())
|
|
15
|
+
throw new Error('Hermes requires a prepared runtime home');
|
|
16
|
+
return prep.env.HERMES_HOME;
|
|
17
|
+
}
|
|
18
|
+
/** Fresh-only Hermes implementation of Fleet's existing live-session factory. */
|
|
19
|
+
export class HermesAgentSessionAdapter {
|
|
20
|
+
transport;
|
|
21
|
+
checks;
|
|
22
|
+
constructor(transport = AcpSession.start, checks) {
|
|
23
|
+
this.transport = transport;
|
|
24
|
+
this.checks = checks;
|
|
25
|
+
}
|
|
26
|
+
resolveBrain(brain) {
|
|
27
|
+
const model = requireModel(brain.model);
|
|
28
|
+
if (brain.effort != null)
|
|
29
|
+
throw new Error('Hermes does not support effort');
|
|
30
|
+
requireValid(validateHermesOptions(brain.harnessOptions));
|
|
31
|
+
return { model, ...(brain.harnessOptions ? { harnessOptions: brain.harnessOptions } : {}) };
|
|
32
|
+
}
|
|
33
|
+
modelEnvironmentVariable() { return undefined; }
|
|
34
|
+
prepareLaunch(role, prep) {
|
|
35
|
+
requireValid(validateHermesRole(role));
|
|
36
|
+
preparedHome(prep);
|
|
37
|
+
const command = role.session_options?.acp?.command;
|
|
38
|
+
const argv = Array.isArray(command) ? [...command]
|
|
39
|
+
: typeof command === 'string' ? ['sh', '-c', command] : ['hermes-acp'];
|
|
40
|
+
return { argv, env: { ...prep.env } };
|
|
41
|
+
}
|
|
42
|
+
sessionConfigSelections(role) {
|
|
43
|
+
requireValid(validateHermesRole(role));
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
async start(options) {
|
|
47
|
+
const { role, prep, launch } = options;
|
|
48
|
+
requireValid(validateHermesRole({ ...role, permissions: options.permissions }));
|
|
49
|
+
const model = requireModel(role.model);
|
|
50
|
+
if (!this.checks)
|
|
51
|
+
throw new Error('Hermes startup checks are required before launching a managed session');
|
|
52
|
+
// Reapply the complete environment boundary after runner routing/isolation
|
|
53
|
+
// composition; no second ambient merge may reintroduce provider secrets.
|
|
54
|
+
const env = hermesChildEnvironment(role, preparedHome(prep), launch.env, launch.env);
|
|
55
|
+
delete env.OURS_AUTOSTART;
|
|
56
|
+
await this.checks.preflight?.(options, env);
|
|
57
|
+
const nativeProvider = await this.checks.expectedProvider(role, prep);
|
|
58
|
+
if (typeof nativeProvider !== 'string' || !nativeProvider.trim())
|
|
59
|
+
throw new Error('Hermes requires an independently resolved native provider');
|
|
60
|
+
const provider = nativeProvider.trim().toLowerCase();
|
|
61
|
+
// model_catalog.encode_model_choice preserves model colons; its catalog
|
|
62
|
+
// constructor promotes Ollama to the named custom provider identity.
|
|
63
|
+
const expectedModelId = `${provider === 'ollama' ? 'custom:ollama' : provider}:${model}`;
|
|
64
|
+
const modeId = hermesPermissionMode(options.permissions);
|
|
65
|
+
return this.transport({
|
|
66
|
+
name: role.name, harness: 'hermes', argv: launch.argv, cwd: options.cwd, env,
|
|
67
|
+
inheritEnvironment: false, stateDir: options.stateDir, mode: 'fresh',
|
|
68
|
+
permissions: options.permissions, modeId, requireMode: true,
|
|
69
|
+
permissionMode: { fleetMode: options.permissionMode.fleetMode, nativeMode: modeId },
|
|
70
|
+
permissionTimeoutMs: 50_000,
|
|
71
|
+
mcpServers: hermesMcpServers(role, env),
|
|
72
|
+
scrubObsoleteOursAutostart: true,
|
|
73
|
+
validateStartupResponse: async (initialized, created) => {
|
|
74
|
+
await this.checks.validateArtifact(initialized);
|
|
75
|
+
if (typeof created.models?.currentModelId !== 'string'
|
|
76
|
+
|| created.models.currentModelId !== expectedModelId)
|
|
77
|
+
throw new Error('Hermes fresh-session model/provider report does not match the Brain model and provisioned provider');
|
|
78
|
+
},
|
|
79
|
+
...(role.monitor?.mode === 'fleet' && role.monitor.stall_recovery ? {
|
|
80
|
+
stallRecovery: { timeoutMs: role.monitor.stall_timeout_ms },
|
|
81
|
+
} : {}),
|
|
82
|
+
log: options.log,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import YAML from 'yaml';
|
|
4
|
+
export { validateHermesInitialize as validateHermesHandshake } from './hermes-compatibility.js';
|
|
5
|
+
/** Compare a native provisioned identity, never infer it from the child's report. */
|
|
6
|
+
export function hermesConfiguredProvider(home) {
|
|
7
|
+
let provider;
|
|
8
|
+
try {
|
|
9
|
+
provider = YAML.parse(readFileSync(join(home, 'config.yaml'), 'utf8'))?.model?.provider;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
throw new Error('Cannot read Hermes native provider; repair config.yaml in the stopped role home');
|
|
13
|
+
}
|
|
14
|
+
if (typeof provider !== 'string' || !provider.trim() || provider.trim().toLowerCase() === 'auto' || provider.includes('${'))
|
|
15
|
+
throw new Error('Hermes requires a literal native model.provider in the stopped role home so startup can detect provider fallback');
|
|
16
|
+
const canonical = provider.trim().toLowerCase();
|
|
17
|
+
// The tested runtime collapses named custom endpoints to custom, while its
|
|
18
|
+
// ACP model catalog reports Ollama under custom:ollama.
|
|
19
|
+
return canonical === 'ollama' || canonical === 'custom:ollama' ? 'custom:ollama'
|
|
20
|
+
: canonical.startsWith('custom:') ? 'custom' : canonical;
|
|
21
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type Exec } from '../exec.js';
|
|
2
|
+
import type { AcpSessionTransport } from './acp-session-transport.js';
|
|
3
|
+
import type { HarnessAdapter } from './types.js';
|
|
4
|
+
export declare function makeHermesAdapter(exec?: Exec, transport?: AcpSessionTransport): HarnessAdapter;
|
|
5
|
+
export declare const hermesAdapter: HarnessAdapter;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { realExec } from '../exec.js';
|
|
2
|
+
import { HermesAgentSessionAdapter } from './hermes-session.js';
|
|
3
|
+
import { inspectHermesCompatibility } from './hermes-compatibility.js';
|
|
4
|
+
import { hermesConfiguredProvider, validateHermesHandshake } from './hermes-startup.js';
|
|
5
|
+
import { prepareHermesConfig, validateHermesOptions, validateHermesRole } from './hermes-config.js';
|
|
6
|
+
import { hermesPermissionMode, translateHermesPermissions } from './hermes-permissions.js';
|
|
7
|
+
import { registerAdapter } from './registry.js';
|
|
8
|
+
const wakeNote = 'Your mail wake-ups are delivered by the fleet supervisor as `[fleet-monitor]` lines. '
|
|
9
|
+
+ 'Call **get_messages**, handle the mail, and reply with send_message. '
|
|
10
|
+
+ 'Do NOT arm arm_monitor, foreground_monitor or a native Hermes monitor.';
|
|
11
|
+
export function makeHermesAdapter(exec = realExec, transport) {
|
|
12
|
+
return {
|
|
13
|
+
id: 'hermes',
|
|
14
|
+
agentSession: new HermesAgentSessionAdapter(transport, {
|
|
15
|
+
expectedProvider: (_role, prep) => hermesConfiguredProvider(prep.env.HERMES_HOME),
|
|
16
|
+
validateArtifact: validateHermesHandshake,
|
|
17
|
+
async preflight(options, env) {
|
|
18
|
+
const original = new HermesAgentSessionAdapter().prepareLaunch(options.role, options.prep);
|
|
19
|
+
const report = await inspectHermesCompatibility({ argv: original.argv, env, home: options.prep.env.HERMES_HOME }, exec);
|
|
20
|
+
options.log(`Hermes ${report.artifact.hermesVersion}, ACP ${report.artifact.acpVersion}; fresh conversation, MCP availability unverified until actual use`);
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
supportsResume: false,
|
|
24
|
+
async checkPrereqs() {
|
|
25
|
+
const result = await exec('hermes-acp', ['--help'], { timeout: 10_000 });
|
|
26
|
+
const ok = result.code === 0;
|
|
27
|
+
return { ok, checks: [{ name: 'hermes-acp', ok, detail: ok
|
|
28
|
+
? 'Hermes ACP executable found; launch still requires a tested compatible artifact, an explicit Brain model and provider/credentials provisioned in the exact stopped role home. Home/plugin MCP providers must be disabled.'
|
|
29
|
+
: 'hermes-acp unavailable; install the tested Hermes artifact and provision provider/credentials in the exact stopped role home.' }] };
|
|
30
|
+
},
|
|
31
|
+
validateOptions(options, role) {
|
|
32
|
+
return role ? validateHermesRole({ ...role, harness_options: options })
|
|
33
|
+
: validateHermesOptions(options);
|
|
34
|
+
},
|
|
35
|
+
prepareSession: prepareHermesConfig,
|
|
36
|
+
// The selected home is already beneath the role state directory, which
|
|
37
|
+
// Fleet mounts writable. No operator home or credential path is shared.
|
|
38
|
+
isolationPaths: () => ({ shared: [] }),
|
|
39
|
+
nativePermissionOverrides: () => ({}),
|
|
40
|
+
translatePermissions: translateHermesPermissions,
|
|
41
|
+
effectivePermissions: role => translateHermesPermissions(role.permissions),
|
|
42
|
+
effectivePermissionMode(role) {
|
|
43
|
+
const nativeMode = hermesPermissionMode(role.permissions);
|
|
44
|
+
return { fleetMode: role.permissions.approval, nativeMode };
|
|
45
|
+
},
|
|
46
|
+
vocabulary: {
|
|
47
|
+
bindTool: 'choose_identity', createTool: 'create_identity',
|
|
48
|
+
temporaryCreateTool: 'create_temporary_identity', setBioTool: 'set_bio',
|
|
49
|
+
setPersonaTool: 'set_persona', currentIdentityTool: 'current_identity',
|
|
50
|
+
sendTool: 'send_message', getMessagesTool: 'get_messages',
|
|
51
|
+
listHistoryTool: 'list_history', getHistoryItemTool: 'get_history_item',
|
|
52
|
+
monitorInstruction: () => wakeNote,
|
|
53
|
+
supervisedWakeNote: () => wakeNote,
|
|
54
|
+
launchNote: name => `You were launched as Fleet role ${name} in a fresh Hermes ACP conversation. Confirm you are running.`,
|
|
55
|
+
restartPrompt: (identity, worklog) => `This is a fresh Hermes conversation. Follow the full role briefing, bind identity "${identity}" without force, `
|
|
56
|
+
+ `and continue from ${worklog}. Native memory and skills persist; the previous conversation is not restored. ${wakeNote}`,
|
|
57
|
+
},
|
|
58
|
+
exitPolicy: { cleanExitIsFresh: true, fastFailSecs: 20 },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export const hermesAdapter = makeHermesAdapter();
|
|
62
|
+
registerAdapter(hermesAdapter);
|
package/dist/harness/registry.js
CHANGED
|
@@ -29,7 +29,7 @@ export function knownAdapters() {
|
|
|
29
29
|
* registry" is not the same question — this is the set doctor falls back to
|
|
30
30
|
* when a broken configuration names no harness at all.
|
|
31
31
|
*/
|
|
32
|
-
const PRODUCTION_ADAPTERS = ['claude-code', 'codex'];
|
|
32
|
+
const PRODUCTION_ADAPTERS = ['claude-code', 'codex', 'hermes'];
|
|
33
33
|
/** Production adapters actually registered in this process. */
|
|
34
34
|
export function productionAdapters() {
|
|
35
35
|
return PRODUCTION_ADAPTERS.filter(id => adapters.has(id));
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { OwnerChannel } from './owner-channel/channel.js';
|
|
|
9
9
|
export { registerAdapter, getAdapter, knownAdapters } from './harness/registry.js';
|
|
10
10
|
export { claudeCodeAdapter, makeClaudeCodeAdapter } from './harness/claude-code.js';
|
|
11
11
|
export { codexAdapter, makeCodexAdapter } from './harness/codex.js';
|
|
12
|
+
export { hermesAdapter, makeHermesAdapter } from './harness/hermes.js';
|
|
12
13
|
export { generateBriefing } from './briefing.js';
|
|
13
14
|
export { effectivePermissionMode } from './permissions.js';
|
|
14
15
|
export { pickBackend } from './supervisor/index.js';
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export { OwnerChannel } from './owner-channel/channel.js';
|
|
|
6
6
|
export { registerAdapter, getAdapter, knownAdapters } from './harness/registry.js';
|
|
7
7
|
export { claudeCodeAdapter, makeClaudeCodeAdapter } from './harness/claude-code.js';
|
|
8
8
|
export { codexAdapter, makeCodexAdapter } from './harness/codex.js';
|
|
9
|
+
export { hermesAdapter, makeHermesAdapter } from './harness/hermes.js';
|
|
9
10
|
export { generateBriefing } from './briefing.js';
|
|
10
11
|
export { effectivePermissionMode } from './permissions.js';
|
|
11
12
|
export { pickBackend } from './supervisor/index.js';
|
package/dist/init-wizard.d.ts
CHANGED
|
@@ -42,18 +42,23 @@ export interface InitPublishResult {
|
|
|
42
42
|
manifestExisted: boolean;
|
|
43
43
|
rootExisted: boolean;
|
|
44
44
|
}
|
|
45
|
+
export interface InitExecutionDeps {
|
|
46
|
+
hostSetup(): Promise<void>;
|
|
47
|
+
publish(configuration: string, setup: GeneratedSetup): Promise<InitPublishResult>;
|
|
48
|
+
generate?(answers: InitAnswers): GeneratedSetup;
|
|
49
|
+
preflight?(configuration: string): InitPathState;
|
|
50
|
+
}
|
|
45
51
|
export declare function validateCatalog(value: BrainCatalog, path?: string): BrainCatalog;
|
|
52
|
+
/** Read and validate the complete public InitAnswers JSON shape without side effects. */
|
|
53
|
+
export declare function readInitSettings(path: string): InitAnswers;
|
|
46
54
|
export declare function askInitQuestions(prompter: InitPrompter, configuration: string): Promise<InitAnswers | undefined>;
|
|
47
55
|
export declare function formatSetupSummary(answers: InitAnswers, configuration: string): string;
|
|
48
56
|
/** Build the exact, deterministic default experience without writing to disk. */
|
|
49
57
|
export declare function generateSetup(answers: InitAnswers): GeneratedSetup;
|
|
50
58
|
/** Run the mutation boundary only after the complete questionnaire is accepted. */
|
|
51
|
-
export declare function executeInitWizard(prompter: InitPrompter, configuration: string, deps:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
generate?(answers: InitAnswers): GeneratedSetup;
|
|
55
|
-
preflight?(configuration: string): InitPathState;
|
|
56
|
-
}): Promise<InitPublishResult | undefined>;
|
|
59
|
+
export declare function executeInitWizard(prompter: InitPrompter, configuration: string, deps: InitExecutionDeps): Promise<InitPublishResult | undefined>;
|
|
60
|
+
/** Execute already-collected answers through the same validation and mutation boundary. */
|
|
61
|
+
export declare function executeInitAnswers(answers: InitAnswers, configuration: string, deps: InitExecutionDeps): Promise<InitPublishResult>;
|
|
57
62
|
export interface InitPathInspectionDeps {
|
|
58
63
|
exists?(path: string): boolean;
|
|
59
64
|
lstat?(path: string): Stats;
|
package/dist/init-wizard.js
CHANGED
|
@@ -49,6 +49,35 @@ function catalog() {
|
|
|
49
49
|
const path = join(packagedPresetRoot(), 'brain-catalog.json');
|
|
50
50
|
return validateCatalog(JSON.parse(readFileSync(path, 'utf8')), path);
|
|
51
51
|
}
|
|
52
|
+
const hasExactKeys = (value, keys) => Object.keys(value).sort().join('\0') === [...keys].sort().join('\0');
|
|
53
|
+
/** Read and validate the complete public InitAnswers JSON shape without side effects. */
|
|
54
|
+
export function readInitSettings(path) {
|
|
55
|
+
let value;
|
|
56
|
+
try {
|
|
57
|
+
value = JSON.parse(readFileSync(resolve(path), 'utf8'));
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
throw new Error(`cannot read valid Fleet init settings ${path}: ${error.message}`, { cause: error });
|
|
61
|
+
}
|
|
62
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)
|
|
63
|
+
|| !hasExactKeys(value, ['subscriptions', 'assignmentStrategy', 'models', 'reasoning']))
|
|
64
|
+
throw new Error('Fleet init settings must contain exactly subscriptions, assignmentStrategy, models, and reasoning');
|
|
65
|
+
const candidate = value;
|
|
66
|
+
const models = candidate.models;
|
|
67
|
+
if (!models || typeof models !== 'object' || Array.isArray(models)
|
|
68
|
+
|| !hasExactKeys(models, WORK_KINDS))
|
|
69
|
+
throw new Error('Fleet init settings models must contain exactly development, review, and coordination');
|
|
70
|
+
for (const work of WORK_KINDS) {
|
|
71
|
+
const model = models[work];
|
|
72
|
+
if (!model || typeof model !== 'object' || Array.isArray(model)
|
|
73
|
+
|| !hasExactKeys(model, ['harness', 'session', 'model', 'efforts']))
|
|
74
|
+
throw new Error(`Fleet init settings ${work} model must contain exactly harness, session, model, and efforts`);
|
|
75
|
+
}
|
|
76
|
+
const answers = value;
|
|
77
|
+
// Generation is the single semantic/catalog validator for wizard and file input.
|
|
78
|
+
generateSetup(answers);
|
|
79
|
+
return answers;
|
|
80
|
+
}
|
|
52
81
|
const subscriptionFor = (model) => model.harness === 'codex' ? 'codex' : 'claude';
|
|
53
82
|
export async function askInitQuestions(prompter, configuration) {
|
|
54
83
|
const configPath = resolve(configuration);
|
|
@@ -216,6 +245,10 @@ export async function executeInitWizard(prompter, configuration, deps) {
|
|
|
216
245
|
const answers = await askInitQuestions(prompter, configuration);
|
|
217
246
|
if (!answers)
|
|
218
247
|
return undefined;
|
|
248
|
+
return executeInitAnswers(answers, configuration, deps);
|
|
249
|
+
}
|
|
250
|
+
/** Execute already-collected answers through the same validation and mutation boundary. */
|
|
251
|
+
export async function executeInitAnswers(answers, configuration, deps) {
|
|
219
252
|
// Resolve every catalog/preset dependency before crossing the first mutation
|
|
220
253
|
// boundary. Production owns this new map; the optional seam forces failures
|
|
221
254
|
// deterministically in tests.
|
package/dist/monitor.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AttachOursClientOptions } from '@ours.network/sdk/client';
|
|
1
2
|
import type { MonitorConfig, MonitorInterrupt, NotifyEventType } from './config.js';
|
|
2
3
|
import { type FailureEvidence } from './model-recovery.js';
|
|
3
4
|
/** A content-free arrival event as the daemon serves it over the notifications API. */
|
|
@@ -26,6 +27,28 @@ export type FetchLike = (url: string, init?: {
|
|
|
26
27
|
headers?: Record<string, string>;
|
|
27
28
|
signal?: AbortSignal;
|
|
28
29
|
}) => Promise<FetchResponse>;
|
|
30
|
+
export interface MonitorPageClient {
|
|
31
|
+
readNotificationPage(identity: string, options?: {
|
|
32
|
+
since?: number | 'tip';
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
requestTimeoutMs?: number;
|
|
35
|
+
}): Promise<{
|
|
36
|
+
cursor: number;
|
|
37
|
+
events: Array<Record<string, unknown>>;
|
|
38
|
+
}>;
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export interface IdentityProbeClient {
|
|
42
|
+
identities(): Promise<Array<string | {
|
|
43
|
+
name?: unknown;
|
|
44
|
+
temporary?: unknown;
|
|
45
|
+
stale?: unknown;
|
|
46
|
+
}>>;
|
|
47
|
+
close(): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
export interface IdentityProbeDeps {
|
|
50
|
+
attachClient?(options: AttachOursClientOptions): IdentityProbeClient | Promise<IdentityProbeClient>;
|
|
51
|
+
}
|
|
29
52
|
export interface MonitorDeps {
|
|
30
53
|
fetch: FetchLike;
|
|
31
54
|
isAlive(pid: number): boolean;
|
|
@@ -37,6 +60,8 @@ export interface MonitorDeps {
|
|
|
37
60
|
set(fn: () => void, ms: number): ReturnType<typeof setTimeout>;
|
|
38
61
|
clear(t: ReturnType<typeof setTimeout>): void;
|
|
39
62
|
};
|
|
63
|
+
/** Test seam; production attaches through the SDK's verified selection path. */
|
|
64
|
+
attachClient?(options: AttachOursClientOptions): MonitorPageClient | Promise<MonitorPageClient>;
|
|
40
65
|
/**
|
|
41
66
|
* Structured prompt delivery used by agent sessions.
|
|
42
67
|
* `succeeded` is the turn's TERMINAL result, not merely that the session took
|
|
@@ -101,13 +126,13 @@ export type IdentityPresence = {
|
|
|
101
126
|
detail: string;
|
|
102
127
|
};
|
|
103
128
|
/** Resolve the daemon endpoint + auth header from env → config → defaults. */
|
|
104
|
-
export declare function resolveEndpoint(env: NodeJS.ProcessEnv): DaemonEndpoint;
|
|
129
|
+
export declare function resolveEndpoint(env: NodeJS.ProcessEnv, includeToken?: boolean): DaemonEndpoint;
|
|
105
130
|
/**
|
|
106
131
|
* Ask the daemon's authoritative identity index. The notifications endpoint is
|
|
107
132
|
* intentionally unsuitable for lifecycle: it serves an empty 200 page for a
|
|
108
133
|
* valid but missing identity, which made a closed temp identity look healthy.
|
|
109
134
|
*/
|
|
110
|
-
export declare function probeIdentityPresence(name: string, fetch: FetchLike, env: NodeJS.ProcessEnv): Promise<IdentityPresence>;
|
|
135
|
+
export declare function probeIdentityPresence(name: string, fetch: FetchLike, env: NodeJS.ProcessEnv, deps?: IdentityProbeDeps): Promise<IdentityPresence>;
|
|
111
136
|
/** Actionable, secret-free description of every token source for this profile. */
|
|
112
137
|
export declare function authResolutionHint(ep: DaemonEndpoint): string;
|
|
113
138
|
/** Keep only the events whose type the role asked to wake on. */
|
|
@@ -171,7 +196,11 @@ export declare class Monitor {
|
|
|
171
196
|
private readonly identity;
|
|
172
197
|
private readonly cfg;
|
|
173
198
|
private readonly deps;
|
|
174
|
-
private readonly ep
|
|
199
|
+
private readonly ep?;
|
|
200
|
+
private readonly profile?;
|
|
201
|
+
private readonly profileKey;
|
|
202
|
+
private readonly monitorLeaseToken;
|
|
203
|
+
private profileClientPromise?;
|
|
175
204
|
private readonly statusPath;
|
|
176
205
|
private readonly cursorPath;
|
|
177
206
|
private readonly statePath;
|
|
@@ -212,6 +241,8 @@ export declare class Monitor {
|
|
|
212
241
|
* no-ops (delivery is still verified downstream).
|
|
213
242
|
*/
|
|
214
243
|
private doFetch;
|
|
244
|
+
private profileClient;
|
|
245
|
+
private disposeProfileClient;
|
|
215
246
|
private advance;
|
|
216
247
|
private persistCursor;
|
|
217
248
|
private readPersistedCursor;
|