@dougbots/avenor-core 0.8.3 → 0.10.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 +27 -1
- package/dist/index.d.ts +68 -6
- package/dist/index.js +563 -100
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -50,7 +50,33 @@ const result = await spawnTool({
|
|
|
50
50
|
// { run_id: '...', label: 'reverse-string', supervisor_id: '/path/to/socket' }
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
Accepts `agent`, `prompt`, `promptFile`, `label`, `dir`, `timeout`, `model`, `backend`, `serverUrl`, `sessionId`, and `supervisorId` (to target a specific socket).
|
|
53
|
+
Accepts `agent`, `prompt`, `promptFile`, `label`, `dir`, `timeout`, `model`, `thinking`, `backend`, `rosterFile`, `rosterEntry`, `serverUrl`, `sessionId`, and `supervisorId` (to target a specific socket).
|
|
54
|
+
|
|
55
|
+
### Roster selection
|
|
56
|
+
|
|
57
|
+
`rosterFile` is the path to a top-level JSON roster map and `rosterEntry` is the key selected from it. Each entry must contain `backend` and at least one of `agent` or `model`:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"planner": {
|
|
62
|
+
"backend": "opencode-acp",
|
|
63
|
+
"model": "provider/planner"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
A direct roster spawn forwards both selectors and must not also provide `agent`, `model`, or `backend`. Direct mode without a roster allows each of those identity fields to be omitted independently, including a backend-only request; the selected runtime supplies defaults for omitted values. `thinking` is run-level, not a roster field, and is checked against the effective backend. The shared selector validation rejects missing selector pairs, mixed direct/roster fields, and unknown selector keys. Roster-file loading rejects deferred entry fields such as `system`; `thinking` remains a valid run-level option but is never read from a roster entry.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
await spawnTool({
|
|
72
|
+
prompt: 'Analyze the repository',
|
|
73
|
+
dir: '/repo',
|
|
74
|
+
rosterFile: '/repo/roster.json',
|
|
75
|
+
rosterEntry: 'planner',
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Roster phase selection is resolved by Avenor's Go workflow runners. A roster entry may override backend/agent/model per phase. CLI workflow fallback and nested roster inheritance are CLI-only; stable nesting keeps its existing behavior. A resumed session must use the same effective backend as the original session.
|
|
54
80
|
|
|
55
81
|
### `statusTool`
|
|
56
82
|
|
package/dist/index.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ interface HistoryResult<TEvent = Event> {
|
|
|
24
24
|
events: TEvent[];
|
|
25
25
|
latest_seq?: number;
|
|
26
26
|
}
|
|
27
|
+
type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
27
28
|
interface SpawnParams {
|
|
28
29
|
agent?: string;
|
|
29
30
|
prompt_file?: string;
|
|
@@ -32,8 +33,11 @@ interface SpawnParams {
|
|
|
32
33
|
label?: string;
|
|
33
34
|
timeout?: number;
|
|
34
35
|
model?: string;
|
|
36
|
+
thinking?: ThinkingLevel;
|
|
35
37
|
agent_profile?: string;
|
|
36
38
|
backend?: string;
|
|
39
|
+
roster_file?: string;
|
|
40
|
+
roster_entry?: string;
|
|
37
41
|
server_url?: string;
|
|
38
42
|
session_id?: string;
|
|
39
43
|
sentinel_file?: string;
|
|
@@ -41,9 +45,28 @@ interface SpawnParams {
|
|
|
41
45
|
auto_approve?: boolean;
|
|
42
46
|
[key: string]: unknown;
|
|
43
47
|
}
|
|
48
|
+
interface SpawnResult {
|
|
49
|
+
runtime_id?: string;
|
|
50
|
+
session_id?: string;
|
|
51
|
+
on_event?: string;
|
|
52
|
+
sentinel_file?: string;
|
|
53
|
+
broker_url?: string;
|
|
54
|
+
parent_token?: string;
|
|
55
|
+
agent?: string;
|
|
56
|
+
agent_profile?: string;
|
|
57
|
+
model?: string;
|
|
58
|
+
backend?: string;
|
|
59
|
+
roster_file?: string;
|
|
60
|
+
roster_entry?: string;
|
|
61
|
+
effective_agent?: string;
|
|
62
|
+
effective_model?: string;
|
|
63
|
+
effective_backend?: string;
|
|
64
|
+
[key: string]: unknown;
|
|
65
|
+
}
|
|
44
66
|
declare class Client {
|
|
45
67
|
private socket;
|
|
46
68
|
private rl;
|
|
69
|
+
private closed;
|
|
47
70
|
private nextID;
|
|
48
71
|
private pending;
|
|
49
72
|
private started;
|
|
@@ -66,12 +89,13 @@ declare class Client {
|
|
|
66
89
|
status(runtimeId?: string): Promise<Record<string, unknown>>;
|
|
67
90
|
result(runtimeId?: string): Promise<Record<string, unknown>>;
|
|
68
91
|
list(): Promise<Array<Record<string, unknown>>>;
|
|
69
|
-
spawn(params: SpawnParams): Promise<
|
|
92
|
+
spawn(params: SpawnParams): Promise<SpawnResult>;
|
|
70
93
|
shutdown(mode: string): Promise<void>;
|
|
71
94
|
answerPermission(runtimeId: string, requestId: string, optionId: string, message?: string): Promise<void>;
|
|
72
95
|
prompt(runtimeId: string, text: string): Promise<void>;
|
|
73
96
|
interruptAndPrompt(runtimeId: string, text: string, keepQueue?: boolean): Promise<void>;
|
|
74
97
|
cancel(runtimeId?: string): Promise<void>;
|
|
98
|
+
isClosed(): boolean;
|
|
75
99
|
close(): void;
|
|
76
100
|
}
|
|
77
101
|
declare function dial(socketPath: string, opts?: {
|
|
@@ -241,6 +265,12 @@ interface RunInfo {
|
|
|
241
265
|
agentProfile?: string;
|
|
242
266
|
backend?: string;
|
|
243
267
|
model?: string;
|
|
268
|
+
effectiveAgent?: string;
|
|
269
|
+
effectiveModel?: string;
|
|
270
|
+
effectiveBackend?: string;
|
|
271
|
+
rosterFile?: string;
|
|
272
|
+
rosterEntry?: string;
|
|
273
|
+
thinking?: ThinkingLevel;
|
|
244
274
|
dir?: string;
|
|
245
275
|
brokerUrl?: string;
|
|
246
276
|
parentToken?: string;
|
|
@@ -250,6 +280,14 @@ interface SupervisorOptions {
|
|
|
250
280
|
binaryPath?: string;
|
|
251
281
|
callTimeoutMs?: number;
|
|
252
282
|
}
|
|
283
|
+
/** Metadata retained locally when a follow-up uses a resolved direct identity. */
|
|
284
|
+
interface SpawnMetadata {
|
|
285
|
+
rosterFile?: string;
|
|
286
|
+
rosterEntry?: string;
|
|
287
|
+
effectiveAgent?: string;
|
|
288
|
+
effectiveModel?: string;
|
|
289
|
+
effectiveBackend?: string;
|
|
290
|
+
}
|
|
253
291
|
declare function findAvenorBinary(): string;
|
|
254
292
|
declare class Supervisor {
|
|
255
293
|
private static instance;
|
|
@@ -262,6 +300,7 @@ declare class Supervisor {
|
|
|
262
300
|
private crashed;
|
|
263
301
|
private crashCode;
|
|
264
302
|
private runs;
|
|
303
|
+
private aliases;
|
|
265
304
|
private binaryPath;
|
|
266
305
|
private callTimeoutMs;
|
|
267
306
|
private constructor();
|
|
@@ -272,7 +311,7 @@ declare class Supervisor {
|
|
|
272
311
|
static isCurrentInstance(supervisorId: string): boolean;
|
|
273
312
|
static currentInstance(): Supervisor | null;
|
|
274
313
|
getClient(): Client;
|
|
275
|
-
spawn(params: SpawnParams, runId?: `${string}-${string}-${string}-${string}-${string}
|
|
314
|
+
spawn(params: SpawnParams, runId?: `${string}-${string}-${string}-${string}-${string}`, metadata?: SpawnMetadata): Promise<RunInfo>;
|
|
276
315
|
close(options?: {
|
|
277
316
|
skipShutdown?: boolean;
|
|
278
317
|
}): Promise<void>;
|
|
@@ -282,7 +321,7 @@ declare class Supervisor {
|
|
|
282
321
|
declare function socketsRoot(): string;
|
|
283
322
|
//#endregion
|
|
284
323
|
//#region src/tools/spawn.d.ts
|
|
285
|
-
|
|
324
|
+
interface SpawnToolArgs {
|
|
286
325
|
agent?: string;
|
|
287
326
|
prompt?: string;
|
|
288
327
|
promptFile?: string;
|
|
@@ -290,20 +329,36 @@ declare function spawnTool(args: {
|
|
|
290
329
|
dir?: string;
|
|
291
330
|
timeout?: string;
|
|
292
331
|
model?: string;
|
|
332
|
+
thinking?: ThinkingLevel;
|
|
293
333
|
agentProfile?: string;
|
|
294
334
|
backend?: string;
|
|
335
|
+
rosterFile?: string;
|
|
336
|
+
rosterEntry?: string;
|
|
295
337
|
serverUrl?: string;
|
|
296
338
|
sessionId?: string;
|
|
297
339
|
supervisorId?: string;
|
|
298
340
|
parent_run_id?: string;
|
|
299
|
-
}
|
|
341
|
+
}
|
|
342
|
+
interface SpawnToolResult {
|
|
300
343
|
run_id: string;
|
|
301
344
|
label: string;
|
|
302
345
|
supervisor_id: string;
|
|
303
346
|
runtime_id?: string;
|
|
304
347
|
broker_url?: string;
|
|
305
348
|
parent_token?: string;
|
|
306
|
-
}
|
|
349
|
+
}
|
|
350
|
+
declare const spawnTool: (args: SpawnToolArgs) => Promise<SpawnToolResult>;
|
|
351
|
+
//#endregion
|
|
352
|
+
//#region src/spawn-selection.d.ts
|
|
353
|
+
interface SpawnSelectionInput {
|
|
354
|
+
agent?: string;
|
|
355
|
+
model?: string;
|
|
356
|
+
backend?: string;
|
|
357
|
+
roster_file?: string;
|
|
358
|
+
roster_entry?: string;
|
|
359
|
+
}
|
|
360
|
+
/** Validate the direct-versus-roster selector contract. */
|
|
361
|
+
declare function validateSpawnSelection(input: unknown, rosterConfigured?: boolean): void;
|
|
307
362
|
//#endregion
|
|
308
363
|
//#region src/tools/status.d.ts
|
|
309
364
|
type StatusView = 'lifecycle' | 'full';
|
|
@@ -331,9 +386,15 @@ interface StatusResult {
|
|
|
331
386
|
};
|
|
332
387
|
session_id?: string;
|
|
333
388
|
stop_reason?: string;
|
|
389
|
+
roster_file?: string;
|
|
390
|
+
roster_entry?: string;
|
|
334
391
|
backend?: string;
|
|
335
392
|
agent?: string;
|
|
336
393
|
model?: string;
|
|
394
|
+
agent_profile?: string;
|
|
395
|
+
effective_backend?: string;
|
|
396
|
+
effective_agent?: string;
|
|
397
|
+
effective_model?: string;
|
|
337
398
|
dir?: string;
|
|
338
399
|
parent_id?: string;
|
|
339
400
|
children?: string[];
|
|
@@ -430,6 +491,7 @@ interface FollowUpToolArgs {
|
|
|
430
491
|
interface FollowUpToolResult {
|
|
431
492
|
run_id: string;
|
|
432
493
|
label: string;
|
|
494
|
+
runtime_id?: string;
|
|
433
495
|
}
|
|
434
496
|
declare const followUpTool: (args: FollowUpToolArgs) => Promise<FollowUpToolResult>;
|
|
435
497
|
//#endregion
|
|
@@ -442,4 +504,4 @@ declare function shutdownTool(args: {
|
|
|
442
504
|
cleaned_up: string[];
|
|
443
505
|
}>;
|
|
444
506
|
//#endregion
|
|
445
|
-
export { type Client, DEFAULT_RUN_REDUCER_LIMITS, type Event, type HistoryOptions, type HistoryResult, type InspectResult, type NormalizedRunEvent, type ObserveRunOptions, type ResultResult, type ResultToolArgs, type RunChunkRole, type RunIdentity, type RunInfo, type RunObserver, type RunObserverClient, type RunPermissionOption, type RunPermissionSummary, RunReducer, type RunReducerLimits, type RunSnapshot, type RunSnapshotSeed, type RunToolState, type RunToolSummary, type RunTranscriptEntry, type SpawnParams, type StatusResult, type StatusView, type SubscribeOptions, Supervisor, type SupervisorOptions, answerPermissionTool, createRunSnapshot, dial, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, normalizeRunEvent, observeRun, reduceRunSnapshot, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool };
|
|
507
|
+
export { type Client, DEFAULT_RUN_REDUCER_LIMITS, type Event, type FollowUpToolArgs, type FollowUpToolResult, type HistoryOptions, type HistoryResult, type InspectResult, type NormalizedRunEvent, type ObserveRunOptions, type ResultResult, type ResultToolArgs, type RunChunkRole, type RunIdentity, type RunInfo, type RunObserver, type RunObserverClient, type RunPermissionOption, type RunPermissionSummary, RunReducer, type RunReducerLimits, type RunSnapshot, type RunSnapshotSeed, type RunToolState, type RunToolSummary, type RunTranscriptEntry, type SpawnMetadata, type SpawnParams, type SpawnResult, type SpawnSelectionInput, type SpawnToolArgs, type SpawnToolResult, type StatusResult, type StatusToolArgs, type StatusView, type SubscribeOptions, Supervisor, type SupervisorOptions, type ThinkingLevel, answerPermissionTool, createRunSnapshot, dial, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, normalizeRunEvent, observeRun, reduceRunSnapshot, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection };
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,8 @@ import * as fs from "node:fs";
|
|
|
6
6
|
import * as os from "node:os";
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import * as crypto from "node:crypto";
|
|
9
|
+
import { umpire } from "@umpire/core";
|
|
10
|
+
import { fromJson } from "@umpire/json";
|
|
9
11
|
//#region src/client.ts
|
|
10
12
|
function normalizeSubscribeOptions(options) {
|
|
11
13
|
if (!options) return void 0;
|
|
@@ -26,6 +28,7 @@ function removeResolver(resolvers, target) {
|
|
|
26
28
|
var Client = class {
|
|
27
29
|
socket;
|
|
28
30
|
rl;
|
|
31
|
+
closed = false;
|
|
29
32
|
nextID = 0;
|
|
30
33
|
pending = /* @__PURE__ */ new Map();
|
|
31
34
|
started = false;
|
|
@@ -44,11 +47,15 @@ var Client = class {
|
|
|
44
47
|
});
|
|
45
48
|
this.callTimeout = opts?.callTimeoutMs ?? 3e4;
|
|
46
49
|
this.socket.on("error", (err) => {
|
|
50
|
+
this.closed = true;
|
|
47
51
|
this.pushEvent({
|
|
48
52
|
event: "protocol-error",
|
|
49
53
|
message: err.message
|
|
50
54
|
});
|
|
51
55
|
});
|
|
56
|
+
this.socket.on("close", () => {
|
|
57
|
+
this.closed = true;
|
|
58
|
+
});
|
|
52
59
|
}
|
|
53
60
|
startReadLoop() {
|
|
54
61
|
if (this.started) return;
|
|
@@ -85,6 +92,7 @@ var Client = class {
|
|
|
85
92
|
}
|
|
86
93
|
});
|
|
87
94
|
this.rl.on("close", () => {
|
|
95
|
+
this.closed = true;
|
|
88
96
|
for (const [, pc] of this.pending) {
|
|
89
97
|
clearTimeout(pc.timer);
|
|
90
98
|
pc.reject(/* @__PURE__ */ new Error("read response: connection closed"));
|
|
@@ -116,6 +124,7 @@ var Client = class {
|
|
|
116
124
|
else this.dropped++;
|
|
117
125
|
}
|
|
118
126
|
async call(method, params) {
|
|
127
|
+
if (this.isClosed()) throw new Error("control socket is closed");
|
|
119
128
|
this.startReadLoop();
|
|
120
129
|
const id = ++this.nextID;
|
|
121
130
|
const req = {
|
|
@@ -137,6 +146,7 @@ var Client = class {
|
|
|
137
146
|
});
|
|
138
147
|
this.socket.write(data, (err) => {
|
|
139
148
|
if (err) {
|
|
149
|
+
this.closed = true;
|
|
140
150
|
clearTimeout(timer);
|
|
141
151
|
this.pending.delete(id);
|
|
142
152
|
reject(/* @__PURE__ */ new Error(`write request: ${err.message}`));
|
|
@@ -272,7 +282,11 @@ var Client = class {
|
|
|
272
282
|
const params = runtimeId ? { runtime_id: runtimeId } : void 0;
|
|
273
283
|
await this.call("cancel", params);
|
|
274
284
|
}
|
|
285
|
+
isClosed() {
|
|
286
|
+
return this.closed || this.socket.destroyed || this.socket.writableEnded;
|
|
287
|
+
}
|
|
275
288
|
close() {
|
|
289
|
+
this.closed = true;
|
|
276
290
|
this.rl.close();
|
|
277
291
|
this.socket.destroy();
|
|
278
292
|
}
|
|
@@ -1062,6 +1076,95 @@ function observeRun(client, options = {}) {
|
|
|
1062
1076
|
};
|
|
1063
1077
|
}
|
|
1064
1078
|
//#endregion
|
|
1079
|
+
//#region ../../schemas/spawn_selection.umpire.json
|
|
1080
|
+
var spawn_selection_umpire_default = {
|
|
1081
|
+
version: 1,
|
|
1082
|
+
conditions: { "rosterConfigured": {
|
|
1083
|
+
"type": "boolean",
|
|
1084
|
+
"description": "Whether the caller has a configured roster context"
|
|
1085
|
+
} },
|
|
1086
|
+
fields: {
|
|
1087
|
+
"agent": { "isEmpty": "string" },
|
|
1088
|
+
"model": { "isEmpty": "string" },
|
|
1089
|
+
"backend": { "isEmpty": "string" },
|
|
1090
|
+
"roster_file": { "isEmpty": "string" },
|
|
1091
|
+
"roster_entry": { "isEmpty": "string" }
|
|
1092
|
+
},
|
|
1093
|
+
rules: [
|
|
1094
|
+
{
|
|
1095
|
+
"type": "requires",
|
|
1096
|
+
"field": "roster_file",
|
|
1097
|
+
"dependency": "roster_entry",
|
|
1098
|
+
"reason": "roster_file requires roster_entry"
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
"type": "fairWhen",
|
|
1102
|
+
"field": "roster_entry",
|
|
1103
|
+
"when": {
|
|
1104
|
+
"op": "or",
|
|
1105
|
+
"exprs": [{
|
|
1106
|
+
"op": "present",
|
|
1107
|
+
"field": "roster_file"
|
|
1108
|
+
}, {
|
|
1109
|
+
"op": "cond",
|
|
1110
|
+
"condition": "rosterConfigured"
|
|
1111
|
+
}]
|
|
1112
|
+
},
|
|
1113
|
+
"reason": "roster_entry requires roster_file unless a roster context is configured"
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
"type": "disables",
|
|
1117
|
+
"source": "roster_entry",
|
|
1118
|
+
"targets": [
|
|
1119
|
+
"agent",
|
|
1120
|
+
"model",
|
|
1121
|
+
"backend"
|
|
1122
|
+
],
|
|
1123
|
+
"reason": "direct identity fields are disabled in roster mode"
|
|
1124
|
+
}
|
|
1125
|
+
]
|
|
1126
|
+
};
|
|
1127
|
+
//#endregion
|
|
1128
|
+
//#region src/spawn-selection.ts
|
|
1129
|
+
const selectorFields = [
|
|
1130
|
+
"agent",
|
|
1131
|
+
"model",
|
|
1132
|
+
"backend",
|
|
1133
|
+
"roster_file",
|
|
1134
|
+
"roster_entry"
|
|
1135
|
+
];
|
|
1136
|
+
const selectorFieldSet = new Set(selectorFields);
|
|
1137
|
+
const parsedSchema = fromJson(spawn_selection_umpire_default);
|
|
1138
|
+
const spawnSelectionUmpire = umpire({
|
|
1139
|
+
fields: parsedSchema.fields,
|
|
1140
|
+
rules: parsedSchema.rules
|
|
1141
|
+
});
|
|
1142
|
+
function assertInput(input) {
|
|
1143
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) throw new Error("invalid spawn selector: expected an object");
|
|
1144
|
+
for (const key of Object.keys(input)) {
|
|
1145
|
+
if (!selectorFieldSet.has(key)) throw new Error(`invalid spawn selector: unknown field "${key}"`);
|
|
1146
|
+
const value = input[key];
|
|
1147
|
+
if (value !== void 0 && typeof value !== "string") throw new Error(`invalid spawn selector: ${key} must be a string`);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
/** Validate the direct-versus-roster selector contract. */
|
|
1151
|
+
function validateSpawnSelection(input, rosterConfigured = false) {
|
|
1152
|
+
assertInput(input);
|
|
1153
|
+
if (typeof rosterConfigured !== "boolean") throw new Error("invalid spawn selector: rosterConfigured must be a boolean");
|
|
1154
|
+
const values = {};
|
|
1155
|
+
for (const field of selectorFields) {
|
|
1156
|
+
const value = input[field];
|
|
1157
|
+
if (value !== void 0 && value !== "") values[field] = value;
|
|
1158
|
+
}
|
|
1159
|
+
const availability = spawnSelectionUmpire.check(values, { rosterConfigured });
|
|
1160
|
+
for (const field of selectorFields) {
|
|
1161
|
+
const value = values[field];
|
|
1162
|
+
const status = availability[field];
|
|
1163
|
+
if (value === void 0 || status?.enabled && status.fair) continue;
|
|
1164
|
+
throw new Error(`invalid spawn selector: ${status?.reason ?? `${field} is disabled`}`);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
//#endregion
|
|
1065
1168
|
//#region src/paths.ts
|
|
1066
1169
|
function avenorHome() {
|
|
1067
1170
|
return process.env.AVENOR_HOME || path.join(os.homedir(), ".avenor");
|
|
@@ -1086,6 +1189,37 @@ function ensureRunPaths(runId) {
|
|
|
1086
1189
|
}
|
|
1087
1190
|
//#endregion
|
|
1088
1191
|
//#region src/supervisor.ts
|
|
1192
|
+
function retainLiveIdentity(runInfo, liveStatus) {
|
|
1193
|
+
const value = (candidate) => typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
|
|
1194
|
+
const liveIdentity = (effectiveKey, directKey) => {
|
|
1195
|
+
if (typeof liveStatus[effectiveKey] === "string") return {
|
|
1196
|
+
present: true,
|
|
1197
|
+
value: value(liveStatus[effectiveKey])
|
|
1198
|
+
};
|
|
1199
|
+
if (typeof liveStatus[directKey] === "string") return {
|
|
1200
|
+
present: true,
|
|
1201
|
+
value: value(liveStatus[directKey])
|
|
1202
|
+
};
|
|
1203
|
+
return { present: false };
|
|
1204
|
+
};
|
|
1205
|
+
const effectiveAgent = liveIdentity("effective_agent", "agent");
|
|
1206
|
+
const effectiveModel = liveIdentity("effective_model", "model");
|
|
1207
|
+
const effectiveBackend = liveIdentity("effective_backend", "backend");
|
|
1208
|
+
const sessionId = value(liveStatus.session_id);
|
|
1209
|
+
const rosterFilePresent = typeof liveStatus.roster_file === "string";
|
|
1210
|
+
const rosterFile = value(liveStatus.roster_file);
|
|
1211
|
+
const rosterEntryPresent = typeof liveStatus.roster_entry === "string";
|
|
1212
|
+
const rosterEntry = value(liveStatus.roster_entry);
|
|
1213
|
+
const agentProfilePresent = typeof liveStatus.agent_profile === "string";
|
|
1214
|
+
const agentProfile = value(liveStatus.agent_profile);
|
|
1215
|
+
if (effectiveAgent.present) runInfo.agent = runInfo.effectiveAgent = effectiveAgent.value;
|
|
1216
|
+
if (effectiveModel.present) runInfo.model = runInfo.effectiveModel = effectiveModel.value;
|
|
1217
|
+
if (effectiveBackend.present) runInfo.backend = runInfo.effectiveBackend = effectiveBackend.value;
|
|
1218
|
+
if (sessionId) runInfo.sessionId = sessionId;
|
|
1219
|
+
if (rosterFilePresent) runInfo.rosterFile = rosterFile;
|
|
1220
|
+
if (rosterEntryPresent) runInfo.rosterEntry = rosterEntry;
|
|
1221
|
+
if (agentProfilePresent) runInfo.agentProfile = agentProfile;
|
|
1222
|
+
}
|
|
1089
1223
|
function findAvenorBinary() {
|
|
1090
1224
|
const envBin = process.env.AVENOR_BIN;
|
|
1091
1225
|
if (envBin) try {
|
|
@@ -1142,6 +1276,7 @@ var Supervisor = class Supervisor {
|
|
|
1142
1276
|
crashed = false;
|
|
1143
1277
|
crashCode = null;
|
|
1144
1278
|
runs = /* @__PURE__ */ new Map();
|
|
1279
|
+
aliases = /* @__PURE__ */ new Map();
|
|
1145
1280
|
binaryPath;
|
|
1146
1281
|
callTimeoutMs;
|
|
1147
1282
|
constructor(binaryPath, socketPath, opts) {
|
|
@@ -1150,7 +1285,14 @@ var Supervisor = class Supervisor {
|
|
|
1150
1285
|
this.callTimeoutMs = opts?.callTimeoutMs ?? 3e4;
|
|
1151
1286
|
}
|
|
1152
1287
|
static get(opts) {
|
|
1153
|
-
|
|
1288
|
+
const current = Supervisor.instance;
|
|
1289
|
+
if (current && !current.crashed) {
|
|
1290
|
+
if (current.client && !current.client.isClosed()) return Promise.resolve(current);
|
|
1291
|
+
current.crashed = true;
|
|
1292
|
+
current.crashCode = 0;
|
|
1293
|
+
current.client?.close();
|
|
1294
|
+
current.client = null;
|
|
1295
|
+
}
|
|
1154
1296
|
if (Supervisor.starting) return Supervisor.starting;
|
|
1155
1297
|
const starting = Supervisor.start(opts);
|
|
1156
1298
|
Supervisor.starting = starting;
|
|
@@ -1216,15 +1358,13 @@ var Supervisor = class Supervisor {
|
|
|
1216
1358
|
reject(/* @__PURE__ */ new Error(`avenor exited with code ${code}${signal ? " signal " + signal : ""} during startup`));
|
|
1217
1359
|
return;
|
|
1218
1360
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
this.client = null;
|
|
1227
|
-
}
|
|
1361
|
+
this.crashed = true;
|
|
1362
|
+
this.crashCode = code ?? -1;
|
|
1363
|
+
if (this.client) {
|
|
1364
|
+
try {
|
|
1365
|
+
this.client.close();
|
|
1366
|
+
} catch {}
|
|
1367
|
+
this.client = null;
|
|
1228
1368
|
}
|
|
1229
1369
|
});
|
|
1230
1370
|
this.childProcess.on("error", (err) => {
|
|
@@ -1262,11 +1402,27 @@ var Supervisor = class Supervisor {
|
|
|
1262
1402
|
return Supervisor.instance;
|
|
1263
1403
|
}
|
|
1264
1404
|
getClient() {
|
|
1265
|
-
if (this.crashed)
|
|
1405
|
+
if (this.crashed) {
|
|
1406
|
+
const state = this.crashCode === 0 ? "exited" : "crashed";
|
|
1407
|
+
throw new Error(`avenor supervisor ${state} (exit code ${this.crashCode}). Call avenor_shutdown to clean up and retry.`);
|
|
1408
|
+
}
|
|
1266
1409
|
if (!this.client) throw new Error("supervisor not started");
|
|
1410
|
+
if (this.client.isClosed()) {
|
|
1411
|
+
this.crashed = true;
|
|
1412
|
+
this.crashCode = 0;
|
|
1413
|
+
this.client = null;
|
|
1414
|
+
throw new Error("avenor supervisor connection closed");
|
|
1415
|
+
}
|
|
1267
1416
|
return this.client;
|
|
1268
1417
|
}
|
|
1269
|
-
async spawn(params, runId = crypto.randomUUID()) {
|
|
1418
|
+
async spawn(params, runId = crypto.randomUUID(), metadata) {
|
|
1419
|
+
if (!(typeof params.loop_file === "string" || typeof params.team_file === "string")) validateSpawnSelection({
|
|
1420
|
+
agent: params.agent,
|
|
1421
|
+
model: params.model,
|
|
1422
|
+
backend: params.backend,
|
|
1423
|
+
roster_file: params.roster_file,
|
|
1424
|
+
roster_entry: params.roster_entry
|
|
1425
|
+
});
|
|
1270
1426
|
const client = this.getClient();
|
|
1271
1427
|
const { sentinelPath, eventLogPath } = ensureRunPaths(runId);
|
|
1272
1428
|
const spawnParams = {
|
|
@@ -1275,25 +1431,60 @@ var Supervisor = class Supervisor {
|
|
|
1275
1431
|
sentinel_file: sentinelPath,
|
|
1276
1432
|
on_event: eventLogPath
|
|
1277
1433
|
};
|
|
1434
|
+
if (spawnParams.roster_entry) {
|
|
1435
|
+
for (const field of [
|
|
1436
|
+
"agent",
|
|
1437
|
+
"model",
|
|
1438
|
+
"backend"
|
|
1439
|
+
]) if (spawnParams[field] === "") delete spawnParams[field];
|
|
1440
|
+
}
|
|
1278
1441
|
const result = await client.spawn(spawnParams);
|
|
1442
|
+
let identityResult = result;
|
|
1443
|
+
if (spawnParams.roster_entry && result.runtime_id) {
|
|
1444
|
+
const hasBackend = [result.effective_backend, result.backend].some((value) => typeof value === "string" && value.length > 0);
|
|
1445
|
+
const hasAgentOrModel = [
|
|
1446
|
+
result.effective_agent,
|
|
1447
|
+
result.agent,
|
|
1448
|
+
result.effective_model,
|
|
1449
|
+
result.model
|
|
1450
|
+
].some((value) => typeof value === "string" && value.length > 0);
|
|
1451
|
+
if (!(hasBackend && hasAgentOrModel)) try {
|
|
1452
|
+
identityResult = {
|
|
1453
|
+
...result,
|
|
1454
|
+
...await client.status(result.runtime_id)
|
|
1455
|
+
};
|
|
1456
|
+
} catch {}
|
|
1457
|
+
}
|
|
1458
|
+
const value = (candidate) => typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
|
|
1459
|
+
const effectiveAgent = metadata?.effectiveAgent ?? value(identityResult.effective_agent) ?? value(identityResult.agent) ?? value(spawnParams.agent);
|
|
1460
|
+
const effectiveModel = metadata?.effectiveModel ?? value(identityResult.effective_model) ?? value(identityResult.model) ?? value(spawnParams.model);
|
|
1461
|
+
const effectiveBackend = metadata?.effectiveBackend ?? value(identityResult.effective_backend) ?? value(identityResult.backend) ?? value(spawnParams.backend);
|
|
1462
|
+
const rosterFile = metadata?.rosterFile ?? value(identityResult.roster_file) ?? value(spawnParams.roster_file);
|
|
1463
|
+
const rosterEntry = metadata?.rosterEntry ?? value(identityResult.roster_entry) ?? value(spawnParams.roster_entry);
|
|
1279
1464
|
const runInfo = {
|
|
1280
1465
|
runId,
|
|
1281
1466
|
label: spawnParams.label ?? runId,
|
|
1282
1467
|
sentinelPath: result.sentinel_file ?? sentinelPath,
|
|
1283
1468
|
eventLogPath: result.on_event ?? eventLogPath,
|
|
1284
|
-
runtimeId:
|
|
1285
|
-
sessionId:
|
|
1286
|
-
agent:
|
|
1287
|
-
agentProfile: spawnParams.agent_profile,
|
|
1288
|
-
backend:
|
|
1289
|
-
model:
|
|
1469
|
+
runtimeId: value(identityResult.runtime_id),
|
|
1470
|
+
sessionId: value(identityResult.session_id),
|
|
1471
|
+
agent: effectiveAgent,
|
|
1472
|
+
agentProfile: value(identityResult.agent_profile) ?? spawnParams.agent_profile,
|
|
1473
|
+
backend: effectiveBackend,
|
|
1474
|
+
model: effectiveModel,
|
|
1475
|
+
effectiveAgent,
|
|
1476
|
+
effectiveModel,
|
|
1477
|
+
effectiveBackend,
|
|
1478
|
+
rosterFile,
|
|
1479
|
+
rosterEntry,
|
|
1480
|
+
thinking: spawnParams.thinking,
|
|
1290
1481
|
dir: spawnParams.dir,
|
|
1291
1482
|
brokerUrl: result.broker_url,
|
|
1292
1483
|
parentToken: result.parent_token,
|
|
1293
1484
|
autoApprove: spawnParams.auto_approve
|
|
1294
1485
|
};
|
|
1295
|
-
this.runs.set(runInfo.label, runInfo);
|
|
1296
1486
|
this.runs.set(runId, runInfo);
|
|
1487
|
+
this.aliases.set(runInfo.label, runInfo);
|
|
1297
1488
|
return runInfo;
|
|
1298
1489
|
}
|
|
1299
1490
|
async close(options = {}) {
|
|
@@ -1316,6 +1507,7 @@ var Supervisor = class Supervisor {
|
|
|
1316
1507
|
this.crashed = false;
|
|
1317
1508
|
this.crashCode = null;
|
|
1318
1509
|
this.runs.clear();
|
|
1510
|
+
this.aliases.clear();
|
|
1319
1511
|
Supervisor.instance = null;
|
|
1320
1512
|
}
|
|
1321
1513
|
};
|
|
@@ -1361,8 +1553,125 @@ async function getSupervisorClient(supervisorId) {
|
|
|
1361
1553
|
};
|
|
1362
1554
|
}
|
|
1363
1555
|
//#endregion
|
|
1556
|
+
//#region src/tools/run-registry.ts
|
|
1557
|
+
const externalRuns = /* @__PURE__ */ new Map();
|
|
1558
|
+
function runsFor(supervisorId) {
|
|
1559
|
+
let runs = externalRuns.get(supervisorId);
|
|
1560
|
+
if (!runs) {
|
|
1561
|
+
runs = {
|
|
1562
|
+
byRunId: /* @__PURE__ */ new Map(),
|
|
1563
|
+
byAlias: /* @__PURE__ */ new Map()
|
|
1564
|
+
};
|
|
1565
|
+
externalRuns.set(supervisorId, runs);
|
|
1566
|
+
}
|
|
1567
|
+
return runs;
|
|
1568
|
+
}
|
|
1569
|
+
function indexRun(runInfo) {
|
|
1570
|
+
const runs = runsFor(runInfo.supervisorId);
|
|
1571
|
+
runs.byRunId.set(runInfo.runId, runInfo);
|
|
1572
|
+
runs.byAlias.set(runInfo.label, runInfo);
|
|
1573
|
+
if (runInfo.runtimeId) runs.byAlias.set(runInfo.runtimeId, runInfo);
|
|
1574
|
+
}
|
|
1575
|
+
function isSafeRunReference(reference) {
|
|
1576
|
+
return /^[a-zA-Z0-9_-]+$/.test(reference);
|
|
1577
|
+
}
|
|
1578
|
+
function externalRunMetadataPath(runId) {
|
|
1579
|
+
if (!isSafeRunReference(runId)) throw new Error(`unsafe external run reference: ${runId}`);
|
|
1580
|
+
return path.join(runsRoot(), runId, "run.json");
|
|
1581
|
+
}
|
|
1582
|
+
function optionalString(value) {
|
|
1583
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1584
|
+
}
|
|
1585
|
+
function readPersistedRun(supervisorId, runId) {
|
|
1586
|
+
if (!isSafeRunReference(runId)) return void 0;
|
|
1587
|
+
try {
|
|
1588
|
+
const parsed = JSON.parse(fs.readFileSync(externalRunMetadataPath(runId), "utf-8"));
|
|
1589
|
+
if (parsed.schemaVersion !== 1 || parsed.runId !== runId || parsed.supervisorId !== supervisorId || typeof parsed.label !== "string") return;
|
|
1590
|
+
const runDir = path.join(runsRoot(), runId);
|
|
1591
|
+
const runInfo = {
|
|
1592
|
+
runId,
|
|
1593
|
+
label: parsed.label,
|
|
1594
|
+
supervisorId,
|
|
1595
|
+
sentinelPath: path.join(runDir, "sentinel.done"),
|
|
1596
|
+
eventLogPath: path.join(runDir, "events.log"),
|
|
1597
|
+
runtimeId: optionalString(parsed.runtimeId),
|
|
1598
|
+
sessionId: optionalString(parsed.sessionId),
|
|
1599
|
+
agent: optionalString(parsed.agent),
|
|
1600
|
+
agentProfile: optionalString(parsed.agentProfile),
|
|
1601
|
+
backend: optionalString(parsed.backend),
|
|
1602
|
+
model: optionalString(parsed.model),
|
|
1603
|
+
effectiveAgent: optionalString(parsed.effectiveAgent),
|
|
1604
|
+
effectiveModel: optionalString(parsed.effectiveModel),
|
|
1605
|
+
effectiveBackend: optionalString(parsed.effectiveBackend),
|
|
1606
|
+
rosterFile: optionalString(parsed.rosterFile),
|
|
1607
|
+
rosterEntry: optionalString(parsed.rosterEntry),
|
|
1608
|
+
thinking: parsed.thinking,
|
|
1609
|
+
dir: optionalString(parsed.dir),
|
|
1610
|
+
brokerUrl: optionalString(parsed.brokerUrl),
|
|
1611
|
+
parentToken: optionalString(parsed.parentToken),
|
|
1612
|
+
autoApprove: typeof parsed.autoApprove === "boolean" ? parsed.autoApprove : void 0
|
|
1613
|
+
};
|
|
1614
|
+
indexRun(runInfo);
|
|
1615
|
+
return runInfo;
|
|
1616
|
+
} catch {
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
/** Persist the public-to-runtime identity for runs spawned through an external supervisor. */
|
|
1621
|
+
function registerExternalRun(runInfo) {
|
|
1622
|
+
const persisted = {
|
|
1623
|
+
schemaVersion: 1,
|
|
1624
|
+
...runInfo,
|
|
1625
|
+
parentToken: void 0
|
|
1626
|
+
};
|
|
1627
|
+
const metadataPath = externalRunMetadataPath(runInfo.runId);
|
|
1628
|
+
const temporaryPath = `${metadataPath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
1629
|
+
fs.writeFileSync(temporaryPath, `${JSON.stringify(persisted, null, 2)}\n`, {
|
|
1630
|
+
mode: 384,
|
|
1631
|
+
flag: "wx"
|
|
1632
|
+
});
|
|
1633
|
+
try {
|
|
1634
|
+
fs.renameSync(temporaryPath, metadataPath);
|
|
1635
|
+
} catch (error) {
|
|
1636
|
+
try {
|
|
1637
|
+
fs.unlinkSync(temporaryPath);
|
|
1638
|
+
} catch {}
|
|
1639
|
+
throw error;
|
|
1640
|
+
}
|
|
1641
|
+
indexRun(runInfo);
|
|
1642
|
+
}
|
|
1643
|
+
function findExternalRun(supervisorId, reference) {
|
|
1644
|
+
const exact = externalRuns.get(supervisorId)?.byRunId.get(reference);
|
|
1645
|
+
if (exact) return exact;
|
|
1646
|
+
const persisted = readPersistedRun(supervisorId, reference);
|
|
1647
|
+
if (persisted) return persisted;
|
|
1648
|
+
listExternalRuns(supervisorId);
|
|
1649
|
+
return externalRuns.get(supervisorId)?.byAlias.get(reference);
|
|
1650
|
+
}
|
|
1651
|
+
function listExternalRuns(supervisorId) {
|
|
1652
|
+
try {
|
|
1653
|
+
for (const entry of fs.readdirSync(runsRoot(), { withFileTypes: true })) if (entry.isDirectory()) readPersistedRun(supervisorId, entry.name);
|
|
1654
|
+
} catch {}
|
|
1655
|
+
return [...externalRuns.get(supervisorId)?.byRunId.values() ?? []];
|
|
1656
|
+
}
|
|
1657
|
+
function forgetExternalRuns(supervisorId) {
|
|
1658
|
+
externalRuns.delete(supervisorId);
|
|
1659
|
+
}
|
|
1660
|
+
function clearExternalRuns(supervisorId) {
|
|
1661
|
+
const runs = listExternalRuns(supervisorId);
|
|
1662
|
+
forgetExternalRuns(supervisorId);
|
|
1663
|
+
return runs;
|
|
1664
|
+
}
|
|
1665
|
+
//#endregion
|
|
1364
1666
|
//#region src/tools/spawn.ts
|
|
1365
|
-
async function
|
|
1667
|
+
async function executeSpawnTool(args, getSupervisorClient) {
|
|
1668
|
+
validateSpawnSelection({
|
|
1669
|
+
agent: args.agent,
|
|
1670
|
+
model: args.model,
|
|
1671
|
+
backend: args.backend,
|
|
1672
|
+
roster_file: args.rosterFile,
|
|
1673
|
+
roster_entry: args.rosterEntry
|
|
1674
|
+
});
|
|
1366
1675
|
const runId = crypto.randomUUID();
|
|
1367
1676
|
const label = args.label ?? runId;
|
|
1368
1677
|
const baseParams = {
|
|
@@ -1378,9 +1687,23 @@ async function spawnTool(args) {
|
|
|
1378
1687
|
session_id: args.sessionId,
|
|
1379
1688
|
parent_run_id: args.parent_run_id
|
|
1380
1689
|
};
|
|
1690
|
+
if (args.rosterFile) baseParams.roster_file = args.rosterFile;
|
|
1691
|
+
if (args.rosterEntry) baseParams.roster_entry = args.rosterEntry;
|
|
1381
1692
|
if (args.agent) baseParams.agent = args.agent;
|
|
1693
|
+
if (args.thinking !== void 0) baseParams.thinking = args.thinking;
|
|
1382
1694
|
if (args.supervisorId) {
|
|
1383
|
-
const { client, isSingleton, supervisorId } = await getSupervisorClient(args.supervisorId);
|
|
1695
|
+
const { client, isSingleton, sup, supervisorId } = await getSupervisorClient(args.supervisorId);
|
|
1696
|
+
if (isSingleton && sup) {
|
|
1697
|
+
const runInfo = await sup.spawn(baseParams, runId);
|
|
1698
|
+
return {
|
|
1699
|
+
run_id: runInfo.runId,
|
|
1700
|
+
label: runInfo.label,
|
|
1701
|
+
supervisor_id: supervisorId,
|
|
1702
|
+
runtime_id: runInfo.runtimeId,
|
|
1703
|
+
broker_url: runInfo.brokerUrl,
|
|
1704
|
+
parent_token: runInfo.parentToken
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1384
1707
|
const { sentinelPath, eventLogPath } = ensureRunPaths(runId);
|
|
1385
1708
|
try {
|
|
1386
1709
|
const result = await client.spawn({
|
|
@@ -1388,13 +1711,37 @@ async function spawnTool(args) {
|
|
|
1388
1711
|
sentinel_file: sentinelPath,
|
|
1389
1712
|
on_event: eventLogPath
|
|
1390
1713
|
});
|
|
1714
|
+
const stringValue = (candidate) => typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
|
|
1715
|
+
const runtimeId = stringValue(result.runtime_id);
|
|
1716
|
+
registerExternalRun({
|
|
1717
|
+
runId,
|
|
1718
|
+
label,
|
|
1719
|
+
supervisorId,
|
|
1720
|
+
sentinelPath,
|
|
1721
|
+
eventLogPath,
|
|
1722
|
+
runtimeId,
|
|
1723
|
+
sessionId: stringValue(result.session_id),
|
|
1724
|
+
agent: stringValue(result.effective_agent) ?? stringValue(result.agent) ?? baseParams.agent,
|
|
1725
|
+
model: stringValue(result.effective_model) ?? stringValue(result.model) ?? baseParams.model,
|
|
1726
|
+
backend: stringValue(result.effective_backend) ?? stringValue(result.backend) ?? baseParams.backend,
|
|
1727
|
+
effectiveAgent: stringValue(result.effective_agent) ?? stringValue(result.agent) ?? baseParams.agent,
|
|
1728
|
+
effectiveModel: stringValue(result.effective_model) ?? stringValue(result.model) ?? baseParams.model,
|
|
1729
|
+
effectiveBackend: stringValue(result.effective_backend) ?? stringValue(result.backend) ?? baseParams.backend,
|
|
1730
|
+
agentProfile: stringValue(result.agent_profile) ?? baseParams.agent_profile,
|
|
1731
|
+
rosterFile: stringValue(result.roster_file) ?? baseParams.roster_file,
|
|
1732
|
+
rosterEntry: stringValue(result.roster_entry) ?? baseParams.roster_entry,
|
|
1733
|
+
thinking: baseParams.thinking,
|
|
1734
|
+
dir: baseParams.dir,
|
|
1735
|
+
brokerUrl: stringValue(result.broker_url),
|
|
1736
|
+
parentToken: stringValue(result.parent_token)
|
|
1737
|
+
});
|
|
1391
1738
|
return {
|
|
1392
|
-
run_id:
|
|
1739
|
+
run_id: runId,
|
|
1393
1740
|
label,
|
|
1394
1741
|
supervisor_id: supervisorId,
|
|
1395
|
-
runtime_id:
|
|
1396
|
-
broker_url: result.broker_url,
|
|
1397
|
-
parent_token: result.parent_token
|
|
1742
|
+
runtime_id: runtimeId,
|
|
1743
|
+
broker_url: stringValue(result.broker_url),
|
|
1744
|
+
parent_token: stringValue(result.parent_token)
|
|
1398
1745
|
};
|
|
1399
1746
|
} finally {
|
|
1400
1747
|
if (!isSingleton) client.close();
|
|
@@ -1411,14 +1758,25 @@ async function spawnTool(args) {
|
|
|
1411
1758
|
parent_token: runInfo.parentToken
|
|
1412
1759
|
};
|
|
1413
1760
|
}
|
|
1761
|
+
function createSpawnTool(getSupervisorClient) {
|
|
1762
|
+
return (args) => executeSpawnTool(args, getSupervisorClient);
|
|
1763
|
+
}
|
|
1764
|
+
const spawnTool = createSpawnTool(getSupervisorClient);
|
|
1414
1765
|
//#endregion
|
|
1415
|
-
//#region src/tools/
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
const
|
|
1419
|
-
|
|
1420
|
-
|
|
1766
|
+
//#region src/tools/run-resolution.ts
|
|
1767
|
+
/** Resolve a local public run ID before considering a label alias. */
|
|
1768
|
+
function findLocalRunByReference(sup, reference) {
|
|
1769
|
+
const { runs, aliases } = sup;
|
|
1770
|
+
return runs.get(reference) ?? aliases.get(reference);
|
|
1771
|
+
}
|
|
1772
|
+
/** Match a supervisor list entry to its stored runtime identity. */
|
|
1773
|
+
function findLocalRunByRuntimeId(sup, runtimeId) {
|
|
1774
|
+
if (!runtimeId) return void 0;
|
|
1775
|
+
const { runs } = sup;
|
|
1776
|
+
for (const runInfo of runs.values()) if (runInfo.runtimeId === runtimeId) return runInfo;
|
|
1421
1777
|
}
|
|
1778
|
+
//#endregion
|
|
1779
|
+
//#region src/tools/status.ts
|
|
1422
1780
|
async function parseSentinel$1(filePath) {
|
|
1423
1781
|
try {
|
|
1424
1782
|
const content = (await fs.promises.readFile(filePath, "utf-8")).trim();
|
|
@@ -1506,6 +1864,15 @@ function extractPendingPermission(liveStatus) {
|
|
|
1506
1864
|
return normalizePermission(asRecord(pendingPermission));
|
|
1507
1865
|
}
|
|
1508
1866
|
function buildBaseStatus(source, fallback, translatedStatus) {
|
|
1867
|
+
const identity = fallback.identity;
|
|
1868
|
+
const authoritativeValue = (effectiveKey, directKey, fallbackEffective, fallbackDirect) => {
|
|
1869
|
+
if (typeof source[effectiveKey] === "string") return stringField(source, effectiveKey);
|
|
1870
|
+
if (typeof source[directKey] === "string") return stringField(source, directKey);
|
|
1871
|
+
return fallbackEffective ?? fallbackDirect;
|
|
1872
|
+
};
|
|
1873
|
+
const effectiveAgent = authoritativeValue("effective_agent", "agent", identity?.effective_agent, identity?.agent);
|
|
1874
|
+
const effectiveModel = authoritativeValue("effective_model", "model", identity?.effective_model, identity?.model);
|
|
1875
|
+
const effectiveBackend = authoritativeValue("effective_backend", "backend", identity?.effective_backend, identity?.backend);
|
|
1509
1876
|
return {
|
|
1510
1877
|
run_id: fallback.prefer_fallback_run_id ? fallback.run_id : stringField(source, "run_id") ?? fallback.run_id,
|
|
1511
1878
|
label: stringField(source, "label") ?? fallback.label,
|
|
@@ -1516,9 +1883,15 @@ function buildBaseStatus(source, fallback, translatedStatus) {
|
|
|
1516
1883
|
pending_permission: extractPendingPermission(source),
|
|
1517
1884
|
session_id: stringField(source, "session_id") ?? fallback.session_id,
|
|
1518
1885
|
stop_reason: stringField(source, "stop_reason") ?? fallback.stop_reason,
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1886
|
+
roster_file: typeof source.roster_file === "string" ? stringField(source, "roster_file") : identity?.roster_file,
|
|
1887
|
+
roster_entry: typeof source.roster_entry === "string" ? stringField(source, "roster_entry") : identity?.roster_entry,
|
|
1888
|
+
backend: effectiveBackend,
|
|
1889
|
+
agent: effectiveAgent,
|
|
1890
|
+
model: effectiveModel,
|
|
1891
|
+
agent_profile: typeof source.agent_profile === "string" ? stringField(source, "agent_profile") : identity?.agent_profile,
|
|
1892
|
+
effective_backend: effectiveBackend,
|
|
1893
|
+
effective_agent: effectiveAgent,
|
|
1894
|
+
effective_model: effectiveModel,
|
|
1522
1895
|
dir: stringField(source, "dir"),
|
|
1523
1896
|
parent_id: stringField(source, "parent_id", "parentId"),
|
|
1524
1897
|
children: stringArrayField(source, "children"),
|
|
@@ -1545,6 +1918,7 @@ function shapeStatusResult(result, view = "full") {
|
|
|
1545
1918
|
};
|
|
1546
1919
|
}
|
|
1547
1920
|
async function buildRunStatus(runInfo, liveStatus) {
|
|
1921
|
+
if (liveStatus) retainLiveIdentity(runInfo, liveStatus);
|
|
1548
1922
|
let sentinel = null;
|
|
1549
1923
|
if (await sentinelExists(runInfo.sentinelPath)) sentinel = await parseSentinel$1(runInfo.sentinelPath);
|
|
1550
1924
|
const translated = translateStatus(rawStatusPhase(liveStatus), sentinel);
|
|
@@ -1554,7 +1928,18 @@ async function buildRunStatus(runInfo, liveStatus) {
|
|
|
1554
1928
|
prefer_fallback_run_id: true,
|
|
1555
1929
|
runtime_id: runInfo.runtimeId,
|
|
1556
1930
|
session_id: sentinel?.SESSION ?? liveStatus?.session_id ?? runInfo.sessionId,
|
|
1557
|
-
stop_reason: sentinel?.
|
|
1931
|
+
stop_reason: sentinel?.STOP_REASON,
|
|
1932
|
+
identity: {
|
|
1933
|
+
roster_file: runInfo.rosterFile,
|
|
1934
|
+
roster_entry: runInfo.rosterEntry,
|
|
1935
|
+
agent: runInfo.agent,
|
|
1936
|
+
model: runInfo.model,
|
|
1937
|
+
backend: runInfo.backend,
|
|
1938
|
+
agent_profile: runInfo.agentProfile,
|
|
1939
|
+
effective_agent: runInfo.effectiveAgent,
|
|
1940
|
+
effective_model: runInfo.effectiveModel,
|
|
1941
|
+
effective_backend: runInfo.effectiveBackend
|
|
1942
|
+
}
|
|
1558
1943
|
}, translated);
|
|
1559
1944
|
}
|
|
1560
1945
|
async function queryLiveStatus(client, runtimeId) {
|
|
@@ -1567,11 +1952,11 @@ async function queryLiveStatus(client, runtimeId) {
|
|
|
1567
1952
|
}
|
|
1568
1953
|
async function executeStatusTool(args, getSupervisorClient) {
|
|
1569
1954
|
if (args.supervisorId) {
|
|
1570
|
-
const { client, isSingleton, sup } = await getSupervisorClient(args.supervisorId);
|
|
1955
|
+
const { client, isSingleton, sup, supervisorId } = await getSupervisorClient(args.supervisorId);
|
|
1571
1956
|
try {
|
|
1572
1957
|
if (args.runId) {
|
|
1573
1958
|
validateRunId(args.runId);
|
|
1574
|
-
const runInfo = isSingleton && sup ?
|
|
1959
|
+
const runInfo = isSingleton && sup ? findLocalRunByReference(sup, args.runId) : findExternalRun(supervisorId, args.runId);
|
|
1575
1960
|
if (runInfo) return shapeStatusResult(await buildRunStatus(runInfo, await queryLiveStatus(client, runInfo.runtimeId)), args.view);
|
|
1576
1961
|
let liveStatus = await queryLiveStatus(client, args.runId);
|
|
1577
1962
|
if (!liveStatus) try {
|
|
@@ -1589,17 +1974,30 @@ async function executeStatusTool(args, getSupervisorClient) {
|
|
|
1589
1974
|
return shapeStatusResult(buildBaseStatus(liveStatus ?? {}, {
|
|
1590
1975
|
run_id: args.runId,
|
|
1591
1976
|
label: args.runId,
|
|
1977
|
+
prefer_fallback_run_id: true,
|
|
1592
1978
|
runtime_id: stringField(liveStatus ?? {}, "runtime_id", "id"),
|
|
1593
1979
|
session_id: sentinel?.SESSION ?? liveStatus?.session_id,
|
|
1594
|
-
stop_reason: sentinel?.
|
|
1980
|
+
stop_reason: sentinel?.STOP_REASON
|
|
1595
1981
|
}, translateStatus(rawStatusPhase(liveStatus), sentinel)), args.view);
|
|
1596
1982
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1983
|
+
const list = await client.list();
|
|
1984
|
+
if (!isSingleton) listExternalRuns(supervisorId);
|
|
1985
|
+
const results = [];
|
|
1986
|
+
for (const entry of list) {
|
|
1987
|
+
const entryId = String(entry.runtime_id ?? entry.id ?? "");
|
|
1988
|
+
const runInfo = !isSingleton ? findExternalRun(supervisorId, entryId) ?? findExternalRun(supervisorId, String(entry.run_id ?? "")) ?? findExternalRun(supervisorId, String(entry.label ?? "")) : void 0;
|
|
1989
|
+
if (runInfo) {
|
|
1990
|
+
results.push(shapeStatusResult(await buildRunStatus(runInfo, entry), args.view));
|
|
1991
|
+
continue;
|
|
1992
|
+
}
|
|
1993
|
+
results.push(shapeStatusResult(buildBaseStatus(entry, {
|
|
1994
|
+
run_id: String(entry.run_id ?? entryId),
|
|
1995
|
+
label: String(entry.label ?? entryId),
|
|
1996
|
+
runtime_id: entryId,
|
|
1997
|
+
session_id: entry.session_id
|
|
1998
|
+
}, translateStatus(rawStatusPhase(entry), null)), args.view));
|
|
1999
|
+
}
|
|
2000
|
+
return results;
|
|
1603
2001
|
} finally {
|
|
1604
2002
|
if (!isSingleton) client.close();
|
|
1605
2003
|
}
|
|
@@ -1607,7 +2005,7 @@ async function executeStatusTool(args, getSupervisorClient) {
|
|
|
1607
2005
|
const sup = await Supervisor.get();
|
|
1608
2006
|
const client = sup.getClient();
|
|
1609
2007
|
if (args.runId) {
|
|
1610
|
-
const runInfo =
|
|
2008
|
+
const runInfo = findLocalRunByReference(sup, args.runId);
|
|
1611
2009
|
if (runInfo) return shapeStatusResult(await buildRunStatus(runInfo, await queryLiveStatus(client, runInfo.runtimeId)), args.view);
|
|
1612
2010
|
validateRunId(args.runId);
|
|
1613
2011
|
const liveStatus = await queryLiveStatus(client, args.runId);
|
|
@@ -1619,7 +2017,7 @@ async function executeStatusTool(args, getSupervisorClient) {
|
|
|
1619
2017
|
label: args.runId,
|
|
1620
2018
|
runtime_id: stringField(liveStatus ?? {}, "runtime_id", "id"),
|
|
1621
2019
|
session_id: sentinel?.SESSION ?? liveStatus?.session_id,
|
|
1622
|
-
stop_reason: sentinel?.
|
|
2020
|
+
stop_reason: sentinel?.STOP_REASON
|
|
1623
2021
|
}, translateStatus(rawStatusPhase(liveStatus), sentinel)), args.view);
|
|
1624
2022
|
}
|
|
1625
2023
|
const list = await client.list();
|
|
@@ -1627,9 +2025,9 @@ async function executeStatusTool(args, getSupervisorClient) {
|
|
|
1627
2025
|
for (const entry of list) {
|
|
1628
2026
|
const entryId = String(entry.runtime_id ?? entry.id ?? "");
|
|
1629
2027
|
const entryLabel = String(entry.label ?? entryId);
|
|
1630
|
-
const runInfo =
|
|
2028
|
+
const runInfo = findLocalRunByRuntimeId(sup, entryId) ?? findLocalRunByReference(sup, entryId) ?? findLocalRunByReference(sup, entryLabel);
|
|
1631
2029
|
const liveStatus = runInfo ? await queryLiveStatus(client, runInfo.runtimeId) : null;
|
|
1632
|
-
if (runInfo
|
|
2030
|
+
if (runInfo) results.push(shapeStatusResult(await buildRunStatus(runInfo, liveStatus), args.view));
|
|
1633
2031
|
else results.push(shapeStatusResult(buildBaseStatus(entry, {
|
|
1634
2032
|
run_id: String(entry.run_id ?? entryId),
|
|
1635
2033
|
label: entryLabel,
|
|
@@ -1649,12 +2047,6 @@ const MAX_EVENT_LIMIT = 1e3;
|
|
|
1649
2047
|
const MAX_STRING_CHARS = 4e3;
|
|
1650
2048
|
const MAX_ARRAY_ITEMS = 32;
|
|
1651
2049
|
const MAX_OBJECT_KEYS = 64;
|
|
1652
|
-
function findRunByLabel$2(sup, runId) {
|
|
1653
|
-
const runs = sup.runs;
|
|
1654
|
-
const byKey = runs.get(runId);
|
|
1655
|
-
if (byKey) return byKey;
|
|
1656
|
-
for (const info of runs.values()) if (info.label === runId) return info;
|
|
1657
|
-
}
|
|
1658
2050
|
function normalizeLimit(limit) {
|
|
1659
2051
|
return Math.max(1, Math.min(MAX_EVENT_LIMIT, Math.trunc(limit ?? 50)));
|
|
1660
2052
|
}
|
|
@@ -1724,8 +2116,8 @@ async function readNdjsonEvents(filePath, args) {
|
|
|
1724
2116
|
}
|
|
1725
2117
|
async function resolveRun(args, getSupervisorClient) {
|
|
1726
2118
|
if (args.supervisorId) {
|
|
1727
|
-
const { client, isSingleton, sup } = await getSupervisorClient(args.supervisorId);
|
|
1728
|
-
const runInfo = isSingleton && sup ?
|
|
2119
|
+
const { client, isSingleton, sup, supervisorId } = await getSupervisorClient(args.supervisorId);
|
|
2120
|
+
const runInfo = isSingleton && sup ? findLocalRunByReference(sup, args.runId) : findExternalRun(supervisorId, args.runId);
|
|
1729
2121
|
if (runInfo) return {
|
|
1730
2122
|
client,
|
|
1731
2123
|
isSingleton,
|
|
@@ -1768,7 +2160,7 @@ async function resolveRun(args, getSupervisorClient) {
|
|
|
1768
2160
|
};
|
|
1769
2161
|
}
|
|
1770
2162
|
const sup = await Supervisor.get();
|
|
1771
|
-
const runInfo =
|
|
2163
|
+
const runInfo = findLocalRunByReference(sup, args.runId);
|
|
1772
2164
|
if (runInfo) return {
|
|
1773
2165
|
client: sup.getClient(),
|
|
1774
2166
|
isSingleton: true,
|
|
@@ -1794,6 +2186,7 @@ async function resolveRun(args, getSupervisorClient) {
|
|
|
1794
2186
|
};
|
|
1795
2187
|
}
|
|
1796
2188
|
async function executeEventsTool(args, getSupervisorClient) {
|
|
2189
|
+
validateRunId(args.runId);
|
|
1797
2190
|
const limit = normalizeLimit(args.limit);
|
|
1798
2191
|
const resolved = await resolveRun(args, getSupervisorClient);
|
|
1799
2192
|
try {
|
|
@@ -2002,12 +2395,6 @@ async function resultTool(args) {
|
|
|
2002
2395
|
}
|
|
2003
2396
|
//#endregion
|
|
2004
2397
|
//#region src/tools/answer-permission.ts
|
|
2005
|
-
function findRunByLabel$1(sup, runId) {
|
|
2006
|
-
const runs = sup.runs;
|
|
2007
|
-
const byKey = runs.get(runId);
|
|
2008
|
-
if (byKey) return byKey;
|
|
2009
|
-
for (const info of runs.values()) if (info.label === runId) return info;
|
|
2010
|
-
}
|
|
2011
2398
|
function pendingPermissionRequestID(liveStatus) {
|
|
2012
2399
|
const status = asRecord(liveStatus);
|
|
2013
2400
|
if (!status) return void 0;
|
|
@@ -2027,7 +2414,7 @@ async function answerPermissionTool(args) {
|
|
|
2027
2414
|
}
|
|
2028
2415
|
try {
|
|
2029
2416
|
validateRunId(args.runId);
|
|
2030
|
-
const runtimeId = (sup ?
|
|
2417
|
+
const runtimeId = (sup ? findLocalRunByReference(sup, args.runId) : supervisorId ? findExternalRun(supervisorId, args.runId) : void 0)?.runtimeId ?? args.runId;
|
|
2031
2418
|
if (!requestId) {
|
|
2032
2419
|
requestId = pendingPermissionRequestID(await client.status(runtimeId));
|
|
2033
2420
|
if (!requestId) throw new Error("no pending permission request for this run");
|
|
@@ -2040,20 +2427,18 @@ async function answerPermissionTool(args) {
|
|
|
2040
2427
|
}
|
|
2041
2428
|
//#endregion
|
|
2042
2429
|
//#region src/tools/follow-up.ts
|
|
2043
|
-
function findRunByLabel(sup, runId) {
|
|
2044
|
-
const runs = sup.runs;
|
|
2045
|
-
const byKey = runs.get(runId);
|
|
2046
|
-
if (byKey) return byKey;
|
|
2047
|
-
for (const info of runs.values()) if (info.label === runId) return info;
|
|
2048
|
-
}
|
|
2049
2430
|
function resolveAutoApprove(liveStatus, runInfo) {
|
|
2050
2431
|
const liveAutoApprove = liveStatus?.auto_approve;
|
|
2051
2432
|
if (typeof liveAutoApprove === "boolean") return liveAutoApprove;
|
|
2052
2433
|
return typeof runInfo?.autoApprove === "boolean" ? runInfo.autoApprove : void 0;
|
|
2053
2434
|
}
|
|
2054
|
-
function
|
|
2055
|
-
|
|
2056
|
-
|
|
2435
|
+
function value(candidate) {
|
|
2436
|
+
return typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
|
|
2437
|
+
}
|
|
2438
|
+
function resolvedIdentity(liveStatus, effectiveKey, directKey, fallbackEffective, fallbackDirect) {
|
|
2439
|
+
if (liveStatus && typeof liveStatus[effectiveKey] === "string") return value(liveStatus[effectiveKey]);
|
|
2440
|
+
if (liveStatus && typeof liveStatus[directKey] === "string") return value(liveStatus[directKey]);
|
|
2441
|
+
return fallbackEffective ?? fallbackDirect;
|
|
2057
2442
|
}
|
|
2058
2443
|
async function parseSentinel(filePath) {
|
|
2059
2444
|
try {
|
|
@@ -2073,81 +2458,144 @@ async function parseSentinel(filePath) {
|
|
|
2073
2458
|
return null;
|
|
2074
2459
|
}
|
|
2075
2460
|
}
|
|
2461
|
+
function rejectSessionConflict(sentinel, liveStatus) {
|
|
2462
|
+
const sentinelStopReason = value(sentinel?.STOP_REASON);
|
|
2463
|
+
const liveStopReason = value(liveStatus?.stop_reason);
|
|
2464
|
+
if (sentinelStopReason === "session_id_conflict" || liveStopReason === "session_id_conflict") throw new Error("run is not resumable: session_id_conflict");
|
|
2465
|
+
}
|
|
2076
2466
|
async function executeFollowUpTool(args, getSupervisorClient) {
|
|
2077
2467
|
if (args.supervisorId) {
|
|
2078
|
-
const { client, isSingleton, sup } = await getSupervisorClient(args.supervisorId);
|
|
2468
|
+
const { client, isSingleton, sup, supervisorId } = await getSupervisorClient(args.supervisorId);
|
|
2079
2469
|
try {
|
|
2080
2470
|
validateRunId(args.runId);
|
|
2081
2471
|
let runInfo;
|
|
2082
|
-
if (isSingleton && sup) runInfo =
|
|
2472
|
+
if (isSingleton && sup) runInfo = findLocalRunByReference(sup, args.runId);
|
|
2473
|
+
else runInfo = findExternalRun(supervisorId, args.runId);
|
|
2083
2474
|
const fallbackSessionId = runInfo?.sessionId;
|
|
2084
2475
|
let liveStatus = null;
|
|
2085
2476
|
try {
|
|
2086
2477
|
liveStatus = await client.status(runInfo?.runtimeId ?? args.runId);
|
|
2087
2478
|
} catch {}
|
|
2088
|
-
const
|
|
2479
|
+
const sentinel = await parseSentinel(liveStatus?.sentinel_file ?? runInfo?.sentinelPath ?? path.join(runsRoot(), args.runId, "sentinel.done"));
|
|
2480
|
+
rejectSessionConflict(sentinel, liveStatus);
|
|
2481
|
+
const sessionId = sentinel?.SESSION ?? liveStatus?.session_id ?? fallbackSessionId;
|
|
2089
2482
|
if (!sessionId) throw new Error("run has no session to resume");
|
|
2090
|
-
const agent =
|
|
2091
|
-
const
|
|
2092
|
-
const
|
|
2483
|
+
const agent = resolvedIdentity(liveStatus, "effective_agent", "agent", runInfo?.effectiveAgent, runInfo?.agent);
|
|
2484
|
+
const model = resolvedIdentity(liveStatus, "effective_model", "model", runInfo?.effectiveModel, runInfo?.model);
|
|
2485
|
+
const backend = resolvedIdentity(liveStatus, "effective_backend", "backend", runInfo?.effectiveBackend, runInfo?.backend);
|
|
2486
|
+
if (runInfo && liveStatus) retainLiveIdentity(runInfo, liveStatus);
|
|
2487
|
+
const thinking = liveStatus?.thinking ?? runInfo?.thinking;
|
|
2093
2488
|
const dir = liveStatus?.dir ?? runInfo?.dir;
|
|
2094
2489
|
const agentProfile = liveStatus?.agent_profile ?? runInfo?.agentProfile;
|
|
2095
2490
|
const autoApprove = resolveAutoApprove(liveStatus, runInfo);
|
|
2096
2491
|
const followUpRunId = crypto.randomUUID();
|
|
2097
2492
|
const followUpLabel = args.label ?? `${args.runId}-followup`;
|
|
2098
|
-
const { sentinelPath: followUpSentinelPath, eventLogPath } = ensureRunPaths(followUpRunId);
|
|
2099
2493
|
const spawnParams = {
|
|
2100
|
-
agent,
|
|
2101
2494
|
prompt: args.message,
|
|
2102
2495
|
label: followUpLabel,
|
|
2103
|
-
session_id: sessionId
|
|
2104
|
-
sentinel_file: followUpSentinelPath,
|
|
2105
|
-
on_event: eventLogPath
|
|
2496
|
+
session_id: sessionId
|
|
2106
2497
|
};
|
|
2498
|
+
if (agent) spawnParams.agent = agent;
|
|
2107
2499
|
if (backend) spawnParams.backend = backend;
|
|
2108
2500
|
if (model) spawnParams.model = model;
|
|
2501
|
+
if (thinking) spawnParams.thinking = thinking;
|
|
2109
2502
|
if (dir) spawnParams.dir = dir;
|
|
2110
2503
|
if (agentProfile) spawnParams.agent_profile = agentProfile;
|
|
2111
2504
|
if (autoApprove === true) spawnParams.auto_approve = true;
|
|
2505
|
+
if (isSingleton && sup) {
|
|
2506
|
+
const followUpRun = await sup.spawn(spawnParams, followUpRunId, {
|
|
2507
|
+
rosterFile: runInfo?.rosterFile,
|
|
2508
|
+
rosterEntry: runInfo?.rosterEntry,
|
|
2509
|
+
effectiveAgent: agent,
|
|
2510
|
+
effectiveModel: model,
|
|
2511
|
+
effectiveBackend: backend
|
|
2512
|
+
});
|
|
2513
|
+
return {
|
|
2514
|
+
run_id: followUpRun.runId,
|
|
2515
|
+
label: followUpRun.label,
|
|
2516
|
+
runtime_id: followUpRun.runtimeId
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
const { sentinelPath: followUpSentinelPath, eventLogPath } = ensureRunPaths(followUpRunId);
|
|
2520
|
+
spawnParams.sentinel_file = followUpSentinelPath;
|
|
2521
|
+
spawnParams.on_event = eventLogPath;
|
|
2522
|
+
const result = await client.spawn(spawnParams);
|
|
2523
|
+
const runtimeId = value(result.runtime_id);
|
|
2524
|
+
registerExternalRun({
|
|
2525
|
+
runId: followUpRunId,
|
|
2526
|
+
label: followUpLabel,
|
|
2527
|
+
supervisorId,
|
|
2528
|
+
sentinelPath: followUpSentinelPath,
|
|
2529
|
+
eventLogPath,
|
|
2530
|
+
runtimeId,
|
|
2531
|
+
sessionId: value(result.session_id) ?? sessionId,
|
|
2532
|
+
agent,
|
|
2533
|
+
model,
|
|
2534
|
+
backend,
|
|
2535
|
+
effectiveAgent: agent,
|
|
2536
|
+
effectiveModel: model,
|
|
2537
|
+
effectiveBackend: backend,
|
|
2538
|
+
rosterFile: runInfo?.rosterFile,
|
|
2539
|
+
rosterEntry: runInfo?.rosterEntry,
|
|
2540
|
+
thinking,
|
|
2541
|
+
dir,
|
|
2542
|
+
agentProfile,
|
|
2543
|
+
autoApprove
|
|
2544
|
+
});
|
|
2112
2545
|
return {
|
|
2113
|
-
run_id:
|
|
2114
|
-
label: followUpLabel
|
|
2546
|
+
run_id: followUpRunId,
|
|
2547
|
+
label: followUpLabel,
|
|
2548
|
+
runtime_id: runtimeId
|
|
2115
2549
|
};
|
|
2116
2550
|
} finally {
|
|
2117
2551
|
if (!isSingleton) client.close();
|
|
2118
2552
|
}
|
|
2119
2553
|
}
|
|
2120
2554
|
const sup = await Supervisor.get();
|
|
2121
|
-
const runInfo =
|
|
2555
|
+
const runInfo = findLocalRunByReference(sup, args.runId);
|
|
2122
2556
|
if (!runInfo) throw new Error(`run not found: ${args.runId}`);
|
|
2123
|
-
const
|
|
2557
|
+
const sentinel = await parseSentinel(runInfo.sentinelPath);
|
|
2558
|
+
rejectSessionConflict(sentinel, null);
|
|
2559
|
+
const sessionId = sentinel?.SESSION ?? runInfo.sessionId;
|
|
2124
2560
|
if (!sessionId) throw new Error("run has no session to resume");
|
|
2125
2561
|
const client = sup.getClient();
|
|
2126
2562
|
let liveStatus = null;
|
|
2127
2563
|
if (runInfo.runtimeId) try {
|
|
2128
2564
|
liveStatus = await client.status(runInfo.runtimeId);
|
|
2129
2565
|
} catch {}
|
|
2130
|
-
|
|
2131
|
-
const
|
|
2132
|
-
const model = liveStatus
|
|
2566
|
+
rejectSessionConflict(sentinel, liveStatus);
|
|
2567
|
+
const agent = resolvedIdentity(liveStatus, "effective_agent", "agent", runInfo.effectiveAgent, runInfo.agent);
|
|
2568
|
+
const model = resolvedIdentity(liveStatus, "effective_model", "model", runInfo.effectiveModel, runInfo.model);
|
|
2569
|
+
const backend = resolvedIdentity(liveStatus, "effective_backend", "backend", runInfo.effectiveBackend, runInfo.backend);
|
|
2570
|
+
if (liveStatus) retainLiveIdentity(runInfo, liveStatus);
|
|
2571
|
+
const thinking = liveStatus?.thinking ?? runInfo.thinking;
|
|
2133
2572
|
const dir = liveStatus?.dir ?? runInfo.dir;
|
|
2134
2573
|
const agentProfile = liveStatus?.agent_profile ?? runInfo.agentProfile;
|
|
2135
2574
|
const autoApprove = resolveAutoApprove(liveStatus, runInfo);
|
|
2136
2575
|
const followUpLabel = args.label ?? `${runInfo.label}-followup`;
|
|
2137
|
-
const
|
|
2138
|
-
agent,
|
|
2139
|
-
backend,
|
|
2140
|
-
model,
|
|
2141
|
-
|
|
2142
|
-
|
|
2576
|
+
const followUpParams = {
|
|
2577
|
+
...agent ? { agent } : {},
|
|
2578
|
+
...backend ? { backend } : {},
|
|
2579
|
+
...model ? { model } : {},
|
|
2580
|
+
...thinking ? { thinking } : {},
|
|
2581
|
+
...dir ? { dir } : {},
|
|
2582
|
+
...agentProfile ? { agent_profile: agentProfile } : {},
|
|
2143
2583
|
prompt: args.message,
|
|
2144
2584
|
session_id: sessionId,
|
|
2145
2585
|
label: followUpLabel,
|
|
2146
2586
|
...autoApprove === true ? { auto_approve: true } : {}
|
|
2587
|
+
};
|
|
2588
|
+
const followUpRun = await sup.spawn(followUpParams, void 0, {
|
|
2589
|
+
rosterFile: runInfo.rosterFile,
|
|
2590
|
+
rosterEntry: runInfo.rosterEntry,
|
|
2591
|
+
effectiveAgent: agent,
|
|
2592
|
+
effectiveModel: model,
|
|
2593
|
+
effectiveBackend: backend
|
|
2147
2594
|
});
|
|
2148
2595
|
return {
|
|
2149
2596
|
run_id: followUpRun.runId,
|
|
2150
|
-
label: followUpRun.label
|
|
2597
|
+
label: followUpRun.label,
|
|
2598
|
+
runtime_id: followUpRun.runtimeId
|
|
2151
2599
|
};
|
|
2152
2600
|
}
|
|
2153
2601
|
function createFollowUpTool(getSupervisorClient) {
|
|
@@ -2180,9 +2628,24 @@ async function shutdownTool(args) {
|
|
|
2180
2628
|
const client = await dial(supervisorId);
|
|
2181
2629
|
try {
|
|
2182
2630
|
await client.shutdown(args.force ? "force" : "graceful");
|
|
2183
|
-
}
|
|
2631
|
+
} finally {
|
|
2184
2632
|
client.close();
|
|
2185
2633
|
}
|
|
2634
|
+
for (const info of clearExternalRuns(supervisorId)) {
|
|
2635
|
+
try {
|
|
2636
|
+
await fs.promises.unlink(info.sentinelPath);
|
|
2637
|
+
cleanedUp.push(info.sentinelPath);
|
|
2638
|
+
} catch {}
|
|
2639
|
+
try {
|
|
2640
|
+
await fs.promises.unlink(info.eventLogPath);
|
|
2641
|
+
cleanedUp.push(info.eventLogPath);
|
|
2642
|
+
} catch {}
|
|
2643
|
+
const metadataPath = externalRunMetadataPath(info.runId);
|
|
2644
|
+
try {
|
|
2645
|
+
await fs.promises.unlink(metadataPath);
|
|
2646
|
+
cleanedUp.push(metadataPath);
|
|
2647
|
+
} catch {}
|
|
2648
|
+
}
|
|
2186
2649
|
}
|
|
2187
2650
|
return {
|
|
2188
2651
|
ok: true,
|
|
@@ -2211,4 +2674,4 @@ async function shutdownTool(args) {
|
|
|
2211
2674
|
};
|
|
2212
2675
|
}
|
|
2213
2676
|
//#endregion
|
|
2214
|
-
export { DEFAULT_RUN_REDUCER_LIMITS, RunReducer, Supervisor, answerPermissionTool, createRunSnapshot, dial, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, normalizeRunEvent, observeRun, reduceRunSnapshot, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool };
|
|
2677
|
+
export { DEFAULT_RUN_REDUCER_LIMITS, RunReducer, Supervisor, answerPermissionTool, createRunSnapshot, dial, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, normalizeRunEvent, observeRun, reduceRunSnapshot, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dougbots/avenor-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,5 +34,9 @@
|
|
|
34
34
|
"tsdown": "^0.22.0",
|
|
35
35
|
"typescript": "^6.0.3",
|
|
36
36
|
"unrun": "^0.3.0"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@umpire/core": "1.0.1",
|
|
40
|
+
"@umpire/json": "1.0.0"
|
|
37
41
|
}
|
|
38
42
|
}
|