@hheei/pi-subagents 0.3.0 → 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 +5 -5
- package/dist/child-control.d.ts +1 -1
- package/dist/child-control.js +89 -26
- package/dist/child-process.d.ts +2 -0
- package/dist/child-process.js +24 -12
- package/dist/domain.d.ts +15 -0
- package/dist/extension.d.ts +3 -4
- package/dist/extension.js +66 -79
- package/dist/host-adapter.d.ts +19 -0
- package/dist/host-adapter.js +139 -0
- package/dist/info.js +32 -6
- package/dist/lease.d.ts +83 -0
- package/dist/lease.js +239 -0
- package/dist/mailbox.d.ts +46 -0
- package/dist/mailbox.js +368 -0
- package/dist/manager.d.ts +17 -19
- package/dist/manager.js +301 -427
- package/dist/process-lock.d.ts +11 -0
- package/dist/process-lock.js +55 -0
- package/dist/protocol.d.ts +2 -2
- package/dist/protocol.js +1 -0
- package/dist/registry.d.ts +1 -1
- package/dist/registry.js +80 -77
- package/dist/rpc-events.d.ts +1 -1
- package/dist/rpc-events.js +2 -0
- package/dist/runtime.d.ts +8 -21
- package/dist/runtime.js +61 -83
- package/dist/state-db.d.ts +52 -0
- package/dist/state-db.js +209 -0
- package/dist/state.d.ts +12 -0
- package/dist/state.js +107 -21
- package/dist/tool-presentation.d.ts +2 -5
- package/dist/tool-presentation.js +38 -5
- package/dist/tools.js +3 -2
- package/package.json +6 -6
- package/dist/bridge-client.d.ts +0 -60
- package/dist/bridge-client.js +0 -459
- package/dist/bridge-server.d.ts +0 -72
- package/dist/bridge-server.js +0 -490
package/dist/mailbox.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readdirSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { errorMessage } from "@hheei/pi-ext-core";
|
|
5
|
+
import { holdProcessLock, processLockHeld, processLockPath, } from "./process-lock.js";
|
|
6
|
+
import { BridgeError } from "./protocol.js";
|
|
7
|
+
import { claimCommand, claimParentCommands, deleteCommand, enqueueCommand, finishCommand, hasPendingCommand, openStateDb, readCommand, requeueAbandonedParentCommands, withWrite, } from "./state-db.js";
|
|
8
|
+
const DEFAULT_POLL_MS = 50;
|
|
9
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
10
|
+
export function createMailboxTransport(options) {
|
|
11
|
+
const db = openStateDb(options.filePath);
|
|
12
|
+
const pollMs = options.pollMs ?? DEFAULT_POLL_MS;
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
const eventListeners = new Set();
|
|
15
|
+
const connectionListeners = new Set();
|
|
16
|
+
let closed = false;
|
|
17
|
+
let polling = false;
|
|
18
|
+
const emitConnection = (childId, connected) => {
|
|
19
|
+
for (const listener of [...connectionListeners]) {
|
|
20
|
+
try {
|
|
21
|
+
listener(childId, connected);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
options.diagnose?.(`connection listener failed: ${errorMessage(error)}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const lockHeld = (childId) => {
|
|
29
|
+
const lockPath = processLockPath(options.filePath, options.parentSessionId, childId);
|
|
30
|
+
if (lockPath === undefined)
|
|
31
|
+
return false;
|
|
32
|
+
try {
|
|
33
|
+
return processLockHeld(lockPath);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
options.diagnose?.(`could not probe the process lock for ${childId}: ${errorMessage(error)}`);
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const noticeLocks = () => {
|
|
41
|
+
if (closed)
|
|
42
|
+
return;
|
|
43
|
+
const directory = join(dirname(options.filePath), "locks", options.parentSessionId);
|
|
44
|
+
let names = [];
|
|
45
|
+
try {
|
|
46
|
+
names = readdirSync(directory);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const missing = typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
50
|
+
if (!missing) {
|
|
51
|
+
options.diagnose?.(`could not read process locks: ${errorMessage(error)}`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const heldNow = new Set();
|
|
56
|
+
for (const name of names) {
|
|
57
|
+
if (!name.endsWith(".lock"))
|
|
58
|
+
continue;
|
|
59
|
+
const childId = name.slice(0, -".lock".length);
|
|
60
|
+
const held = lockHeld(childId);
|
|
61
|
+
if (held === true || (held === undefined && seen.has(childId)))
|
|
62
|
+
heldNow.add(childId);
|
|
63
|
+
}
|
|
64
|
+
for (const childId of heldNow) {
|
|
65
|
+
if (seen.has(childId))
|
|
66
|
+
continue;
|
|
67
|
+
seen.add(childId);
|
|
68
|
+
emitConnection(childId, true);
|
|
69
|
+
}
|
|
70
|
+
for (const childId of [...seen]) {
|
|
71
|
+
if (heldNow.has(childId))
|
|
72
|
+
continue;
|
|
73
|
+
seen.delete(childId);
|
|
74
|
+
emitConnection(childId, false);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const takeParentCommands = async () => {
|
|
78
|
+
if (closed || polling)
|
|
79
|
+
return;
|
|
80
|
+
polling = true;
|
|
81
|
+
try {
|
|
82
|
+
noticeLocks();
|
|
83
|
+
// Empty is the common tick. A read sees it; claiming would take a write lock and fsync.
|
|
84
|
+
if (!hasPendingCommand(db, options.parentSessionId, "to_parent"))
|
|
85
|
+
return;
|
|
86
|
+
const claimed = withWrite(db, () => claimParentCommands(db, options.parentSessionId));
|
|
87
|
+
const eventIds = [];
|
|
88
|
+
const dropEvents = () => {
|
|
89
|
+
if (eventIds.length === 0)
|
|
90
|
+
return;
|
|
91
|
+
const ids = eventIds.splice(0);
|
|
92
|
+
withWrite(db, () => {
|
|
93
|
+
for (const id of ids)
|
|
94
|
+
deleteCommand(db, id);
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
for (const command of claimed) {
|
|
98
|
+
if (!seen.has(command.subagentId) && lockHeld(command.subagentId) === true) {
|
|
99
|
+
seen.add(command.subagentId);
|
|
100
|
+
emitConnection(command.subagentId, true);
|
|
101
|
+
}
|
|
102
|
+
if (command.operation === "event") {
|
|
103
|
+
let event = command.payload;
|
|
104
|
+
try {
|
|
105
|
+
event = JSON.parse(command.payload);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
event = command.payload;
|
|
109
|
+
}
|
|
110
|
+
for (const listener of [...eventListeners]) {
|
|
111
|
+
try {
|
|
112
|
+
listener(command.subagentId, event);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
options.diagnose?.(`event listener failed: ${errorMessage(error)}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
eventIds.push(command.id);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
dropEvents();
|
|
122
|
+
try {
|
|
123
|
+
const payload = JSON.parse(command.payload);
|
|
124
|
+
const result = await options.handleRequest({
|
|
125
|
+
childId: command.subagentId,
|
|
126
|
+
operation: command.operation,
|
|
127
|
+
payload,
|
|
128
|
+
});
|
|
129
|
+
finishCommand(db, command.id, { status: "done", result: JSON.stringify(result ?? null) });
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const bridge = error instanceof BridgeError ? error : undefined;
|
|
133
|
+
finishCommand(db, command.id, {
|
|
134
|
+
status: "failed",
|
|
135
|
+
errorCode: bridge?.code ?? "request_failed",
|
|
136
|
+
error: bridge?.message ?? errorMessage(error),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
dropEvents();
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
polling = false;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
withWrite(db, () => {
|
|
147
|
+
requeueAbandonedParentCommands(db, options.parentSessionId);
|
|
148
|
+
});
|
|
149
|
+
const timer = setInterval(() => {
|
|
150
|
+
void takeParentCommands();
|
|
151
|
+
}, pollMs);
|
|
152
|
+
timer.unref?.();
|
|
153
|
+
return {
|
|
154
|
+
endpoint: options.filePath,
|
|
155
|
+
isConnected(childId) {
|
|
156
|
+
return lockHeld(childId) !== false;
|
|
157
|
+
},
|
|
158
|
+
disconnect(childId) {
|
|
159
|
+
if (lockHeld(childId) !== false)
|
|
160
|
+
return;
|
|
161
|
+
if (seen.delete(childId))
|
|
162
|
+
emitConnection(childId, false);
|
|
163
|
+
},
|
|
164
|
+
async request(childId, operation, payload, requestOptions) {
|
|
165
|
+
const id = randomUUID();
|
|
166
|
+
enqueueCommand(db, {
|
|
167
|
+
id,
|
|
168
|
+
parentSessionId: options.parentSessionId,
|
|
169
|
+
subagentId: childId,
|
|
170
|
+
direction: "to_child",
|
|
171
|
+
operation,
|
|
172
|
+
payload: JSON.stringify(payload ?? null),
|
|
173
|
+
createdAt: Date.now(),
|
|
174
|
+
});
|
|
175
|
+
return waitForCommand(db, id, requestOptions?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS, pollMs, requestOptions?.signal);
|
|
176
|
+
},
|
|
177
|
+
onEvent(listener) {
|
|
178
|
+
eventListeners.add(listener);
|
|
179
|
+
return () => {
|
|
180
|
+
eventListeners.delete(listener);
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
onConnectionChange(listener) {
|
|
184
|
+
connectionListeners.add(listener);
|
|
185
|
+
return () => {
|
|
186
|
+
connectionListeners.delete(listener);
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
async waitUntilSeen(childId, waitOptions) {
|
|
190
|
+
const timeoutMs = waitOptions?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
191
|
+
const started = Date.now();
|
|
192
|
+
while (Date.now() - started < timeoutMs) {
|
|
193
|
+
if (waitOptions?.signal?.aborted)
|
|
194
|
+
return false;
|
|
195
|
+
if (lockHeld(childId) === true) {
|
|
196
|
+
if (!seen.has(childId)) {
|
|
197
|
+
seen.add(childId);
|
|
198
|
+
emitConnection(childId, true);
|
|
199
|
+
}
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
await delay(pollMs);
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
},
|
|
206
|
+
close() {
|
|
207
|
+
closed = true;
|
|
208
|
+
clearInterval(timer);
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
export function attachChildMailbox(options) {
|
|
213
|
+
let db;
|
|
214
|
+
let processLock;
|
|
215
|
+
const pollMs = options.pollMs ?? DEFAULT_POLL_MS;
|
|
216
|
+
let closed = false;
|
|
217
|
+
let started = false;
|
|
218
|
+
let working = false;
|
|
219
|
+
let timer;
|
|
220
|
+
const database = () => {
|
|
221
|
+
if (db === undefined)
|
|
222
|
+
throw new BridgeError("not_connected", "the state database is not open");
|
|
223
|
+
return db;
|
|
224
|
+
};
|
|
225
|
+
const tick = async () => {
|
|
226
|
+
if (closed || working || db === undefined)
|
|
227
|
+
return;
|
|
228
|
+
working = true;
|
|
229
|
+
const current = db;
|
|
230
|
+
try {
|
|
231
|
+
if (!hasPendingCommand(current, options.parentSessionId, "to_child", options.subagentId)) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const command = withWrite(current, () => claimCommand(current, options.parentSessionId, options.subagentId, "to_child"));
|
|
235
|
+
if (command === undefined)
|
|
236
|
+
return;
|
|
237
|
+
await answer(current, command, options.handleRequest, options.diagnose);
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
working = false;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
return {
|
|
244
|
+
get connected() {
|
|
245
|
+
return started && !closed;
|
|
246
|
+
},
|
|
247
|
+
start() {
|
|
248
|
+
if (started || closed)
|
|
249
|
+
return;
|
|
250
|
+
try {
|
|
251
|
+
db = openStateDb(options.filePath);
|
|
252
|
+
const lockPath = processLockPath(options.filePath, options.parentSessionId, options.subagentId);
|
|
253
|
+
if (lockPath === undefined)
|
|
254
|
+
throw new Error("invalid process lock id");
|
|
255
|
+
processLock = holdProcessLock(lockPath);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
processLock?.release();
|
|
259
|
+
processLock = undefined;
|
|
260
|
+
db = undefined;
|
|
261
|
+
options.diagnose?.(`could not hold the child process lock: ${errorMessage(error)}`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
started = true;
|
|
265
|
+
timer = setInterval(() => {
|
|
266
|
+
void tick();
|
|
267
|
+
}, pollMs);
|
|
268
|
+
timer.unref?.();
|
|
269
|
+
void tick();
|
|
270
|
+
},
|
|
271
|
+
report(operation, payload, signal) {
|
|
272
|
+
const current = database();
|
|
273
|
+
const id = randomUUID();
|
|
274
|
+
enqueueCommand(current, {
|
|
275
|
+
id,
|
|
276
|
+
parentSessionId: options.parentSessionId,
|
|
277
|
+
subagentId: options.subagentId,
|
|
278
|
+
direction: "to_parent",
|
|
279
|
+
operation,
|
|
280
|
+
payload: JSON.stringify(payload ?? null),
|
|
281
|
+
createdAt: Date.now(),
|
|
282
|
+
});
|
|
283
|
+
return waitForCommand(current, id, DEFAULT_REQUEST_TIMEOUT_MS, pollMs, signal);
|
|
284
|
+
},
|
|
285
|
+
sendEvent(event) {
|
|
286
|
+
if (db === undefined)
|
|
287
|
+
return;
|
|
288
|
+
const current = db;
|
|
289
|
+
enqueueCommand(current, {
|
|
290
|
+
id: randomUUID(),
|
|
291
|
+
parentSessionId: options.parentSessionId,
|
|
292
|
+
subagentId: options.subagentId,
|
|
293
|
+
direction: "to_parent",
|
|
294
|
+
operation: "event",
|
|
295
|
+
payload: JSON.stringify(event ?? null),
|
|
296
|
+
createdAt: Date.now(),
|
|
297
|
+
});
|
|
298
|
+
},
|
|
299
|
+
close() {
|
|
300
|
+
closed = true;
|
|
301
|
+
started = false;
|
|
302
|
+
if (timer !== undefined)
|
|
303
|
+
clearInterval(timer);
|
|
304
|
+
processLock?.release();
|
|
305
|
+
processLock = undefined;
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
async function answer(db, command, handleRequest, diagnose) {
|
|
310
|
+
try {
|
|
311
|
+
const payload = JSON.parse(command.payload);
|
|
312
|
+
const result = await handleRequest({ operation: command.operation, payload });
|
|
313
|
+
finishCommand(db, command.id, { status: "done", result: JSON.stringify(result ?? null) });
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
const bridge = error instanceof BridgeError ? error : undefined;
|
|
317
|
+
diagnose?.(`child command ${command.operation} failed: ${errorMessage(error)}`);
|
|
318
|
+
finishCommand(db, command.id, {
|
|
319
|
+
status: "failed",
|
|
320
|
+
errorCode: bridge?.code ?? "request_failed",
|
|
321
|
+
error: bridge?.message ?? errorMessage(error),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function waitForCommand(db, id, timeoutMs, pollMs, signal) {
|
|
326
|
+
const started = Date.now();
|
|
327
|
+
return new Promise((resolve, reject) => {
|
|
328
|
+
const timer = setInterval(() => {
|
|
329
|
+
if (signal?.aborted) {
|
|
330
|
+
clearInterval(timer);
|
|
331
|
+
withWrite(db, () => {
|
|
332
|
+
deleteCommand(db, id);
|
|
333
|
+
});
|
|
334
|
+
reject(new BridgeError("aborted", "The mailbox request was cancelled"));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const command = readCommand(db, id);
|
|
338
|
+
if (command === undefined)
|
|
339
|
+
return;
|
|
340
|
+
if (command.status === "done" ||
|
|
341
|
+
command.status === "failed" ||
|
|
342
|
+
Date.now() - started >= timeoutMs) {
|
|
343
|
+
clearInterval(timer);
|
|
344
|
+
withWrite(db, () => {
|
|
345
|
+
deleteCommand(db, command.id);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
if (command.status === "done") {
|
|
349
|
+
resolve(command.result === null ? null : JSON.parse(command.result));
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (command.status === "failed") {
|
|
353
|
+
reject(new BridgeError(command.errorCode ?? "request_failed", command.error ?? "The command failed"));
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (Date.now() - started >= timeoutMs) {
|
|
357
|
+
reject(new BridgeError("timeout", `No answer for ${id} after ${timeoutMs}ms`));
|
|
358
|
+
}
|
|
359
|
+
}, pollMs);
|
|
360
|
+
timer.unref?.();
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
function delay(ms) {
|
|
364
|
+
return new Promise((resolve) => {
|
|
365
|
+
const timer = setTimeout(resolve, ms);
|
|
366
|
+
timer.unref?.();
|
|
367
|
+
});
|
|
368
|
+
}
|
package/dist/manager.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import type {
|
|
3
|
-
import type { EffectiveLaunchConfig, OperationError, Presentation, PublicSubagent, SendMode, SpawnSubagentInput, SubagentRecord, SubagentState } from "./domain.js";
|
|
1
|
+
import { type ChildRuntime } from "./child-process.js";
|
|
2
|
+
import type { EffectiveLaunchConfig, OperationError, Presentation, PublicSubagent, RuntimeAttachment, SendMode, SpawnSubagentInput, SubagentRecord, SubagentState } from "./domain.js";
|
|
4
3
|
import type { HostAttachment } from "./host-adapter.js";
|
|
4
|
+
import type { HostDirectory, LeaseReady } from "./lease.js";
|
|
5
5
|
import { type BridgeOperation } from "./protocol.js";
|
|
6
6
|
import type { SubagentRegistry } from "./registry.js";
|
|
7
7
|
import type { LaunchOutcome, RuntimeTokenStore } from "./runtime.js";
|
|
@@ -25,7 +25,6 @@ export interface ParentChannelReport {
|
|
|
25
25
|
}
|
|
26
26
|
export interface ParentChannel {
|
|
27
27
|
deliver(report: ParentChannelReport): Promise<void>;
|
|
28
|
-
dismiss?(childId: string): void;
|
|
29
28
|
}
|
|
30
29
|
/**
|
|
31
30
|
* The parent side of the bridge, as the manager needs it: one connection per child, requests the
|
|
@@ -45,7 +44,7 @@ export interface ChildBridgeTransport {
|
|
|
45
44
|
}
|
|
46
45
|
export interface ManagerDependencies {
|
|
47
46
|
readonly parentSessionId: string;
|
|
48
|
-
readonly registry: Pick<SubagentRegistry, "get" | "list" | "update">;
|
|
47
|
+
readonly registry: Pick<SubagentRegistry, "get" | "list" | "update" | "path">;
|
|
49
48
|
readonly resolve: (input: SpawnSubagentInput) => Promise<EffectiveLaunchConfig>;
|
|
50
49
|
readonly bootstrap: (input: {
|
|
51
50
|
readonly parentSessionId: string;
|
|
@@ -64,6 +63,13 @@ export interface ManagerDependencies {
|
|
|
64
63
|
readonly presentation?: PresentationDeps;
|
|
65
64
|
/** Waits for an already-running runtime to reconnect; false when it does not. */
|
|
66
65
|
readonly connect: (record: SubagentRecord) => Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Host listing used to restore this session's panels before the next turn. Absent in tests that
|
|
68
|
+
* still exercise the bridge-only recover path.
|
|
69
|
+
*/
|
|
70
|
+
readonly directory?: HostDirectory & {
|
|
71
|
+
rebind?(attachment: RuntimeAttachment): HostAttachment | undefined;
|
|
72
|
+
};
|
|
67
73
|
readonly deadlineMs?: number;
|
|
68
74
|
readonly settledCloseTimeoutMs?: number;
|
|
69
75
|
readonly idleTimeoutMs?: number;
|
|
@@ -101,13 +107,7 @@ export interface RecoveryResult {
|
|
|
101
107
|
export declare function extractLastAssistantText(entries: readonly unknown[]): string | undefined;
|
|
102
108
|
export declare class SubagentManager {
|
|
103
109
|
#private;
|
|
104
|
-
bindTaskRegistry(registry: TaskRegistry): () => void;
|
|
105
110
|
constructor(deps: ManagerDependencies);
|
|
106
|
-
/**
|
|
107
|
-
* Observes the raw bridge events of every child. A Task producer needs the child's
|
|
108
|
-
* `task_result` and settle events without also owning the bridge connection.
|
|
109
|
-
*/
|
|
110
|
-
onChildEvent(listener: (childId: string, event: unknown) => void): () => void;
|
|
111
111
|
/** Presentation-only: status and similar projections re-read list() after this. */
|
|
112
112
|
onChange(listener: () => void): () => void;
|
|
113
113
|
dispose(): void;
|
|
@@ -117,7 +117,7 @@ export declare class SubagentManager {
|
|
|
117
117
|
* not when extensions are reloaded.
|
|
118
118
|
*/
|
|
119
119
|
closeLocalConnections(): void;
|
|
120
|
-
/** Reads a child's session entries;
|
|
120
|
+
/** Reads a child's session entries; used by transcript inspection fallbacks. */
|
|
121
121
|
getEntries(id: string): Promise<readonly unknown[] | undefined>;
|
|
122
122
|
/**
|
|
123
123
|
* Adopts the children that are still running. Nothing is launched here: a child that survived a
|
|
@@ -129,19 +129,17 @@ export declare class SubagentManager {
|
|
|
129
129
|
send(id: string, rawMessage: string, mode?: SendMode, signal?: AbortSignal): Promise<OperationError | PublicSubagent>;
|
|
130
130
|
get(id: string): Promise<PublicSubagent | OperationError>;
|
|
131
131
|
list(): Promise<readonly PublicSubagent[]>;
|
|
132
|
-
/** Child startup and runs count until Pi settles, including post-agent_end continuation. */
|
|
133
|
-
get activeCount(): number;
|
|
134
132
|
stop(id: string): Promise<OperationError | PublicSubagent>;
|
|
135
133
|
contactParent(id: string, reason: string, message: string, knownRecord?: SubagentRecord): Promise<OperationError | {
|
|
136
134
|
readonly delivered: true;
|
|
137
135
|
}>;
|
|
138
|
-
/**
|
|
139
|
-
* Handles a request a child sent to its parent. The two child-initiated operations are the
|
|
140
|
-
* child's report channel and its Task result, and both are answered here so the child learns
|
|
141
|
-
* whether the parent accepted them.
|
|
142
|
-
*/
|
|
136
|
+
/** Answers a request a child sent to its parent, so the child learns whether the parent accepted it. */
|
|
143
137
|
handleChildRequest(childId: string, operation: string, payload: unknown): Promise<unknown>;
|
|
144
138
|
/** Applies a bridge event to this child's record and notifies the raw child-event listeners. */
|
|
145
139
|
handleEvent(childId: string, event: unknown): void;
|
|
146
140
|
handleConnectionChange(childId: string, connected: boolean): void;
|
|
141
|
+
holdAttachment(id: string, attachment: HostAttachment): void;
|
|
142
|
+
/** Starts restoration. `before_agent_start` waits on `ready` so the turn sees the same ledger. */
|
|
143
|
+
reconcile(): Promise<LeaseReady>;
|
|
144
|
+
ready(): Promise<LeaseReady>;
|
|
147
145
|
}
|