@orkestrel/agent 0.0.7 → 0.0.9

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.
@@ -74,6 +74,24 @@ var THINK_CLOSE = "</think>";
74
74
  */
75
75
  var WORKSPACE_SECTION_HEADER = "## Workspace";
76
76
  /**
77
+ * The estimated per-message role/framing overhead {@link import('./helpers.js').estimateMessages}
78
+ * adds on top of a message's content estimate — accounts for the fixed wire framing every
79
+ * conversation turn carries (its role tag, delimiters) that {@link import('./helpers.js').estimateTokens}'s
80
+ * content-only heuristic does not otherwise capture.
81
+ */
82
+ var MESSAGE_TOKEN_OVERHEAD = 4;
83
+ /**
84
+ * The coarse, deliberately-approximate per-image token cost {@link import('./helpers.js').estimateMessages}
85
+ * charges for each attached image.
86
+ *
87
+ * @remarks
88
+ * A base64 image payload's LENGTH is NOT a reliable token proxy (a vision model's actual image
89
+ * token cost depends on resolution / tiling, not byte size), so this is a fixed, coarse
90
+ * per-image estimate rather than a derivation from `image.length` — a planning heuristic, not an
91
+ * exact count.
92
+ */
93
+ var IMAGE_TOKEN_ESTIMATE = 512;
94
+ /**
77
95
  * The extension→language table {@link import('./helpers.js').inferLanguage} reads to
78
96
  * map a file path's extension to a fenced-code language tag. Covers the common source /
79
97
  * markup / data extensions; an unlisted extension falls back to `'text'`.
@@ -202,18 +220,21 @@ function isAgentJobError(value) {
202
220
  }
203
221
  /**
204
222
  * Thrown by a {@link ConversationInterface}'s `compact()` when the conversation has no
205
- * {@link ConversationSummarizer} to fold its messages with — carries a machine-readable
206
- * `code`.
223
+ * {@link ConversationSummarizer} to fold its messages with, or when its `sections` cap is
224
+ * structurally invalid — carries a machine-readable `code`.
207
225
  *
208
226
  * @remarks
209
227
  * Compaction REQUIRES a summarizer (it digests the folded slice into a section summary and
210
228
  * regenerates the rollup); a conversation created without one can still store + `view()` its
211
- * live tail, but a `compact()` is a programmer error (§12) and throws this. `code` is
212
- * `'SUMMARIZER'` (the only condition so far). Narrow a caught value with
229
+ * live tail, but a `compact()` is a programmer error (§12) and throws this with `'SUMMARIZER'`.
230
+ * A `sections` cap (§F2, on {@link import('./types.js').ConversationOptions} /
231
+ * {@link import('./types.js').ConversationManagerOptions} /
232
+ * {@link import('./types.js').CompactOptions}) must be `>= 1` — a sub-1 cap is a programmer
233
+ * error and throws this with `'SECTIONS'`. Narrow a caught value with
213
234
  * {@link isConversationError} and branch on `error.code`.
214
235
  */
215
236
  var ConversationError = class extends Error {
216
- /** The machine-readable condition — `'SUMMARIZER'`: a `compact()` with no summarizer. */
237
+ /** The machine-readable condition — `'SUMMARIZER'`: a `compact()` with no summarizer; `'SECTIONS'`: a sub-1 `sections` cap. */
217
238
  code;
218
239
  constructor(code, message) {
219
240
  super(message);
@@ -257,7 +278,7 @@ var WorkspaceError = class extends Error {
257
278
  super(message);
258
279
  this.name = "WorkspaceError";
259
280
  this.code = code;
260
- this.context = context;
281
+ if (context !== void 0) this.context = context;
261
282
  }
262
283
  };
263
284
  /**
@@ -278,6 +299,45 @@ var WorkspaceError = class extends Error {
278
299
  function isWorkspaceError(value) {
279
300
  return value instanceof WorkspaceError;
280
301
  }
302
+ /**
303
+ * Thrown synchronously by an {@link AgentInterface}'s `stream()` when a concurrent run would
304
+ * corrupt SHARED per-agent accounting — carries a machine-readable `code`.
305
+ *
306
+ * @remarks
307
+ * A run already in flight on the same agent, PLUS a construction-level `window` (a shared
308
+ * context budget) or a construction-level `budget` with no per-run override (a shared cost
309
+ * budget), means a second concurrent `stream()` would race its charges against the same
310
+ * shared instance — corrupting the accounting. `code` is `'CONCURRENCY'` (the only condition
311
+ * so far). Use separate agents, or per-run `budget` overrides with no `window`, for genuinely
312
+ * concurrent runs. Narrow a caught value with {@link isAgentError} and branch on `error.code`.
313
+ */
314
+ var AgentError = class extends Error {
315
+ /** The machine-readable condition — `'CONCURRENCY'`: a concurrent run on a shared accounting agent. */
316
+ code;
317
+ constructor(code, message) {
318
+ super(message);
319
+ this.name = "AgentError";
320
+ this.code = code;
321
+ }
322
+ };
323
+ /**
324
+ * Narrow an unknown caught value to an {@link AgentError}.
325
+ *
326
+ * @param value - The value to test (typically a `catch` binding)
327
+ * @returns `true` when `value` is an {@link AgentError}
328
+ *
329
+ * @example
330
+ * ```ts
331
+ * try {
332
+ * agent.stream()
333
+ * } catch (error) {
334
+ * if (isAgentError(error) && error.code === 'CONCURRENCY') useSeparateAgents()
335
+ * }
336
+ * ```
337
+ */
338
+ function isAgentError(value) {
339
+ return value instanceof AgentError;
340
+ }
281
341
  //#endregion
282
342
  //#region src/core/conversations/Conversation.ts
283
343
  /**
@@ -331,17 +391,20 @@ var Conversation = class {
331
391
  #emitter;
332
392
  #summarize;
333
393
  #keep;
394
+ #cap;
334
395
  #sections = [];
335
396
  #summary;
336
397
  #messages = /* @__PURE__ */ new Map();
337
398
  constructor(options, seed) {
338
399
  this.#id = seed?.id ?? options?.id ?? crypto.randomUUID();
339
400
  this.#emitter = new _orkestrel_emitter.Emitter({
340
- on: options?.on,
341
- error: options?.error
401
+ ...options?.on === void 0 ? {} : { on: options.on },
402
+ ...options?.error === void 0 ? {} : { error: options.error }
342
403
  });
343
404
  this.#summarize = options?.summarize;
344
405
  this.#keep = options?.keep ?? 0;
406
+ if (options?.sections !== void 0 && options.sections < 1) throw new ConversationError("SECTIONS", "a sections cap must be >= 1");
407
+ this.#cap = options?.sections;
345
408
  if (seed) {
346
409
  this.#summary = seed.summary;
347
410
  for (const section of seed.sections) this.#sections.push(section);
@@ -393,6 +456,8 @@ var Conversation = class {
393
456
  async compact(options) {
394
457
  const summarize = this.#summarize;
395
458
  if (summarize === void 0) throw new ConversationError("SUMMARIZER", "cannot compact a conversation without a summarizer");
459
+ const cap = options?.sections ?? this.#cap;
460
+ if (cap !== void 0 && cap < 1) throw new ConversationError("SECTIONS", "a sections cap must be >= 1");
396
461
  const keep = options?.keep ?? this.#keep;
397
462
  const live = [...this.#messages.values()];
398
463
  const fold = keep <= 0 ? live.length : live.length - keep;
@@ -406,6 +471,23 @@ var Conversation = class {
406
471
  };
407
472
  for (const message of slice) this.#messages.delete(message.id);
408
473
  this.#sections.push(section);
474
+ if (cap !== void 0 && this.#sections.length > cap) {
475
+ const overflow = this.#sections.length - cap + 1;
476
+ const folded = this.#sections.slice(0, overflow);
477
+ try {
478
+ const merged = {
479
+ id: crypto.randomUUID(),
480
+ summary: await summarize(folded.map((one) => this.#summaryMessage(one))),
481
+ messages: folded.flatMap((one) => one.messages)
482
+ };
483
+ this.#sections.splice(0, overflow, merged);
484
+ this.#emitter.emit("collapse", merged);
485
+ } catch (error) {
486
+ this.#summary = await summarize(this.#sections.map((one) => this.#summaryMessage(one)));
487
+ this.#emitter.emit("summary", this.#summary);
488
+ throw error;
489
+ }
490
+ }
409
491
  this.#summary = await summarize(this.#sections.map((one) => this.#summaryMessage(one)));
410
492
  this.#emitter.emit("summary", this.#summary);
411
493
  this.#emitter.emit("compact", section);
@@ -505,10 +587,12 @@ var ConversationManager = class {
505
587
  #active;
506
588
  #summarize;
507
589
  #keep;
590
+ #sections;
508
591
  #store;
509
592
  constructor(options) {
510
593
  this.#summarize = options?.summarize;
511
594
  this.#keep = options?.keep ?? 0;
595
+ this.#sections = options?.sections;
512
596
  this.#store = options?.store;
513
597
  }
514
598
  get count() {
@@ -524,11 +608,14 @@ var ConversationManager = class {
524
608
  return [...this.#conversations.values()];
525
609
  }
526
610
  add(input) {
611
+ const sections = input?.sections ?? this.#sections;
612
+ const summarize = input?.summarize ?? this.#summarize;
527
613
  const conversation = new Conversation({
528
614
  ...input?.id === void 0 ? {} : { id: input.id },
529
615
  ...input?.on === void 0 ? {} : { on: input.on },
530
- summarize: input?.summarize ?? this.#summarize,
531
- keep: input?.keep ?? this.#keep
616
+ ...summarize === void 0 ? {} : { summarize },
617
+ keep: input?.keep ?? this.#keep,
618
+ ...sections === void 0 ? {} : { sections }
532
619
  }, input?.snapshot);
533
620
  this.#conversations.set(conversation.id, conversation);
534
621
  if (this.#active === void 0) this.#active = conversation.id;
@@ -669,23 +756,40 @@ function estimateTokens(text) {
669
756
  * (the {@link import('./types.js').AgentOptions} `window`).
670
757
  *
671
758
  * @remarks
672
- * Sums {@link estimateTokens} over each message's `content` (the `ceil(length / 4)` char
673
- * heuristic), so it is deterministic and provider-free — the same messages always yield the
674
- * same estimate, with an empty batch `0`. It is the fully-swappable default an agent's
675
- * auto-compaction context budget charges each turn's new messages through; a caller wanting a
676
- * sharper count supplies its own `consume` to `createBudget` instead. Total — never throws.
759
+ * Sums, per message, {@link estimateTokens} over its `content` (the `ceil(length / 4)` char
760
+ * heuristic) PLUS {@link import('./constants.js').MESSAGE_TOKEN_OVERHEAD} (a fixed per-message
761
+ * role/framing overhead) PLUS, when present, {@link estimateTokens} over its JSON-stringified
762
+ * `calls` PLUS `images.length * `{@link import('./constants.js').IMAGE_TOKEN_ESTIMATE} (a coarse,
763
+ * deliberately-approximate per-image cost — a base64 length is NOT a token proxy). Deterministic
764
+ * and provider-free — the same messages always yield the same estimate, with an empty batch `0`.
765
+ * It is the fully-swappable default an agent's auto-compaction context budget charges each
766
+ * turn's new messages through; a caller wanting a sharper count supplies its own `consume` to
767
+ * `createBudget` instead. Total — never throws: a `calls` `JSON.stringify` that throws (a
768
+ * circular `ToolCall.arguments`) is caught and replaced with a conservative fixed contribution of
769
+ * {@link import('./constants.js').MESSAGE_TOKEN_OVERHEAD} (the same per-message overhead scale)
770
+ * instead of estimating the (unreachable) serialized length.
677
771
  *
678
772
  * @param messages - The messages to estimate (a turn's appended assistant + tool messages)
679
- * @returns The summed estimated token count (`Σ estimateTokens(m.content)`; `0` when empty)
773
+ * @returns The summed estimated token count (`0` when empty)
680
774
  *
681
775
  * @example
682
776
  * ```ts
683
777
  * estimateMessages([]) // 0
684
- * estimateMessages([{ id: '1', role: 'user', content: 'hello' }]) // 2
778
+ * estimateMessages([{ id: '1', role: 'user', content: 'hello' }]) // 6 (2 content + 4 overhead)
685
779
  * ```
686
780
  */
687
781
  function estimateMessages(messages) {
688
- return messages.reduce((sum, message) => sum + estimateTokens(message.content), 0);
782
+ return messages.reduce((sum, message) => {
783
+ const content = estimateTokens(message.content) + 4;
784
+ let calls = 0;
785
+ if (message.calls?.length) try {
786
+ calls = estimateTokens(JSON.stringify(message.calls));
787
+ } catch {
788
+ calls = 4;
789
+ }
790
+ const images = (message.images?.length ?? 0) * 512;
791
+ return sum + content + calls + images;
792
+ }, 0);
689
793
  }
690
794
  /**
691
795
  * Run one rehydrated agent and apply the partial-as-configurable-failure policy — the
@@ -715,6 +819,38 @@ async function settleAgentJob(agent, allowPartial) {
715
819
  return result;
716
820
  }
717
821
  /**
822
+ * Handle one queued agent job by rehydrating it through a registry with the queue
823
+ * attempt's signal, then applying the shared partial-result policy.
824
+ *
825
+ * @param registry - The registry that rehydrates the serializable job
826
+ * @param allowPartial - Whether a partial result resolves instead of throwing
827
+ * @param input - The serializable agent job
828
+ * @param execution - The queue attempt whose signal bounds the agent
829
+ * @returns The settled agent result
830
+ */
831
+ function handleAgentQueueJob(registry, allowPartial, input, execution) {
832
+ return settleAgentJob(registry.build(input, execution.signal), allowPartial);
833
+ }
834
+ /**
835
+ * Handle one runner agent job by fanning out its declared children, rehydrating the
836
+ * parent through a registry with the controller signal, and applying the shared
837
+ * partial-result policy.
838
+ *
839
+ * @remarks
840
+ * Children are fired and tracked through the runner controller without awaiting them
841
+ * inline, preserving bounded-runner progress.
842
+ *
843
+ * @param registry - The registry that rehydrates serializable jobs
844
+ * @param allowPartial - Whether a partial result resolves instead of throwing
845
+ * @param controller - The runner controller for this parent job
846
+ * @returns The settled parent agent result
847
+ */
848
+ function handleAgentRunnerJob(registry, allowPartial, controller) {
849
+ const children = controller.input.children;
850
+ if (children !== void 0) for (const child of children) controller.spawn(child);
851
+ return settleAgentJob(registry.build(controller.input, controller.signal), allowPartial);
852
+ }
853
+ /**
718
854
  * Whether a {@link FileContent} is the TEXT arm — the narrowing guard for the text-vs-binary
719
855
  * union (AGENTS §14: narrow an untyped arm via a guard, never an `as`).
720
856
  *
@@ -1311,6 +1447,40 @@ function fencedFile(path, language, content) {
1311
1447
  function escapeRegExp(value) {
1312
1448
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1313
1449
  }
1450
+ /**
1451
+ * Sanitize one reported token count into a safe non-negative integer.
1452
+ *
1453
+ * @param value - The token count to sanitize
1454
+ * @returns The floored count, or `0` when the value is non-finite or non-positive
1455
+ */
1456
+ function sanitizeToken(value) {
1457
+ return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
1458
+ }
1459
+ /**
1460
+ * Sanitize a {@link TokenUsage} into safe, non-negative integers — the guard an agent's
1461
+ * abort-usage path applies to a provider's partial usage before it is charged against a
1462
+ * budget or folded into the run total.
1463
+ *
1464
+ * @remarks
1465
+ * Per field (`prompt` / `completion` / `total`): a non-finite value (`NaN`, `+Infinity`,
1466
+ * `-Infinity`) or a negative value floors to `0`; a fractional value floors to its
1467
+ * non-negative integer part. No upper cap is applied. Total — never throws.
1468
+ *
1469
+ * @param usage - The token usage to sanitize (e.g. a provider's abort-partial usage)
1470
+ * @returns A new {@link TokenUsage} with every field a safe non-negative integer
1471
+ *
1472
+ * @example
1473
+ * ```ts
1474
+ * sanitizeUsage({ prompt: -5, completion: NaN, total: 12.7 }) // { prompt: 0, completion: 0, total: 12 }
1475
+ * ```
1476
+ */
1477
+ function sanitizeUsage(usage) {
1478
+ return {
1479
+ prompt: sanitizeToken(usage.prompt),
1480
+ completion: sanitizeToken(usage.completion),
1481
+ total: sanitizeToken(usage.total)
1482
+ };
1483
+ }
1314
1484
  //#endregion
1315
1485
  //#region src/core/instructions/Instruction.ts
1316
1486
  /**
@@ -1392,8 +1562,8 @@ var InstructionManager = class {
1392
1562
  #format;
1393
1563
  constructor(options) {
1394
1564
  this.#emitter = new _orkestrel_emitter.Emitter({
1395
- on: options?.on,
1396
- error: options?.error
1565
+ ...options?.on === void 0 ? {} : { on: options.on },
1566
+ ...options?.error === void 0 ? {} : { error: options.error }
1397
1567
  });
1398
1568
  this.#format = options?.format;
1399
1569
  }
@@ -1595,8 +1765,8 @@ var Workspace = class {
1595
1765
  constructor(options, seed) {
1596
1766
  this.#id = options?.id ?? crypto.randomUUID();
1597
1767
  this.#emitter = new _orkestrel_emitter.Emitter({
1598
- on: options?.on,
1599
- error: options?.error
1768
+ ...options?.on === void 0 ? {} : { on: options.on },
1769
+ ...options?.error === void 0 ? {} : { error: options.error }
1600
1770
  });
1601
1771
  if (seed) for (const [path, file] of seed) this.#files.set(path, file);
1602
1772
  }
@@ -1889,10 +2059,12 @@ var WorkspaceManager = class {
1889
2059
  return [...this.#workspaces.values()];
1890
2060
  }
1891
2061
  add(input) {
2062
+ const on = input?.on ?? this.#on;
2063
+ const error = input?.error ?? this.#error;
1892
2064
  const workspace = new Workspace({
1893
2065
  ...input?.id === void 0 ? {} : { id: input.id },
1894
- on: input?.on ?? this.#on,
1895
- error: input?.error ?? this.#error
2066
+ ...on === void 0 ? {} : { on },
2067
+ ...error === void 0 ? {} : { error }
1896
2068
  }, input?.seed);
1897
2069
  this.#workspaces.set(workspace.id, workspace);
1898
2070
  if (this.#active === void 0) this.#active = workspace.id;
@@ -2236,6 +2408,7 @@ var Agent = class {
2236
2408
  #signal;
2237
2409
  #authority;
2238
2410
  #window;
2411
+ #strict;
2239
2412
  #emitter;
2240
2413
  id = crypto.randomUUID();
2241
2414
  #status = "idle";
@@ -2243,9 +2416,12 @@ var Agent = class {
2243
2416
  constructor(provider, options) {
2244
2417
  this.#provider = provider;
2245
2418
  this.#context = new AgentContext({
2246
- system: options?.system,
2247
- tools: options?.tools,
2248
- conversations: options?.conversations
2419
+ ...options?.system === void 0 ? {} : { system: options.system },
2420
+ ...options?.tools === void 0 ? {} : { tools: options.tools },
2421
+ ...options?.instructions === void 0 ? {} : { instructions: options.instructions },
2422
+ ...options?.workspaces === void 0 ? {} : { workspaces: options.workspaces },
2423
+ ...options?.scope === void 0 ? {} : { scope: options.scope },
2424
+ ...options?.conversations === void 0 ? {} : { conversations: options.conversations }
2249
2425
  });
2250
2426
  this.#limit = options?.limit ?? 10;
2251
2427
  this.#timeoutMs = options?.timeout;
@@ -2254,9 +2430,10 @@ var Agent = class {
2254
2430
  this.#signal = options?.signal;
2255
2431
  this.#authority = options?.authority;
2256
2432
  this.#window = options?.window;
2433
+ this.#strict = options?.strict ?? false;
2257
2434
  this.#emitter = new _orkestrel_emitter.Emitter({
2258
- on: options?.on,
2259
- error: options?.error
2435
+ ...options?.on === void 0 ? {} : { on: options.on },
2436
+ ...options?.error === void 0 ? {} : { error: options.error }
2260
2437
  });
2261
2438
  }
2262
2439
  get emitter() {
@@ -2272,48 +2449,49 @@ var Agent = class {
2272
2449
  return this.stream(options).result;
2273
2450
  }
2274
2451
  stream(options) {
2452
+ if (this.#runs.size > 0 && (this.#window !== void 0 || this.#budget !== void 0 && options?.budget === void 0)) throw new AgentError("CONCURRENCY", "concurrent runs on one agent with a shared construction window/budget corrupt accounting; use separate agents or per-run budgets");
2275
2453
  const timeoutMs = options?.timeout ?? this.#timeoutMs;
2276
2454
  const timeout = timeoutMs === void 0 ? void 0 : (0, _orkestrel_timeout.createTimeout)({ ms: timeoutMs });
2277
2455
  timeout?.start();
2278
2456
  const budget = options?.budget ?? this.#budget;
2279
2457
  budget?.start();
2280
2458
  const limit = options?.limit ?? this.#limit;
2281
- const abort = (0, _orkestrel_abort.createAbort)({ signal: this.#parents(timeout, budget, options?.signal) });
2459
+ const signal = this.#parents(timeout, budget, options?.signal);
2460
+ const abort = (0, _orkestrel_abort.createAbort)(signal === void 0 ? {} : { signal });
2282
2461
  this.#runs.add(abort);
2283
2462
  this.#status = "running";
2284
2463
  this.#emitter.emit("start", this.id);
2285
- const outcome = {
2464
+ const state = { outcome: {
2286
2465
  content: "",
2287
2466
  thinking: void 0,
2288
2467
  usage: void 0,
2289
2468
  partial: false,
2290
2469
  exhausted: false
2291
- };
2470
+ } };
2292
2471
  const channel = new Channel();
2293
2472
  const settled = (0, _orkestrel_workflow.createDeferred)();
2294
- this.#pump(abort, outcome, timeout, channel, settled, options?.think, options?.schema, limit, budget);
2473
+ this.#pump(abort, state, timeout, channel, settled, options?.think, options?.schema, limit, budget);
2295
2474
  settled.promise.catch(() => {});
2296
2475
  return {
2297
2476
  events: this.#events(channel, abort),
2298
2477
  result: settled.promise,
2299
- abort: (reason) => {
2300
- abort.abort(reason);
2301
- }
2478
+ abort: abort.abort.bind(abort)
2302
2479
  };
2303
2480
  }
2304
2481
  abort(reason) {
2305
2482
  for (const abort of [...this.#runs]) abort.abort(reason);
2306
2483
  }
2307
- async #pump(abort, outcome, timeout, channel, settled, think, schema, limit, budget) {
2484
+ async #pump(abort, state, timeout, channel, settled, think, schema, limit, budget) {
2308
2485
  let failure;
2309
2486
  try {
2310
- for await (const chunk of this.#run(abort, outcome, think, schema, limit, budget)) channel.push(chunk);
2487
+ for await (const chunk of this.#run(abort, state, think, schema, limit, budget)) channel.push(chunk);
2311
2488
  } catch (error) {
2312
2489
  failure = { error };
2313
2490
  } finally {
2314
2491
  timeout?.clear();
2315
2492
  this.#runs.delete(abort);
2316
2493
  if (failure === void 0) {
2494
+ const outcome = state.outcome;
2317
2495
  this.#status = "done";
2318
2496
  channel.close();
2319
2497
  const result = this.#result(outcome);
@@ -2336,7 +2514,7 @@ var Agent = class {
2336
2514
  abort.abort();
2337
2515
  }
2338
2516
  }
2339
- async *#run(abort, outcome, think, schema, limit, budget) {
2517
+ async *#run(abort, state, think, schema, limit, budget) {
2340
2518
  const messages = [...this.#context.build(this.#provider.format)];
2341
2519
  const tools = this.#context.tools;
2342
2520
  let content = "";
@@ -2344,7 +2522,7 @@ var Agent = class {
2344
2522
  let usage;
2345
2523
  let pending = false;
2346
2524
  let broke = false;
2347
- const compaction = { futile: false };
2525
+ const compaction = { state: { futile: false } };
2348
2526
  const compacting = this.#window !== void 0 && this.#context.conversations.active?.summarizable === true;
2349
2527
  if (compacting) {
2350
2528
  this.#window?.clear();
@@ -2356,14 +2534,20 @@ var Agent = class {
2356
2534
  await this.#scheduler?.yield({ signal: abort.signal });
2357
2535
  } catch (error) {
2358
2536
  if (abort.signal.aborted) {
2359
- outcome.partial = true;
2537
+ state.outcome = {
2538
+ ...state.outcome,
2539
+ partial: true
2540
+ };
2360
2541
  broke = true;
2361
2542
  break;
2362
2543
  }
2363
2544
  throw error;
2364
2545
  }
2365
2546
  if (abort.signal.aborted) {
2366
- outcome.partial = true;
2547
+ state.outcome = {
2548
+ ...state.outcome,
2549
+ partial: true
2550
+ };
2367
2551
  broke = true;
2368
2552
  break;
2369
2553
  }
@@ -2391,7 +2575,7 @@ var Agent = class {
2391
2575
  if (isProviderAbortError(error)) {
2392
2576
  if (error.partial.thinking !== void 0) thinking = this.#thought(thinking, error.partial.thinking);
2393
2577
  if (error.partial.usage !== void 0) {
2394
- const abortUsage = error.partial.usage;
2578
+ const abortUsage = sanitizeUsage(error.partial.usage);
2395
2579
  budget?.consume({
2396
2580
  prompt: abortUsage.prompt,
2397
2581
  completion: Math.max(0, abortUsage.completion - charged),
@@ -2400,7 +2584,10 @@ var Agent = class {
2400
2584
  usage = this.#sum(usage, abortUsage);
2401
2585
  }
2402
2586
  }
2403
- outcome.partial = true;
2587
+ state.outcome = {
2588
+ ...state.outcome,
2589
+ partial: true
2590
+ };
2404
2591
  broke = true;
2405
2592
  break;
2406
2593
  }
@@ -2408,16 +2595,17 @@ var Agent = class {
2408
2595
  }
2409
2596
  if (result.thinking !== void 0 && result.thinking.length > 0) thinking = this.#thought(thinking, result.thinking);
2410
2597
  if (result.usage !== void 0) {
2598
+ const resultUsage = sanitizeUsage(result.usage);
2411
2599
  budget?.consume({
2412
- prompt: result.usage.prompt,
2413
- completion: Math.max(0, result.usage.completion - charged),
2414
- total: Math.max(0, result.usage.total - charged)
2600
+ prompt: resultUsage.prompt,
2601
+ completion: Math.max(0, resultUsage.completion - charged),
2602
+ total: Math.max(0, resultUsage.total - charged)
2415
2603
  });
2416
- usage = this.#sum(usage, result.usage);
2417
- this.#emitter.emit("usage", result.usage);
2604
+ usage = this.#sum(usage, resultUsage);
2605
+ this.#emitter.emit("usage", resultUsage);
2418
2606
  yield {
2419
2607
  type: "usage",
2420
- usage: result.usage
2608
+ usage: resultUsage
2421
2609
  };
2422
2610
  }
2423
2611
  if (result.tools !== void 0 && result.tools.length > 0) {
@@ -2457,17 +2645,21 @@ var Agent = class {
2457
2645
  broke = true;
2458
2646
  break;
2459
2647
  }
2460
- if (!broke && pending) {
2461
- outcome.partial = true;
2462
- outcome.exhausted = !abort.signal.aborted;
2463
- }
2464
- outcome.content = content;
2465
- outcome.thinking = thinking;
2466
- outcome.usage = usage;
2648
+ if (!broke && pending) state.outcome = {
2649
+ ...state.outcome,
2650
+ partial: true,
2651
+ exhausted: !abort.signal.aborted
2652
+ };
2653
+ state.outcome = {
2654
+ ...state.outcome,
2655
+ content,
2656
+ thinking,
2657
+ usage
2658
+ };
2467
2659
  }
2468
2660
  async #trim(messages, compaction, latchFutile) {
2469
2661
  const conversation = this.#context.conversations.active;
2470
- if (this.#window === void 0 || conversation?.summarizable !== true || compaction.futile) return;
2662
+ if (this.#window === void 0 || conversation?.summarizable !== true || compaction.state.futile) return;
2471
2663
  this.#window.clear();
2472
2664
  this.#window.consume(messages);
2473
2665
  if (!this.#window.exhausted) return;
@@ -2476,10 +2668,11 @@ var Agent = class {
2476
2668
  section = await conversation.compact();
2477
2669
  } catch (error) {
2478
2670
  this.#emitter.emit("compactError", error);
2671
+ if (this.#strict) throw error;
2479
2672
  return;
2480
2673
  }
2481
2674
  if (section === void 0) {
2482
- if (latchFutile) compaction.futile = true;
2675
+ if (latchFutile) compaction.state = { futile: true };
2483
2676
  return;
2484
2677
  }
2485
2678
  messages.splice(0, messages.length, ...this.#context.build(this.#provider.format));
@@ -2641,16 +2834,17 @@ var AgentRegistry = class {
2641
2834
  return agent;
2642
2835
  }
2643
2836
  #options(input, signal) {
2837
+ const budget = this.#budget(input.budget);
2644
2838
  return {
2645
- system: input.system,
2646
2839
  tools: this.#manager(input.tools),
2647
- limit: input.limit,
2648
- timeout: input.timeout,
2649
- budget: this.#budget(input.budget),
2650
- authority: input.authority === void 0 ? void 0 : this.authority(input.authority),
2651
- scheduler: input.scheduler === void 0 ? void 0 : this.scheduler(input.scheduler),
2652
- conversations: this.#store === void 0 ? void 0 : new ConversationManager({ store: this.#store }),
2653
- signal
2840
+ ...input.system === void 0 ? {} : { system: input.system },
2841
+ ...input.limit === void 0 ? {} : { limit: input.limit },
2842
+ ...input.timeout === void 0 ? {} : { timeout: input.timeout },
2843
+ ...budget === void 0 ? {} : { budget },
2844
+ ...input.authority === void 0 ? {} : { authority: this.authority(input.authority) },
2845
+ ...input.scheduler === void 0 ? {} : { scheduler: this.scheduler(input.scheduler) },
2846
+ ...this.#store === void 0 ? {} : { conversations: new ConversationManager({ store: this.#store }) },
2847
+ ...signal === void 0 ? {} : { signal }
2654
2848
  };
2655
2849
  }
2656
2850
  #manager(names) {
@@ -2716,7 +2910,7 @@ var Authority = class {
2716
2910
  for (const rule of this.#rules) if (rule.match(context)) return {
2717
2911
  zone: rule.zone,
2718
2912
  allowed: rule.allowed ?? true,
2719
- reason: rule.reason
2913
+ ...rule.reason === void 0 ? {} : { reason: rule.reason }
2720
2914
  };
2721
2915
  return this.#fallback;
2722
2916
  }
@@ -2895,16 +3089,19 @@ var Scope = class Scope {
2895
3089
  files;
2896
3090
  constructor(input) {
2897
3091
  this.name = input.name;
2898
- this.instructions = input.instructions === void 0 ? void 0 : [...input.instructions];
2899
- this.tools = input.tools === void 0 ? void 0 : [...input.tools];
2900
- this.files = input.files === void 0 ? void 0 : [...input.files];
3092
+ if (input.instructions !== void 0) this.instructions = [...input.instructions];
3093
+ if (input.tools !== void 0) this.tools = [...input.tools];
3094
+ if (input.files !== void 0) this.files = [...input.files];
2901
3095
  }
2902
3096
  narrow(config) {
3097
+ const instructions = Scope.#intersect(this.instructions, config.instructions);
3098
+ const tools = Scope.#intersect(this.tools, config.tools);
3099
+ const files = Scope.#intersect(this.files, config.files);
2903
3100
  return new Scope({
2904
3101
  name: this.name,
2905
- instructions: Scope.#intersect(this.instructions, config.instructions),
2906
- tools: Scope.#intersect(this.tools, config.tools),
2907
- files: Scope.#intersect(this.files, config.files)
3102
+ ...instructions === void 0 ? {} : { instructions },
3103
+ ...tools === void 0 ? {} : { tools },
3104
+ ...files === void 0 ? {} : { files }
2908
3105
  });
2909
3106
  }
2910
3107
  static #intersect(parent, child) {
@@ -2948,8 +3145,8 @@ var ScopeManager = class {
2948
3145
  #emitter;
2949
3146
  constructor(on, error) {
2950
3147
  this.#emitter = new _orkestrel_emitter.Emitter({
2951
- on,
2952
- error
3148
+ ...on === void 0 ? {} : { on },
3149
+ ...error === void 0 ? {} : { error }
2953
3150
  });
2954
3151
  }
2955
3152
  get emitter() {
@@ -3139,9 +3336,9 @@ var Tool = class {
3139
3336
  #execute;
3140
3337
  constructor(options) {
3141
3338
  this.name = options.name;
3142
- this.description = options.description;
3143
- this.summary = options.summary;
3144
- this.parameters = options.parameters;
3339
+ if (options.description !== void 0) this.description = options.description;
3340
+ if (options.summary !== void 0) this.summary = options.summary;
3341
+ if (options.parameters !== void 0) this.parameters = options.parameters;
3145
3342
  this.#execute = options.execute;
3146
3343
  }
3147
3344
  execute(args) {
@@ -3799,11 +3996,11 @@ function createAgentRegistry(options) {
3799
3996
  function createAgentQueue(options) {
3800
3997
  const { registry, allowPartial = false, concurrency, retries, timeout, store } = options;
3801
3998
  return (0, _orkestrel_queue.createQueue)({
3802
- concurrency,
3803
- retries,
3804
- timeout,
3805
- store,
3806
- handler: (input, execution) => settleAgentJob(registry.build(input, execution.signal), allowPartial)
3999
+ ...concurrency === void 0 ? {} : { concurrency },
4000
+ ...retries === void 0 ? {} : { retries },
4001
+ ...timeout === void 0 ? {} : { timeout },
4002
+ ...store === void 0 ? {} : { store },
4003
+ handler: handleAgentQueueJob.bind(void 0, registry, allowPartial)
3807
4004
  });
3808
4005
  }
3809
4006
  /**
@@ -3845,14 +4042,10 @@ function createAgentQueue(options) {
3845
4042
  function createAgentRunner(options) {
3846
4043
  const { registry, allowPartial = false, concurrency, retries, timeout } = options;
3847
4044
  return (0, _orkestrel_workflow.createRunner)({
3848
- concurrency,
3849
- retries,
3850
- timeout,
3851
- handler: (controller) => {
3852
- const children = controller.input.children;
3853
- if (children !== void 0) for (const child of children) controller.spawn(child);
3854
- return settleAgentJob(registry.build(controller.input, controller.signal), allowPartial);
3855
- }
4045
+ ...concurrency === void 0 ? {} : { concurrency },
4046
+ ...retries === void 0 ? {} : { retries },
4047
+ ...timeout === void 0 ? {} : { timeout },
4048
+ handler: handleAgentRunnerJob.bind(void 0, registry, allowPartial)
3856
4049
  });
3857
4050
  }
3858
4051
  /**
@@ -4064,6 +4257,7 @@ function createWorkspaceManager(options) {
4064
4257
  //#endregion
4065
4258
  exports.Agent = Agent;
4066
4259
  exports.AgentContext = AgentContext;
4260
+ exports.AgentError = AgentError;
4067
4261
  exports.AgentJobError = AgentJobError;
4068
4262
  exports.AgentRegistry = AgentRegistry;
4069
4263
  exports.Authority = Authority;
@@ -4078,8 +4272,10 @@ exports.DEFAULT_CONVERSATION_KEEP = DEFAULT_CONVERSATION_KEEP;
4078
4272
  exports.DatabaseConversationStore = DatabaseConversationStore;
4079
4273
  exports.DatabaseWorkspaceStore = DatabaseWorkspaceStore;
4080
4274
  exports.EXTENSION_TO_LANGUAGE = EXTENSION_TO_LANGUAGE;
4275
+ exports.IMAGE_TOKEN_ESTIMATE = IMAGE_TOKEN_ESTIMATE;
4081
4276
  exports.Instruction = Instruction;
4082
4277
  exports.InstructionManager = InstructionManager;
4278
+ exports.MESSAGE_TOKEN_OVERHEAD = MESSAGE_TOKEN_OVERHEAD;
4083
4279
  exports.MemoryConversationStore = MemoryConversationStore;
4084
4280
  exports.MemoryWorkspaceStore = MemoryWorkspaceStore;
4085
4281
  exports.ProviderAbortError = ProviderAbortError;
@@ -4129,7 +4325,10 @@ exports.estimateMessages = estimateMessages;
4129
4325
  exports.estimateTokens = estimateTokens;
4130
4326
  exports.fencedFile = fencedFile;
4131
4327
  exports.filterAllowList = filterAllowList;
4328
+ exports.handleAgentQueueJob = handleAgentQueueJob;
4329
+ exports.handleAgentRunnerJob = handleAgentRunnerJob;
4132
4330
  exports.inferLanguage = inferLanguage;
4331
+ exports.isAgentError = isAgentError;
4133
4332
  exports.isAgentJobError = isAgentJobError;
4134
4333
  exports.isBinary = isBinary;
4135
4334
  exports.isConversationError = isConversationError;
@@ -4146,6 +4345,8 @@ exports.isWorkspaceError = isWorkspaceError;
4146
4345
  exports.isWorkspaceSnapshot = isWorkspaceSnapshot;
4147
4346
  exports.offsetAt = offsetAt;
4148
4347
  exports.rangeOf = rangeOf;
4348
+ exports.sanitizeToken = sanitizeToken;
4349
+ exports.sanitizeUsage = sanitizeUsage;
4149
4350
  exports.settleAgentJob = settleAgentJob;
4150
4351
  exports.sliceRange = sliceRange;
4151
4352
  exports.spliceRange = spliceRange;