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