@ours.network/fleet 0.17.3 → 0.17.4
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/dist/build-info.json +4 -4
- package/dist/runner.d.ts +9 -1
- package/dist/runner.js +21 -3
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.17.
|
|
3
|
-
"buildId": "
|
|
4
|
-
"commit": "
|
|
2
|
+
"version": "0.17.4",
|
|
3
|
+
"buildId": "b94609824510",
|
|
4
|
+
"commit": "094a6fcb714541b03404e0fb39605105e390510b",
|
|
5
5
|
"dirty": false,
|
|
6
|
-
"builtAt": "2026-08-
|
|
6
|
+
"builtAt": "2026-08-14T21:36:46.539Z",
|
|
7
7
|
"capabilities": [
|
|
8
8
|
"monitor.interrupt.after_tool"
|
|
9
9
|
]
|
package/dist/runner.d.ts
CHANGED
|
@@ -3,7 +3,9 @@ import type { Launch } from './harness/types.js';
|
|
|
3
3
|
import { Tmux } from './tmux.js';
|
|
4
4
|
import { type MonitorHandle, type MonitorOpts, type FetchLike } from './monitor.js';
|
|
5
5
|
import { type Exec } from './exec.js';
|
|
6
|
-
import type
|
|
6
|
+
import { type AcpSessionOptions } from './session/acp.js';
|
|
7
|
+
import { RoleControlServer } from './session/control.js';
|
|
8
|
+
import type { ExitRecord, SessionHandle, TurnResult } from './session/types.js';
|
|
7
9
|
import { type OwnerChannelHandle, type OwnerChannelOptions } from './owner-channel/channel.js';
|
|
8
10
|
import { type OwnerBinderLease } from './owner-channel/binder.js';
|
|
9
11
|
export interface RunnerDeps {
|
|
@@ -20,6 +22,10 @@ export interface RunnerDeps {
|
|
|
20
22
|
createMonitor(opts: MonitorOpts): MonitorHandle;
|
|
21
23
|
/** Construct trusted owner ingress (injectable for lifecycle tests). */
|
|
22
24
|
createOwnerChannel(opts: OwnerChannelOptions): OwnerChannelHandle;
|
|
25
|
+
/** Start the ACP transport (injectable for deterministic runner lifecycle tests). */
|
|
26
|
+
startAcpSession(opts: AcpSessionOptions): Promise<SessionHandle>;
|
|
27
|
+
/** Construct the authenticated role control route (injectable where sockets are unavailable). */
|
|
28
|
+
createControlServer(stateDir: string, session: SessionHandle, log: (line: string) => void): Pick<RoleControlServer, 'start' | 'close' | 'setFleetSpawner' | 'setOwnerChannel' | 'setConfigReloader' | 'setLoopManager'>;
|
|
23
29
|
/** Acquire the cross-process owner-channel binder lease before replacing the control socket. */
|
|
24
30
|
acquireOwnerBinder(stateDir: string, role: string, identity: string): Promise<OwnerBinderLease>;
|
|
25
31
|
/** Ask the still-authenticated predecessor to emit the fixed recovery notice. */
|
|
@@ -107,6 +113,8 @@ export interface AttemptResult {
|
|
|
107
113
|
export declare const TEMP_IDENTITY_CLOSE_DEBOUNCE_MS = 5000;
|
|
108
114
|
/** Lifecycle polling is deliberately slower than the 500ms stop-signal loop. */
|
|
109
115
|
export declare const TEMP_IDENTITY_POLL_MS = 2000;
|
|
116
|
+
/** Only authenticated wake interrupts may turn a temp startup cancellation into readiness. */
|
|
117
|
+
export declare function isRecoverableTempStartupCancellation(temp: boolean, result: TurnResult): boolean;
|
|
110
118
|
/** One session lifecycle. `runSupervised` (or a one-shot caller) drives it. */
|
|
111
119
|
export declare function runOnce(name: string, opts?: {
|
|
112
120
|
temp?: boolean;
|
package/dist/runner.js
CHANGED
|
@@ -42,6 +42,8 @@ const defaultDeps = () => ({
|
|
|
42
42
|
fetch: (url, init) => globalThis.fetch(url, init),
|
|
43
43
|
createMonitor: opts => createMonitor(opts),
|
|
44
44
|
createOwnerChannel: opts => new OwnerChannel(opts),
|
|
45
|
+
startAcpSession: opts => AcpSession.start(opts),
|
|
46
|
+
createControlServer: (stateDir, session, log) => new RoleControlServer(stateDir, session, log),
|
|
45
47
|
acquireOwnerBinder: (stateDir, role, identity) => acquireOwnerBinderLease(stateDir, role, identity),
|
|
46
48
|
reportOwnerStartupFailure: async (stateDir) => {
|
|
47
49
|
const response = await controlRequest(stateDir, {
|
|
@@ -352,6 +354,12 @@ function resolveConfigPath(dir, explicit) {
|
|
|
352
354
|
export const TEMP_IDENTITY_CLOSE_DEBOUNCE_MS = 5_000;
|
|
353
355
|
/** Lifecycle polling is deliberately slower than the 500ms stop-signal loop. */
|
|
354
356
|
export const TEMP_IDENTITY_POLL_MS = 2_000;
|
|
357
|
+
/** Only authenticated wake interrupts may turn a temp startup cancellation into readiness. */
|
|
358
|
+
export function isRecoverableTempStartupCancellation(temp, result) {
|
|
359
|
+
return temp && result.accepted && result.outcome === 'cancelled'
|
|
360
|
+
&& (result.cancellationSource === 'local-console'
|
|
361
|
+
|| result.cancellationSource === 'fleet-monitor');
|
|
362
|
+
}
|
|
355
363
|
/** One session lifecycle. `runSupervised` (or a one-shot caller) drives it. */
|
|
356
364
|
export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
357
365
|
const deps = { ...defaultDeps(), ...partialDeps };
|
|
@@ -512,7 +520,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
512
520
|
if (perms.unattended === 'deny')
|
|
513
521
|
deps.log(`[${name}] permission policy: unattended=deny — with no console attached, ` +
|
|
514
522
|
`permission requests are automatically denied once each (reject_once) and the turn continues`);
|
|
515
|
-
acpSession = await
|
|
523
|
+
acpSession = await deps.startAcpSession({
|
|
516
524
|
name,
|
|
517
525
|
argv: wrappedArgv,
|
|
518
526
|
cwd: runCwd,
|
|
@@ -555,7 +563,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
555
563
|
+ `${error?.message ?? String(error)}`);
|
|
556
564
|
}
|
|
557
565
|
}
|
|
558
|
-
control =
|
|
566
|
+
control = deps.createControlServer(dir, arbiter, deps.log);
|
|
559
567
|
try {
|
|
560
568
|
await control.start();
|
|
561
569
|
}
|
|
@@ -640,7 +648,14 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
640
648
|
// success, so there is neither a deaf gap nor a boot-cancellation loop.
|
|
641
649
|
monitorLoop = monitor?.run(pid);
|
|
642
650
|
const started = await starting;
|
|
643
|
-
|
|
651
|
+
// A temporary role's first turn can be the active turn when an ours wake
|
|
652
|
+
// needs immediate attention. A typed console/monitor cancellation ends
|
|
653
|
+
// only that turn: the already-live ACP session and any queued wake remain
|
|
654
|
+
// valid. Keep every unproven cancellation, refusal, shutdown, and genuine
|
|
655
|
+
// failure terminal so a role that never accepted its briefing is not
|
|
656
|
+
// silently reported as healthy.
|
|
657
|
+
const interruptedForWake = isRecoverableTempStartupCancellation(temp, started);
|
|
658
|
+
if (!started.succeeded && !interruptedForWake) {
|
|
644
659
|
monitor?.stop();
|
|
645
660
|
await control.close();
|
|
646
661
|
ownerBinder?.release();
|
|
@@ -664,6 +679,9 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
|
|
|
664
679
|
throw new Error(`[${name}] ACP startup prompt ${started.outcome}` +
|
|
665
680
|
`${started.detail ? `: ${started.detail}` : ''}`);
|
|
666
681
|
}
|
|
682
|
+
if (interruptedForWake)
|
|
683
|
+
deps.log(`[${name}] ACP startup prompt cancelled by ${started.cancellationSource}; `
|
|
684
|
+
+ 'keeping temporary supervisor alive');
|
|
667
685
|
acpStartupComplete = true;
|
|
668
686
|
if (role.owner_channel) {
|
|
669
687
|
ownerChannel = deps.createOwnerChannel({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ours.network/fleet",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.4",
|
|
4
4
|
"description": "Harness-agnostic fleet of persistent, identity-bound AI agents. Declarative fleet.yaml, tmux or ACP sessions, supervision, and ours.network messaging.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "FSL-1.1-Apache-2.0",
|