@dougbots/avenor-core 0.5.1 → 0.6.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 CHANGED
@@ -67,7 +67,7 @@ Status values: `running`, `done`, `failed`, `timeout`, `killed`. If a permission
67
67
 
68
68
  ### `resultTool`
69
69
 
70
- Waits for one run to finish and returns its bounded final output without transcript or event details.
70
+ Waits for one run to finish and returns its complete final output without transcript or event details.
71
71
 
72
72
  ```ts
73
73
  import { resultTool } from '@dougbots/avenor-core'
@@ -138,7 +138,7 @@ const { run_id } = await spawnTool({
138
138
  label: 'security-review',
139
139
  })
140
140
 
141
- // 3. Wait for the bounded final output
141
+ // 3. Wait for the complete final output
142
142
  const result = await resultTool({ runId: run_id })
143
143
  console.log(result) // { run_id, label, status: 'done', ready: true, output: '...' }
144
144
 
@@ -156,7 +156,7 @@ await shutdownTool()
156
156
  | `findAvenorBinary` | function | Discover the avenor binary path |
157
157
  | `spawnTool` | function | Start a new agent run |
158
158
  | `statusTool` | function | Query run lifecycle status (single or all) |
159
- | `resultTool` | function | Wait for and retrieve one run's bounded final output |
159
+ | `resultTool` | function | Wait for and retrieve one run's complete final output |
160
160
  | `answerPermissionTool` | function | Answer a pending permission request |
161
161
  | `followUpTool` | function | Resume a session with a follow-up prompt |
162
162
  | `eventsTool` | function | Read recent events from a run's event log |
package/dist/index.d.ts CHANGED
@@ -62,11 +62,13 @@ declare class Client {
62
62
  events(options?: SubscribeOptions): AsyncIterableIterator<Event>;
63
63
  history(options: HistoryOptions): Promise<HistoryResult>;
64
64
  status(runtimeId?: string): Promise<Record<string, unknown>>;
65
+ result(runtimeId?: string): Promise<Record<string, unknown>>;
65
66
  list(): Promise<Array<Record<string, unknown>>>;
66
67
  spawn(params: SpawnParams): Promise<Record<string, unknown>>;
67
68
  shutdown(mode: string): Promise<void>;
68
69
  answerPermission(runtimeId: string, requestId: string, optionId: string): Promise<void>;
69
70
  prompt(runtimeId: string, text: string): Promise<void>;
71
+ interruptAndPrompt(runtimeId: string, text: string, keepQueue?: boolean): Promise<void>;
70
72
  cancel(runtimeId?: string): Promise<void>;
71
73
  close(): void;
72
74
  }
@@ -232,6 +234,9 @@ interface RunInfo {
232
234
  eventLogPath: string;
233
235
  runtimeId?: string;
234
236
  sessionId?: string;
237
+ agent?: string;
238
+ backend?: string;
239
+ dir?: string;
235
240
  brokerUrl?: string;
236
241
  parentToken?: string;
237
242
  }
@@ -318,6 +323,7 @@ interface StatusResult {
318
323
  usage?: Record<string, unknown>;
319
324
  latest_seq?: number;
320
325
  final_output?: string;
326
+ final_output_truncated?: boolean;
321
327
  }
322
328
  declare function statusTool(args: {
323
329
  runId?: string;
@@ -356,6 +362,8 @@ interface ResultResult {
356
362
  stop_reason?: string;
357
363
  pending_permission?: StatusResult['pending_permission'];
358
364
  output?: string;
365
+ output_truncated?: boolean;
366
+ output_event_path?: string;
359
367
  timed_out?: boolean;
360
368
  }
361
369
  interface ResultToolArgs {
package/dist/index.js CHANGED
@@ -233,6 +233,10 @@ var Client = class {
233
233
  const params = runtimeId ? { runtime_id: runtimeId } : void 0;
234
234
  return this.call("status", params);
235
235
  }
236
+ async result(runtimeId) {
237
+ const params = runtimeId ? { runtime_id: runtimeId } : void 0;
238
+ return this.call("result", params);
239
+ }
236
240
  async list() {
237
241
  return this.call("list");
238
242
  }
@@ -255,6 +259,13 @@ var Client = class {
255
259
  text
256
260
  });
257
261
  }
262
+ async interruptAndPrompt(runtimeId, text, keepQueue = false) {
263
+ await this.call("interrupt_and_prompt", {
264
+ runtime_id: runtimeId,
265
+ text,
266
+ keep_queue: keepQueue
267
+ });
268
+ }
258
269
  async cancel(runtimeId) {
259
270
  const params = runtimeId ? { runtime_id: runtimeId } : void 0;
260
271
  await this.call("cancel", params);
@@ -1257,6 +1268,9 @@ var Supervisor = class Supervisor {
1257
1268
  eventLogPath: result.on_event ?? eventLogPath,
1258
1269
  runtimeId: result.runtime_id,
1259
1270
  sessionId: result.session_id,
1271
+ agent: spawnParams.agent ?? params.agent,
1272
+ backend: spawnParams.backend,
1273
+ dir: spawnParams.dir,
1260
1274
  brokerUrl: result.broker_url,
1261
1275
  parentToken: result.parent_token
1262
1276
  };
@@ -1497,7 +1511,8 @@ function buildBaseStatus(source, fallback, translatedStatus) {
1497
1511
  event_path: stringField(source, "event_path", "on_event"),
1498
1512
  usage: asRecord(source.usage) ?? void 0,
1499
1513
  latest_seq: numberField(source, "latest_seq"),
1500
- final_output: stringField(source, "final_output", "finalOutput")
1514
+ final_output: stringField(source, "final_output", "finalOutput"),
1515
+ ...source.final_output_truncated === true && { final_output_truncated: true }
1501
1516
  };
1502
1517
  }
1503
1518
  function shapeStatusResult(result, view = "full") {
@@ -1874,9 +1889,23 @@ function sleep(ms, signal) {
1874
1889
  signal?.addEventListener("abort", onAbort, { once: true });
1875
1890
  });
1876
1891
  }
1892
+ async function fullOutput(runtimeId, supervisorId) {
1893
+ if (supervisorId) {
1894
+ const { client, isSingleton } = await getSupervisorClient(supervisorId);
1895
+ try {
1896
+ const result = await client.result(runtimeId);
1897
+ return typeof result.final_output === "string" ? result.final_output : void 0;
1898
+ } finally {
1899
+ if (!isSingleton) client.close();
1900
+ }
1901
+ }
1902
+ const result = await (await Supervisor.get()).getClient().result(runtimeId);
1903
+ return typeof result.final_output === "string" ? result.final_output : void 0;
1904
+ }
1877
1905
  const defaultDeps = {
1878
1906
  status: statusTool,
1879
1907
  inspect: inspectTool,
1908
+ fullOutput,
1880
1909
  sleep,
1881
1910
  now: Date.now,
1882
1911
  pollIntervalMs: DEFAULT_POLL_INTERVAL_MS
@@ -1893,6 +1922,8 @@ function toResult(status, timedOut = false) {
1893
1922
  stop_reason: status.stop_reason,
1894
1923
  pending_permission: status.pending_permission,
1895
1924
  output: ready ? status.final_output : void 0,
1925
+ output_truncated: ready && status.final_output_truncated ? true : void 0,
1926
+ output_event_path: ready && status.final_output_truncated ? status.event_path : void 0,
1896
1927
  timed_out: timedOut || void 0
1897
1928
  };
1898
1929
  }
@@ -1911,13 +1942,23 @@ async function executeResultTool(args, deps) {
1911
1942
  const status = Array.isArray(raw) ? raw[0] : raw;
1912
1943
  if (!status) throw new Error(`run not found: ${args.runId}`);
1913
1944
  if (TERMINAL_STATUSES.has(status.status)) {
1914
- if (!status.final_output) try {
1945
+ let exactOutputRetrieved = false;
1946
+ try {
1947
+ const output = await deps.fullOutput?.(status.runtime_id, args.supervisorId);
1948
+ if (output !== void 0) {
1949
+ status.final_output = output;
1950
+ status.final_output_truncated = false;
1951
+ exactOutputRetrieved = true;
1952
+ }
1953
+ } catch {}
1954
+ if (status.final_output === void 0) try {
1915
1955
  const inspected = await deps.inspect({
1916
1956
  runId: args.runId,
1917
1957
  supervisorId: args.supervisorId
1918
1958
  });
1919
1959
  status.final_output = inspected.final_output ?? inspected.snapshot.final_output ?? inspected.snapshot.assistant_text;
1920
1960
  } catch {}
1961
+ if (!exactOutputRetrieved && status.final_output !== void 0) status.final_output_truncated = true;
1921
1962
  return toResult(status);
1922
1963
  }
1923
1964
  if (status.status === "waiting" || !wait) return toResult(status);
@@ -1970,6 +2011,10 @@ function findRunByLabel(sup, runId) {
1970
2011
  if (byKey) return byKey;
1971
2012
  for (const info of runs.values()) if (info.label === runId) return info;
1972
2013
  }
2014
+ function requireResumableAgent(agent) {
2015
+ if (agent === void 0) throw new Error("run has no agent to resume");
2016
+ return agent;
2017
+ }
1973
2018
  async function parseSentinel(filePath) {
1974
2019
  try {
1975
2020
  const content = (await fs.promises.readFile(filePath, "utf-8")).trim();
@@ -1990,28 +2035,36 @@ async function parseSentinel(filePath) {
1990
2035
  }
1991
2036
  async function followUpTool(args) {
1992
2037
  if (args.supervisorId) {
1993
- const { client, isSingleton } = await getSupervisorClient(args.supervisorId);
2038
+ const { client, isSingleton, sup } = await getSupervisorClient(args.supervisorId);
1994
2039
  try {
1995
2040
  validateRunId(args.runId);
2041
+ let runInfo;
2042
+ if (isSingleton && sup) runInfo = findRunByLabel(sup, args.runId);
2043
+ const fallbackSessionId = runInfo?.sessionId;
1996
2044
  let liveStatus = null;
1997
2045
  try {
1998
2046
  liveStatus = await client.status(args.runId);
1999
2047
  } 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";
2048
+ const sessionId = (await parseSentinel(liveStatus?.sentinel_file ?? path.join(runsRoot(), args.runId, "sentinel.done")))?.SESSION ?? liveStatus?.session_id ?? fallbackSessionId;
2049
+ if (!sessionId) throw new Error("run has no session to resume");
2050
+ const agent = requireResumableAgent(liveStatus?.agent ?? runInfo?.agent);
2051
+ const backend = liveStatus?.backend ?? runInfo?.backend;
2052
+ const dir = liveStatus?.dir ?? runInfo?.dir;
2003
2053
  const followUpRunId = crypto.randomUUID();
2004
2054
  const followUpLabel = args.label ?? `${args.runId}-followup`;
2005
2055
  const { sentinelPath: followUpSentinelPath, eventLogPath } = ensureRunPaths(followUpRunId);
2056
+ const spawnParams = {
2057
+ agent,
2058
+ prompt: args.message,
2059
+ label: followUpLabel,
2060
+ session_id: sessionId,
2061
+ sentinel_file: followUpSentinelPath,
2062
+ on_event: eventLogPath
2063
+ };
2064
+ if (backend) spawnParams.backend = backend;
2065
+ if (dir) spawnParams.dir = dir;
2006
2066
  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,
2067
+ run_id: (await client.spawn(spawnParams)).runtime_id ?? followUpRunId,
2015
2068
  label: followUpLabel
2016
2069
  };
2017
2070
  } finally {
@@ -2021,18 +2074,23 @@ async function followUpTool(args) {
2021
2074
  const sup = await Supervisor.get();
2022
2075
  const runInfo = findRunByLabel(sup, args.runId);
2023
2076
  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");
2077
+ const sessionId = (await parseSentinel(runInfo.sentinelPath))?.SESSION ?? runInfo.sessionId;
2078
+ if (!sessionId) throw new Error("run has no session to resume");
2026
2079
  const client = sup.getClient();
2027
- let agent = "codex";
2080
+ let liveStatus = null;
2028
2081
  if (runInfo.runtimeId) try {
2029
- agent = (await client.status(runInfo.runtimeId))?.agent ?? "codex";
2082
+ liveStatus = await client.status(runInfo.runtimeId);
2030
2083
  } catch {}
2084
+ const agent = requireResumableAgent(liveStatus?.agent ?? runInfo.agent);
2085
+ const backend = liveStatus?.backend ?? runInfo.backend;
2086
+ const dir = liveStatus?.dir ?? runInfo.dir;
2031
2087
  const followUpLabel = args.label ?? `${runInfo.label}-followup`;
2032
2088
  const followUpRun = await sup.spawn({
2033
2089
  agent,
2090
+ backend,
2091
+ dir,
2034
2092
  prompt: args.message,
2035
- session_id: sentinel.SESSION,
2093
+ session_id: sessionId,
2036
2094
  label: followUpLabel
2037
2095
  });
2038
2096
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dougbots/avenor-core",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",