@dougbots/avenor-core 0.4.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -11
- package/dist/index.d.ts +45 -21
- package/dist/index.js +225 -127
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,11 +59,24 @@ Reports status for one or all runs. When `runId` is omitted it lists every run a
|
|
|
59
59
|
```ts
|
|
60
60
|
import { statusTool } from '@dougbots/avenor-core'
|
|
61
61
|
|
|
62
|
-
const status = await statusTool({ runId: 'reverse-string' })
|
|
62
|
+
const status = await statusTool({ runId: 'reverse-string', view: 'lifecycle' })
|
|
63
63
|
// { run_id: 'reverse-string', label: 'reverse-string', status: 'running', ... }
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Status values: `running`, `done`, `failed`, `timeout`, `killed`. If a permission request is pending, the result includes `pending_permission` with available options.
|
|
66
|
+
Status values: `running`, `done`, `failed`, `timeout`, `killed`. If a permission request is pending, the result includes `pending_permission` with available options. The `lifecycle` view omits final output and diagnostic metadata; `full` remains the default for compatibility.
|
|
67
|
+
|
|
68
|
+
### `resultTool`
|
|
69
|
+
|
|
70
|
+
Waits for one run to finish and returns its bounded final output without transcript or event details.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { resultTool } from '@dougbots/avenor-core'
|
|
74
|
+
|
|
75
|
+
const result = await resultTool({ runId: 'reverse-string', timeout: '5m' })
|
|
76
|
+
// { run_id: 'reverse-string', status: 'done', ready: true, output: '...' }
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Set `wait: false` for a non-blocking availability check. A pending permission returns immediately; an expired result wait returns `ready: false` and `timed_out: true` without stopping the run.
|
|
67
80
|
|
|
68
81
|
### `answerPermissionTool`
|
|
69
82
|
|
|
@@ -113,7 +126,7 @@ const result = await shutdownTool({ force: false })
|
|
|
113
126
|
## Quick start
|
|
114
127
|
|
|
115
128
|
```ts
|
|
116
|
-
import { Supervisor,
|
|
129
|
+
import { Supervisor, resultTool, spawnTool, shutdownTool } from '@dougbots/avenor-core'
|
|
117
130
|
|
|
118
131
|
// 1. The supervisor auto-starts avenor. Call get() early — it's a singleton.
|
|
119
132
|
const sup = await Supervisor.get()
|
|
@@ -125,13 +138,9 @@ const { run_id } = await spawnTool({
|
|
|
125
138
|
label: 'security-review',
|
|
126
139
|
})
|
|
127
140
|
|
|
128
|
-
// 3.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
await new Promise(r => setTimeout(r, 2000))
|
|
132
|
-
state = await statusTool({ runId: run_id })
|
|
133
|
-
}
|
|
134
|
-
console.log(state) // { run_id, label, status: 'done', ... }
|
|
141
|
+
// 3. Wait for the bounded final output
|
|
142
|
+
const result = await resultTool({ runId: run_id })
|
|
143
|
+
console.log(result) // { run_id, label, status: 'done', ready: true, output: '...' }
|
|
135
144
|
|
|
136
145
|
// 4. Clean up
|
|
137
146
|
await shutdownTool()
|
|
@@ -146,7 +155,8 @@ await shutdownTool()
|
|
|
146
155
|
| `Supervisor` | class | Singleton avenor process lifecycle manager |
|
|
147
156
|
| `findAvenorBinary` | function | Discover the avenor binary path |
|
|
148
157
|
| `spawnTool` | function | Start a new agent run |
|
|
149
|
-
| `statusTool` | function | Query run status (single or all) |
|
|
158
|
+
| `statusTool` | function | Query run lifecycle status (single or all) |
|
|
159
|
+
| `resultTool` | function | Wait for and retrieve one run's bounded final output |
|
|
150
160
|
| `answerPermissionTool` | function | Answer a pending permission request |
|
|
151
161
|
| `followUpTool` | function | Resume a session with a follow-up prompt |
|
|
152
162
|
| `eventsTool` | function | Read recent events from a run's event log |
|
package/dist/index.d.ts
CHANGED
|
@@ -289,6 +289,7 @@ declare function spawnTool(args: {
|
|
|
289
289
|
}>;
|
|
290
290
|
//#endregion
|
|
291
291
|
//#region src/tools/status.d.ts
|
|
292
|
+
type StatusView = 'lifecycle' | 'full';
|
|
292
293
|
interface StatusResult {
|
|
293
294
|
run_id: string;
|
|
294
295
|
label: string;
|
|
@@ -321,8 +322,51 @@ interface StatusResult {
|
|
|
321
322
|
declare function statusTool(args: {
|
|
322
323
|
runId?: string;
|
|
323
324
|
supervisorId?: string;
|
|
325
|
+
view?: StatusView;
|
|
324
326
|
}): Promise<StatusResult | StatusResult[]>;
|
|
325
327
|
//#endregion
|
|
328
|
+
//#region src/tools/inspect.d.ts
|
|
329
|
+
interface InspectResult {
|
|
330
|
+
run_id: string;
|
|
331
|
+
label: string;
|
|
332
|
+
status: StatusResult;
|
|
333
|
+
snapshot: Omit<RunSnapshot, '_seen_seq_by_scope' | '_recent_legacy_keys' | '_last_semantic_fingerprint' | '_last_source_event'>;
|
|
334
|
+
transcript: RunSnapshot['transcript'];
|
|
335
|
+
tools: RunSnapshot['tools'];
|
|
336
|
+
live_tools: RunSnapshot['live_tools'];
|
|
337
|
+
permissions: RunSnapshot['permissions'];
|
|
338
|
+
pending_permission?: RunSnapshot['pending_permission'];
|
|
339
|
+
final_output?: string;
|
|
340
|
+
}
|
|
341
|
+
declare function inspectTool(args: {
|
|
342
|
+
runId: string;
|
|
343
|
+
supervisorId?: string;
|
|
344
|
+
limit?: number;
|
|
345
|
+
after_seq?: number;
|
|
346
|
+
}): Promise<InspectResult>;
|
|
347
|
+
//#endregion
|
|
348
|
+
//#region src/tools/result.d.ts
|
|
349
|
+
interface ResultResult {
|
|
350
|
+
run_id: string;
|
|
351
|
+
label: string;
|
|
352
|
+
status: string;
|
|
353
|
+
ready: boolean;
|
|
354
|
+
runtime_id?: string;
|
|
355
|
+
session_id?: string;
|
|
356
|
+
stop_reason?: string;
|
|
357
|
+
pending_permission?: StatusResult['pending_permission'];
|
|
358
|
+
output?: string;
|
|
359
|
+
timed_out?: boolean;
|
|
360
|
+
}
|
|
361
|
+
interface ResultToolArgs {
|
|
362
|
+
runId: string;
|
|
363
|
+
supervisorId?: string;
|
|
364
|
+
wait?: boolean;
|
|
365
|
+
timeout?: string;
|
|
366
|
+
signal?: AbortSignal;
|
|
367
|
+
}
|
|
368
|
+
declare function resultTool(args: ResultToolArgs): Promise<ResultResult>;
|
|
369
|
+
//#endregion
|
|
326
370
|
//#region src/tools/answer-permission.d.ts
|
|
327
371
|
declare function answerPermissionTool(args: {
|
|
328
372
|
runId: string;
|
|
@@ -360,26 +404,6 @@ declare function eventsTool(args: {
|
|
|
360
404
|
}>;
|
|
361
405
|
}>;
|
|
362
406
|
//#endregion
|
|
363
|
-
//#region src/tools/inspect.d.ts
|
|
364
|
-
interface InspectResult {
|
|
365
|
-
run_id: string;
|
|
366
|
-
label: string;
|
|
367
|
-
status: StatusResult;
|
|
368
|
-
snapshot: Omit<RunSnapshot, '_seen_seq_by_scope' | '_recent_legacy_keys' | '_last_semantic_fingerprint' | '_last_source_event'>;
|
|
369
|
-
transcript: RunSnapshot['transcript'];
|
|
370
|
-
tools: RunSnapshot['tools'];
|
|
371
|
-
live_tools: RunSnapshot['live_tools'];
|
|
372
|
-
permissions: RunSnapshot['permissions'];
|
|
373
|
-
pending_permission?: RunSnapshot['pending_permission'];
|
|
374
|
-
final_output?: string;
|
|
375
|
-
}
|
|
376
|
-
declare function inspectTool(args: {
|
|
377
|
-
runId: string;
|
|
378
|
-
supervisorId?: string;
|
|
379
|
-
limit?: number;
|
|
380
|
-
after_seq?: number;
|
|
381
|
-
}): Promise<InspectResult>;
|
|
382
|
-
//#endregion
|
|
383
407
|
//#region src/tools/shutdown.d.ts
|
|
384
408
|
declare function shutdownTool(args: {
|
|
385
409
|
supervisorId?: string;
|
|
@@ -389,4 +413,4 @@ declare function shutdownTool(args: {
|
|
|
389
413
|
cleaned_up: string[];
|
|
390
414
|
}>;
|
|
391
415
|
//#endregion
|
|
392
|
-
export { type Client, DEFAULT_RUN_REDUCER_LIMITS, type Event, type HistoryOptions, type HistoryResult, type InspectResult, type NormalizedRunEvent, type ObserveRunOptions, 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 SubscribeOptions, Supervisor, type SupervisorOptions, answerPermissionTool, createRunSnapshot, dial, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, normalizeRunEvent, observeRun, reduceRunSnapshot, shutdownTool, spawnTool, statusTool };
|
|
416
|
+
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, spawnTool, statusTool };
|
package/dist/index.js
CHANGED
|
@@ -1479,7 +1479,7 @@ function extractPendingPermission(liveStatus) {
|
|
|
1479
1479
|
}
|
|
1480
1480
|
function buildBaseStatus(source, fallback, translatedStatus) {
|
|
1481
1481
|
return {
|
|
1482
|
-
run_id: stringField(source, "run_id") ?? fallback.run_id,
|
|
1482
|
+
run_id: fallback.prefer_fallback_run_id ? fallback.run_id : stringField(source, "run_id") ?? fallback.run_id,
|
|
1483
1483
|
label: stringField(source, "label") ?? fallback.label,
|
|
1484
1484
|
status: translatedStatus,
|
|
1485
1485
|
runtime_id: stringField(source, "runtime_id", "id") ?? fallback.runtime_id,
|
|
@@ -1500,6 +1500,20 @@ function buildBaseStatus(source, fallback, translatedStatus) {
|
|
|
1500
1500
|
final_output: stringField(source, "final_output", "finalOutput")
|
|
1501
1501
|
};
|
|
1502
1502
|
}
|
|
1503
|
+
function shapeStatusResult(result, view = "full") {
|
|
1504
|
+
if (view === "full") return result;
|
|
1505
|
+
if (view !== "lifecycle") throw new Error(`invalid status view: ${String(view)}`);
|
|
1506
|
+
return {
|
|
1507
|
+
run_id: result.run_id,
|
|
1508
|
+
label: result.label,
|
|
1509
|
+
status: result.status,
|
|
1510
|
+
...result.runtime_id !== void 0 && { runtime_id: result.runtime_id },
|
|
1511
|
+
...result.phase !== void 0 && { phase: result.phase },
|
|
1512
|
+
...result.phase_label !== void 0 && { phase_label: result.phase_label },
|
|
1513
|
+
...result.pending_permission !== void 0 && { pending_permission: result.pending_permission },
|
|
1514
|
+
...result.latest_seq !== void 0 && { latest_seq: result.latest_seq }
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1503
1517
|
async function buildRunStatus(runInfo, liveStatus) {
|
|
1504
1518
|
let sentinel = null;
|
|
1505
1519
|
if (await sentinelExists(runInfo.sentinelPath)) sentinel = await parseSentinel$1(runInfo.sentinelPath);
|
|
@@ -1507,6 +1521,7 @@ async function buildRunStatus(runInfo, liveStatus) {
|
|
|
1507
1521
|
return buildBaseStatus(liveStatus ?? {}, {
|
|
1508
1522
|
run_id: runInfo.runId,
|
|
1509
1523
|
label: runInfo.label,
|
|
1524
|
+
prefer_fallback_run_id: true,
|
|
1510
1525
|
runtime_id: runInfo.runtimeId,
|
|
1511
1526
|
session_id: sentinel?.SESSION ?? liveStatus?.session_id ?? runInfo.sessionId,
|
|
1512
1527
|
stop_reason: sentinel?._status
|
|
@@ -1527,7 +1542,7 @@ async function statusTool(args) {
|
|
|
1527
1542
|
if (args.runId) {
|
|
1528
1543
|
validateRunId(args.runId);
|
|
1529
1544
|
const runInfo = isSingleton && sup ? findRunByLabel$3(sup, args.runId) : void 0;
|
|
1530
|
-
if (runInfo) return buildRunStatus(runInfo, await queryLiveStatus(client, runInfo.runtimeId));
|
|
1545
|
+
if (runInfo) return shapeStatusResult(await buildRunStatus(runInfo, await queryLiveStatus(client, runInfo.runtimeId)), args.view);
|
|
1531
1546
|
let liveStatus = await queryLiveStatus(client, args.runId);
|
|
1532
1547
|
if (!liveStatus) try {
|
|
1533
1548
|
const entry = (await client.list()).find((item) => {
|
|
@@ -1541,20 +1556,20 @@ async function statusTool(args) {
|
|
|
1541
1556
|
const sentinelPath = liveStatus?.sentinel_file ?? path.join(runsRoot(), args.runId, "sentinel.done");
|
|
1542
1557
|
let sentinel = null;
|
|
1543
1558
|
if (await sentinelExists(sentinelPath)) sentinel = await parseSentinel$1(sentinelPath);
|
|
1544
|
-
return buildBaseStatus(liveStatus ?? {}, {
|
|
1559
|
+
return shapeStatusResult(buildBaseStatus(liveStatus ?? {}, {
|
|
1545
1560
|
run_id: args.runId,
|
|
1546
1561
|
label: args.runId,
|
|
1547
1562
|
runtime_id: stringField(liveStatus ?? {}, "runtime_id", "id"),
|
|
1548
1563
|
session_id: sentinel?.SESSION ?? liveStatus?.session_id,
|
|
1549
1564
|
stop_reason: sentinel?._status
|
|
1550
|
-
}, translateStatus(liveStatus?.phase ?? liveStatus?.status ?? "running", sentinel));
|
|
1565
|
+
}, translateStatus(liveStatus?.phase ?? liveStatus?.status ?? "running", sentinel)), args.view);
|
|
1551
1566
|
}
|
|
1552
|
-
return (await client.list()).map((entry) => buildBaseStatus(entry, {
|
|
1567
|
+
return (await client.list()).map((entry) => shapeStatusResult(buildBaseStatus(entry, {
|
|
1553
1568
|
run_id: String(entry.run_id ?? entry.runtime_id ?? entry.id ?? ""),
|
|
1554
1569
|
label: String(entry.label ?? entry.runtime_id ?? entry.id ?? ""),
|
|
1555
1570
|
runtime_id: String(entry.runtime_id ?? entry.id ?? ""),
|
|
1556
1571
|
session_id: entry.session_id
|
|
1557
|
-
}, translateStatus(entry.phase ?? entry.status ?? "running", null)));
|
|
1572
|
+
}, translateStatus(entry.phase ?? entry.status ?? "running", null)), args.view));
|
|
1558
1573
|
} finally {
|
|
1559
1574
|
if (!isSingleton) client.close();
|
|
1560
1575
|
}
|
|
@@ -1563,19 +1578,19 @@ async function statusTool(args) {
|
|
|
1563
1578
|
const client = sup.getClient();
|
|
1564
1579
|
if (args.runId) {
|
|
1565
1580
|
const runInfo = findRunByLabel$3(sup, args.runId);
|
|
1566
|
-
if (runInfo) return buildRunStatus(runInfo, await queryLiveStatus(client, runInfo.runtimeId));
|
|
1581
|
+
if (runInfo) return shapeStatusResult(await buildRunStatus(runInfo, await queryLiveStatus(client, runInfo.runtimeId)), args.view);
|
|
1567
1582
|
validateRunId(args.runId);
|
|
1568
1583
|
const liveStatus = await queryLiveStatus(client, args.runId);
|
|
1569
1584
|
const sentinelPath = path.join(runsRoot(), args.runId, "sentinel.done");
|
|
1570
1585
|
let sentinel = null;
|
|
1571
1586
|
if (await sentinelExists(sentinelPath)) sentinel = await parseSentinel$1(sentinelPath);
|
|
1572
|
-
return buildBaseStatus(liveStatus ?? {}, {
|
|
1587
|
+
return shapeStatusResult(buildBaseStatus(liveStatus ?? {}, {
|
|
1573
1588
|
run_id: args.runId,
|
|
1574
1589
|
label: args.runId,
|
|
1575
1590
|
runtime_id: stringField(liveStatus ?? {}, "runtime_id", "id"),
|
|
1576
1591
|
session_id: sentinel?.SESSION ?? liveStatus?.session_id,
|
|
1577
1592
|
stop_reason: sentinel?._status
|
|
1578
|
-
}, translateStatus(liveStatus?.phase ?? liveStatus?.status ?? "running", sentinel));
|
|
1593
|
+
}, translateStatus(liveStatus?.phase ?? liveStatus?.status ?? "running", sentinel)), args.view);
|
|
1579
1594
|
}
|
|
1580
1595
|
const list = await client.list();
|
|
1581
1596
|
const results = [];
|
|
@@ -1584,134 +1599,23 @@ async function statusTool(args) {
|
|
|
1584
1599
|
const entryLabel = String(entry.label ?? entryId);
|
|
1585
1600
|
const runInfo = findRunByLabel$3(sup, entryId) ?? findRunByLabel$3(sup, entryLabel);
|
|
1586
1601
|
const liveStatus = runInfo ? await queryLiveStatus(client, runInfo.runtimeId) : null;
|
|
1587
|
-
if (runInfo && liveStatus) results.push(await buildRunStatus(runInfo, liveStatus));
|
|
1588
|
-
else results.push(buildBaseStatus(entry, {
|
|
1602
|
+
if (runInfo && liveStatus) results.push(shapeStatusResult(await buildRunStatus(runInfo, liveStatus), args.view));
|
|
1603
|
+
else results.push(shapeStatusResult(buildBaseStatus(entry, {
|
|
1589
1604
|
run_id: String(entry.run_id ?? entryId),
|
|
1590
1605
|
label: entryLabel,
|
|
1591
1606
|
runtime_id: entryId,
|
|
1592
1607
|
session_id: entry.session_id
|
|
1593
|
-
}, translateStatus(entry.phase ?? entry.status ?? "running", null)));
|
|
1608
|
+
}, translateStatus(entry.phase ?? entry.status ?? "running", null)), args.view));
|
|
1594
1609
|
}
|
|
1595
1610
|
return results;
|
|
1596
1611
|
}
|
|
1597
1612
|
//#endregion
|
|
1598
|
-
//#region src/tools/answer-permission.ts
|
|
1599
|
-
function findRunByLabel$2(sup, runId) {
|
|
1600
|
-
const runs = sup.runs;
|
|
1601
|
-
const byKey = runs.get(runId);
|
|
1602
|
-
if (byKey) return byKey;
|
|
1603
|
-
for (const info of runs.values()) if (info.label === runId) return info;
|
|
1604
|
-
}
|
|
1605
|
-
async function answerPermissionTool(args) {
|
|
1606
|
-
let client;
|
|
1607
|
-
let sup = null;
|
|
1608
|
-
let requestId = args.requestId;
|
|
1609
|
-
let supervisorId;
|
|
1610
|
-
let isSingleton = false;
|
|
1611
|
-
if (args.supervisorId) ({client, isSingleton, sup, supervisorId} = await getSupervisorClient(args.supervisorId));
|
|
1612
|
-
else {
|
|
1613
|
-
sup = await Supervisor.get();
|
|
1614
|
-
client = sup.getClient();
|
|
1615
|
-
}
|
|
1616
|
-
try {
|
|
1617
|
-
validateRunId(args.runId);
|
|
1618
|
-
const runtimeId = (sup ? findRunByLabel$2(sup, args.runId) : void 0)?.runtimeId ?? args.runId;
|
|
1619
|
-
if (!requestId) {
|
|
1620
|
-
const pp = (await client.status(runtimeId))?.pending_permission;
|
|
1621
|
-
if (!pp?.request_id) throw new Error("no pending permission request for this run");
|
|
1622
|
-
requestId = pp.request_id;
|
|
1623
|
-
}
|
|
1624
|
-
await client.answerPermission(runtimeId, requestId, args.optionId);
|
|
1625
|
-
return { ok: true };
|
|
1626
|
-
} finally {
|
|
1627
|
-
if (supervisorId && !isSingleton) client.close();
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
//#endregion
|
|
1631
|
-
//#region src/tools/follow-up.ts
|
|
1632
|
-
function findRunByLabel$1(sup, runId) {
|
|
1633
|
-
const runs = sup.runs;
|
|
1634
|
-
const byKey = runs.get(runId);
|
|
1635
|
-
if (byKey) return byKey;
|
|
1636
|
-
for (const info of runs.values()) if (info.label === runId) return info;
|
|
1637
|
-
}
|
|
1638
|
-
async function parseSentinel(filePath) {
|
|
1639
|
-
try {
|
|
1640
|
-
const content = (await fs.promises.readFile(filePath, "utf-8")).trim();
|
|
1641
|
-
if (!content) return null;
|
|
1642
|
-
const lines = content.split("\n");
|
|
1643
|
-
const result = {};
|
|
1644
|
-
result._status = lines[0];
|
|
1645
|
-
for (let i = 1; i < lines.length; i++) {
|
|
1646
|
-
const line = lines[i].trim();
|
|
1647
|
-
if (!line) continue;
|
|
1648
|
-
const eq = line.indexOf("=");
|
|
1649
|
-
if (eq > 0) result[line.substring(0, eq)] = line.substring(eq + 1);
|
|
1650
|
-
}
|
|
1651
|
-
return result;
|
|
1652
|
-
} catch {
|
|
1653
|
-
return null;
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
async function followUpTool(args) {
|
|
1657
|
-
if (args.supervisorId) {
|
|
1658
|
-
const { client, isSingleton } = await getSupervisorClient(args.supervisorId);
|
|
1659
|
-
try {
|
|
1660
|
-
validateRunId(args.runId);
|
|
1661
|
-
let liveStatus = null;
|
|
1662
|
-
try {
|
|
1663
|
-
liveStatus = await client.status(args.runId);
|
|
1664
|
-
} catch {}
|
|
1665
|
-
const sentinel = await parseSentinel(liveStatus?.sentinel_file ?? path.join(runsRoot(), args.runId, "sentinel.done"));
|
|
1666
|
-
if (!sentinel?.SESSION) throw new Error("run has no session to resume");
|
|
1667
|
-
const agent = liveStatus?.agent ?? "codex";
|
|
1668
|
-
const followUpRunId = crypto.randomUUID();
|
|
1669
|
-
const followUpLabel = args.label ?? `${args.runId}-followup`;
|
|
1670
|
-
const { sentinelPath: followUpSentinelPath, eventLogPath } = ensureRunPaths(followUpRunId);
|
|
1671
|
-
return {
|
|
1672
|
-
run_id: (await client.spawn({
|
|
1673
|
-
agent,
|
|
1674
|
-
prompt: args.message,
|
|
1675
|
-
label: followUpLabel,
|
|
1676
|
-
session_id: sentinel.SESSION,
|
|
1677
|
-
sentinel_file: followUpSentinelPath,
|
|
1678
|
-
on_event: eventLogPath
|
|
1679
|
-
})).runtime_id ?? followUpRunId,
|
|
1680
|
-
label: followUpLabel
|
|
1681
|
-
};
|
|
1682
|
-
} finally {
|
|
1683
|
-
if (!isSingleton) client.close();
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
const sup = await Supervisor.get();
|
|
1687
|
-
const runInfo = findRunByLabel$1(sup, args.runId);
|
|
1688
|
-
if (!runInfo) throw new Error(`run not found: ${args.runId}`);
|
|
1689
|
-
const sentinel = await parseSentinel(runInfo.sentinelPath);
|
|
1690
|
-
if (!sentinel?.SESSION) throw new Error("run has no session to resume");
|
|
1691
|
-
const client = sup.getClient();
|
|
1692
|
-
let agent = "codex";
|
|
1693
|
-
if (runInfo.runtimeId) try {
|
|
1694
|
-
agent = (await client.status(runInfo.runtimeId))?.agent ?? "codex";
|
|
1695
|
-
} catch {}
|
|
1696
|
-
const followUpLabel = args.label ?? `${runInfo.label}-followup`;
|
|
1697
|
-
const followUpRun = await sup.spawn({
|
|
1698
|
-
agent,
|
|
1699
|
-
prompt: args.message,
|
|
1700
|
-
session_id: sentinel.SESSION,
|
|
1701
|
-
label: followUpLabel
|
|
1702
|
-
});
|
|
1703
|
-
return {
|
|
1704
|
-
run_id: followUpRun.runId,
|
|
1705
|
-
label: followUpRun.label
|
|
1706
|
-
};
|
|
1707
|
-
}
|
|
1708
|
-
//#endregion
|
|
1709
1613
|
//#region src/tools/events.ts
|
|
1710
1614
|
const MAX_EVENT_LIMIT = 1e3;
|
|
1711
1615
|
const MAX_STRING_CHARS = 4e3;
|
|
1712
1616
|
const MAX_ARRAY_ITEMS = 32;
|
|
1713
1617
|
const MAX_OBJECT_KEYS = 64;
|
|
1714
|
-
function findRunByLabel(sup, runId) {
|
|
1618
|
+
function findRunByLabel$2(sup, runId) {
|
|
1715
1619
|
const runs = sup.runs;
|
|
1716
1620
|
const byKey = runs.get(runId);
|
|
1717
1621
|
if (byKey) return byKey;
|
|
@@ -1787,7 +1691,7 @@ async function readNdjsonEvents(filePath, args) {
|
|
|
1787
1691
|
async function resolveRun(args) {
|
|
1788
1692
|
if (args.supervisorId) {
|
|
1789
1693
|
const { client, isSingleton, sup } = await getSupervisorClient(args.supervisorId);
|
|
1790
|
-
const runInfo = isSingleton && sup ? findRunByLabel(sup, args.runId) : void 0;
|
|
1694
|
+
const runInfo = isSingleton && sup ? findRunByLabel$2(sup, args.runId) : void 0;
|
|
1791
1695
|
if (runInfo) return {
|
|
1792
1696
|
client,
|
|
1793
1697
|
isSingleton,
|
|
@@ -1830,7 +1734,7 @@ async function resolveRun(args) {
|
|
|
1830
1734
|
};
|
|
1831
1735
|
}
|
|
1832
1736
|
const sup = await Supervisor.get();
|
|
1833
|
-
const runInfo = findRunByLabel(sup, args.runId);
|
|
1737
|
+
const runInfo = findRunByLabel$2(sup, args.runId);
|
|
1834
1738
|
if (runInfo) return {
|
|
1835
1739
|
client: sup.getClient(),
|
|
1836
1740
|
isSingleton: true,
|
|
@@ -1943,6 +1847,200 @@ async function inspectTool(args) {
|
|
|
1943
1847
|
};
|
|
1944
1848
|
}
|
|
1945
1849
|
//#endregion
|
|
1850
|
+
//#region src/tools/result.ts
|
|
1851
|
+
const DEFAULT_POLL_INTERVAL_MS = 1e3;
|
|
1852
|
+
const TERMINAL_STATUSES = new Set([
|
|
1853
|
+
"done",
|
|
1854
|
+
"failed",
|
|
1855
|
+
"timeout",
|
|
1856
|
+
"killed"
|
|
1857
|
+
]);
|
|
1858
|
+
function abortError() {
|
|
1859
|
+
const error = /* @__PURE__ */ new Error("result wait cancelled");
|
|
1860
|
+
error.name = "AbortError";
|
|
1861
|
+
return error;
|
|
1862
|
+
}
|
|
1863
|
+
function sleep(ms, signal) {
|
|
1864
|
+
if (signal?.aborted) return Promise.reject(abortError());
|
|
1865
|
+
return new Promise((resolve, reject) => {
|
|
1866
|
+
const timer = setTimeout(() => {
|
|
1867
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1868
|
+
resolve();
|
|
1869
|
+
}, ms);
|
|
1870
|
+
const onAbort = () => {
|
|
1871
|
+
clearTimeout(timer);
|
|
1872
|
+
reject(abortError());
|
|
1873
|
+
};
|
|
1874
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
const defaultDeps = {
|
|
1878
|
+
status: statusTool,
|
|
1879
|
+
inspect: inspectTool,
|
|
1880
|
+
sleep,
|
|
1881
|
+
now: Date.now,
|
|
1882
|
+
pollIntervalMs: DEFAULT_POLL_INTERVAL_MS
|
|
1883
|
+
};
|
|
1884
|
+
function toResult(status, timedOut = false) {
|
|
1885
|
+
const ready = TERMINAL_STATUSES.has(status.status);
|
|
1886
|
+
return {
|
|
1887
|
+
run_id: status.run_id,
|
|
1888
|
+
label: status.label,
|
|
1889
|
+
status: status.status,
|
|
1890
|
+
ready,
|
|
1891
|
+
runtime_id: status.runtime_id,
|
|
1892
|
+
session_id: status.session_id,
|
|
1893
|
+
stop_reason: status.stop_reason,
|
|
1894
|
+
pending_permission: status.pending_permission,
|
|
1895
|
+
output: ready ? status.final_output : void 0,
|
|
1896
|
+
timed_out: timedOut || void 0
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
async function executeResultTool(args, deps) {
|
|
1900
|
+
const wait = args.wait ?? true;
|
|
1901
|
+
const timeoutMs = args.timeout === void 0 ? void 0 : validateTimeout(args.timeout) * 1e3;
|
|
1902
|
+
const deadline = timeoutMs === void 0 ? void 0 : deps.now() + timeoutMs;
|
|
1903
|
+
const pollIntervalMs = Math.max(1, deps.pollIntervalMs);
|
|
1904
|
+
while (true) {
|
|
1905
|
+
if (args.signal?.aborted) throw abortError();
|
|
1906
|
+
const raw = await deps.status({
|
|
1907
|
+
runId: args.runId,
|
|
1908
|
+
supervisorId: args.supervisorId,
|
|
1909
|
+
view: "full"
|
|
1910
|
+
});
|
|
1911
|
+
const status = Array.isArray(raw) ? raw[0] : raw;
|
|
1912
|
+
if (!status) throw new Error(`run not found: ${args.runId}`);
|
|
1913
|
+
if (TERMINAL_STATUSES.has(status.status)) {
|
|
1914
|
+
if (!status.final_output) try {
|
|
1915
|
+
const inspected = await deps.inspect({
|
|
1916
|
+
runId: args.runId,
|
|
1917
|
+
supervisorId: args.supervisorId
|
|
1918
|
+
});
|
|
1919
|
+
status.final_output = inspected.final_output ?? inspected.snapshot.final_output ?? inspected.snapshot.assistant_text;
|
|
1920
|
+
} catch {}
|
|
1921
|
+
return toResult(status);
|
|
1922
|
+
}
|
|
1923
|
+
if (status.status === "waiting" || !wait) return toResult(status);
|
|
1924
|
+
if (deadline !== void 0 && deps.now() >= deadline) return toResult(status, true);
|
|
1925
|
+
const delay = deadline === void 0 ? pollIntervalMs : Math.min(pollIntervalMs, Math.max(1, deadline - deps.now()));
|
|
1926
|
+
await deps.sleep(delay, args.signal);
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
async function resultTool(args) {
|
|
1930
|
+
return executeResultTool(args, defaultDeps);
|
|
1931
|
+
}
|
|
1932
|
+
//#endregion
|
|
1933
|
+
//#region src/tools/answer-permission.ts
|
|
1934
|
+
function findRunByLabel$1(sup, runId) {
|
|
1935
|
+
const runs = sup.runs;
|
|
1936
|
+
const byKey = runs.get(runId);
|
|
1937
|
+
if (byKey) return byKey;
|
|
1938
|
+
for (const info of runs.values()) if (info.label === runId) return info;
|
|
1939
|
+
}
|
|
1940
|
+
async function answerPermissionTool(args) {
|
|
1941
|
+
let client;
|
|
1942
|
+
let sup = null;
|
|
1943
|
+
let requestId = args.requestId;
|
|
1944
|
+
let supervisorId;
|
|
1945
|
+
let isSingleton = false;
|
|
1946
|
+
if (args.supervisorId) ({client, isSingleton, sup, supervisorId} = await getSupervisorClient(args.supervisorId));
|
|
1947
|
+
else {
|
|
1948
|
+
sup = await Supervisor.get();
|
|
1949
|
+
client = sup.getClient();
|
|
1950
|
+
}
|
|
1951
|
+
try {
|
|
1952
|
+
validateRunId(args.runId);
|
|
1953
|
+
const runtimeId = (sup ? findRunByLabel$1(sup, args.runId) : void 0)?.runtimeId ?? args.runId;
|
|
1954
|
+
if (!requestId) {
|
|
1955
|
+
const pp = (await client.status(runtimeId))?.pending_permission;
|
|
1956
|
+
if (!pp?.request_id) throw new Error("no pending permission request for this run");
|
|
1957
|
+
requestId = pp.request_id;
|
|
1958
|
+
}
|
|
1959
|
+
await client.answerPermission(runtimeId, requestId, args.optionId);
|
|
1960
|
+
return { ok: true };
|
|
1961
|
+
} finally {
|
|
1962
|
+
if (supervisorId && !isSingleton) client.close();
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
//#endregion
|
|
1966
|
+
//#region src/tools/follow-up.ts
|
|
1967
|
+
function findRunByLabel(sup, runId) {
|
|
1968
|
+
const runs = sup.runs;
|
|
1969
|
+
const byKey = runs.get(runId);
|
|
1970
|
+
if (byKey) return byKey;
|
|
1971
|
+
for (const info of runs.values()) if (info.label === runId) return info;
|
|
1972
|
+
}
|
|
1973
|
+
async function parseSentinel(filePath) {
|
|
1974
|
+
try {
|
|
1975
|
+
const content = (await fs.promises.readFile(filePath, "utf-8")).trim();
|
|
1976
|
+
if (!content) return null;
|
|
1977
|
+
const lines = content.split("\n");
|
|
1978
|
+
const result = {};
|
|
1979
|
+
result._status = lines[0];
|
|
1980
|
+
for (let i = 1; i < lines.length; i++) {
|
|
1981
|
+
const line = lines[i].trim();
|
|
1982
|
+
if (!line) continue;
|
|
1983
|
+
const eq = line.indexOf("=");
|
|
1984
|
+
if (eq > 0) result[line.substring(0, eq)] = line.substring(eq + 1);
|
|
1985
|
+
}
|
|
1986
|
+
return result;
|
|
1987
|
+
} catch {
|
|
1988
|
+
return null;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
async function followUpTool(args) {
|
|
1992
|
+
if (args.supervisorId) {
|
|
1993
|
+
const { client, isSingleton } = await getSupervisorClient(args.supervisorId);
|
|
1994
|
+
try {
|
|
1995
|
+
validateRunId(args.runId);
|
|
1996
|
+
let liveStatus = null;
|
|
1997
|
+
try {
|
|
1998
|
+
liveStatus = await client.status(args.runId);
|
|
1999
|
+
} catch {}
|
|
2000
|
+
const sentinel = await parseSentinel(liveStatus?.sentinel_file ?? path.join(runsRoot(), args.runId, "sentinel.done"));
|
|
2001
|
+
if (!sentinel?.SESSION) throw new Error("run has no session to resume");
|
|
2002
|
+
const agent = liveStatus?.agent ?? "codex";
|
|
2003
|
+
const followUpRunId = crypto.randomUUID();
|
|
2004
|
+
const followUpLabel = args.label ?? `${args.runId}-followup`;
|
|
2005
|
+
const { sentinelPath: followUpSentinelPath, eventLogPath } = ensureRunPaths(followUpRunId);
|
|
2006
|
+
return {
|
|
2007
|
+
run_id: (await client.spawn({
|
|
2008
|
+
agent,
|
|
2009
|
+
prompt: args.message,
|
|
2010
|
+
label: followUpLabel,
|
|
2011
|
+
session_id: sentinel.SESSION,
|
|
2012
|
+
sentinel_file: followUpSentinelPath,
|
|
2013
|
+
on_event: eventLogPath
|
|
2014
|
+
})).runtime_id ?? followUpRunId,
|
|
2015
|
+
label: followUpLabel
|
|
2016
|
+
};
|
|
2017
|
+
} finally {
|
|
2018
|
+
if (!isSingleton) client.close();
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
const sup = await Supervisor.get();
|
|
2022
|
+
const runInfo = findRunByLabel(sup, args.runId);
|
|
2023
|
+
if (!runInfo) throw new Error(`run not found: ${args.runId}`);
|
|
2024
|
+
const sentinel = await parseSentinel(runInfo.sentinelPath);
|
|
2025
|
+
if (!sentinel?.SESSION) throw new Error("run has no session to resume");
|
|
2026
|
+
const client = sup.getClient();
|
|
2027
|
+
let agent = "codex";
|
|
2028
|
+
if (runInfo.runtimeId) try {
|
|
2029
|
+
agent = (await client.status(runInfo.runtimeId))?.agent ?? "codex";
|
|
2030
|
+
} catch {}
|
|
2031
|
+
const followUpLabel = args.label ?? `${runInfo.label}-followup`;
|
|
2032
|
+
const followUpRun = await sup.spawn({
|
|
2033
|
+
agent,
|
|
2034
|
+
prompt: args.message,
|
|
2035
|
+
session_id: sentinel.SESSION,
|
|
2036
|
+
label: followUpLabel
|
|
2037
|
+
});
|
|
2038
|
+
return {
|
|
2039
|
+
run_id: followUpRun.runId,
|
|
2040
|
+
label: followUpRun.label
|
|
2041
|
+
};
|
|
2042
|
+
}
|
|
2043
|
+
//#endregion
|
|
1946
2044
|
//#region src/tools/shutdown.ts
|
|
1947
2045
|
async function shutdownTool(args) {
|
|
1948
2046
|
const cleanedUp = [];
|
|
@@ -1999,4 +2097,4 @@ async function shutdownTool(args) {
|
|
|
1999
2097
|
};
|
|
2000
2098
|
}
|
|
2001
2099
|
//#endregion
|
|
2002
|
-
export { DEFAULT_RUN_REDUCER_LIMITS, RunReducer, Supervisor, answerPermissionTool, createRunSnapshot, dial, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, normalizeRunEvent, observeRun, reduceRunSnapshot, shutdownTool, spawnTool, statusTool };
|
|
2100
|
+
export { DEFAULT_RUN_REDUCER_LIMITS, RunReducer, Supervisor, answerPermissionTool, createRunSnapshot, dial, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, normalizeRunEvent, observeRun, reduceRunSnapshot, resultTool, shutdownTool, spawnTool, statusTool };
|