@ours.network/fleet 0.19.0-nightly.1 → 0.19.0-nightly.10
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 +30 -0
- package/dist/build-info.json +4 -4
- package/dist/cli.js +5 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +44 -3
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +28 -0
- package/dist/doctor.js +125 -1
- package/dist/owner-channel/channel.d.ts +7 -0
- package/dist/owner-channel/channel.js +47 -0
- package/dist/owner-channel/commands.d.ts +9 -0
- package/dist/owner-channel/commands.js +393 -0
- package/dist/rooms-tasks/cli.d.ts +4 -0
- package/dist/rooms-tasks/cli.js +1102 -0
- package/dist/rooms-tasks/close.d.ts +21 -0
- package/dist/rooms-tasks/close.js +159 -0
- package/dist/rooms-tasks/config.d.ts +15 -0
- package/dist/rooms-tasks/config.js +174 -0
- package/dist/rooms-tasks/cowork-adapter.d.ts +77 -0
- package/dist/rooms-tasks/cowork-adapter.js +272 -0
- package/dist/rooms-tasks/external-worker.d.ts +2 -0
- package/dist/rooms-tasks/external-worker.js +40 -0
- package/dist/rooms-tasks/index.d.ts +10 -0
- package/dist/rooms-tasks/index.js +10 -0
- package/dist/rooms-tasks/provision.d.ts +21 -0
- package/dist/rooms-tasks/provision.js +243 -0
- package/dist/rooms-tasks/room-state.d.ts +28 -0
- package/dist/rooms-tasks/room-state.js +221 -0
- package/dist/rooms-tasks/task-state.d.ts +43 -0
- package/dist/rooms-tasks/task-state.js +277 -0
- package/dist/rooms-tasks/templates.d.ts +6 -0
- package/dist/rooms-tasks/templates.js +94 -0
- package/dist/rooms-tasks/terminal.d.ts +23 -0
- package/dist/rooms-tasks/terminal.js +53 -0
- package/dist/rooms-tasks/types.d.ts +187 -0
- package/dist/rooms-tasks/types.js +20 -0
- package/dist/temp-lifecycle.d.ts +9 -2
- package/dist/temp-lifecycle.js +61 -0
- package/package.json +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { withFileLock } from '../atomic-file.js';
|
|
3
|
+
import { stateRoot } from '../paths.js';
|
|
4
|
+
import { closeManagedRoom } from './close.js';
|
|
5
|
+
import { beginTaskTerminalIntent, finishTaskTerminalIntent, getTask, setTaskTerminalIntentError, } from './task-state.js';
|
|
6
|
+
const TASK_OPERATION_LOCK_STALE_MS = 10 * 60_000;
|
|
7
|
+
function taskOperationLockPath(taskId) {
|
|
8
|
+
return join(stateRoot(), 'locks', 'task-terminal', encodeURIComponent(taskId));
|
|
9
|
+
}
|
|
10
|
+
function errorText(error) {
|
|
11
|
+
return error instanceof Error ? error.message : String(error);
|
|
12
|
+
}
|
|
13
|
+
/** Persist first-wins terminal intent before any acknowledgement or close effect. */
|
|
14
|
+
export function acceptTaskTerminalIntent(input) {
|
|
15
|
+
return withFileLock(taskOperationLockPath(input.taskId), () => {
|
|
16
|
+
const accepted = beginTaskTerminalIntent(input.taskId, input);
|
|
17
|
+
if (!input.roomId)
|
|
18
|
+
return finishTaskTerminalIntent(input.taskId);
|
|
19
|
+
return accepted;
|
|
20
|
+
}, {}, TASK_OPERATION_LOCK_STALE_MS);
|
|
21
|
+
}
|
|
22
|
+
/** Close the recorded room, then atomically settle the persisted task intent. */
|
|
23
|
+
export function settleTaskTerminalIntent(input) {
|
|
24
|
+
return withFileLock(taskOperationLockPath(input.taskId), async () => {
|
|
25
|
+
const current = getTask(input.taskId);
|
|
26
|
+
const intent = current.terminal_intent;
|
|
27
|
+
if (!intent)
|
|
28
|
+
throw new Error(`task ${input.taskId} has no accepted terminal intent`);
|
|
29
|
+
if (intent.status === 'settled')
|
|
30
|
+
return current;
|
|
31
|
+
try {
|
|
32
|
+
if (intent.room_id) {
|
|
33
|
+
const room = await closeManagedRoom({
|
|
34
|
+
roomId: intent.room_id,
|
|
35
|
+
cowork: input.cowork,
|
|
36
|
+
deps: input.deps?.roomClose,
|
|
37
|
+
});
|
|
38
|
+
if (room.state !== 'closed')
|
|
39
|
+
throw new Error(`room ${intent.room_id} did not close`);
|
|
40
|
+
await input.deps?.afterRoomClosed?.();
|
|
41
|
+
}
|
|
42
|
+
return finishTaskTerminalIntent(input.taskId);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
setTaskTerminalIntentError(input.taskId, errorText(error), `Retry 'ours-fleet task recover ${input.taskId}'.`);
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}, {}, TASK_OPERATION_LOCK_STALE_MS);
|
|
49
|
+
}
|
|
50
|
+
/** Persist failure to launch an external settle worker under the same task lock. */
|
|
51
|
+
export function recordTaskTerminalIntentError(taskId, error, recoveryHint) {
|
|
52
|
+
return withFileLock(taskOperationLockPath(taskId), () => setTaskTerminalIntentError(taskId, error, recoveryHint), {}, TASK_OPERATION_LOCK_STALE_MS);
|
|
53
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rooms & Tasks domain types for ours-fleet.
|
|
3
|
+
*
|
|
4
|
+
* Task and Room are Fleet orchestration records. Cowork owns room content,
|
|
5
|
+
* membership, and archive. Messenger Server owns owner presentation.
|
|
6
|
+
* Fleet stores only IDs, CIDs, orchestration state, and saga cursors.
|
|
7
|
+
*/
|
|
8
|
+
export type TaskState = 'backlog' | 'provisioning' | 'active' | 'review' | 'done' | 'cancelled' | 'failed';
|
|
9
|
+
export declare const TASK_TERMINAL_STATES: readonly TaskState[];
|
|
10
|
+
export declare const TASK_CANCELLABLE_STATES: readonly TaskState[];
|
|
11
|
+
export interface TaskBlocked {
|
|
12
|
+
reason: string;
|
|
13
|
+
at: string;
|
|
14
|
+
}
|
|
15
|
+
export interface TaskTemplateRef {
|
|
16
|
+
name: string;
|
|
17
|
+
version: number;
|
|
18
|
+
content_hash: string;
|
|
19
|
+
}
|
|
20
|
+
export interface TaskMemberRole {
|
|
21
|
+
name: string;
|
|
22
|
+
identity_cid: string;
|
|
23
|
+
slot: string;
|
|
24
|
+
cowork_role: string;
|
|
25
|
+
}
|
|
26
|
+
export interface TaskOrigin {
|
|
27
|
+
type: 'cli' | 'owner_channel';
|
|
28
|
+
owner_cid?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface TaskOutcome {
|
|
31
|
+
summary: string;
|
|
32
|
+
artifacts?: string[];
|
|
33
|
+
}
|
|
34
|
+
export interface TaskTerminalIntent {
|
|
35
|
+
kind: 'done' | 'cancelled';
|
|
36
|
+
status: 'pending' | 'settled';
|
|
37
|
+
room_id?: string;
|
|
38
|
+
outcome?: TaskOutcome;
|
|
39
|
+
accepted_at: string;
|
|
40
|
+
settled_at?: string;
|
|
41
|
+
error?: string;
|
|
42
|
+
error_at?: string;
|
|
43
|
+
recovery_hint?: string;
|
|
44
|
+
first_failure?: string;
|
|
45
|
+
first_recovery_hint?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface TaskRecord {
|
|
48
|
+
task_id: string;
|
|
49
|
+
title: string;
|
|
50
|
+
brief?: string;
|
|
51
|
+
brief_file?: string;
|
|
52
|
+
state: TaskState;
|
|
53
|
+
blocked?: TaskBlocked;
|
|
54
|
+
template?: TaskTemplateRef;
|
|
55
|
+
no_room?: boolean;
|
|
56
|
+
room_id?: string;
|
|
57
|
+
room_identity_cid?: string;
|
|
58
|
+
member_roles: TaskMemberRole[];
|
|
59
|
+
origin: TaskOrigin;
|
|
60
|
+
idempotency_key: string;
|
|
61
|
+
created_at: string;
|
|
62
|
+
started_at?: string;
|
|
63
|
+
ended_at?: string;
|
|
64
|
+
outcome?: TaskOutcome;
|
|
65
|
+
terminal_intent?: TaskTerminalIntent;
|
|
66
|
+
}
|
|
67
|
+
export type RoomOrchestrationState = 'provisioning' | 'active' | 'closing' | 'closed';
|
|
68
|
+
export type SagaPhase = 'persist_intent' | 'create_room' | 'attach_owner' | 'create_members' | 'join_role_groups' | 'wait_seats' | 'launch_work' | 'activate' | 'completed' | 'failed';
|
|
69
|
+
export interface SagaCursor {
|
|
70
|
+
phase: SagaPhase;
|
|
71
|
+
step_index: number;
|
|
72
|
+
error?: string;
|
|
73
|
+
recovery_hint?: string;
|
|
74
|
+
}
|
|
75
|
+
export type ProvisioningDetail = 'waiting_cowork' | 'waiting_owner_invite' | 'owner_cid_mismatch' | 'member_failed' | 'waiting_seats' | 'uncertain';
|
|
76
|
+
export type MemberRetirementPhase = 'stop_requested' | 'liveness_absent' | 'archive_secured' | 'identity_absent';
|
|
77
|
+
export interface MemberRetirement {
|
|
78
|
+
phase: MemberRetirementPhase;
|
|
79
|
+
launch_id: string;
|
|
80
|
+
updated_at: string;
|
|
81
|
+
archive_path?: string;
|
|
82
|
+
}
|
|
83
|
+
export type RoomClosePhase = 'retire_members' | 'close_cowork' | 'completed';
|
|
84
|
+
export interface RoomCloseCursor {
|
|
85
|
+
phase: RoomClosePhase;
|
|
86
|
+
accepted_at: string;
|
|
87
|
+
error?: string;
|
|
88
|
+
error_at?: string;
|
|
89
|
+
recovery_hint?: string;
|
|
90
|
+
first_failure?: string;
|
|
91
|
+
first_recovery_hint?: string;
|
|
92
|
+
}
|
|
93
|
+
export interface RoomMemberSeat {
|
|
94
|
+
role_name: string;
|
|
95
|
+
identity_cid: string;
|
|
96
|
+
slot: string;
|
|
97
|
+
cowork_role: string;
|
|
98
|
+
seat_state: 'pending' | 'active' | 'removed';
|
|
99
|
+
retirement?: MemberRetirement;
|
|
100
|
+
}
|
|
101
|
+
export interface RoomOrchestrationRecord {
|
|
102
|
+
room_id: string;
|
|
103
|
+
room_identity_cid?: string;
|
|
104
|
+
room_name: string;
|
|
105
|
+
goal?: string;
|
|
106
|
+
task_id?: string;
|
|
107
|
+
template_snapshot?: TemplateSnapshot;
|
|
108
|
+
saga: SagaCursor;
|
|
109
|
+
provisioning_detail?: ProvisioningDetail;
|
|
110
|
+
owner_seat_cid?: string;
|
|
111
|
+
owner_invite_fingerprint?: string;
|
|
112
|
+
member_seats: RoomMemberSeat[];
|
|
113
|
+
state: RoomOrchestrationState;
|
|
114
|
+
created_at: string;
|
|
115
|
+
activated_at?: string;
|
|
116
|
+
closed_at?: string;
|
|
117
|
+
close?: RoomCloseCursor;
|
|
118
|
+
}
|
|
119
|
+
export interface TemplateMemberSlot {
|
|
120
|
+
slot: string;
|
|
121
|
+
role: string;
|
|
122
|
+
count: number;
|
|
123
|
+
role_ref: string;
|
|
124
|
+
overrides?: TemplateRoleOverrides;
|
|
125
|
+
}
|
|
126
|
+
export interface TemplateRoleOverrides {
|
|
127
|
+
harness?: string;
|
|
128
|
+
model?: string;
|
|
129
|
+
model_chain?: string[];
|
|
130
|
+
permissions?: Record<string, unknown>;
|
|
131
|
+
isolation?: Record<string, unknown>;
|
|
132
|
+
cwd?: string;
|
|
133
|
+
env?: Record<string, string>;
|
|
134
|
+
persona?: string;
|
|
135
|
+
mission?: string;
|
|
136
|
+
}
|
|
137
|
+
export interface TemplateRoomConfig {
|
|
138
|
+
quiet_membership?: boolean;
|
|
139
|
+
anonymous?: boolean;
|
|
140
|
+
}
|
|
141
|
+
export interface TemplateDefinition {
|
|
142
|
+
name: string;
|
|
143
|
+
version: number;
|
|
144
|
+
description: string;
|
|
145
|
+
builtin?: boolean;
|
|
146
|
+
room?: TemplateRoomConfig;
|
|
147
|
+
contract?: string;
|
|
148
|
+
members: TemplateMemberSlot[];
|
|
149
|
+
}
|
|
150
|
+
export interface TemplateSnapshot extends TemplateDefinition {
|
|
151
|
+
content_hash: string;
|
|
152
|
+
}
|
|
153
|
+
export interface RoomsOwnerConfig {
|
|
154
|
+
provider: string;
|
|
155
|
+
public_invite?: string;
|
|
156
|
+
public_invite_file?: string;
|
|
157
|
+
expected_cid: string;
|
|
158
|
+
role: string;
|
|
159
|
+
}
|
|
160
|
+
export interface RoomsCoworkConfig {
|
|
161
|
+
config?: string;
|
|
162
|
+
}
|
|
163
|
+
export interface RoomsDefaults {
|
|
164
|
+
template?: string;
|
|
165
|
+
attach_owner?: boolean;
|
|
166
|
+
close_when_task_done?: boolean;
|
|
167
|
+
}
|
|
168
|
+
export interface RoomsConfig {
|
|
169
|
+
cowork?: RoomsCoworkConfig;
|
|
170
|
+
owner: RoomsOwnerConfig;
|
|
171
|
+
defaults?: RoomsDefaults;
|
|
172
|
+
}
|
|
173
|
+
export interface TasksConfig {
|
|
174
|
+
default_room_template?: string;
|
|
175
|
+
create_mode?: 'start' | 'backlog';
|
|
176
|
+
close_room_on_done?: boolean;
|
|
177
|
+
retain_completed_for?: string;
|
|
178
|
+
}
|
|
179
|
+
export type RoomTemplatesConfig = Record<string, TemplateDefinition>;
|
|
180
|
+
export declare const ROOMS_KEYS: readonly ["cowork", "owner", "defaults"];
|
|
181
|
+
export declare const ROOMS_OWNER_KEYS: readonly ["provider", "public_invite", "public_invite_file", "expected_cid", "role"];
|
|
182
|
+
export declare const ROOMS_COWORK_KEYS: readonly ["config"];
|
|
183
|
+
export declare const ROOMS_DEFAULTS_KEYS: readonly ["template", "attach_owner", "close_when_task_done"];
|
|
184
|
+
export declare const TASKS_KEYS: readonly ["default_room_template", "create_mode", "close_room_on_done", "retain_completed_for"];
|
|
185
|
+
export declare const TEMPLATE_KEYS: readonly ["version", "description", "room", "contract", "members", "override_builtin"];
|
|
186
|
+
export declare const TEMPLATE_MEMBER_KEYS: readonly ["slot", "role", "count", "role_ref", "overrides"];
|
|
187
|
+
export declare const TEMPLATE_OVERRIDE_KEYS: readonly ["harness", "model", "model_chain", "permissions", "isolation", "cwd", "env", "persona", "mission"];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rooms & Tasks domain types for ours-fleet.
|
|
3
|
+
*
|
|
4
|
+
* Task and Room are Fleet orchestration records. Cowork owns room content,
|
|
5
|
+
* membership, and archive. Messenger Server owns owner presentation.
|
|
6
|
+
* Fleet stores only IDs, CIDs, orchestration state, and saga cursors.
|
|
7
|
+
*/
|
|
8
|
+
export const TASK_TERMINAL_STATES = ['done', 'cancelled', 'failed'];
|
|
9
|
+
export const TASK_CANCELLABLE_STATES = ['backlog', 'provisioning', 'active', 'review'];
|
|
10
|
+
// ── Validation keys ─────────────────────────────────────────────────────
|
|
11
|
+
export const ROOMS_KEYS = ['cowork', 'owner', 'defaults'];
|
|
12
|
+
export const ROOMS_OWNER_KEYS = ['provider', 'public_invite', 'public_invite_file', 'expected_cid', 'role'];
|
|
13
|
+
export const ROOMS_COWORK_KEYS = ['config'];
|
|
14
|
+
export const ROOMS_DEFAULTS_KEYS = ['template', 'attach_owner', 'close_when_task_done'];
|
|
15
|
+
export const TASKS_KEYS = ['default_room_template', 'create_mode', 'close_room_on_done', 'retain_completed_for'];
|
|
16
|
+
export const TEMPLATE_KEYS = ['version', 'description', 'room', 'contract', 'members', 'override_builtin'];
|
|
17
|
+
export const TEMPLATE_MEMBER_KEYS = ['slot', 'role', 'count', 'role_ref', 'overrides'];
|
|
18
|
+
export const TEMPLATE_OVERRIDE_KEYS = [
|
|
19
|
+
'harness', 'model', 'model_chain', 'permissions', 'isolation', 'cwd', 'env', 'persona', 'mission',
|
|
20
|
+
];
|
package/dist/temp-lifecycle.d.ts
CHANGED
|
@@ -46,11 +46,19 @@ export declare function markTempSupervisorActive(dir: string, pid?: number): Pro
|
|
|
46
46
|
export declare function requestedTempStopReason(dir: string): 'operator-stop' | undefined;
|
|
47
47
|
/** Move retired state out of the live roster without deleting any evidence. */
|
|
48
48
|
export declare function archiveTempState(role: string, reason: TempTerminationReason, outcome: TempTerminationRecord['outcome'], detail: string, now?: Date): string | undefined;
|
|
49
|
-
|
|
49
|
+
/** Resolve only an exact role+launch archive; names and timestamps are never proof. */
|
|
50
|
+
export declare function tempArchiveForLaunch(role: string, launchId: string): string | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Preserve a definitively stopped launch as recovery evidence, or prove that
|
|
53
|
+
* its supervisor already did so. Never creates a second archive.
|
|
54
|
+
*/
|
|
55
|
+
export declare function secureStoppedTempArchive(role: string, launchId: string, deps?: TempLifecycleDeps): Promise<string>;
|
|
56
|
+
export interface TempLifecycleDeps {
|
|
50
57
|
exec?: Exec;
|
|
51
58
|
now?(): number;
|
|
52
59
|
kill?(pid: number, signal: NodeJS.Signals | 0): void;
|
|
53
60
|
log?(line: string): void;
|
|
61
|
+
sleep?(ms: number): Promise<void>;
|
|
54
62
|
}
|
|
55
63
|
export declare function tempSupervisorLiveness(dir: string, deps?: TempLifecycleDeps): Promise<'running' | 'stopped' | 'unknown'>;
|
|
56
64
|
export declare function stopTempSupervisor(role: string, deps?: TempLifecycleDeps): Promise<'stopped' | 'already-stopped'>;
|
|
@@ -59,4 +67,3 @@ export declare function stopTempSupervisor(role: string, deps?: TempLifecycleDep
|
|
|
59
67
|
* Unknown/legacy/live entries are preserved; absence of proof is never cleanup authority.
|
|
60
68
|
*/
|
|
61
69
|
export declare function reclaimStaleTempState(deps?: TempLifecycleDeps): Promise<string[]>;
|
|
62
|
-
export {};
|
package/dist/temp-lifecycle.js
CHANGED
|
@@ -191,6 +191,67 @@ export function archiveTempState(role, reason, outcome, detail, now = new Date()
|
|
|
191
191
|
}
|
|
192
192
|
return paths.target;
|
|
193
193
|
}
|
|
194
|
+
function isArchiveForLaunch(path, role, launchId) {
|
|
195
|
+
const supervisor = readTempSupervisor(path);
|
|
196
|
+
if (supervisor?.role !== role || supervisor.launchId !== launchId)
|
|
197
|
+
return false;
|
|
198
|
+
try {
|
|
199
|
+
return readFileSync(join(path, TEMP_TERMINATION_FILE), 'utf8')
|
|
200
|
+
.split('\n')
|
|
201
|
+
.filter(Boolean)
|
|
202
|
+
.some(line => {
|
|
203
|
+
const record = JSON.parse(line);
|
|
204
|
+
return record.version === 1 && record.role === role && record.launchId === launchId;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/** Resolve only an exact role+launch archive; names and timestamps are never proof. */
|
|
212
|
+
export function tempArchiveForLaunch(role, launchId) {
|
|
213
|
+
let entries;
|
|
214
|
+
try {
|
|
215
|
+
entries = readdirSync(archiveRoot(), { withFileTypes: true })
|
|
216
|
+
.filter(entry => entry.isDirectory() && !entry.isSymbolicLink() && !entry.name.startsWith('.'))
|
|
217
|
+
.map(entry => join(archiveRoot(), entry.name));
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
const matches = entries.filter(path => isArchiveForLaunch(path, role, launchId));
|
|
223
|
+
if (matches.length > 1) {
|
|
224
|
+
throw new Error(`temporary role '${role}' launch ${launchId} has ${matches.length} recovery archives; refusing ambiguity`);
|
|
225
|
+
}
|
|
226
|
+
return matches[0];
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Preserve a definitively stopped launch as recovery evidence, or prove that
|
|
230
|
+
* its supervisor already did so. Never creates a second archive.
|
|
231
|
+
*/
|
|
232
|
+
export async function secureStoppedTempArchive(role, launchId, deps = {}) {
|
|
233
|
+
const dir = join(tmpRoot(), role);
|
|
234
|
+
if (existsSync(dir)) {
|
|
235
|
+
const supervisor = readTempSupervisor(dir);
|
|
236
|
+
if (!supervisor || supervisor.role !== role || supervisor.launchId !== launchId) {
|
|
237
|
+
throw new Error(`temporary role '${role}' no longer matches recorded launch ${launchId}; refusing to archive`);
|
|
238
|
+
}
|
|
239
|
+
const live = await tempSupervisorLiveness(dir, deps);
|
|
240
|
+
if (live !== 'stopped') {
|
|
241
|
+
throw new Error(`temporary role '${role}' supervisor is ${live}; refusing to archive before proven stop`);
|
|
242
|
+
}
|
|
243
|
+
const archived = archiveTempState(role, 'operator-stop', 'retired', 'room close proved the supervisor stopped; full role evidence preserved');
|
|
244
|
+
if (!archived || !isArchiveForLaunch(archived, role, launchId)) {
|
|
245
|
+
throw new Error(`temporary role '${role}' archive could not be verified for launch ${launchId}`);
|
|
246
|
+
}
|
|
247
|
+
return archived;
|
|
248
|
+
}
|
|
249
|
+
const archived = tempArchiveForLaunch(role, launchId);
|
|
250
|
+
if (!archived) {
|
|
251
|
+
throw new Error(`temporary role '${role}' live state disappeared without an exact recovery archive for launch ${launchId}`);
|
|
252
|
+
}
|
|
253
|
+
return archived;
|
|
254
|
+
}
|
|
194
255
|
/** Finish bounded archive renames interrupted by process or host termination. */
|
|
195
256
|
function recoverInterruptedArchives(now) {
|
|
196
257
|
let names;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ours.network/fleet",
|
|
3
|
-
"version": "0.19.0-nightly.
|
|
3
|
+
"version": "0.19.0-nightly.10",
|
|
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",
|