norn-cli 3.3.0 → 4.0.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.
Files changed (58) hide show
  1. package/dist/cli.js +1374 -260
  2. package/dist/knowledgeIndexWorker.js +68 -22
  3. package/package.json +3 -3
  4. package/schemas/norn.config.schema.json +13 -0
  5. package/CLAUDE.md +0 -69
  6. package/NOW.md +0 -223
  7. package/demos/agent-workbench/README.md +0 -156
  8. package/demos/agent-workbench/agents.nornagent +0 -87
  9. package/demos/agent-workbench/contracts/domain-answer.schema.json +0 -28
  10. package/demos/agent-workbench/contracts/domain-question.schema.json +0 -34
  11. package/demos/agent-workbench/contracts/router-verdict.schema.json +0 -43
  12. package/demos/agent-workbench/contracts/ticket.schema.json +0 -29
  13. package/demos/agent-workbench/contracts/verdict.schema.json +0 -48
  14. package/demos/agent-workbench/fake-openai-server.js +0 -128
  15. package/demos/agent-workbench/fixtures/aligned-ticket.json +0 -10
  16. package/demos/agent-workbench/fixtures/cross-domain-ticket.json +0 -10
  17. package/demos/agent-workbench/fixtures/invalid-output-ticket.json +0 -8
  18. package/demos/agent-workbench/norn.config.json +0 -15
  19. package/demos/agent-workbench/prompts/ticket-router.md +0 -11
  20. package/demos/agent-workbench/workbench.norn +0 -32
  21. package/demos/knowledge-editor/README.md +0 -74
  22. package/demos/knowledge-editor/agents.nornagent +0 -16
  23. package/demos/knowledge-editor/api.nornapi +0 -10
  24. package/demos/knowledge-editor/handbook/ops/flow.svg +0 -1
  25. package/demos/knowledge-editor/handbook/ops/retired.md +0 -9
  26. package/demos/knowledge-editor/handbook/ops/runbook.md +0 -14
  27. package/demos/knowledge-editor/handbook/payments/charges.md +0 -25
  28. package/demos/knowledge-editor/handbook/payments/pricing-tiers.draft.md +0 -7
  29. package/demos/knowledge-editor/handbook/payments/refunds/policy.md +0 -12
  30. package/demos/knowledge-editor/support.norn +0 -10
  31. package/demos/mcp-ticket-testing/README.md +0 -114
  32. package/demos/mcp-ticket-testing/agents.nornagent +0 -77
  33. package/demos/mcp-ticket-testing/contracts/test-run.schema.json +0 -31
  34. package/demos/mcp-ticket-testing/expectations/proj-142.md +0 -12
  35. package/demos/mcp-ticket-testing/fixtures/proj-142.json +0 -13
  36. package/demos/mcp-ticket-testing/prompts/backend-tester.md +0 -12
  37. package/demos/mcp-ticket-testing/prompts/frontend-tester.md +0 -14
  38. package/demos/mcp-ticket-testing/prompts/reporter.md +0 -10
  39. package/demos/mcp-ticket-testing/servers/browser-server.js +0 -133
  40. package/demos/mcp-ticket-testing/servers/house-server.js +0 -125
  41. package/demos/mcp-ticket-testing/tickets.norn +0 -32
  42. package/demos/nornenv-region-refactor/README.md +0 -64
  43. package/demos/nornenv-showcase/README.md +0 -62
  44. package/demos/nornenv-showcase/norn.config.json +0 -16
  45. package/demos/nornenv-showcase/showcase.norn +0 -70
  46. package/demos/nornenv-showcase/showcase.nornapi +0 -26
  47. package/demos/nornenv-showcase/showcase.nornsql +0 -20
  48. package/demos/tests-showcase/01-single-requests.norn +0 -31
  49. package/demos/tests-showcase/02-sequences.norn +0 -54
  50. package/demos/tests-showcase/03-sidecars.norn +0 -42
  51. package/demos/tests-showcase/04-api-plus-sql.norn +0 -27
  52. package/demos/tests-showcase/db/testDb.nornsql +0 -12
  53. package/demos/tests-showcase/demo-api.nornapi +0 -17
  54. package/demos/tests-showcase/norn.config.json +0 -16
  55. package/playground/ai.norn +0 -15
  56. package/playground/ai_orchastration.nornagent +0 -34
  57. package/playground/knowedge_base/ACDC/testing_notes_new_clients.md +0 -11
  58. package/playground/knowedge_base/nexus_system_prompt.md +0 -1
package/dist/cli.js CHANGED
@@ -101513,10 +101513,10 @@ var init_values = __esm({
101513
101513
  });
101514
101514
 
101515
101515
  // node_modules/@anthropic-ai/sdk/internal/utils/sleep.mjs
101516
- var sleep2;
101516
+ var sleep;
101517
101517
  var init_sleep = __esm({
101518
101518
  "node_modules/@anthropic-ai/sdk/internal/utils/sleep.mjs"() {
101519
- sleep2 = (ms, signal) => new Promise((resolve28) => {
101519
+ sleep = (ms, signal) => new Promise((resolve28) => {
101520
101520
  if (signal?.aborted)
101521
101521
  return resolve28();
101522
101522
  const onAbort = () => {
@@ -106070,14 +106070,14 @@ var init_poller = __esm({
106070
106070
  const wait = applyJitter(backoff2(attempt));
106071
106071
  log.warn("poll failed, backing off", { error: String(e2), backoff_ms: wait });
106072
106072
  attempt++;
106073
- await sleep2(wait, __classPrivateFieldGet2(this, _WorkPoller_controller, "f").signal);
106073
+ await sleep(wait, __classPrivateFieldGet2(this, _WorkPoller_controller, "f").signal);
106074
106074
  continue;
106075
106075
  }
106076
106076
  attempt = 0;
106077
106077
  if (work == null) {
106078
106078
  if (__classPrivateFieldGet2(this, _WorkPoller_drain, "f"))
106079
106079
  return;
106080
- await sleep2(jitter(1e3, 3e3), __classPrivateFieldGet2(this, _WorkPoller_controller, "f").signal);
106080
+ await sleep(jitter(1e3, 3e3), __classPrivateFieldGet2(this, _WorkPoller_controller, "f").signal);
106081
106081
  continue;
106082
106082
  }
106083
106083
  log.info("claimed work", {
@@ -106470,7 +106470,7 @@ var init_SessionToolRunner = __esm({
106470
106470
  });
106471
106471
  }
106472
106472
  ctrl.signal.throwIfAborted();
106473
- await sleep2(backoff3, ctrl.signal);
106473
+ await sleep(backoff3, ctrl.signal);
106474
106474
  backoff3 = Math.min(backoff3 * 2, STREAM_BACKOFF_CAP_MS);
106475
106475
  }
106476
106476
  }, _SessionToolRunner_reconcile = /**
@@ -106710,7 +106710,7 @@ var init_SessionToolRunner = __esm({
106710
106710
  if (isFatal4xx(e2))
106711
106711
  break;
106712
106712
  if (i2 < SEND_RETRIES - 1)
106713
- await sleep2((i2 + 1) * 1e3, ctrl.signal);
106713
+ await sleep((i2 + 1) * 1e3, ctrl.signal);
106714
106714
  }
106715
106715
  }
106716
106716
  __classPrivateFieldGet2(this, _SessionToolRunner_logger, "f").error("failed to send tool result", {
@@ -106722,7 +106722,7 @@ var init_SessionToolRunner = __esm({
106722
106722
  async function _SessionToolRunner_drain2() {
106723
106723
  if (__classPrivateFieldGet2(this, _SessionToolRunner_inFlightCount, "f") === 0)
106724
106724
  return;
106725
- await Promise.race([new Promise((r2) => __classPrivateFieldSet2(this, _SessionToolRunner_onIdle, r2, "f")), sleep2(DRAIN_TIMEOUT_MS)]);
106725
+ await Promise.race([new Promise((r2) => __classPrivateFieldSet2(this, _SessionToolRunner_onIdle, r2, "f")), sleep(DRAIN_TIMEOUT_MS)]);
106726
106726
  __classPrivateFieldSet2(this, _SessionToolRunner_onIdle, null, "f");
106727
106727
  if (__classPrivateFieldGet2(this, _SessionToolRunner_inFlightCount, "f") > 0) {
106728
106728
  __classPrivateFieldGet2(this, _SessionToolRunner_logger, "f").warn("drain timeout exceeded");
@@ -107698,7 +107698,7 @@ async function heartbeatLoop(client, work, ctrl, logger, requestOptions) {
107698
107698
  };
107699
107699
  await beat();
107700
107700
  while (!ctrl.signal.aborted) {
107701
- await sleep2(intervalMs, ctrl.signal);
107701
+ await sleep(intervalMs, ctrl.signal);
107702
107702
  ctrl.signal.throwIfAborted();
107703
107703
  await beat();
107704
107704
  }
@@ -113627,7 +113627,7 @@ var init_client = __esm({
113627
113627
  const maxRetries = options.maxRetries ?? this.maxRetries;
113628
113628
  timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries);
113629
113629
  }
113630
- await sleep2(timeoutMillis);
113630
+ await sleep(timeoutMillis);
113631
113631
  return this.makeRequest(options, retriesRemaining - 1, requestLogID);
113632
113632
  }
113633
113633
  calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries) {
@@ -149218,7 +149218,7 @@ async function uploadBlobInternal(file2, uploadUrl, apiClient, httpOptions) {
149218
149218
  break;
149219
149219
  }
149220
149220
  retryCount++;
149221
- await sleep3(currentDelayMs);
149221
+ await sleep2(currentDelayMs);
149222
149222
  currentDelayMs = currentDelayMs * DELAY_MULTIPLIER;
149223
149223
  }
149224
149224
  offset += chunkSize;
@@ -149235,7 +149235,7 @@ async function getBlobStat(file2) {
149235
149235
  const fileStat = { size: file2.size, type: file2.type };
149236
149236
  return fileStat;
149237
149237
  }
149238
- function sleep3(ms) {
149238
+ function sleep2(ms) {
149239
149239
  return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
149240
149240
  }
149241
149241
  function resolveCloudFlag(options) {
@@ -156759,7 +156759,7 @@ var init_node2 = __esm({
156759
156759
  break;
156760
156760
  }
156761
156761
  retryCount++;
156762
- await sleep3(currentDelayMs);
156762
+ await sleep2(currentDelayMs);
156763
156763
  currentDelayMs = currentDelayMs * DELAY_MULTIPLIER;
156764
156764
  }
156765
156765
  offset += bytesRead;
@@ -158648,11 +158648,20 @@ function readAgentPromptBlock(lines, openingLineNumber, opening, stripTrailingCo
158648
158648
  };
158649
158649
  }
158650
158650
 
158651
+ // src/agents/agentTypes.ts
158652
+ var AGENT_REASONING_EFFORTS = ["none", "low", "medium", "high", "xhigh", "max"];
158653
+ var DEFAULT_AGENT_MAX_TOKENS = 16e3;
158654
+ var DEFAULT_LOCAL_AGENT_MAX_TOKENS = 4096;
158655
+ var DEFAULT_AGENT_MAX_DEPTH = 5;
158656
+ var DEFAULT_AGENT_MAX_INVOCATIONS = 25;
158657
+ var DEFAULT_AGENT_MAX_TURNS = 100;
158658
+ var DEFAULT_AGENT_CONTRACT_RETRIES = 2;
158659
+
158651
158660
  // src/agents/nornAgentParser.ts
158652
158661
  var IDENTIFIER = "[a-zA-Z_][a-zA-Z0-9_]*";
158653
- var MODEL_PROVIDERS = ["anthropic", "google", "local", "openai"];
158662
+ var MODEL_PROVIDERS = ["anthropic", "google", "local", "openai", "openai_compatible"];
158654
158663
  var MODEL_PROVIDER = new RegExp(`^(${MODEL_PROVIDERS.join("|")})$`, "i");
158655
- var MODEL_DIRECTIVES = ["provider", "name", "apiKey", "baseUrl"];
158664
+ var MODEL_DIRECTIVES = ["provider", "name", "reasoning", "apiKey", "baseUrl"];
158656
158665
  var MCP_DIRECTIVES = {
158657
158666
  transport: { transport: "both", repeatable: false },
158658
158667
  command: { transport: "stdio", repeatable: false },
@@ -158691,6 +158700,7 @@ function splitHeaderDirective(value) {
158691
158700
  }
158692
158701
  var LIMIT_DIRECTIVES = {
158693
158702
  max_tokens: "maxTokens",
158703
+ max_input_tokens: "maxInputTokens",
158694
158704
  max_depth: "maxDepth",
158695
158705
  max_invocations: "maxInvocations",
158696
158706
  max_turns: "maxTurns",
@@ -159125,6 +159135,23 @@ end mcp
159125
159135
  ));
159126
159136
  model.invalid = true;
159127
159137
  }
159138
+ const reasoning = model.values.reasoning;
159139
+ if (reasoning && !reasoning.value.includes("{{") && !AGENT_REASONING_EFFORTS.some((effort) => effort === reasoning.value.toLowerCase())) {
159140
+ errors.push(parseError(
159141
+ reasoning.lineNumber,
159142
+ `Unknown reasoning effort '${reasoning.value}' in model '${model.alias}'. Expected one of: ${AGENT_REASONING_EFFORTS.join(", ")}.`,
159143
+ "agent-model-reasoning-invalid"
159144
+ ));
159145
+ model.invalid = true;
159146
+ }
159147
+ if (reasoning && provider && !provider.value.includes("{{") && provider.value.toLowerCase() !== "openai") {
159148
+ errors.push(parseError(
159149
+ reasoning.lineNumber,
159150
+ `Model '${model.alias}' can declare reasoning only with provider openai. Provider '${provider.value}' does not support this directive yet.`,
159151
+ "agent-model-reasoning-provider"
159152
+ ));
159153
+ model.invalid = true;
159154
+ }
159128
159155
  if (model.invalid || !provider || !name) {
159129
159156
  return;
159130
159157
  }
@@ -159133,6 +159160,7 @@ end mcp
159133
159160
  providerTemplate: provider.value,
159134
159161
  nameTemplate: name.value,
159135
159162
  targetTemplate: `${provider.value}/${name.value}`,
159163
+ ...reasoning ? { reasoningTemplate: reasoning.value } : {},
159136
159164
  ...model.values.apiKey ? { apiKeyTemplate: model.values.apiKey.value } : {},
159137
159165
  ...model.values.baseUrl ? { baseUrlTemplate: model.values.baseUrl.value } : {},
159138
159166
  lineNumber: model.lineNumber,
@@ -159172,6 +159200,13 @@ end mcp
159172
159200
  "agent-model-alias-unknown"
159173
159201
  ));
159174
159202
  pending.invalid = true;
159203
+ } else if (model.alias !== pending.modelAlias.value) {
159204
+ errors.push(parseError(
159205
+ pending.modelAlias.lineNumber,
159206
+ `Model alias '${pending.modelAlias.value}' in agent '${pending.name}' does not match the declared alias '${model.alias}'. Model aliases are case-sensitive.`,
159207
+ "agent-model-alias-case-mismatch"
159208
+ ));
159209
+ pending.invalid = true;
159175
159210
  }
159176
159211
  }
159177
159212
  }
@@ -159207,6 +159242,7 @@ end mcp
159207
159242
  accepts: pending.accepts,
159208
159243
  returns: pending.returns,
159209
159244
  maxTokens: pending.maxTokens?.value,
159245
+ maxInputTokens: pending.maxInputTokens?.value,
159210
159246
  maxDepth: pending.maxDepth?.value,
159211
159247
  maxInvocations: pending.maxInvocations?.value,
159212
159248
  maxTurns: pending.maxTurns?.value,
@@ -159611,29 +159647,38 @@ end model`,
159611
159647
  }
159612
159648
  const knowledgeGrantMatch = trimmed.match(/^knowledge(?:\s+(.*))?$/i);
159613
159649
  if (knowledgeGrantMatch) {
159614
- const alias = (knowledgeGrantMatch[1] ?? "").trim();
159615
- if (!new RegExp(`^${IDENTIFIER}$`).test(alias)) {
159650
+ const rawValue = (knowledgeGrantMatch[1] ?? "").trim();
159651
+ const aliases = rawValue ? rawValue.split(",").map((part) => part.trim()) : [];
159652
+ if (aliases.length === 0 || aliases.some((alias) => !new RegExp(`^${IDENTIFIER}$`).test(alias))) {
159616
159653
  errors.push(parseError(
159617
159654
  index,
159618
- `Invalid knowledge grant '${trimmed}' in agent '${pending.name}'. Expected: knowledge <Alias>.`,
159655
+ `Invalid knowledge grant '${trimmed}' in agent '${pending.name}'. Expected: knowledge <Alias>[, <Alias>\u2026].`,
159619
159656
  "agent-knowledge-grant-invalid"
159620
159657
  ));
159621
159658
  pending.invalid = true;
159622
- } else if (pending.knowledge.some((grant) => grant.alias.toLowerCase() === alias.toLowerCase())) {
159623
- errors.push(parseError(
159624
- index,
159625
- `Agent '${pending.name}' grants knowledge '${alias}' more than once.`,
159626
- "agent-knowledge-grant-duplicate"
159627
- ));
159628
- pending.invalid = true;
159629
- } else if (!knowledgeBases.has(alias.toLowerCase())) {
159630
- errors.push(parseError(
159631
- index,
159632
- `Unknown knowledge alias '${alias}' in agent '${pending.name}'.`,
159633
- "agent-knowledge-alias-unknown"
159634
- ));
159635
- pending.invalid = true;
159636
- } else {
159659
+ continue;
159660
+ }
159661
+ const granted = new Set(pending.knowledge.map((grant) => grant.alias.toLowerCase()));
159662
+ for (const alias of aliases) {
159663
+ if (granted.has(alias.toLowerCase())) {
159664
+ errors.push(parseError(
159665
+ index,
159666
+ `Agent '${pending.name}' grants knowledge '${alias}' more than once.`,
159667
+ "agent-knowledge-grant-duplicate"
159668
+ ));
159669
+ pending.invalid = true;
159670
+ continue;
159671
+ }
159672
+ if (!knowledgeBases.has(alias.toLowerCase())) {
159673
+ errors.push(parseError(
159674
+ index,
159675
+ `Unknown knowledge alias '${alias}' in agent '${pending.name}'.`,
159676
+ "agent-knowledge-alias-unknown"
159677
+ ));
159678
+ pending.invalid = true;
159679
+ continue;
159680
+ }
159681
+ granted.add(alias.toLowerCase());
159637
159682
  pending.knowledge.push({ alias, lineNumber: index });
159638
159683
  }
159639
159684
  continue;
@@ -159948,6 +159993,7 @@ function isNonNegativeInteger(value) {
159948
159993
  var AGENT_LIMIT_CONFIG_KEYS = /* @__PURE__ */ new Set([
159949
159994
  "_comment",
159950
159995
  "max_tokens",
159996
+ "max_input_tokens",
159951
159997
  "max_depth",
159952
159998
  "max_invocations",
159953
159999
  "max_turns",
@@ -159957,7 +160003,7 @@ function hasOnlyKeys(value, allowedKeys) {
159957
160003
  return Object.keys(value).every((key) => allowedKeys.has(key));
159958
160004
  }
159959
160005
  function hasValidNornAgentLimits(value) {
159960
- return (value.max_tokens === void 0 || isPositiveInteger(value.max_tokens)) && (value.max_depth === void 0 || isPositiveInteger(value.max_depth)) && (value.max_invocations === void 0 || isPositiveInteger(value.max_invocations)) && (value.max_turns === void 0 || isPositiveInteger(value.max_turns)) && (value.contract_retries === void 0 || isNonNegativeInteger(value.contract_retries));
160006
+ return (value.max_tokens === void 0 || isPositiveInteger(value.max_tokens)) && (value.max_input_tokens === void 0 || isPositiveInteger(value.max_input_tokens)) && (value.max_depth === void 0 || isPositiveInteger(value.max_depth)) && (value.max_invocations === void 0 || isPositiveInteger(value.max_invocations)) && (value.max_turns === void 0 || isPositiveInteger(value.max_turns)) && (value.contract_retries === void 0 || isNonNegativeInteger(value.contract_retries));
159961
160007
  }
159962
160008
  function isNornAgentProviderConfig(value) {
159963
160009
  if (!isObjectRecord(value)) {
@@ -159977,7 +160023,13 @@ function isNornAgentRecordingConfig(value) {
159977
160023
  }
159978
160024
  return hasOnlyKeys(value, /* @__PURE__ */ new Set(["_comment", "enabled"])) && (value.enabled === void 0 || typeof value.enabled === "boolean");
159979
160025
  }
159980
- var AGENT_PROVIDER_NAMES = /* @__PURE__ */ new Set(["openai", "anthropic", "google", "local"]);
160026
+ var AGENT_PROVIDER_NAMES = /* @__PURE__ */ new Set([
160027
+ "openai",
160028
+ "openai_compatible",
160029
+ "anthropic",
160030
+ "google",
160031
+ "local"
160032
+ ]);
159981
160033
  function isNornAgentsConfig(value) {
159982
160034
  if (value === void 0) {
159983
160035
  return true;
@@ -166666,6 +166718,68 @@ function encodeFormUrlEncodedBody(body) {
166666
166718
  };
166667
166719
  }
166668
166720
 
166721
+ // src/sequenceCancellation.ts
166722
+ var SEQUENCE_CANCELLED_MESSAGE = "Sequence run cancelled by user.";
166723
+ var SequenceCancelledError = class extends Error {
166724
+ constructor() {
166725
+ super(SEQUENCE_CANCELLED_MESSAGE);
166726
+ this.name = "SequenceCancelledError";
166727
+ }
166728
+ };
166729
+ function throwIfSequenceCancelled(signal) {
166730
+ if (signal?.aborted) {
166731
+ throw new SequenceCancelledError();
166732
+ }
166733
+ }
166734
+ function raceWithSequenceCancellation(operation, signal) {
166735
+ if (!signal) {
166736
+ return operation;
166737
+ }
166738
+ throwIfSequenceCancelled(signal);
166739
+ return new Promise((resolve28, reject) => {
166740
+ const cancel = () => {
166741
+ signal.removeEventListener("abort", cancel);
166742
+ reject(new SequenceCancelledError());
166743
+ };
166744
+ signal.addEventListener("abort", cancel, { once: true });
166745
+ if (signal.aborted) {
166746
+ cancel();
166747
+ return;
166748
+ }
166749
+ operation.then(
166750
+ (value) => {
166751
+ signal.removeEventListener("abort", cancel);
166752
+ resolve28(value);
166753
+ },
166754
+ (error2) => {
166755
+ signal.removeEventListener("abort", cancel);
166756
+ reject(error2);
166757
+ }
166758
+ );
166759
+ });
166760
+ }
166761
+ function cancellableDelay(ms, signal) {
166762
+ if (!signal) {
166763
+ return new Promise((resolve28) => setTimeout(resolve28, ms));
166764
+ }
166765
+ throwIfSequenceCancelled(signal);
166766
+ return new Promise((resolve28, reject) => {
166767
+ const timer = setTimeout(() => {
166768
+ signal.removeEventListener("abort", cancel);
166769
+ resolve28();
166770
+ }, ms);
166771
+ const cancel = () => {
166772
+ clearTimeout(timer);
166773
+ signal.removeEventListener("abort", cancel);
166774
+ reject(new SequenceCancelledError());
166775
+ };
166776
+ signal.addEventListener("abort", cancel, { once: true });
166777
+ if (signal.aborted) {
166778
+ cancel();
166779
+ }
166780
+ });
166781
+ }
166782
+
166669
166783
  // src/httpClient.ts
166670
166784
  var sharedCookieJar = new CookieJar();
166671
166785
  var insecureHttpsAgent = new https2.Agent({ rejectUnauthorized: false });
@@ -166688,11 +166802,8 @@ async function getAllCookies(jar) {
166688
166802
  secure: Boolean(c.secure)
166689
166803
  }));
166690
166804
  }
166691
- async function sendRequest(request, retryOptions) {
166692
- return sendRequestWithJar(request, sharedCookieJar, retryOptions);
166693
- }
166694
- function sleep(ms) {
166695
- return new Promise((resolve28) => setTimeout(resolve28, ms));
166805
+ async function sendRequest(request, retryOptions, signal) {
166806
+ return sendRequestWithJar(request, sharedCookieJar, retryOptions, signal);
166696
166807
  }
166697
166808
  function shouldRetry(response, error2) {
166698
166809
  if (error2) {
@@ -166703,13 +166814,14 @@ function shouldRetry(response, error2) {
166703
166814
  }
166704
166815
  return false;
166705
166816
  }
166706
- async function sendRequestWithJar(request, jar, retryOptions) {
166817
+ async function sendRequestWithJar(request, jar, retryOptions, signal) {
166707
166818
  const totalAttempts = (retryOptions?.retryCount ?? 0) + 1;
166708
166819
  const backoffMs = retryOptions?.backoffMs ?? 1e3;
166709
166820
  const retriedErrors = [];
166710
166821
  let lastError = null;
166711
166822
  let attemptsMade = 0;
166712
166823
  for (let attempt = 1; attempt <= totalAttempts; attempt++) {
166824
+ throwIfSequenceCancelled(signal);
166713
166825
  attemptsMade = attempt;
166714
166826
  const startTime = Date.now();
166715
166827
  const maxRedirects = 10;
@@ -166722,6 +166834,7 @@ async function sendRequestWithJar(request, jar, retryOptions) {
166722
166834
  let sentRequestHeaders = {};
166723
166835
  try {
166724
166836
  while (redirectCount <= maxRedirects) {
166837
+ throwIfSequenceCancelled(signal);
166725
166838
  const existingCookies = await jar.getCookies(currentUrl);
166726
166839
  const cookieHeader = existingCookies.map((c) => `${c.key}=${c.value}`).join("; ");
166727
166840
  const headers = { ...request.headers };
@@ -166776,7 +166889,8 @@ async function sendRequestWithJar(request, jar, retryOptions) {
166776
166889
  timeout: getRequestTimeoutMs(),
166777
166890
  maxRedirects: 0,
166778
166891
  validateStatus: () => true,
166779
- httpsAgent: getHttpsAgent()
166892
+ httpsAgent: getHttpsAgent(),
166893
+ signal
166780
166894
  });
166781
166895
  const setCookieHeader = response.headers["set-cookie"];
166782
166896
  if (setCookieHeader) {
@@ -166811,7 +166925,7 @@ async function sendRequestWithJar(request, jar, retryOptions) {
166811
166925
  if (retryOptions?.onRetry) {
166812
166926
  retryOptions.onRetry(attempt, totalAttempts, errorMessage, waitMs);
166813
166927
  }
166814
- await sleep(waitMs);
166928
+ await cancellableDelay(waitMs, signal);
166815
166929
  continue;
166816
166930
  }
166817
166931
  const cookies = await getAllCookies(jar);
@@ -166834,6 +166948,9 @@ async function sendRequestWithJar(request, jar, retryOptions) {
166834
166948
  }
166835
166949
  return result;
166836
166950
  } catch (error2) {
166951
+ if (signal?.aborted) {
166952
+ throw new SequenceCancelledError();
166953
+ }
166837
166954
  const errorMessage = error2.message || String(error2);
166838
166955
  const effectiveUrl = currentUrl || request.url;
166839
166956
  const isUrlError = /invalid url/i.test(errorMessage) || /unsupported protocol/i.test(errorMessage);
@@ -166876,7 +166993,7 @@ URL used: ${effectiveUrl}`);
166876
166993
  if (retryOptions?.onRetry) {
166877
166994
  retryOptions.onRetry(attempt, totalAttempts, errorMessage, waitMs);
166878
166995
  }
166879
- await sleep(waitMs);
166996
+ await cancellableDelay(waitMs, signal);
166880
166997
  continue;
166881
166998
  }
166882
166999
  throw lastError;
@@ -167080,7 +167197,8 @@ function isRunCommand(line2) {
167080
167197
  const trimmed = line2.trim();
167081
167198
  return /^(var\s+[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*)?run\s+(bash|powershell|pwsh|js)\s+/i.test(trimmed);
167082
167199
  }
167083
- async function runScript(type, scriptPath, args, workingDir, variables = {}, captureVar) {
167200
+ async function runScript(type, scriptPath, args, workingDir, variables = {}, captureVar, signal) {
167201
+ throwIfSequenceCancelled(signal);
167084
167202
  const startTime = Date.now();
167085
167203
  const resolvedPath = path5.isAbsolute(scriptPath) ? scriptPath : path5.resolve(workingDir, scriptPath);
167086
167204
  let command;
@@ -167111,7 +167229,7 @@ async function runScript(type, scriptPath, args, workingDir, variables = {}, cap
167111
167229
  captureVar
167112
167230
  };
167113
167231
  }
167114
- return new Promise((resolve28) => {
167232
+ return new Promise((resolve28, reject) => {
167115
167233
  const env4 = {
167116
167234
  ...process.env,
167117
167235
  // Pass variables as environment variables with NORN_ prefix
@@ -167128,6 +167246,28 @@ async function runScript(type, scriptPath, args, workingDir, variables = {}, cap
167128
167246
  });
167129
167247
  let stdout = "";
167130
167248
  let stderr = "";
167249
+ let settled = false;
167250
+ const cancel = () => {
167251
+ if (settled) {
167252
+ return;
167253
+ }
167254
+ settled = true;
167255
+ signal?.removeEventListener("abort", cancel);
167256
+ child.kill("SIGTERM");
167257
+ reject(new SequenceCancelledError());
167258
+ };
167259
+ signal?.addEventListener("abort", cancel, { once: true });
167260
+ if (signal?.aborted) {
167261
+ cancel();
167262
+ }
167263
+ const finish = (result) => {
167264
+ if (settled) {
167265
+ return;
167266
+ }
167267
+ settled = true;
167268
+ signal?.removeEventListener("abort", cancel);
167269
+ resolve28(result);
167270
+ };
167131
167271
  child.stdout.on("data", (data) => {
167132
167272
  stdout += data.toString();
167133
167273
  });
@@ -167135,7 +167275,7 @@ async function runScript(type, scriptPath, args, workingDir, variables = {}, cap
167135
167275
  stderr += data.toString();
167136
167276
  });
167137
167277
  child.on("error", (err) => {
167138
- resolve28({
167278
+ finish({
167139
167279
  success: false,
167140
167280
  output: cleanScriptOutput(stdout),
167141
167281
  error: `Failed to execute script: ${err.message}`,
@@ -167149,7 +167289,7 @@ async function runScript(type, scriptPath, args, workingDir, variables = {}, cap
167149
167289
  child.on("close", (code) => {
167150
167290
  const exitCode = code ?? 0;
167151
167291
  const cleanedOutput = cleanScriptOutput(stdout);
167152
- resolve28({
167292
+ finish({
167153
167293
  success: exitCode === 0,
167154
167294
  output: cleanedOutput,
167155
167295
  error: stderr.trim(),
@@ -168730,7 +168870,8 @@ function buildAdapterError(message, stderr, stdout) {
168730
168870
  }
168731
168871
  return new Error(details.join("\n"));
168732
168872
  }
168733
- async function runExternalSqlAdapter(command, request, workingDir) {
168873
+ async function runExternalSqlAdapter(command, request, workingDir, signal) {
168874
+ throwIfSequenceCancelled(signal);
168734
168875
  if (!command.length) {
168735
168876
  throw new Error("SQL adapter command is empty");
168736
168877
  }
@@ -168743,11 +168884,25 @@ async function runExternalSqlAdapter(command, request, workingDir) {
168743
168884
  let stdout = "";
168744
168885
  let stderr = "";
168745
168886
  let settled = false;
168887
+ const cancel = () => {
168888
+ if (settled) {
168889
+ return;
168890
+ }
168891
+ settled = true;
168892
+ signal?.removeEventListener("abort", cancel);
168893
+ child.kill("SIGTERM");
168894
+ reject(new SequenceCancelledError());
168895
+ };
168896
+ signal?.addEventListener("abort", cancel, { once: true });
168897
+ if (signal?.aborted) {
168898
+ cancel();
168899
+ }
168746
168900
  const fail3 = (error2) => {
168747
168901
  if (settled) {
168748
168902
  return;
168749
168903
  }
168750
168904
  settled = true;
168905
+ signal?.removeEventListener("abort", cancel);
168751
168906
  reject(error2);
168752
168907
  };
168753
168908
  const succeed = (response) => {
@@ -168755,6 +168910,7 @@ async function runExternalSqlAdapter(command, request, workingDir) {
168755
168910
  return;
168756
168911
  }
168757
168912
  settled = true;
168913
+ signal?.removeEventListener("abort", cancel);
168758
168914
  resolve28(response);
168759
168915
  };
168760
168916
  child.stdout.on("data", (chunk) => {
@@ -168812,11 +168968,11 @@ async function runExternalSqlAdapter(command, request, workingDir) {
168812
168968
  }
168813
168969
  });
168814
168970
  }
168815
- async function runSqlAdapter(target, request, workingDir) {
168971
+ async function runSqlAdapter(target, request, workingDir, signal) {
168816
168972
  if (target.kind === "builtin") {
168817
- return runBuiltInSqlAdapter(target.adapterId, request);
168973
+ return raceWithSequenceCancellation(runBuiltInSqlAdapter(target.adapterId, request), signal);
168818
168974
  }
168819
- return runExternalSqlAdapter(target.command, request, workingDir);
168975
+ return runExternalSqlAdapter(target.command, request, workingDir, signal);
168820
168976
  }
168821
168977
 
168822
168978
  // src/mcpClient.ts
@@ -168831,10 +168987,10 @@ var util3;
168831
168987
  function assertIs2(_arg) {
168832
168988
  }
168833
168989
  util4.assertIs = assertIs2;
168834
- function assertNever5(_x) {
168990
+ function assertNever6(_x) {
168835
168991
  throw new Error();
168836
168992
  }
168837
- util4.assertNever = assertNever5;
168993
+ util4.assertNever = assertNever6;
168838
168994
  util4.arrayToEnum = (items) => {
168839
168995
  const obj = {};
168840
168996
  for (const item of items) {
@@ -186229,15 +186385,155 @@ function describeUnmetExpectations(verdict) {
186229
186385
 
186230
186386
  // src/agents/providers/providerUtils.ts
186231
186387
  var import_crypto6 = require("crypto");
186388
+
186389
+ // src/agents/toolResultContext.ts
186390
+ var MAX_INLINE_TOOL_RESULT_TOKENS = 2e4;
186391
+ var MAX_INLINE_TOOL_RESULT_CHARACTERS = MAX_INLINE_TOOL_RESULT_TOKENS * 4;
186392
+ var MAX_RETAINED_INLINE_TOOL_RESULTS = 3;
186393
+ var TOOL_RESULT_PREVIEW_CHARACTERS = 2e3;
186394
+ var TOOL_RESULT_READ_CHARACTERS = 12e3;
186395
+ function outputToText(output2) {
186396
+ if (typeof output2 === "string") {
186397
+ return output2;
186398
+ }
186399
+ if (output2 === void 0) {
186400
+ return "";
186401
+ }
186402
+ try {
186403
+ return JSON.stringify(output2);
186404
+ } catch {
186405
+ return String(output2);
186406
+ }
186407
+ }
186408
+ function referenceText(input2) {
186409
+ return JSON.stringify({
186410
+ nornToolResult: {
186411
+ status: input2.released ? "released_from_context" : "offloaded",
186412
+ handle: input2.handle,
186413
+ tool: input2.toolName,
186414
+ originalCharacters: input2.originalCharacters,
186415
+ estimatedTokens: input2.estimatedTokens,
186416
+ ...input2.preview !== void 0 ? { preview: input2.preview } : {},
186417
+ instruction: `Use norn_read_tool_result with handle '${input2.handle}' to read or search bounded portions.`
186418
+ }
186419
+ });
186420
+ }
186421
+ var PreparedAgentToolResult = class {
186422
+ constructor(recordedOutput, immediateOutput, immediateText, historyPlaceholder) {
186423
+ this.recordedOutput = recordedOutput;
186424
+ this.immediateOutput = immediateOutput;
186425
+ this.immediateText = immediateText;
186426
+ this.historyPlaceholder = historyPlaceholder;
186427
+ }
186428
+ };
186429
+ var AgentToolResultStore = class {
186430
+ results = /* @__PURE__ */ new Map();
186431
+ nextHandle = 1;
186432
+ prepare(toolName2, output2) {
186433
+ const text = outputToText(output2);
186434
+ const handle = `tool-result-${this.nextHandle++}`;
186435
+ const estimatedTokens = Math.ceil(text.length / 4);
186436
+ const stored = { handle, toolName: toolName2, text, estimatedTokens };
186437
+ this.results.set(handle, stored);
186438
+ const historyPlaceholder = referenceText({
186439
+ handle,
186440
+ toolName: toolName2,
186441
+ originalCharacters: text.length,
186442
+ estimatedTokens,
186443
+ released: true
186444
+ });
186445
+ if (text.length <= MAX_INLINE_TOOL_RESULT_CHARACTERS) {
186446
+ return new PreparedAgentToolResult(output2, output2, text, historyPlaceholder);
186447
+ }
186448
+ const offloadedReference = referenceText({
186449
+ handle,
186450
+ toolName: toolName2,
186451
+ originalCharacters: text.length,
186452
+ estimatedTokens,
186453
+ preview: text.slice(0, TOOL_RESULT_PREVIEW_CHARACTERS),
186454
+ released: false
186455
+ });
186456
+ return new PreparedAgentToolResult(output2, offloadedReference, offloadedReference, historyPlaceholder);
186457
+ }
186458
+ read(input2) {
186459
+ const handle = typeof input2.handle === "string" ? input2.handle : "";
186460
+ const stored = this.results.get(handle);
186461
+ if (!stored) {
186462
+ return { error: `Unknown or expired tool-result handle '${handle}'.` };
186463
+ }
186464
+ const query = typeof input2.query === "string" ? input2.query.trim() : "";
186465
+ if (query) {
186466
+ return this.search(stored, query);
186467
+ }
186468
+ const requestedOffset = typeof input2.offset === "number" && Number.isSafeInteger(input2.offset) ? input2.offset : 0;
186469
+ const offset = Math.max(0, Math.min(requestedOffset, stored.text.length));
186470
+ const text = stored.text.slice(offset, offset + TOOL_RESULT_READ_CHARACTERS);
186471
+ return {
186472
+ handle,
186473
+ tool: stored.toolName,
186474
+ offset,
186475
+ returnedCharacters: text.length,
186476
+ totalCharacters: stored.text.length,
186477
+ hasMore: offset + text.length < stored.text.length,
186478
+ text
186479
+ };
186480
+ }
186481
+ search(stored, query) {
186482
+ const haystack = stored.text.toLocaleLowerCase();
186483
+ const needle = query.toLocaleLowerCase();
186484
+ const matches = [];
186485
+ let from = 0;
186486
+ while (matches.length < 5) {
186487
+ const index = haystack.indexOf(needle, from);
186488
+ if (index < 0) {
186489
+ break;
186490
+ }
186491
+ const start = Math.max(0, index - 300);
186492
+ const end = Math.min(stored.text.length, index + query.length + 500);
186493
+ matches.push({ offset: start, text: stored.text.slice(start, end) });
186494
+ from = Math.max(index + needle.length, index + 1);
186495
+ }
186496
+ return {
186497
+ handle: stored.handle,
186498
+ tool: stored.toolName,
186499
+ query,
186500
+ totalCharacters: stored.text.length,
186501
+ matches
186502
+ };
186503
+ }
186504
+ };
186505
+ var AgentToolResultHistory = class {
186506
+ retained = [];
186507
+ retain(replaceWithReference) {
186508
+ this.retained.push(replaceWithReference);
186509
+ while (this.retained.length > MAX_RETAINED_INLINE_TOOL_RESULTS) {
186510
+ this.retained.shift()?.();
186511
+ }
186512
+ }
186513
+ };
186514
+ function normalizePreparedAgentToolResult(output2) {
186515
+ if (output2 instanceof PreparedAgentToolResult) {
186516
+ return output2;
186517
+ }
186518
+ return {
186519
+ recordedOutput: output2,
186520
+ immediateOutput: output2,
186521
+ immediateText: outputToText(output2),
186522
+ historyPlaceholder: "[Older tool result released from active context by Norn.]"
186523
+ };
186524
+ }
186525
+
186526
+ // src/agents/providers/providerUtils.ts
186232
186527
  var PROVIDER_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]{0,63}$/;
186233
186528
  var PROVIDER_NAME_MAX_LENGTH = 64;
186234
186529
  var AgentTurnLimitError = class extends Error {
186235
- constructor(provider, maxTurns, toolCallCount, lastToolName) {
186530
+ constructor(provider, maxTurns, toolCallCount, lastToolName, usage) {
186236
186531
  super(`${provider} tool loop reached its ${maxTurns}-turn limit before the model finished.`);
186237
186532
  this.provider = provider;
186238
186533
  this.maxTurns = maxTurns;
186239
186534
  this.toolCallCount = toolCallCount;
186240
186535
  this.lastToolName = lastToolName;
186536
+ this.usage = usage;
186241
186537
  this.name = "AgentTurnLimitError";
186242
186538
  }
186243
186539
  };
@@ -186310,19 +186606,6 @@ function agentInputToText(input2) {
186310
186606
  return String(input2);
186311
186607
  }
186312
186608
  }
186313
- function providerToolOutputToText(output2) {
186314
- if (typeof output2 === "string") {
186315
- return output2;
186316
- }
186317
- if (output2 === void 0) {
186318
- return "";
186319
- }
186320
- try {
186321
- return JSON.stringify(output2);
186322
- } catch {
186323
- return String(output2);
186324
- }
186325
- }
186326
186609
  function tryParseStructuredAgentOutput(text, hasSchema) {
186327
186610
  if (!hasSchema) {
186328
186611
  return void 0;
@@ -186405,7 +186688,70 @@ function createRecordedToolCall(records, name, input2, kind) {
186405
186688
 
186406
186689
  // src/agents/providers/anthropicAdapter.ts
186407
186690
  init_sdk();
186408
- init_json_schema();
186691
+
186692
+ // src/agents/agentTokenBudget.ts
186693
+ function estimateSerializedInputTokens(value) {
186694
+ let text;
186695
+ try {
186696
+ text = JSON.stringify(value) ?? "";
186697
+ } catch {
186698
+ text = String(value);
186699
+ }
186700
+ return Math.ceil(text.length / 3);
186701
+ }
186702
+ function formatAgentInputTokenLimitError(agentName, outcome) {
186703
+ const next = outcome.estimatedNextInputTokens === void 0 ? "" : ` The next request was estimated at ${outcome.estimatedNextInputTokens} input tokens.`;
186704
+ return `Agent '${agentName}' reached the top-level run's max_input_tokens budget (${outcome.usedInputTokens}/${outcome.maxInputTokens}) and stopped before continuing.${next} Raise max_input_tokens in ${outcome.sourcePath}:${outcome.lineNumber + 1}, or under 'agents' in norn.config.json.`;
186705
+ }
186706
+ function sumAgentTurnUsage(turns) {
186707
+ return {
186708
+ inputTokens: turns.reduce((total, turn) => total + turn.inputTokens, 0),
186709
+ outputTokens: turns.reduce((total, turn) => total + turn.outputTokens, 0),
186710
+ turns: turns.map((turn) => ({ ...turn }))
186711
+ };
186712
+ }
186713
+ var AgentInputTokenLimitError = class extends Error {
186714
+ constructor(provider, maxInputTokens, usedInputTokens, usage, estimatedNextInputTokens) {
186715
+ const reason = estimatedNextInputTokens === void 0 ? `after using ${usedInputTokens}` : `before an estimated ${estimatedNextInputTokens}-token request with ${usedInputTokens} already used`;
186716
+ super(`${provider} agent input budget ${maxInputTokens} was reached ${reason}.`);
186717
+ this.provider = provider;
186718
+ this.maxInputTokens = maxInputTokens;
186719
+ this.usedInputTokens = usedInputTokens;
186720
+ this.usage = usage;
186721
+ this.estimatedNextInputTokens = estimatedNextInputTokens;
186722
+ this.name = "AgentInputTokenLimitError";
186723
+ }
186724
+ };
186725
+ function assertInputBudgetAllowsCall(provider, budget, estimatedNextInputTokens, turns) {
186726
+ if (!budget || budget.usedInputTokens + estimatedNextInputTokens <= budget.maxInputTokens) {
186727
+ return;
186728
+ }
186729
+ throw new AgentInputTokenLimitError(
186730
+ provider,
186731
+ budget.maxInputTokens,
186732
+ budget.usedInputTokens,
186733
+ sumAgentTurnUsage(turns),
186734
+ estimatedNextInputTokens
186735
+ );
186736
+ }
186737
+ function recordAgentTurnUsage(provider, budget, turns, usage) {
186738
+ const turn = { turn: turns.length + 1, ...usage };
186739
+ turns.push(turn);
186740
+ if (!budget) {
186741
+ return;
186742
+ }
186743
+ budget.usedInputTokens += turn.inputTokens;
186744
+ if (budget.usedInputTokens > budget.maxInputTokens) {
186745
+ throw new AgentInputTokenLimitError(
186746
+ provider,
186747
+ budget.maxInputTokens,
186748
+ budget.usedInputTokens,
186749
+ sumAgentTurnUsage(turns)
186750
+ );
186751
+ }
186752
+ }
186753
+
186754
+ // src/agents/providers/anthropicAdapter.ts
186409
186755
  function supportsAdaptiveThinking(modelId) {
186410
186756
  return /^claude-(?:opus-(?:4-(?:[6-9]|\d{2,})|[5-9])|sonnet-(?:4-[6-9]|[5-9])|fable-[5-9]|mythos(?:-[5-9]|-preview))/i.test(modelId);
186411
186757
  }
@@ -186487,67 +186833,136 @@ var AnthropicAgentAdapter = class {
186487
186833
  })),
186488
186834
  inputMessage
186489
186835
  ];
186490
- let finalMessage;
186491
- let inputTokens = 0;
186492
- let outputTokens = 0;
186493
- if (request.tools.length > 0) {
186494
- const runnableTools = prepareAgentProviderTools(request.tools).map(({ tool, providerName }) => {
186495
- if (tool.inputSchema.type !== "object") {
186496
- throw new Error(`Tool '${tool.name}' must expose an object input schema.`);
186497
- }
186498
- return betaTool({
186499
- name: providerName,
186500
- description: tool.description || `Run ${tool.name}`,
186501
- inputSchema: tool.inputSchema,
186502
- run: async (rawInput) => {
186503
- const input2 = parseToolInput(rawInput);
186504
- const recorded = createRecordedToolCall(toolCalls, tool.name, input2, tool.kind);
186505
- try {
186506
- const output2 = await tool.execute(input2);
186507
- recorded.complete(output2);
186508
- return providerToolOutputToText(output2);
186509
- } catch (error2) {
186510
- const output2 = { error: error2 instanceof Error ? error2.message : String(error2) };
186511
- recorded.complete(output2);
186512
- if (error2 instanceof AgentToolFatalError) {
186513
- throw error2;
186514
- }
186515
- return providerToolOutputToText(output2);
186516
- }
186517
- }
186518
- });
186519
- });
186520
- const runner = client.beta.messages.toolRunner({
186836
+ const turns = [];
186837
+ if (request.tools.length === 0) {
186838
+ const call = {
186521
186839
  model: request.modelId,
186522
186840
  max_tokens: request.maxTokens,
186523
186841
  system: request.systemPrompt,
186524
186842
  messages,
186525
- tools: runnableTools,
186526
- tool_choice: { type: "auto", disable_parallel_tool_use: true },
186527
186843
  output_config: outputConfig,
186528
- thinking,
186529
- max_iterations: request.maxTurns,
186530
- stream: false
186844
+ thinking
186845
+ };
186846
+ assertInputBudgetAllowsCall(
186847
+ this.provider,
186848
+ request.inputTokenBudget,
186849
+ estimateSerializedInputTokens(call),
186850
+ turns
186851
+ );
186852
+ const finalMessage = await client.messages.create(
186853
+ call,
186854
+ request.signal ? { signal: request.signal } : void 0
186855
+ );
186856
+ recordAgentTurnUsage(this.provider, request.inputTokenBudget, turns, {
186857
+ inputTokens: finalMessage.usage.input_tokens + (finalMessage.usage.cache_creation_input_tokens ?? 0) + (finalMessage.usage.cache_read_input_tokens ?? 0),
186858
+ outputTokens: finalMessage.usage.output_tokens,
186859
+ ...typeof finalMessage.usage.cache_read_input_tokens === "number" ? { cachedInputTokens: finalMessage.usage.cache_read_input_tokens } : {},
186860
+ ...typeof finalMessage.usage.cache_creation_input_tokens === "number" ? { cacheWriteInputTokens: finalMessage.usage.cache_creation_input_tokens } : {}
186531
186861
  });
186532
- let latest;
186533
- for await (const message of runner) {
186534
- latest = message;
186535
- inputTokens += message.usage.input_tokens + (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0);
186536
- outputTokens += message.usage.output_tokens;
186862
+ return this.completeResponse(request, finalMessage, toolCalls, turns);
186863
+ }
186864
+ const preparedTools = prepareAgentProviderTools(request.tools);
186865
+ const betaMessages = messages;
186866
+ const toolsByProviderName = new Map(
186867
+ preparedTools.map((prepared) => [prepared.providerName, prepared.tool])
186868
+ );
186869
+ const tools = preparedTools.map(({ tool, providerName }) => {
186870
+ if (tool.inputSchema.type !== "object") {
186871
+ throw new Error(`Tool '${tool.name}' must expose an object input schema.`);
186537
186872
  }
186538
- finalMessage = latest ?? await runner.done();
186539
- } else {
186540
- finalMessage = await client.messages.create({
186873
+ return {
186874
+ name: providerName,
186875
+ description: tool.description || `Run ${tool.name}`,
186876
+ input_schema: tool.inputSchema
186877
+ };
186878
+ });
186879
+ const toolHistory = new AgentToolResultHistory();
186880
+ for (let turn = 1; turn <= request.maxTurns; turn++) {
186881
+ const call = {
186541
186882
  model: request.modelId,
186542
186883
  max_tokens: request.maxTokens,
186543
186884
  system: request.systemPrompt,
186544
- messages,
186885
+ messages: betaMessages,
186886
+ tools,
186887
+ tool_choice: { type: "auto", disable_parallel_tool_use: true },
186545
186888
  output_config: outputConfig,
186546
186889
  thinking
186890
+ };
186891
+ assertInputBudgetAllowsCall(
186892
+ this.provider,
186893
+ request.inputTokenBudget,
186894
+ estimateSerializedInputTokens(call),
186895
+ turns
186896
+ );
186897
+ const response = await client.beta.messages.create(
186898
+ call,
186899
+ request.signal ? { signal: request.signal } : void 0
186900
+ );
186901
+ recordAgentTurnUsage(this.provider, request.inputTokenBudget, turns, {
186902
+ inputTokens: response.usage.input_tokens + (response.usage.cache_creation_input_tokens ?? 0) + (response.usage.cache_read_input_tokens ?? 0),
186903
+ outputTokens: response.usage.output_tokens,
186904
+ ...typeof response.usage.cache_read_input_tokens === "number" ? { cachedInputTokens: response.usage.cache_read_input_tokens } : {},
186905
+ ...typeof response.usage.cache_creation_input_tokens === "number" ? { cacheWriteInputTokens: response.usage.cache_creation_input_tokens } : {}
186906
+ });
186907
+ const calls = response.content.filter((block) => block.type === "tool_use");
186908
+ if (calls.length === 0) {
186909
+ return this.completeResponse(request, response, toolCalls, turns);
186910
+ }
186911
+ if (turn >= request.maxTurns) {
186912
+ throw new AgentTurnLimitError(
186913
+ this.provider,
186914
+ request.maxTurns,
186915
+ toolCalls.length,
186916
+ toolCalls[toolCalls.length - 1]?.name,
186917
+ sumAgentTurnUsage(turns)
186918
+ );
186919
+ }
186920
+ betaMessages.push({
186921
+ role: "assistant",
186922
+ content: response.content
186547
186923
  });
186548
- inputTokens = finalMessage.usage.input_tokens + (finalMessage.usage.cache_creation_input_tokens ?? 0) + (finalMessage.usage.cache_read_input_tokens ?? 0);
186549
- outputTokens = finalMessage.usage.output_tokens;
186924
+ const resultBlocks = [];
186925
+ for (const callBlock of calls) {
186926
+ const tool = toolsByProviderName.get(callBlock.name);
186927
+ if (!tool) {
186928
+ throw new Error(`Anthropic requested unknown tool '${callBlock.name}'.`);
186929
+ }
186930
+ const input2 = parseToolInput(callBlock.input);
186931
+ const recorded = createRecordedToolCall(toolCalls, tool.name, input2, tool.kind);
186932
+ let preparedOutput;
186933
+ try {
186934
+ const output2 = await tool.execute(input2);
186935
+ preparedOutput = normalizePreparedAgentToolResult(output2);
186936
+ recorded.complete(preparedOutput.recordedOutput);
186937
+ } catch (error2) {
186938
+ const output2 = { error: error2 instanceof Error ? error2.message : String(error2) };
186939
+ recorded.complete(output2);
186940
+ if (error2 instanceof AgentToolFatalError) {
186941
+ throw error2;
186942
+ }
186943
+ preparedOutput = normalizePreparedAgentToolResult(output2);
186944
+ }
186945
+ const resultBlock = {
186946
+ type: "tool_result",
186947
+ tool_use_id: callBlock.id,
186948
+ content: preparedOutput.immediateText
186949
+ };
186950
+ resultBlocks.push(resultBlock);
186951
+ toolHistory.retain(() => {
186952
+ resultBlock.content = preparedOutput.historyPlaceholder;
186953
+ });
186954
+ }
186955
+ betaMessages.push({ role: "user", content: resultBlocks });
186550
186956
  }
186957
+ throw new AgentTurnLimitError(
186958
+ this.provider,
186959
+ request.maxTurns,
186960
+ toolCalls.length,
186961
+ toolCalls[toolCalls.length - 1]?.name,
186962
+ sumAgentTurnUsage(turns)
186963
+ );
186964
+ }
186965
+ completeResponse(request, finalMessage, toolCalls, turns) {
186551
186966
  const text = getVisibleText(finalMessage, Boolean(request.outputSchema));
186552
186967
  if (finalMessage.stop_reason === "refusal") {
186553
186968
  throw new AgentProviderRefusalError(`Anthropic model refused the request${text ? `: ${text}` : "."}`, text);
@@ -186560,7 +186975,8 @@ var AnthropicAgentAdapter = class {
186560
186975
  this.provider,
186561
186976
  request.maxTurns,
186562
186977
  toolCalls.length,
186563
- toolCalls[toolCalls.length - 1]?.name
186978
+ toolCalls[toolCalls.length - 1]?.name,
186979
+ sumAgentTurnUsage(turns)
186564
186980
  );
186565
186981
  }
186566
186982
  if (finalMessage.stop_reason === "model_context_window_exceeded") {
@@ -186573,7 +186989,7 @@ var AnthropicAgentAdapter = class {
186573
186989
  text,
186574
186990
  parsed: tryParseStructuredAgentOutput(text, Boolean(request.outputSchema)),
186575
186991
  toolCalls,
186576
- usage: { inputTokens, outputTokens },
186992
+ usage: sumAgentTurnUsage(turns),
186577
186993
  resolvedModelId: finalMessage.model || request.modelId
186578
186994
  };
186579
186995
  }
@@ -186759,6 +187175,7 @@ var GoogleAgentAdapter = class {
186759
187175
  const config2 = {
186760
187176
  systemInstruction: request.systemPrompt,
186761
187177
  maxOutputTokens: request.maxTokens,
187178
+ ...request.signal ? { abortSignal: request.signal } : {},
186762
187179
  ...request.outputSchema ? {
186763
187180
  responseMimeType: "application/json",
186764
187181
  responseJsonSchema: request.outputSchema
@@ -186777,18 +187194,28 @@ var GoogleAgentAdapter = class {
186777
187194
  automaticFunctionCalling: { disable: true }
186778
187195
  } : {}
186779
187196
  };
186780
- let inputTokens = 0;
186781
- let outputTokens = 0;
186782
187197
  let resolvedModelId2 = request.modelId;
187198
+ const turns = [];
187199
+ const toolHistory = new AgentToolResultHistory();
186783
187200
  for (let turn = 0; turn < request.maxTurns; turn++) {
187201
+ const estimatedInputTokens = estimateSerializedInputTokens({ contents, config: config2 });
187202
+ assertInputBudgetAllowsCall(
187203
+ this.provider,
187204
+ request.inputTokenBudget,
187205
+ estimatedInputTokens,
187206
+ turns
187207
+ );
186784
187208
  const response = await client.models.generateContent({
186785
187209
  model: request.modelId,
186786
187210
  contents,
186787
187211
  config: config2
186788
187212
  });
186789
187213
  const usage = response.usageMetadata;
186790
- inputTokens += (usage?.promptTokenCount ?? 0) + (usage?.toolUsePromptTokenCount ?? 0);
186791
- outputTokens += (usage?.candidatesTokenCount ?? 0) + (usage?.thoughtsTokenCount ?? 0);
187214
+ recordAgentTurnUsage(this.provider, request.inputTokenBudget, turns, {
187215
+ inputTokens: usage ? (usage.promptTokenCount ?? 0) + (usage.toolUsePromptTokenCount ?? 0) : estimatedInputTokens,
187216
+ outputTokens: (usage?.candidatesTokenCount ?? 0) + (usage?.thoughtsTokenCount ?? 0),
187217
+ ...usage?.cachedContentTokenCount !== void 0 ? { cachedInputTokens: usage.cachedContentTokenCount } : {}
187218
+ });
186792
187219
  resolvedModelId2 = response.modelVersion || resolvedModelId2;
186793
187220
  const requestedCalls = getFunctionCalls(response);
186794
187221
  const text = getVisibleText2(
@@ -186803,7 +187230,7 @@ var GoogleAgentAdapter = class {
186803
187230
  text,
186804
187231
  parsed: tryParseStructuredAgentOutput(text, Boolean(request.outputSchema)),
186805
187232
  toolCalls,
186806
- usage: { inputTokens, outputTokens },
187233
+ usage: sumAgentTurnUsage(turns),
186807
187234
  resolvedModelId: resolvedModelId2
186808
187235
  };
186809
187236
  }
@@ -186817,7 +187244,8 @@ var GoogleAgentAdapter = class {
186817
187244
  this.provider,
186818
187245
  request.maxTurns,
186819
187246
  toolCalls.length,
186820
- toolCalls[toolCalls.length - 1]?.name
187247
+ toolCalls[toolCalls.length - 1]?.name,
187248
+ sumAgentTurnUsage(turns)
186821
187249
  );
186822
187250
  }
186823
187251
  const modelContent = getCandidate(response)?.content;
@@ -186838,8 +187266,12 @@ var GoogleAgentAdapter = class {
186838
187266
  let responseBody;
186839
187267
  try {
186840
187268
  const output2 = await tool.execute(input2);
186841
- recorded.complete(output2);
186842
- responseBody = { output: output2 };
187269
+ const preparedOutput = normalizePreparedAgentToolResult(output2);
187270
+ recorded.complete(preparedOutput.recordedOutput);
187271
+ responseBody = { output: preparedOutput.immediateOutput };
187272
+ toolHistory.retain(() => {
187273
+ responseBody.output = preparedOutput.historyPlaceholder;
187274
+ });
186843
187275
  } catch (error2) {
186844
187276
  const message = error2 instanceof Error ? error2.message : String(error2);
186845
187277
  const output2 = { error: message };
@@ -186864,7 +187296,8 @@ var GoogleAgentAdapter = class {
186864
187296
  this.provider,
186865
187297
  request.maxTurns,
186866
187298
  toolCalls.length,
186867
- toolCalls[toolCalls.length - 1]?.name
187299
+ toolCalls[toolCalls.length - 1]?.name,
187300
+ sumAgentTurnUsage(turns)
186868
187301
  );
186869
187302
  }
186870
187303
  };
@@ -187108,7 +187541,7 @@ var safeJSON2 = (text) => {
187108
187541
  };
187109
187542
 
187110
187543
  // node_modules/openai/internal/utils/sleep.mjs
187111
- var sleep4 = (ms) => new Promise((resolve28) => setTimeout(resolve28, ms));
187544
+ var sleep3 = (ms) => new Promise((resolve28) => setTimeout(resolve28, ms));
187112
187545
 
187113
187546
  // node_modules/openai/version.mjs
187114
187547
  var VERSION4 = "6.49.0";
@@ -194427,7 +194860,7 @@ var Runs = class extends APIResource2 {
194427
194860
  }
194428
194861
  }
194429
194862
  }
194430
- await sleep4(sleepInterval);
194863
+ await sleep3(sleepInterval);
194431
194864
  break;
194432
194865
  //We return the run in any terminal state.
194433
194866
  case "requires_action":
@@ -195036,7 +195469,7 @@ var Files4 = class extends APIResource2 {
195036
195469
  const start = Date.now();
195037
195470
  let file2 = await this.retrieve(id);
195038
195471
  while (!file2.status || !TERMINAL_STATES.has(file2.status)) {
195039
- await sleep4(pollInterval);
195472
+ await sleep3(pollInterval);
195040
195473
  file2 = await this.retrieve(id);
195041
195474
  if (Date.now() - start > maxWait) {
195042
195475
  throw new APIConnectionTimeoutError3({
@@ -196732,7 +197165,7 @@ var FileBatches = class extends APIResource2 {
196732
197165
  }
196733
197166
  }
196734
197167
  }
196735
- await sleep4(sleepInterval);
197168
+ await sleep3(sleepInterval);
196736
197169
  break;
196737
197170
  case "failed":
196738
197171
  case "cancelled":
@@ -196872,7 +197305,7 @@ var Files5 = class extends APIResource2 {
196872
197305
  }
196873
197306
  }
196874
197307
  }
196875
- await sleep4(sleepInterval);
197308
+ await sleep3(sleepInterval);
196876
197309
  break;
196877
197310
  case "failed":
196878
197311
  case "completed":
@@ -197648,7 +198081,7 @@ var OpenAI = class {
197648
198081
  const maxRetries = options.maxRetries ?? this.maxRetries;
197649
198082
  timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries);
197650
198083
  }
197651
- await sleep4(timeoutMillis);
198084
+ await sleep3(timeoutMillis);
197652
198085
  return this.makeRequest(options, retriesRemaining - 1, requestLogID);
197653
198086
  }
197654
198087
  calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries) {
@@ -197816,6 +198249,93 @@ function isUndiciDispatcherVersionMismatchError(error2) {
197816
198249
  return false;
197817
198250
  }
197818
198251
 
198252
+ // node_modules/openai/lib/responses/ResponseInputItems.mjs
198253
+ function toResponseInputItems(items) {
198254
+ const inputItems = [];
198255
+ for (const item of items) {
198256
+ const inputItem = toResponseInputItem(item);
198257
+ if (inputItem) {
198258
+ inputItems.push(inputItem);
198259
+ }
198260
+ }
198261
+ return inputItems;
198262
+ }
198263
+ function toResponseInputItem(item) {
198264
+ switch (item.type) {
198265
+ case "additional_tools": {
198266
+ if (item.role !== "developer") {
198267
+ return null;
198268
+ }
198269
+ return stripCreatedBy(item);
198270
+ }
198271
+ case "shell_call_output": {
198272
+ const output2 = item.output.map((chunk) => stripCreatedBy(chunk));
198273
+ return {
198274
+ ...stripCreatedBy(item),
198275
+ output: output2
198276
+ };
198277
+ }
198278
+ case "computer_call_output": {
198279
+ const { created_by: _createdBy, ...withoutCreatedBy } = item;
198280
+ if (withoutCreatedBy.status === "failed") {
198281
+ return null;
198282
+ }
198283
+ return withoutCreatedBy;
198284
+ }
198285
+ case "custom_tool_call_output": {
198286
+ if ("status" in item && item.status !== "completed") {
198287
+ return null;
198288
+ }
198289
+ const { created_by: _createdBy, status: _status, ...inputItem } = item;
198290
+ return inputItem;
198291
+ }
198292
+ case "apply_patch_call":
198293
+ case "apply_patch_call_output":
198294
+ case "code_interpreter_call":
198295
+ case "compaction":
198296
+ case "compaction_trigger":
198297
+ case "computer_call":
198298
+ case "custom_tool_call":
198299
+ case "file_search_call":
198300
+ case "function_call":
198301
+ case "function_call_output":
198302
+ case "image_generation_call":
198303
+ case "item_reference":
198304
+ case "local_shell_call":
198305
+ case "local_shell_call_output":
198306
+ case "mcp_approval_request":
198307
+ case "mcp_approval_response":
198308
+ case "mcp_call":
198309
+ case "mcp_list_tools":
198310
+ case "message":
198311
+ case "program":
198312
+ case "program_output":
198313
+ case "reasoning":
198314
+ case "shell_call":
198315
+ case "tool_search_call":
198316
+ case "tool_search_output":
198317
+ case "web_search_call":
198318
+ case null:
198319
+ case void 0: {
198320
+ return stripCreatedBy(item);
198321
+ }
198322
+ default: {
198323
+ return assertNever5(item);
198324
+ }
198325
+ }
198326
+ }
198327
+ function stripCreatedBy(item) {
198328
+ if (!("created_by" in item)) {
198329
+ return item;
198330
+ }
198331
+ const { created_by: _createdBy, ...rest } = item;
198332
+ return rest;
198333
+ }
198334
+ function assertNever5(value) {
198335
+ const type = value.type;
198336
+ throw new TypeError(`Unsupported response item type: ${String(type)}`);
198337
+ }
198338
+
197819
198339
  // src/agents/providers/openaiAdapter.ts
197820
198340
  function parseToolArguments(raw) {
197821
198341
  const parsed = JSON.parse(raw);
@@ -197825,7 +198345,251 @@ function parseToolArguments(raw) {
197825
198345
  return parsed;
197826
198346
  }
197827
198347
  var OpenAIAgentAdapter = class {
197828
- constructor(provider = "openai") {
198348
+ provider = "openai";
198349
+ createClient(request) {
198350
+ const apiKey = request.credentials?.apiKey ?? getScopedEnvironmentValue(request.environment, "OPENAI_API_KEY");
198351
+ const baseURL = request.credentials?.baseUrl ?? getScopedEnvironmentValue(request.environment, "OPENAI_BASE_URL");
198352
+ try {
198353
+ return new OpenAI({
198354
+ ...apiKey ? { apiKey } : {},
198355
+ ...baseURL ? { baseURL } : {}
198356
+ });
198357
+ } catch (error2) {
198358
+ if (error2 instanceof Error && error2.message.includes("Missing credentials")) {
198359
+ throw new Error(
198360
+ "OpenAI authentication failed. Add an apiKey directive to the model block, for example: apiKey {{$env.OPENAI_API_KEY}}.",
198361
+ { cause: error2 }
198362
+ );
198363
+ }
198364
+ throw error2;
198365
+ }
198366
+ }
198367
+ async run(request) {
198368
+ if (request.provider !== this.provider) {
198369
+ throw new Error(`The openai provider adapter cannot run a ${request.provider} request.`);
198370
+ }
198371
+ try {
198372
+ return await this.runRequest(request);
198373
+ } catch (error2) {
198374
+ if (error2 instanceof AgentProviderRefusalError) {
198375
+ throw error2;
198376
+ }
198377
+ if (error2 instanceof OpenAI.APIError) {
198378
+ if (isAgentProviderMaxTokensRejection(error2)) {
198379
+ throw new AgentProviderMaxTokensError(
198380
+ this.provider,
198381
+ request.maxTokens,
198382
+ error2.message,
198383
+ error2
198384
+ );
198385
+ }
198386
+ const status = error2.status ? ` (HTTP ${error2.status})` : "";
198387
+ const requestId = error2.requestID ? ` [request ${error2.requestID}]` : "";
198388
+ throw new Error(`OpenAI API request failed${status}: ${error2.message}${requestId}`, {
198389
+ cause: error2
198390
+ });
198391
+ }
198392
+ throw error2;
198393
+ }
198394
+ }
198395
+ async runRequest(request) {
198396
+ const client = this.createClient(request);
198397
+ const toolCalls = [];
198398
+ const input2 = [
198399
+ ...(request.conversation ?? []).map((message) => ({
198400
+ type: "message",
198401
+ role: message.role,
198402
+ content: message.content
198403
+ })),
198404
+ {
198405
+ type: "message",
198406
+ role: "user",
198407
+ content: agentInputToText(request.input)
198408
+ }
198409
+ ];
198410
+ const preparedTools = prepareAgentProviderTools(request.tools);
198411
+ const tools = preparedTools.map(({ tool, providerName }) => ({
198412
+ type: "function",
198413
+ name: providerName,
198414
+ description: tool.description || `Run ${tool.name}`,
198415
+ parameters: tool.inputSchema,
198416
+ strict: false
198417
+ }));
198418
+ const toolsByProviderName = new Map(
198419
+ preparedTools.map((prepared) => [prepared.providerName, prepared])
198420
+ );
198421
+ const text = request.outputSchema ? {
198422
+ format: {
198423
+ type: "json_schema",
198424
+ name: toProviderSchemaName(request.outputSchemaName),
198425
+ schema: request.outputSchema,
198426
+ strict: true
198427
+ }
198428
+ } : void 0;
198429
+ let resolvedModelId2 = request.modelId;
198430
+ const turns = [];
198431
+ const toolHistory = new AgentToolResultHistory();
198432
+ for (let turn = 1; turn <= request.maxTurns; turn++) {
198433
+ const estimatedInputTokens = estimateSerializedInputTokens({
198434
+ instructions: request.systemPrompt,
198435
+ input: input2,
198436
+ tools,
198437
+ text
198438
+ });
198439
+ assertInputBudgetAllowsCall(
198440
+ this.provider,
198441
+ request.inputTokenBudget,
198442
+ estimatedInputTokens,
198443
+ turns
198444
+ );
198445
+ const response = await client.responses.create({
198446
+ model: request.modelId,
198447
+ instructions: request.systemPrompt,
198448
+ input: input2,
198449
+ max_output_tokens: request.maxTokens,
198450
+ store: false,
198451
+ parallel_tool_calls: false,
198452
+ ...request.reasoningEffort ? { reasoning: { effort: request.reasoningEffort } } : {},
198453
+ ...tools.length > 0 ? {
198454
+ tools,
198455
+ tool_choice: "auto",
198456
+ include: ["reasoning.encrypted_content"]
198457
+ } : {},
198458
+ ...text ? { text } : {}
198459
+ }, request.signal ? { signal: request.signal } : void 0);
198460
+ resolvedModelId2 = response.model || request.modelId;
198461
+ const turnInputTokens = response.usage?.input_tokens ?? estimatedInputTokens;
198462
+ const turnOutputTokens = response.usage?.output_tokens ?? 0;
198463
+ recordAgentTurnUsage(this.provider, request.inputTokenBudget, turns, {
198464
+ inputTokens: turnInputTokens,
198465
+ outputTokens: turnOutputTokens,
198466
+ ...response.usage?.input_tokens_details?.cached_tokens !== void 0 ? { cachedInputTokens: response.usage.input_tokens_details.cached_tokens } : {},
198467
+ ...response.usage?.input_tokens_details?.cache_write_tokens !== void 0 ? { cacheWriteInputTokens: response.usage.input_tokens_details.cache_write_tokens } : {}
198468
+ });
198469
+ const refusal = response.output.filter((item) => item.type === "message").flatMap((message) => message.content).find((content) => content.type === "refusal");
198470
+ if (refusal?.type === "refusal") {
198471
+ throw new AgentProviderRefusalError(
198472
+ `OpenAI model refused the request: ${refusal.refusal}`,
198473
+ response.output_text || refusal.refusal
198474
+ );
198475
+ }
198476
+ if (response.error) {
198477
+ throw new Error(`OpenAI response failed: ${response.error.message}`);
198478
+ }
198479
+ const calls = response.output.filter((item) => item.type === "function_call");
198480
+ if (calls.length === 0) {
198481
+ this.assertCompleted(response, request);
198482
+ const responseText = response.output_text || "";
198483
+ return {
198484
+ text: responseText,
198485
+ parsed: tryParseStructuredAgentOutput(responseText, Boolean(request.outputSchema)),
198486
+ toolCalls,
198487
+ usage: sumAgentTurnUsage(turns),
198488
+ resolvedModelId: resolvedModelId2
198489
+ };
198490
+ }
198491
+ if (turn >= request.maxTurns) {
198492
+ throw new AgentTurnLimitError(
198493
+ this.provider,
198494
+ request.maxTurns,
198495
+ toolCalls.length,
198496
+ toolCalls[toolCalls.length - 1]?.name,
198497
+ sumAgentTurnUsage(turns)
198498
+ );
198499
+ }
198500
+ input2.push(...toResponseInputItems(response.output));
198501
+ for (const call of calls) {
198502
+ const prepared = toolsByProviderName.get(call.name);
198503
+ if (!prepared) {
198504
+ throw new Error(`OpenAI requested unknown agent tool '${call.name}'.`);
198505
+ }
198506
+ let toolInput = {};
198507
+ let output2;
198508
+ try {
198509
+ toolInput = parseToolArguments(call.arguments);
198510
+ const recorded = createRecordedToolCall(
198511
+ toolCalls,
198512
+ prepared.tool.name,
198513
+ toolInput,
198514
+ prepared.tool.kind
198515
+ );
198516
+ try {
198517
+ output2 = await prepared.tool.execute(toolInput);
198518
+ recorded.complete(normalizePreparedAgentToolResult(output2).recordedOutput);
198519
+ } catch (error2) {
198520
+ output2 = { error: error2 instanceof Error ? error2.message : String(error2) };
198521
+ recorded.complete(output2);
198522
+ if (error2 instanceof AgentToolFatalError) {
198523
+ throw error2;
198524
+ }
198525
+ }
198526
+ } catch (error2) {
198527
+ if (error2 instanceof AgentToolFatalError) {
198528
+ throw error2;
198529
+ }
198530
+ output2 = { error: error2 instanceof Error ? error2.message : String(error2) };
198531
+ const recorded = createRecordedToolCall(
198532
+ toolCalls,
198533
+ prepared.tool.name,
198534
+ toolInput,
198535
+ prepared.tool.kind
198536
+ );
198537
+ recorded.complete(output2);
198538
+ }
198539
+ const preparedOutput = normalizePreparedAgentToolResult(output2);
198540
+ const outputItem = {
198541
+ type: "function_call_output",
198542
+ call_id: call.call_id,
198543
+ output: preparedOutput.immediateText
198544
+ };
198545
+ input2.push(outputItem);
198546
+ toolHistory.retain(() => {
198547
+ outputItem.output = preparedOutput.historyPlaceholder;
198548
+ });
198549
+ }
198550
+ }
198551
+ throw new AgentTurnLimitError(
198552
+ this.provider,
198553
+ request.maxTurns,
198554
+ toolCalls.length,
198555
+ toolCalls[toolCalls.length - 1]?.name,
198556
+ sumAgentTurnUsage(turns)
198557
+ );
198558
+ }
198559
+ assertCompleted(response, request) {
198560
+ if (response.status === "incomplete") {
198561
+ if (response.incomplete_details?.reason === "content_filter") {
198562
+ throw new AgentProviderRefusalError(
198563
+ "OpenAI blocked the response through its content filter.",
198564
+ response.output_text || ""
198565
+ );
198566
+ }
198567
+ if (response.incomplete_details?.reason === "max_output_tokens") {
198568
+ throw new Error(
198569
+ `OpenAI model reached its ${request.maxTokens}-token completion limit before finishing.`
198570
+ );
198571
+ }
198572
+ throw new Error("OpenAI returned an incomplete response.");
198573
+ }
198574
+ if (response.status === "failed" || response.status === "cancelled") {
198575
+ throw new Error(`OpenAI response ended with status '${response.status}'.`);
198576
+ }
198577
+ if (response.status === "in_progress" || response.status === "queued") {
198578
+ throw new Error(`OpenAI returned unexpected non-terminal status '${response.status}'.`);
198579
+ }
198580
+ }
198581
+ };
198582
+
198583
+ // src/agents/providers/openaiCompatibleAdapter.ts
198584
+ function parseToolArguments2(raw) {
198585
+ const parsed = JSON.parse(raw);
198586
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
198587
+ throw new Error("Tool input must be a JSON object.");
198588
+ }
198589
+ return parsed;
198590
+ }
198591
+ var OpenAICompatibleAgentAdapter = class {
198592
+ constructor(provider = "openai_compatible") {
197829
198593
  this.provider = provider;
197830
198594
  }
197831
198595
  createClient(request) {
@@ -197839,17 +198603,19 @@ var OpenAIAgentAdapter = class {
197839
198603
  const apiKey2 = request.credentials?.apiKey ?? getEnvironmentValue(request.environment, "LOCAL_API_KEY") ?? "norn-local";
197840
198604
  return new OpenAI({ apiKey: apiKey2, baseURL: baseURL2 });
197841
198605
  }
197842
- const apiKey = request.credentials?.apiKey ?? getScopedEnvironmentValue(request.environment, "OPENAI_API_KEY");
197843
- const baseURL = request.credentials?.baseUrl ?? getScopedEnvironmentValue(request.environment, "OPENAI_BASE_URL");
198606
+ const apiKey = request.credentials?.apiKey ?? getScopedEnvironmentValue(request.environment, "OPENAI_COMPATIBLE_API_KEY") ?? "norn-openai-compatible";
198607
+ const baseURL = request.credentials?.baseUrl ?? getScopedEnvironmentValue(request.environment, "OPENAI_COMPATIBLE_BASE_URL");
198608
+ if (!baseURL) {
198609
+ throw new Error(
198610
+ "An OpenAI-compatible model needs an endpoint. Add a baseUrl directive to the model block, or set OPENAI_COMPATIBLE_BASE_URL in .nornenv or the process environment."
198611
+ );
198612
+ }
197844
198613
  try {
197845
- return new OpenAI({
197846
- ...apiKey ? { apiKey } : {},
197847
- ...baseURL ? { baseURL } : {}
197848
- });
198614
+ return new OpenAI({ apiKey, baseURL });
197849
198615
  } catch (error2) {
197850
198616
  if (error2 instanceof Error && error2.message.includes("Missing credentials")) {
197851
198617
  throw new Error(
197852
- "OpenAI authentication failed. Add an apiKey directive to the model block, for example: apiKey {{$env.OPENAI_API_KEY}}.",
198618
+ "OpenAI-compatible authentication failed. Add an apiKey directive to the model block.",
197853
198619
  { cause: error2 }
197854
198620
  );
197855
198621
  }
@@ -197875,7 +198641,7 @@ var OpenAIAgentAdapter = class {
197875
198641
  error2
197876
198642
  );
197877
198643
  }
197878
- const providerLabel = this.provider === "local" ? "Local OpenAI-compatible" : "OpenAI";
198644
+ const providerLabel = this.provider === "local" ? "Local OpenAI-compatible" : "OpenAI-compatible";
197879
198645
  const status = error2.status ? ` (HTTP ${error2.status})` : "";
197880
198646
  const requestId = error2.requestID ? ` [request ${error2.requestID}]` : "";
197881
198647
  throw new Error(`${providerLabel} API request failed${status}: ${error2.message}${requestId}`, {
@@ -197904,82 +198670,128 @@ var OpenAIAgentAdapter = class {
197904
198670
  strict: true
197905
198671
  }
197906
198672
  } : void 0;
197907
- const tokenLimit = this.provider === "local" ? { max_tokens: request.maxTokens } : { max_completion_tokens: request.maxTokens };
197908
- let text = "";
198673
+ const tokenLimit = { max_tokens: request.maxTokens };
197909
198674
  let resolvedModelId2 = request.modelId;
197910
- let inputTokens = 0;
197911
- let outputTokens = 0;
197912
- if (request.tools.length > 0) {
197913
- const runnableTools = prepareAgentProviderTools(request.tools).map(({ tool, providerName }) => ({
197914
- type: "function",
197915
- function: {
197916
- name: providerName,
197917
- description: tool.description || `Run ${tool.name}`,
197918
- parameters: tool.inputSchema,
197919
- parse: parseToolArguments,
197920
- function: async (input2) => {
198675
+ const preparedTools = prepareAgentProviderTools(request.tools);
198676
+ const toolsByProviderName = new Map(
198677
+ preparedTools.map((prepared) => [prepared.providerName, prepared.tool])
198678
+ );
198679
+ const tools = preparedTools.map(({ tool, providerName }) => ({
198680
+ type: "function",
198681
+ function: {
198682
+ name: providerName,
198683
+ description: tool.description || `Run ${tool.name}`,
198684
+ parameters: tool.inputSchema
198685
+ }
198686
+ }));
198687
+ const turns = [];
198688
+ const toolHistory = new AgentToolResultHistory();
198689
+ for (let turn = 1; turn <= request.maxTurns; turn++) {
198690
+ const call = {
198691
+ model: request.modelId,
198692
+ messages,
198693
+ ...tools.length > 0 ? {
198694
+ tools,
198695
+ tool_choice: "auto",
198696
+ parallel_tool_calls: false
198697
+ } : {},
198698
+ response_format: responseFormat,
198699
+ ...tokenLimit
198700
+ };
198701
+ const estimatedInputTokens = estimateSerializedInputTokens(call);
198702
+ assertInputBudgetAllowsCall(
198703
+ this.provider,
198704
+ request.inputTokenBudget,
198705
+ estimatedInputTokens,
198706
+ turns
198707
+ );
198708
+ const completion = await client.chat.completions.create(
198709
+ call,
198710
+ request.signal ? { signal: request.signal } : void 0
198711
+ );
198712
+ const message = completion.choices[0]?.message;
198713
+ if (!message) {
198714
+ throw new Error("OpenAI returned no assistant message.");
198715
+ }
198716
+ resolvedModelId2 = completion.model || request.modelId;
198717
+ recordAgentTurnUsage(this.provider, request.inputTokenBudget, turns, {
198718
+ inputTokens: completion.usage?.prompt_tokens ?? estimatedInputTokens,
198719
+ outputTokens: completion.usage?.completion_tokens ?? 0,
198720
+ ...completion.usage?.prompt_tokens_details?.cached_tokens !== void 0 ? { cachedInputTokens: completion.usage.prompt_tokens_details.cached_tokens } : {}
198721
+ });
198722
+ const requestedCalls = message.tool_calls?.filter((call2) => call2.type === "function") ?? [];
198723
+ const text = message.content || "";
198724
+ if (requestedCalls.length === 0) {
198725
+ this.assertCompleted(message, completion.choices[0]?.finish_reason, text, request, toolCalls);
198726
+ return {
198727
+ text,
198728
+ parsed: tryParseStructuredAgentOutput(text, Boolean(request.outputSchema)),
198729
+ toolCalls,
198730
+ usage: sumAgentTurnUsage(turns),
198731
+ resolvedModelId: resolvedModelId2
198732
+ };
198733
+ }
198734
+ if (turn >= request.maxTurns) {
198735
+ throw new AgentTurnLimitError(
198736
+ this.provider,
198737
+ request.maxTurns,
198738
+ toolCalls.length,
198739
+ toolCalls[toolCalls.length - 1]?.name,
198740
+ sumAgentTurnUsage(turns)
198741
+ );
198742
+ }
198743
+ messages.push(message);
198744
+ for (const callItem of requestedCalls) {
198745
+ const tool = toolsByProviderName.get(callItem.function.name);
198746
+ let input2 = {};
198747
+ let preparedOutput;
198748
+ if (!tool) {
198749
+ preparedOutput = normalizePreparedAgentToolResult({
198750
+ error: `Unknown tool '${callItem.function.name}'.`
198751
+ });
198752
+ } else {
198753
+ try {
198754
+ input2 = parseToolArguments2(callItem.function.arguments);
197921
198755
  const recorded = createRecordedToolCall(toolCalls, tool.name, input2, tool.kind);
197922
198756
  try {
197923
198757
  const output2 = await tool.execute(input2);
197924
- recorded.complete(output2);
197925
- return providerToolOutputToText(output2);
198758
+ preparedOutput = normalizePreparedAgentToolResult(output2);
198759
+ recorded.complete(preparedOutput.recordedOutput);
197926
198760
  } catch (error2) {
197927
198761
  const output2 = { error: error2 instanceof Error ? error2.message : String(error2) };
197928
198762
  recorded.complete(output2);
197929
198763
  if (error2 instanceof AgentToolFatalError) {
197930
198764
  throw error2;
197931
198765
  }
197932
- return providerToolOutputToText(output2);
198766
+ preparedOutput = normalizePreparedAgentToolResult(output2);
197933
198767
  }
198768
+ } catch (error2) {
198769
+ if (error2 instanceof AgentToolFatalError) {
198770
+ throw error2;
198771
+ }
198772
+ preparedOutput = normalizePreparedAgentToolResult({
198773
+ error: error2 instanceof Error ? error2.message : String(error2)
198774
+ });
197934
198775
  }
197935
198776
  }
197936
- }));
197937
- const runner = client.chat.completions.runTools({
197938
- model: request.modelId,
197939
- messages,
197940
- tools: runnableTools,
197941
- tool_choice: "auto",
197942
- parallel_tool_calls: false,
197943
- response_format: responseFormat,
197944
- ...tokenLimit
197945
- }, { maxChatCompletions: request.maxTurns });
197946
- const [finalCompletion, usage] = await Promise.all([
197947
- runner.finalChatCompletion(),
197948
- runner.totalUsage()
197949
- ]);
197950
- const finalChoice = finalCompletion.choices[0];
197951
- if (!finalChoice) {
197952
- throw new Error("OpenAI returned no completion choice.");
197953
- }
197954
- text = finalChoice.message.content || "";
197955
- this.assertCompleted(finalChoice.message, finalChoice.finish_reason, text, request, toolCalls);
197956
- resolvedModelId2 = finalCompletion.model || request.modelId;
197957
- inputTokens = usage.prompt_tokens;
197958
- outputTokens = usage.completion_tokens;
197959
- } else {
197960
- const completion = await client.chat.completions.create({
197961
- model: request.modelId,
197962
- messages,
197963
- response_format: responseFormat,
197964
- ...tokenLimit
197965
- });
197966
- const message = completion.choices[0]?.message;
197967
- if (!message) {
197968
- throw new Error("OpenAI returned no assistant message.");
198777
+ const toolMessage = {
198778
+ role: "tool",
198779
+ tool_call_id: callItem.id,
198780
+ content: preparedOutput.immediateText
198781
+ };
198782
+ messages.push(toolMessage);
198783
+ toolHistory.retain(() => {
198784
+ toolMessage.content = preparedOutput.historyPlaceholder;
198785
+ });
197969
198786
  }
197970
- text = message.content || "";
197971
- this.assertCompleted(message, completion.choices[0]?.finish_reason, text, request, toolCalls);
197972
- resolvedModelId2 = completion.model || request.modelId;
197973
- inputTokens = completion.usage?.prompt_tokens ?? 0;
197974
- outputTokens = completion.usage?.completion_tokens ?? 0;
197975
198787
  }
197976
- return {
197977
- text,
197978
- parsed: tryParseStructuredAgentOutput(text, Boolean(request.outputSchema)),
197979
- toolCalls,
197980
- usage: { inputTokens, outputTokens },
197981
- resolvedModelId: resolvedModelId2
197982
- };
198788
+ throw new AgentTurnLimitError(
198789
+ this.provider,
198790
+ request.maxTurns,
198791
+ toolCalls.length,
198792
+ toolCalls[toolCalls.length - 1]?.name,
198793
+ sumAgentTurnUsage(turns)
198794
+ );
197983
198795
  }
197984
198796
  assertCompleted(message, finishReason, rawText, request, toolCalls) {
197985
198797
  if (message.refusal) {
@@ -198020,8 +198832,9 @@ var StaticAgentProviderRegistry = class {
198020
198832
  };
198021
198833
  function createDefaultAgentProviderRegistry() {
198022
198834
  return new StaticAgentProviderRegistry([
198023
- new OpenAIAgentAdapter("openai"),
198024
- new OpenAIAgentAdapter("local"),
198835
+ new OpenAIAgentAdapter(),
198836
+ new OpenAICompatibleAgentAdapter("openai_compatible"),
198837
+ new OpenAICompatibleAgentAdapter("local"),
198025
198838
  new AnthropicAgentAdapter(),
198026
198839
  new GoogleAgentAdapter()
198027
198840
  ]);
@@ -198223,21 +199036,15 @@ function getContractDisplaySeverity(assertion) {
198223
199036
  // src/knowledge/knowledgeTypes.ts
198224
199037
  var KNOWLEDGE_TOKEN_ESTIMATOR_VERSION = "knowledge-token-estimate-v1";
198225
199038
 
198226
- // src/agents/agentTypes.ts
198227
- var DEFAULT_AGENT_MAX_TOKENS = 16e3;
198228
- var DEFAULT_LOCAL_AGENT_MAX_TOKENS = 4096;
198229
- var DEFAULT_AGENT_MAX_DEPTH = 5;
198230
- var DEFAULT_AGENT_MAX_INVOCATIONS = 25;
198231
- var DEFAULT_AGENT_MAX_TURNS = 100;
198232
- var DEFAULT_AGENT_CONTRACT_RETRIES = 2;
198233
-
198234
199039
  // src/agents/agentConfig.ts
198235
199040
  function resolveAgentRuntimeLimits(startPath, provider, agent) {
198236
199041
  const loadedConfig = startPath && findNearestConfigFile(startPath, NORN_CONFIG_FILENAME) ? loadNornConfig(startPath).config.agents : void 0;
198237
199042
  const providerConfig = loadedConfig?.providers?.[provider];
198238
199043
  const defaultMaxTokens = provider === "local" ? DEFAULT_LOCAL_AGENT_MAX_TOKENS : DEFAULT_AGENT_MAX_TOKENS;
199044
+ const maxInputTokens = agent.maxInputTokens ?? providerConfig?.max_input_tokens ?? loadedConfig?.max_input_tokens;
198239
199045
  return {
198240
199046
  maxTokens: agent.maxTokens ?? providerConfig?.max_tokens ?? loadedConfig?.max_tokens ?? defaultMaxTokens,
199047
+ ...maxInputTokens !== void 0 ? { maxInputTokens } : {},
198241
199048
  maxDepth: agent.maxDepth ?? providerConfig?.max_depth ?? loadedConfig?.max_depth ?? DEFAULT_AGENT_MAX_DEPTH,
198242
199049
  maxInvocations: agent.maxInvocations ?? providerConfig?.max_invocations ?? loadedConfig?.max_invocations ?? DEFAULT_AGENT_MAX_INVOCATIONS,
198243
199050
  maxTurns: agent.maxTurns ?? providerConfig?.max_turns ?? loadedConfig?.max_turns ?? DEFAULT_AGENT_MAX_TURNS,
@@ -198421,7 +199228,7 @@ function adaptAgentSchemaForGeneration(original, provider) {
198421
199228
  if (provider === "anthropic" && containsRecursiveReference(original)) {
198422
199229
  throw new AgentSchemaAdaptationError("Anthropic structured outputs do not support recursive JSON Schemas.");
198423
199230
  }
198424
- if ((provider === "openai" || provider === "local") && getMaximumObjectDepth(original) > 5) {
199231
+ if ((provider === "openai" || provider === "openai_compatible" || provider === "local") && getMaximumObjectDepth(original) > 5) {
198425
199232
  throw new AgentSchemaAdaptationError("Norn limits OpenAI generation schemas to at most 5 nested object levels.");
198426
199233
  }
198427
199234
  const strippedKeywords = /* @__PURE__ */ new Set();
@@ -198477,7 +199284,7 @@ function adaptAgentSchemaForGeneration(original, provider) {
198477
199284
  transformed.additionalProperties = false;
198478
199285
  additionalPropertiesChanges++;
198479
199286
  }
198480
- if ((provider === "openai" || provider === "local") && properties) {
199287
+ if ((provider === "openai" || provider === "openai_compatible" || provider === "local") && properties) {
198481
199288
  const authoredRequired = new Set(
198482
199289
  Array.isArray(schemaValue.required) ? schemaValue.required.filter((entry) => typeof entry === "string") : []
198483
199290
  );
@@ -198531,6 +199338,19 @@ var DEFAULT_TOOL_INPUT_SCHEMA = {
198531
199338
  required: ["input"],
198532
199339
  additionalProperties: false
198533
199340
  };
199341
+ function mergeAgentUsage(left, right) {
199342
+ const leftTurns = left.turns ?? [];
199343
+ const rightTurns = right.turns ?? [];
199344
+ const turns = [...leftTurns, ...rightTurns].map((turn, index) => ({
199345
+ ...turn,
199346
+ turn: index + 1
199347
+ }));
199348
+ return {
199349
+ inputTokens: left.inputTokens + right.inputTokens,
199350
+ outputTokens: left.outputTokens + right.outputTokens,
199351
+ ...turns.length > 0 ? { turns } : {}
199352
+ };
199353
+ }
198534
199354
  function emptyAgentResult(hasReturns = false) {
198535
199355
  return {
198536
199356
  ...hasReturns ? { body: void 0 } : {},
@@ -198546,16 +199366,36 @@ function resolveAgentModelTarget(template, runtimeVariables) {
198546
199366
  if (/\{\{[^}]+\}\}/.test(resolved)) {
198547
199367
  throw new Error(`Could not resolve model target '${template}'.`);
198548
199368
  }
198549
- const match2 = resolved.match(/^(openai|anthropic|google|local)\/(.+)$/i);
199369
+ const match2 = resolved.match(/^(openai|openai_compatible|anthropic|google|local)\/(.+)$/i);
198550
199370
  if (!match2 || !match2[2].trim()) {
198551
199371
  throw new Error(
198552
- `Invalid resolved model '${resolved}'. Expected openai/<model>, anthropic/<model>, google/<model>, or local/<model>.`
199372
+ `Invalid resolved model '${resolved}'. Expected openai/<model>, openai_compatible/<model>, anthropic/<model>, google/<model>, or local/<model>.`
198553
199373
  );
198554
199374
  }
198555
199375
  const provider = match2[1].toLowerCase();
198556
199376
  const modelId = match2[2].trim();
198557
199377
  return { provider, modelId, displayName: `${provider}/${modelId}` };
198558
199378
  }
199379
+ function resolveAgentReasoningEffort(model, provider, runtimeVariables) {
199380
+ if (model.reasoningTemplate === void 0) {
199381
+ return void 0;
199382
+ }
199383
+ const resolved = substituteVariables(model.reasoningTemplate, runtimeVariables).trim().toLowerCase();
199384
+ if (/\{\{[^}]+\}\}/.test(resolved)) {
199385
+ throw new Error(`Could not resolve reasoning for model '${model.alias}'.`);
199386
+ }
199387
+ if (provider !== "openai") {
199388
+ throw new Error(
199389
+ `Model '${model.alias}' can declare reasoning only with provider openai; the resolved provider is '${provider}'.`
199390
+ );
199391
+ }
199392
+ if (!AGENT_REASONING_EFFORTS.some((effort) => effort === resolved)) {
199393
+ throw new Error(
199394
+ `Invalid resolved reasoning effort '${resolved}' for model '${model.alias}'. Expected one of: ${AGENT_REASONING_EFFORTS.join(", ")}.`
199395
+ );
199396
+ }
199397
+ return resolved;
199398
+ }
198559
199399
  function resolveAgentModelCredentials(model, runtimeVariables) {
198560
199400
  const resolveField = (template, directive) => {
198561
199401
  if (template === void 0) {
@@ -198660,8 +199500,11 @@ var AgentInvocationRunner = class {
198660
199500
  definitions = /* @__PURE__ */ new Map();
198661
199501
  conversations = /* @__PURE__ */ new Map();
198662
199502
  invocationBudget;
199503
+ inputTokenBudget;
198663
199504
  invocationCount = 0;
199505
+ toolResultStore = new AgentToolResultStore();
198664
199506
  async run(agent, input2) {
199507
+ throwIfSequenceCancelled(this.options.signal);
198665
199508
  const rootInvocationIndex2 = this.options.rootInvocationIndex ?? 1;
198666
199509
  if (!Number.isSafeInteger(rootInvocationIndex2) || rootInvocationIndex2 < 1) {
198667
199510
  throw new Error(`Agent root invocation index must be a positive integer, received '${rootInvocationIndex2}'.`);
@@ -198750,6 +199593,7 @@ var AgentInvocationRunner = class {
198750
199593
  provider: request.provider,
198751
199594
  modelId: request.modelId,
198752
199595
  displayModel: request.displayModel,
199596
+ ...request.reasoningEffort ? { reasoningEffort: request.reasoningEffort } : {},
198753
199597
  systemPrompt: request.systemPrompt,
198754
199598
  input: cloneSnapshotValue(request.input, null),
198755
199599
  conversation: (request.conversation ?? []).map((message) => ({ ...message })),
@@ -198757,6 +199601,7 @@ var AgentInvocationRunner = class {
198757
199601
  ...request.outputSchema ? { outputSchema: cloneSnapshotValue(request.outputSchema, {}) } : {},
198758
199602
  ...request.outputSchemaName ? { outputSchemaName: request.outputSchemaName } : {},
198759
199603
  maxTokens: request.maxTokens,
199604
+ ...request.inputTokenBudget ? { maxInputTokens: request.inputTokenBudget.maxInputTokens } : {},
198760
199605
  maxTurns: request.maxTurns,
198761
199606
  ...mcpServerGrants?.length ? { mcpServerGrants: mcpServerGrants.map((grant) => ({
198762
199607
  alias: grant.alias,
@@ -198775,7 +199620,25 @@ var AgentInvocationRunner = class {
198775
199620
  routingRule: knowledge.routing.rule,
198776
199621
  selectedDomainIds: knowledge.assembly.selectedDomainIds,
198777
199622
  estimatedTokens: knowledge.assembly.estimatedTokens,
198778
- estimatorVersion: KNOWLEDGE_TOKEN_ESTIMATOR_VERSION
199623
+ estimatorVersion: KNOWLEDGE_TOKEN_ESTIMATOR_VERSION,
199624
+ domains: knowledge.projection.domains.map((domain2) => {
199625
+ const selected = knowledge.assembly.selectedDomainIds.includes(domain2.encodedId);
199626
+ return {
199627
+ domainId: domain2.encodedId,
199628
+ declarationAlias: domain2.declarationAlias,
199629
+ sourcePath: domain2.sourcePath,
199630
+ domainPath: domain2.ref.domainPath,
199631
+ disposition: selected ? domain2.estimatedTokens <= knowledge.config.domainInlineTokenLimit ? "inline" : "index" : "manifest",
199632
+ estimatedTokens: domain2.estimatedTokens,
199633
+ topicCount: domain2.topics.length,
199634
+ topics: domain2.topics.map((topic) => ({
199635
+ topicHash: topic.topicHash,
199636
+ ...topic.headingText ? { heading: topic.headingText } : {},
199637
+ estimatedTokens: topic.estimatedTokens,
199638
+ itemPaths: [...new Set(topic.occurrences.map((occurrence) => occurrence.ref.itemPath))].sort()
199639
+ }))
199640
+ };
199641
+ })
198779
199642
  } } : {}
198780
199643
  };
198781
199644
  }
@@ -198796,7 +199659,7 @@ var AgentInvocationRunner = class {
198796
199659
  try {
198797
199660
  const output2 = await execute(input2, context);
198798
199661
  record2.output = cloneSnapshotValue(output2, null);
198799
- return output2;
199662
+ return definition.kind === "context" ? output2 : this.toolResultStore.prepare(definition.name, output2);
198800
199663
  } catch (error2) {
198801
199664
  record2.output = getToolErrorOutput(error2);
198802
199665
  throw error2;
@@ -198839,6 +199702,7 @@ var AgentInvocationRunner = class {
198839
199702
  return hop;
198840
199703
  }
198841
199704
  async invokeAgent(agent, input2, depth, parentInvocationId, ancestors, conversation, hopPath) {
199705
+ throwIfSequenceCancelled(this.options.signal);
198842
199706
  const startedAt = Date.now();
198843
199707
  const invocationId = this.nextInvocationId();
198844
199708
  const hop = {
@@ -198869,6 +199733,7 @@ var AgentInvocationRunner = class {
198869
199733
  let resolvedModel;
198870
199734
  let fatalToolError;
198871
199735
  let preparedKnowledge;
199736
+ let accumulatedProviderUsage = { inputTokens: 0, outputTokens: 0 };
198872
199737
  try {
198873
199738
  const requestOverride = await this.options.resolveRequestOverride?.(hopPath);
198874
199739
  const authoredModel = requestOverride?.provider && requestOverride.modelId ? void 0 : resolveAgentModelTarget(
@@ -198883,6 +199748,7 @@ var AgentInvocationRunner = class {
198883
199748
  modelId,
198884
199749
  displayName: requestOverride?.displayModel ?? `${provider}/${modelId}`
198885
199750
  };
199751
+ const reasoningEffort = requestOverride ? requestOverride.reasoningEffort : resolveAgentReasoningEffort(agent.model, provider, this.options.runtimeVariables);
198886
199752
  hop.output.model = resolvedModel.displayName;
198887
199753
  const configuredLimits = resolveAgentRuntimeLimits(
198888
199754
  this.options.startPath,
@@ -198891,7 +199757,8 @@ var AgentInvocationRunner = class {
198891
199757
  );
198892
199758
  const limits = {
198893
199759
  ...configuredLimits,
198894
- ...requestOverride?.maxTokens !== void 0 ? { maxTokens: requestOverride.maxTokens } : {}
199760
+ ...requestOverride?.maxTokens !== void 0 ? { maxTokens: requestOverride.maxTokens } : {},
199761
+ ...requestOverride?.maxInputTokens !== void 0 ? { maxInputTokens: requestOverride.maxInputTokens } : {}
198895
199762
  };
198896
199763
  if (!this.invocationBudget) {
198897
199764
  this.invocationBudget = {
@@ -198899,6 +199766,12 @@ var AgentInvocationRunner = class {
198899
199766
  maxInvocations: limits.maxInvocations
198900
199767
  };
198901
199768
  }
199769
+ if (depth === 0 && limits.maxInputTokens !== void 0) {
199770
+ this.inputTokenBudget = {
199771
+ maxInputTokens: limits.maxInputTokens,
199772
+ usedInputTokens: 0
199773
+ };
199774
+ }
198902
199775
  this.invocationCount++;
198903
199776
  this.addDefinition(agent, resolvedModel);
198904
199777
  const authoredSystemPrompt = requestOverride?.systemPrompt ?? substituteVariables(
@@ -198954,6 +199827,7 @@ var AgentInvocationRunner = class {
198954
199827
  provider: resolvedModel.provider,
198955
199828
  modelId: resolvedModel.modelId,
198956
199829
  displayModel: resolvedModel.displayName,
199830
+ ...reasoningEffort ? { reasoningEffort } : {},
198957
199831
  systemPrompt,
198958
199832
  input: input2,
198959
199833
  conversation,
@@ -198961,9 +199835,11 @@ var AgentInvocationRunner = class {
198961
199835
  outputSchema,
198962
199836
  outputSchemaName,
198963
199837
  maxTokens: limits.maxTokens,
199838
+ ...this.inputTokenBudget ? { inputTokenBudget: this.inputTokenBudget } : {},
198964
199839
  maxTurns: limits.maxTurns,
198965
199840
  environment: this.options.environment,
198966
- ...credentials ? { credentials } : {}
199841
+ ...credentials ? { credentials } : {},
199842
+ ...this.options.signal ? { signal: this.options.signal } : {}
198967
199843
  };
198968
199844
  hop.request = this.createRequestSnapshot(
198969
199845
  providerRequest,
@@ -198985,6 +199861,10 @@ var AgentInvocationRunner = class {
198985
199861
  ...providerRequest,
198986
199862
  conversation: correctionConversation
198987
199863
  });
199864
+ accumulatedProviderUsage = mergeAgentUsage(
199865
+ accumulatedProviderUsage,
199866
+ providerResponse.usage
199867
+ );
198988
199868
  hop.providerCompleted = true;
198989
199869
  if (fatalToolError) {
198990
199870
  throw fatalToolError;
@@ -198995,7 +199875,7 @@ var AgentInvocationRunner = class {
198995
199875
  text: providerResponse.text,
198996
199876
  toolCalls,
198997
199877
  model: `${resolvedModel.provider}/${providerResponse.resolvedModelId}`,
198998
- usage: providerResponse.usage,
199878
+ usage: accumulatedProviderUsage,
198999
199879
  ms: Date.now() - startedAt
199000
199880
  };
199001
199881
  const returnValue = providerResponse.parsed !== void 0 ? providerResponse.parsed : providerResponse.text;
@@ -199033,10 +199913,32 @@ var AgentInvocationRunner = class {
199033
199913
  }
199034
199914
  hop.success = true;
199035
199915
  } catch (error2) {
199916
+ throwIfSequenceCancelled(this.options.signal);
199036
199917
  const reportedError = fatalToolError ?? error2;
199037
199918
  if (reportedError instanceof AgentProviderMaxTokensError) {
199038
199919
  hop.error = formatAgentProviderMaxTokensError(agent.name, reportedError);
199920
+ } else if (reportedError instanceof AgentInputTokenLimitError) {
199921
+ accumulatedProviderUsage = mergeAgentUsage(
199922
+ accumulatedProviderUsage,
199923
+ reportedError.usage
199924
+ );
199925
+ hop.output.usage = accumulatedProviderUsage;
199926
+ hop.inputTokenLimit = {
199927
+ maxInputTokens: reportedError.maxInputTokens,
199928
+ usedInputTokens: reportedError.usedInputTokens,
199929
+ ...reportedError.estimatedNextInputTokens !== void 0 ? { estimatedNextInputTokens: reportedError.estimatedNextInputTokens } : {},
199930
+ sourcePath: agent.sourcePath,
199931
+ lineNumber: agent.lineNumber
199932
+ };
199933
+ hop.error = formatAgentInputTokenLimitError(agent.name, hop.inputTokenLimit);
199039
199934
  } else if (reportedError instanceof AgentTurnLimitError) {
199935
+ if (reportedError.usage) {
199936
+ accumulatedProviderUsage = mergeAgentUsage(
199937
+ accumulatedProviderUsage,
199938
+ reportedError.usage
199939
+ );
199940
+ hop.output.usage = accumulatedProviderUsage;
199941
+ }
199040
199942
  hop.turnLimit = {
199041
199943
  maxTurns: reportedError.maxTurns,
199042
199944
  toolCallCount: reportedError.toolCallCount,
@@ -199097,10 +199999,12 @@ var AgentInvocationRunner = class {
199097
199999
  overrideKeys.add(key);
199098
200000
  }
199099
200001
  let callerLimitBreaches = 0;
199100
- if (agent.callableAgents.some((reference) => reference.name.toLowerCase() === "load_knowledge")) {
199101
- throw new Error("Agent name 'load_knowledge' is reserved by Norn for knowledge retrieval.");
200002
+ if (agent.callableAgents.some((reference) => ["load_knowledge", "norn_read_tool_result"].includes(reference.name.toLowerCase()))) {
200003
+ throw new Error(
200004
+ "Agent names 'load_knowledge' and 'norn_read_tool_result' are reserved by Norn."
200005
+ );
199102
200006
  }
199103
- if (knowledge) {
200007
+ if (knowledge?.hasLoadableKnowledge) {
199104
200008
  const offeredOverride = requestToolsOverride?.find((tool) => tool.kind === "knowledge");
199105
200009
  providerTools.push(this.createRecordedProviderTool(parentHop, {
199106
200010
  name: "load_knowledge",
@@ -199403,6 +200307,25 @@ var AgentInvocationRunner = class {
199403
200307
  return Object.prototype.hasOwnProperty.call(child.output, "body") ? child.output.body : child.output.text;
199404
200308
  }));
199405
200309
  }
200310
+ const offeredContext = requestToolsOverride?.find((tool) => tool.kind === "context");
200311
+ if (!requestToolsOverride && providerTools.length > 0 || offeredContext) {
200312
+ providerTools.push(this.createRecordedProviderTool(parentHop, {
200313
+ name: "norn_read_tool_result",
200314
+ providerName: offeredContext?.providerName ?? "norn_read_tool_result",
200315
+ kind: "context",
200316
+ description: offeredContext?.description ?? "Read or search a bounded portion of a tool result that Norn offloaded from active context.",
200317
+ inputSchema: offeredContext?.inputSchema ?? {
200318
+ type: "object",
200319
+ properties: {
200320
+ handle: { type: "string" },
200321
+ offset: { type: "integer", minimum: 0 },
200322
+ query: { type: "string" }
200323
+ },
200324
+ required: ["handle"],
200325
+ additionalProperties: false
200326
+ }
200327
+ }, async (input2) => this.toolResultStore.read(input2)));
200328
+ }
199406
200329
  if (requestToolsOverride) {
199407
200330
  const currentKeys = new Set(providerTools.map(
199408
200331
  (tool) => `${tool.kind ?? ""}\0${tool.name.toLowerCase()}`
@@ -200542,6 +201465,18 @@ function normalizeHopForDisk(hop, projectRoot) {
200542
201465
  ...extended.request !== void 0 ? {
200543
201466
  request: relativizeTopLevelSourcePaths(extended.request, projectRoot, true)
200544
201467
  } : {},
201468
+ ...hop.turnLimit ? {
201469
+ turnLimit: {
201470
+ ...hop.turnLimit,
201471
+ sourcePath: path26.isAbsolute(hop.turnLimit.sourcePath) ? projectRelativePath(projectRoot, hop.turnLimit.sourcePath) : hop.turnLimit.sourcePath
201472
+ }
201473
+ } : {},
201474
+ ...hop.inputTokenLimit ? {
201475
+ inputTokenLimit: {
201476
+ ...hop.inputTokenLimit,
201477
+ sourcePath: path26.isAbsolute(hop.inputTokenLimit.sourcePath) ? projectRelativePath(projectRoot, hop.inputTokenLimit.sourcePath) : hop.inputTokenLimit.sourcePath
201478
+ }
201479
+ } : {},
200545
201480
  children: hop.children.map((child) => normalizeHopForDisk(child, projectRoot))
200546
201481
  };
200547
201482
  return normalized;
@@ -200709,28 +201644,35 @@ function isSafeProjectRelativePath(value) {
200709
201644
  return isRelativePath(value) && !normalizePathSeparators(value).split("/").includes("..");
200710
201645
  }
200711
201646
  function isAgentToolCall(value) {
200712
- return isObjectRecord2(value) && typeof value.name === "string" && (value.kind === void 0 || value.kind === "mcp" || value.kind === "agent" || value.kind === "knowledge") && isObjectRecord2(value.input) && isFiniteNonNegativeNumber(value.ms) && (value.childInvocationId === void 0 || typeof value.childInvocationId === "string");
201647
+ return isObjectRecord2(value) && typeof value.name === "string" && (value.kind === void 0 || value.kind === "mcp" || value.kind === "agent" || value.kind === "knowledge" || value.kind === "context") && isObjectRecord2(value.input) && isFiniteNonNegativeNumber(value.ms) && (value.childInvocationId === void 0 || typeof value.childInvocationId === "string");
201648
+ }
201649
+ function isAgentKnowledgeTopicSnapshot(value) {
201650
+ return isObjectRecord2(value) && typeof value.topicHash === "string" && (value.heading === void 0 || typeof value.heading === "string") && isFiniteNonNegativeNumber(value.estimatedTokens) && isStringArray(value.itemPaths);
201651
+ }
201652
+ function isAgentKnowledgeDomainSnapshot(value) {
201653
+ return isObjectRecord2(value) && typeof value.domainId === "string" && typeof value.declarationAlias === "string" && typeof value.sourcePath === "string" && value.sourcePath.length > 0 && typeof value.domainPath === "string" && ["inline", "index", "manifest"].includes(String(value.disposition)) && isFiniteNonNegativeNumber(value.estimatedTokens) && Number.isSafeInteger(value.topicCount) && value.topicCount >= 0 && Array.isArray(value.topics) && value.topics.every(isAgentKnowledgeTopicSnapshot);
200713
201654
  }
200714
201655
  function isAgentRequestSnapshot(value) {
200715
- if (!isObjectRecord2(value) || !["openai", "anthropic", "google", "local"].includes(String(value.provider)) || typeof value.modelId !== "string" || typeof value.displayModel !== "string" || typeof value.systemPrompt !== "string" || !Array.isArray(value.conversation) || !Array.isArray(value.tools) || !Number.isSafeInteger(value.maxTokens) || value.maxTokens <= 0 || !/^[0-9a-f]{64}$/.test(String(value.promptHash))) {
201656
+ if (!isObjectRecord2(value) || !["openai", "openai_compatible", "anthropic", "google", "local"].includes(String(value.provider)) || typeof value.modelId !== "string" || typeof value.displayModel !== "string" || typeof value.systemPrompt !== "string" || !Array.isArray(value.conversation) || !Array.isArray(value.tools) || !Number.isSafeInteger(value.maxTokens) || value.maxTokens <= 0 || !/^[0-9a-f]{64}$/.test(String(value.promptHash))) {
200716
201657
  return false;
200717
201658
  }
200718
201659
  const validConversation = value.conversation.every(
200719
201660
  (message) => isObjectRecord2(message) && (message.role === "user" || message.role === "assistant") && typeof message.content === "string"
200720
201661
  );
200721
201662
  const validTools = value.tools.every(
200722
- (tool) => isObjectRecord2(tool) && typeof tool.name === "string" && typeof tool.providerName === "string" && (tool.kind === void 0 || tool.kind === "mcp" || tool.kind === "agent" || tool.kind === "knowledge") && (tool.description === void 0 || typeof tool.description === "string") && isObjectRecord2(tool.inputSchema)
201663
+ (tool) => isObjectRecord2(tool) && typeof tool.name === "string" && typeof tool.providerName === "string" && (tool.kind === void 0 || tool.kind === "mcp" || tool.kind === "agent" || tool.kind === "knowledge" || tool.kind === "context") && (tool.description === void 0 || typeof tool.description === "string") && isObjectRecord2(tool.inputSchema)
200723
201664
  );
200724
201665
  const validKnowledge = value.knowledge === void 0 || isObjectRecord2(value.knowledge) && typeof value.knowledge.runId === "string" && typeof value.knowledge.snapshotId === "string" && isStringArray(value.knowledge.declarationIds) && ["exact_entity", "lexical_unique", "lexical_ambiguous", "no_match"].includes(
200725
201666
  String(value.knowledge.routingRule)
200726
- ) && isStringArray(value.knowledge.selectedDomainIds) && isFiniteNonNegativeNumber(value.knowledge.estimatedTokens) && typeof value.knowledge.estimatorVersion === "string";
200727
- return validConversation && validTools && validKnowledge && (value.outputSchema === void 0 || isObjectRecord2(value.outputSchema)) && (value.outputSchemaName === void 0 || typeof value.outputSchemaName === "string") && (value.sourcePath === void 0 || isSafeProjectRelativePath(value.sourcePath)) && isSafeProjectRelativePath(value.agentSourcePath) && Number.isSafeInteger(value.agentLineNumber) && value.agentLineNumber >= 0 && (value.sequenceName === void 0 || typeof value.sequenceName === "string") && (value.runLineNumber === void 0 || Number.isSafeInteger(value.runLineNumber));
201667
+ ) && isStringArray(value.knowledge.selectedDomainIds) && isFiniteNonNegativeNumber(value.knowledge.estimatedTokens) && typeof value.knowledge.estimatorVersion === "string" && (value.knowledge.domains === void 0 || Array.isArray(value.knowledge.domains) && value.knowledge.domains.every(isAgentKnowledgeDomainSnapshot));
201668
+ return validConversation && validTools && validKnowledge && (value.reasoningEffort === void 0 || ["none", "low", "medium", "high", "xhigh", "max"].includes(String(value.reasoningEffort))) && (value.outputSchema === void 0 || isObjectRecord2(value.outputSchema)) && (value.outputSchemaName === void 0 || typeof value.outputSchemaName === "string") && (value.maxInputTokens === void 0 || Number.isSafeInteger(value.maxInputTokens) && value.maxInputTokens > 0) && (value.maxTurns === void 0 || Number.isSafeInteger(value.maxTurns) && value.maxTurns > 0) && (value.sourcePath === void 0 || isSafeProjectRelativePath(value.sourcePath)) && isSafeProjectRelativePath(value.agentSourcePath) && Number.isSafeInteger(value.agentLineNumber) && value.agentLineNumber >= 0 && (value.sequenceName === void 0 || typeof value.sequenceName === "string") && (value.runLineNumber === void 0 || Number.isSafeInteger(value.runLineNumber));
200728
201669
  }
200729
201670
  function isAgentResult(value) {
200730
201671
  if (!isObjectRecord2(value) || typeof value.text !== "string" || typeof value.model !== "string" || !Array.isArray(value.toolCalls) || !value.toolCalls.every(isAgentToolCall) || !isFiniteNonNegativeNumber(value.ms) || !isObjectRecord2(value.usage)) {
200731
201672
  return false;
200732
201673
  }
200733
- return isFiniteNonNegativeNumber(value.usage.inputTokens) && isFiniteNonNegativeNumber(value.usage.outputTokens);
201674
+ const validTurns = value.usage.turns === void 0 || Array.isArray(value.usage.turns) && value.usage.turns.every((turn, index) => isObjectRecord2(turn) && turn.turn === index + 1 && isFiniteNonNegativeNumber(turn.inputTokens) && isFiniteNonNegativeNumber(turn.outputTokens) && (turn.cachedInputTokens === void 0 || isFiniteNonNegativeNumber(turn.cachedInputTokens)) && (turn.cacheWriteInputTokens === void 0 || isFiniteNonNegativeNumber(turn.cacheWriteInputTokens)));
201675
+ return isFiniteNonNegativeNumber(value.usage.inputTokens) && isFiniteNonNegativeNumber(value.usage.outputTokens) && validTurns;
200734
201676
  }
200735
201677
  function isAgentContract(value) {
200736
201678
  return isObjectRecord2(value) && (value.kind === "accepts" || value.kind === "returns") && typeof value.path === "string" && isObjectRecord2(value.assertion);
@@ -200742,7 +201684,7 @@ function isAgentHop(value) {
200742
201684
  if (!isObjectRecord2(value) || typeof value.invocationId !== "string" || typeof value.agent !== "string" || typeof value.success !== "boolean" || !Number.isSafeInteger(value.depth) || value.depth < 0 || typeof value.cycle !== "boolean" || !isAgentResult(value.output) || !Array.isArray(value.children) || !Array.isArray(value.retries) || !isStringArray(value.warnings)) {
200743
201685
  return false;
200744
201686
  }
200745
- return (value.parentInvocationId === void 0 || typeof value.parentInvocationId === "string") && (value.hopPath === void 0 || typeof value.hopPath === "string") && (value.request === void 0 || isAgentRequestSnapshot(value.request)) && (value.providerAttempted === void 0 || typeof value.providerAttempted === "boolean") && (value.providerCompleted === void 0 || typeof value.providerCompleted === "boolean") && (value.contractExhausted === void 0 || typeof value.contractExhausted === "boolean") && (value.error === void 0 || typeof value.error === "string") && (value.accepts === void 0 || isAgentContract(value.accepts)) && (value.returns === void 0 || isAgentContract(value.returns)) && value.retries.every(isAgentRetry) && value.children.every(isAgentHop);
201687
+ return (value.parentInvocationId === void 0 || typeof value.parentInvocationId === "string") && (value.hopPath === void 0 || typeof value.hopPath === "string") && (value.request === void 0 || isAgentRequestSnapshot(value.request)) && (value.providerAttempted === void 0 || typeof value.providerAttempted === "boolean") && (value.providerCompleted === void 0 || typeof value.providerCompleted === "boolean") && (value.contractExhausted === void 0 || typeof value.contractExhausted === "boolean") && (value.inputTokenLimit === void 0 || isObjectRecord2(value.inputTokenLimit) && Number.isSafeInteger(value.inputTokenLimit.maxInputTokens) && value.inputTokenLimit.maxInputTokens > 0 && isFiniteNonNegativeNumber(value.inputTokenLimit.usedInputTokens) && (value.inputTokenLimit.estimatedNextInputTokens === void 0 || isFiniteNonNegativeNumber(value.inputTokenLimit.estimatedNextInputTokens)) && isSafeProjectRelativePath(value.inputTokenLimit.sourcePath) && Number.isSafeInteger(value.inputTokenLimit.lineNumber) && value.inputTokenLimit.lineNumber >= 0) && (value.error === void 0 || typeof value.error === "string") && (value.accepts === void 0 || isAgentContract(value.accepts)) && (value.returns === void 0 || isAgentContract(value.returns)) && value.retries.every(isAgentRetry) && value.children.every(isAgentHop);
200746
201688
  }
200747
201689
  function isAgentTrace(value) {
200748
201690
  if (!isObjectRecord2(value) || !Array.isArray(value.agents) || !Array.isArray(value.hops)) {
@@ -202959,6 +203901,14 @@ function readKnowledgeSnapshot(startPath, snapshotId, options = {}) {
202959
203901
  function encodeKnowledgeId(parts) {
202960
203902
  return Buffer.from(canonicalJson(parts), "utf8").toString("base64url");
202961
203903
  }
203904
+ function decodeKnowledgeId(value) {
203905
+ try {
203906
+ const parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
203907
+ return Array.isArray(parsed) ? parsed : void 0;
203908
+ } catch {
203909
+ return void 0;
203910
+ }
203911
+ }
202962
203912
 
202963
203913
  // src/knowledge/knowledgeAssembly.ts
202964
203914
  function estimateKnowledgeTokens(rendered) {
@@ -202998,10 +203948,10 @@ function renderKnowledgeTopicIndex(domain2) {
202998
203948
  }))
202999
203949
  };
203000
203950
  }
203001
- function renderKnowledgeDomainBodies(domain2) {
203951
+ function renderKnowledgeDomainBodies(domain2, topics = domain2.topics) {
203002
203952
  return {
203003
203953
  domainId: domain2.encodedId,
203004
- topics: domain2.topics.map((topic) => ({
203954
+ topics: topics.map((topic) => ({
203005
203955
  topicHash: topic.topicHash,
203006
203956
  heading: topic.headingText,
203007
203957
  body: topic.body,
@@ -203363,20 +204313,30 @@ var KnowledgeRunCoordinator = class {
203363
204313
  const routing = routeKnowledge(this.projectRoot, input2, projection, config2, agent.name);
203364
204314
  const assembly = assembleKnowledgePrompt(authoredSystemPrompt, projection, routing, config2, agent.name);
203365
204315
  const loadEvents = [];
204316
+ const loadedTopicHashes = new Set(assembly.initiallyLoadedTopics.map((topic) => topic.topicHash));
204317
+ const loadedTopicIndexes = new Set(projection.domains.filter((domain2) => assembly.selectedDomainIds.includes(domain2.encodedId) && domain2.estimatedTokens > config2.domainInlineTokenLimit).map((domain2) => domain2.encodedId));
204318
+ const loadableDomains = projection.domains.filter(
204319
+ (domain2) => domain2.topics.some((topic) => !loadedTopicHashes.has(topic.topicHash)) || domain2.estimatedTokens > config2.domainInlineTokenLimit && !loadedTopicIndexes.has(domain2.encodedId)
204320
+ );
204321
+ const loadableTopicHashes = [...new Set(loadableDomains.flatMap((domain2) => domain2.topics).filter((topic) => !loadedTopicHashes.has(topic.topicHash)).map((topic) => topic.topicHash))];
203366
204322
  let usedTokens = 0;
203367
204323
  const tool = {
203368
204324
  name: "load_knowledge",
203369
204325
  providerName: "norn_load_knowledge",
203370
204326
  kind: "knowledge",
203371
- description: "Load one granted knowledge domain or a topic occurrence by the exact IDs in the manifest.",
204327
+ description: "Load only knowledge that remains available. Never request a domain listed in norn_routed_knowledge.inlineDomainIds; those bodies were already supplied. For an oversized routed domain whose topic index was supplied, request a specific topicHash rather than the domain index again.",
203372
204328
  inputSchema: { type: "object", properties: {
203373
- domainId: { type: "string" },
203374
- topicHash: { type: "string" },
204329
+ domainId: { type: "string", enum: loadableDomains.map((domain2) => domain2.encodedId) },
204330
+ topicHash: { type: "string", enum: loadableTopicHashes },
203375
204331
  occurrenceId: { type: "string" }
203376
204332
  }, required: ["domainId"], additionalProperties: false },
203377
204333
  execute: async (raw) => {
203378
204334
  const request = raw;
203379
204335
  const before = usedTokens;
204336
+ const alreadyLoaded = () => ({
204337
+ alreadyLoaded: true,
204338
+ message: "This knowledge has already been loaded."
204339
+ });
203380
204340
  const refuse = (code, message) => {
203381
204341
  loadEvents.push({
203382
204342
  order: loadEvents.length + 1,
@@ -203402,6 +204362,8 @@ var KnowledgeRunCoordinator = class {
203402
204362
  return refuse("unknown_or_ungranted_domain", "The requested domain ID is unknown or not granted.");
203403
204363
  }
203404
204364
  let value;
204365
+ let newlyLoadedTopicHashes = [];
204366
+ let loadedTopicIndex = false;
203405
204367
  if (request.topicHash) {
203406
204368
  const topic = domain2.topics.find((item) => item.topicHash === request.topicHash);
203407
204369
  if (!topic) {
@@ -203423,6 +204385,9 @@ var KnowledgeRunCoordinator = class {
203423
204385
  return refuse("unknown_or_ungranted_occurrence", "The occurrence ID is not exposed by this domain.");
203424
204386
  }
203425
204387
  }
204388
+ if (loadedTopicHashes.has(topic.topicHash)) {
204389
+ return alreadyLoaded();
204390
+ }
203426
204391
  value = { domainId: domain2.encodedId, topic: {
203427
204392
  topicHash: topic.topicHash,
203428
204393
  heading: topic.headingText,
@@ -203434,8 +204399,22 @@ var KnowledgeRunCoordinator = class {
203434
204399
  useWhen: item.metadata.useWhen?.value
203435
204400
  }))
203436
204401
  } };
204402
+ newlyLoadedTopicHashes = [topic.topicHash];
203437
204403
  } else {
203438
- value = domain2.estimatedTokens <= config2.domainInlineTokenLimit ? renderKnowledgeDomainBodies(domain2) : renderKnowledgeTopicIndex(domain2);
204404
+ if (domain2.estimatedTokens <= config2.domainInlineTokenLimit) {
204405
+ const unloadedTopics = domain2.topics.filter((topic) => !loadedTopicHashes.has(topic.topicHash));
204406
+ if (!unloadedTopics.length) {
204407
+ return alreadyLoaded();
204408
+ }
204409
+ value = renderKnowledgeDomainBodies(domain2, unloadedTopics);
204410
+ newlyLoadedTopicHashes = unloadedTopics.map((topic) => topic.topicHash);
204411
+ } else {
204412
+ if (loadedTopicIndexes.has(domain2.encodedId)) {
204413
+ return alreadyLoaded();
204414
+ }
204415
+ value = renderKnowledgeTopicIndex(domain2);
204416
+ loadedTopicIndex = true;
204417
+ }
203439
204418
  }
203440
204419
  const output2 = escapedCanonicalKnowledgeJson(value);
203441
204420
  const estimatedTokens = estimateKnowledgeTokens(output2);
@@ -203443,11 +204422,18 @@ var KnowledgeRunCoordinator = class {
203443
204422
  return refuse("tool_token_limit", `The request would exceed knowledge.toolTokenLimit=${config2.toolTokenLimit}.`);
203444
204423
  }
203445
204424
  usedTokens += estimatedTokens;
204425
+ for (const topicHash of newlyLoadedTopicHashes) {
204426
+ loadedTopicHashes.add(topicHash);
204427
+ }
204428
+ if (loadedTopicIndex) {
204429
+ loadedTopicIndexes.add(domain2.encodedId);
204430
+ }
203446
204431
  loadEvents.push({
203447
204432
  order: loadEvents.length + 1,
203448
204433
  request,
203449
204434
  accepted: true,
203450
204435
  estimatedTokens,
204436
+ loadedTopicHashes: newlyLoadedTopicHashes,
203451
204437
  budgetBefore: before,
203452
204438
  budgetAfter: usedTokens
203453
204439
  });
@@ -203455,7 +204441,17 @@ var KnowledgeRunCoordinator = class {
203455
204441
  }
203456
204442
  };
203457
204443
  void invocationId;
203458
- return { runId: this.runId, snapshotId, projection, routing, assembly, tool, loadEvents, config: config2 };
204444
+ return {
204445
+ runId: this.runId,
204446
+ snapshotId,
204447
+ projection,
204448
+ routing,
204449
+ assembly,
204450
+ tool,
204451
+ hasLoadableKnowledge: loadableDomains.length > 0,
204452
+ loadEvents,
204453
+ config: config2
204454
+ };
203459
204455
  }
203460
204456
  recordInvocation(agent, hop, knowledge) {
203461
204457
  this.invocations.push({ agent, hop, knowledge });
@@ -203510,7 +204506,8 @@ var KnowledgeRunCoordinator = class {
203510
204506
  accepts: item.hop.accepts,
203511
204507
  returns: item.hop.returns,
203512
204508
  judge: item.hop.judge,
203513
- turnLimit: item.hop.turnLimit
204509
+ turnLimit: item.hop.turnLimit,
204510
+ inputTokenLimit: item.hop.inputTokenLimit
203514
204511
  })
203515
204512
  );
203516
204513
  if (!item.knowledge) {
@@ -203614,7 +204611,11 @@ var KnowledgeRunCoordinator = class {
203614
204611
  event.estimatedTokens
203615
204612
  );
203616
204613
  } else if (event.accepted) {
204614
+ const loadedTopicHashes = event.loadedTopicHashes ? new Set(event.loadedTopicHashes) : void 0;
203617
204615
  for (const topic of domain2.topics) {
204616
+ if (loadedTopicHashes && !loadedTopicHashes.has(topic.topicHash)) {
204617
+ continue;
204618
+ }
203618
204619
  for (const occurrence of topic.occurrences) {
203619
204620
  index.run(
203620
204621
  `INSERT OR REPLACE INTO run_topics(run_id, invocation_id,
@@ -204824,9 +205825,6 @@ function valueToString2(value) {
204824
205825
  }
204825
205826
  return String(value);
204826
205827
  }
204827
- function sleep5(ms) {
204828
- return new Promise((resolve28) => setTimeout(resolve28, ms));
204829
- }
204830
205828
  function isIfCommand(line2) {
204831
205829
  return /^if\s+.+$/i.test(line2.trim());
204832
205830
  }
@@ -205333,6 +206331,10 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205333
206331
  };
205334
206332
  const runtimeVariables = copyEnvironmentScope({ ...fileVariables, ...sequenceArgs }, fileVariables);
205335
206333
  const mcpSessionManager = executionContext?.mcpSessionManager ?? new McpSessionManager();
206334
+ const cancelMcpSessions = () => {
206335
+ void mcpSessionManager.closeAll();
206336
+ };
206337
+ executionContext?.signal?.addEventListener("abort", cancelMcpSessions, { once: true });
205336
206338
  const agentProviderRegistry = executionContext?.agentProviderRegistry ?? createDefaultAgentProviderRegistry();
205337
206339
  const agentWarningKeys = executionContext?.agentWarningKeys ?? /* @__PURE__ */ new Set();
205338
206340
  const agentEventLog = executionContext?.agentEventLog ?? [];
@@ -205547,6 +206549,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205547
206549
  }
205548
206550
  };
205549
206551
  for (const step of steps) {
206552
+ throwIfSequenceCancelled(executionContext?.signal);
205550
206553
  if (step.type === "judge") {
205551
206554
  const parsedJudge = parseJudgeStep(step);
205552
206555
  const blockingError = parsedJudge.errors[0];
@@ -205644,6 +206647,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205644
206647
  try {
205645
206648
  await preflightAgentGraph(definition, scope);
205646
206649
  } catch (error2) {
206650
+ throwIfSequenceCancelled(executionContext?.signal);
205647
206651
  await mcpSessionManager.closeScope(MCP_PREFLIGHT_SCOPE);
205648
206652
  const message = `Agent '${definition.name}' tool preflight failed: ${error2 instanceof Error ? error2.message : String(error2)}`;
205649
206653
  errors.push(message);
@@ -205663,8 +206667,10 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205663
206667
  await mcpSessionManager.closeScope(MCP_PREFLIGHT_SCOPE);
205664
206668
  }
205665
206669
  for (let stepIdx = 0; stepIdx < steps.length; stepIdx++) {
206670
+ throwIfSequenceCancelled(executionContext?.signal);
205666
206671
  const step = steps[stepIdx];
205667
206672
  await emitBeforeStep(step, stepIdx);
206673
+ throwIfSequenceCancelled(executionContext?.signal);
205668
206674
  const stepStartTime = Date.now();
205669
206675
  try {
205670
206676
  if (step.type === "if") {
@@ -205752,7 +206758,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205752
206758
  if (step.type === "wait") {
205753
206759
  const durationMs = parseWaitCommand(step.content);
205754
206760
  reportProgress(stepIdx, "wait", `wait: ${durationMs}ms`);
205755
- await sleep5(durationMs);
206761
+ await cancellableDelay(durationMs, executionContext?.signal);
205756
206762
  const stepResult = createPrintStepResult(
205757
206763
  stepIdx,
205758
206764
  stepStartTime,
@@ -205954,6 +206960,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205954
206960
  runLineNumber: debugLocation.absoluteLine,
205955
206961
  rootInvocationIndex: rootInvocationIndex2,
205956
206962
  knowledgeCoordinator,
206963
+ signal: executionContext?.signal,
205957
206964
  expandMcpServerGrants: (_hopPath, grantedAgent) => mcpGrantExpansions.get(agentDefinitionKey(grantedAgent)) ?? expandMcpGrantsLive(grantedAgent),
205958
206965
  onInvocationProgress: (agentEvent) => {
205959
206966
  const recordedEvent = cloneSerializableValue(agentEvent);
@@ -206012,6 +207019,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
206012
207019
  }
206013
207020
  }
206014
207021
  } catch (error2) {
207022
+ throwIfSequenceCancelled(executionContext?.signal);
206015
207023
  execution.error = error2 instanceof Error ? error2.message : String(error2);
206016
207024
  }
206017
207025
  const stepResult = {
@@ -206090,6 +207098,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206090
207098
  runLineNumber: debugLocation.absoluteLine,
206091
207099
  rootInvocationIndex: rootInvocationIndex2,
206092
207100
  knowledgeCoordinator,
207101
+ signal: executionContext?.signal,
206093
207102
  expandMcpServerGrants: (_hopPath, grantedAgent) => mcpGrantExpansions.get(agentDefinitionKey(grantedAgent)) ?? expandMcpGrantsLive(grantedAgent),
206094
207103
  onInvocationProgress: (agentEvent) => {
206095
207104
  const recordedEvent = cloneSerializableValue(agentEvent);
@@ -206116,6 +207125,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206116
207125
  addTraceDefinition(definition);
206117
207126
  }
206118
207127
  } catch (error2) {
207128
+ throwIfSequenceCancelled(executionContext?.signal);
206119
207129
  const message = error2 instanceof Error ? error2.message : String(error2);
206120
207130
  if (agent?.returns) {
206121
207131
  output2 = { ...output2, body: void 0 };
@@ -206314,7 +207324,8 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206314
207324
  workingDir: adapterWorkingDir
206315
207325
  }
206316
207326
  },
206317
- adapterWorkingDir
207327
+ adapterWorkingDir,
207328
+ executionContext?.signal
206318
207329
  );
206319
207330
  let storedValue;
206320
207331
  let sqlResult;
@@ -206361,6 +207372,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206361
207372
  const description = operation.type === "query" ? `run sql ${operation.name} \u2192 ${sqlResult.rowCount ?? 0} row(s)` : `run sql ${operation.name} \u2192 ${sqlResult.affectedRows ?? 0} affected`;
206362
207373
  reportProgress(stepIdx, "sql", description, stepResult);
206363
207374
  } catch (error2) {
207375
+ throwIfSequenceCancelled(executionContext?.signal);
206364
207376
  errors.push(`SQL execution failed for '${operation.name}': ${error2.message || String(error2)}`);
206365
207377
  await emitFailure(`SQL execution failed for '${operation.name}'`, step, stepIdx);
206366
207378
  return {
@@ -206414,6 +207426,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206414
207426
  orderedSteps.push(stepResult);
206415
207427
  reportProgress(stepIdx, "mcp", `run mcp list ${parsed.serverAlias} \u2192 ${listResult.tools.length} tool(s)`, stepResult);
206416
207428
  } catch (error2) {
207429
+ throwIfSequenceCancelled(executionContext?.signal);
206417
207430
  const message = `MCP list failed for '${parsed.serverAlias}': ${error2?.message || String(error2)}`;
206418
207431
  errors.push(message);
206419
207432
  await emitFailure(message, step, stepIdx);
@@ -206465,6 +207478,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206465
207478
  parsed.toolName
206466
207479
  );
206467
207480
  } catch (error2) {
207481
+ throwIfSequenceCancelled(executionContext?.signal);
206468
207482
  const message = `MCP call failed for '${parsed.serverAlias}.${parsed.toolName}': ${error2?.message || String(error2)}`;
206469
207483
  errors.push(message);
206470
207484
  await emitFailure(message, step, stepIdx);
@@ -206534,6 +207548,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206534
207548
  };
206535
207549
  }
206536
207550
  } catch (error2) {
207551
+ throwIfSequenceCancelled(executionContext?.signal);
206537
207552
  const message = `MCP call failed for '${parsed.serverAlias}.${parsed.toolName}': ${error2?.message || String(error2)}`;
206538
207553
  errors.push(message);
206539
207554
  await emitFailure(message, step, stepIdx);
@@ -206596,7 +207611,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206596
207611
  reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] ${requestDescription} - waiting ${waitMs}ms`, void 0);
206597
207612
  }
206598
207613
  } : void 0;
206599
- const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
207614
+ const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts, executionContext?.signal) : await sendRequest(requestParsed, retryOpts, executionContext?.signal);
206600
207615
  addResponse(response);
206601
207616
  runContractValidation(preparedRequest.endpointName, response, step, stepIdx);
206602
207617
  responseIndexToVariable.set(responses.length, parsed.varName);
@@ -206622,6 +207637,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206622
207637
  }
206623
207638
  }
206624
207639
  } catch (error2) {
207640
+ throwIfSequenceCancelled(executionContext?.signal);
206625
207641
  const userMessage = formatUserFacingError(
206626
207642
  error2,
206627
207643
  buildRequestValidationContext(stepIdx + 1, void 0, void 0, `var ${parsed.varName}`)
@@ -206674,7 +207690,8 @@ ${indentMultiline(userMessage)}`;
206674
207690
  substitutedArgs,
206675
207691
  workingDir || process.cwd(),
206676
207692
  runtimeVariables,
206677
- parsed.captureAs
207693
+ parsed.captureAs,
207694
+ executionContext?.signal
206678
207695
  );
206679
207696
  scriptResults.push(result);
206680
207697
  const stepResult = {
@@ -206707,6 +207724,7 @@ ${indentMultiline(userMessage)}`;
206707
207724
  runtimeVariables[parsed.captureAs] = outputValue;
206708
207725
  }
206709
207726
  } catch (error2) {
207727
+ throwIfSequenceCancelled(executionContext?.signal);
206710
207728
  errors.push(`Script execution error: ${error2.message}`);
206711
207729
  return {
206712
207730
  name: "",
@@ -206812,7 +207830,8 @@ ${indentMultiline(userMessage)}`;
206812
207830
  agentEventLog,
206813
207831
  agentRootInvocationCounts,
206814
207832
  recordAgentRuns: executionContext?.recordAgentRuns,
206815
- knowledgeCoordinator
207833
+ knowledgeCoordinator,
207834
+ signal: executionContext?.signal
206816
207835
  };
206817
207836
  const subResult = await runSequenceWithJar(
206818
207837
  targetSequence.content,
@@ -206905,7 +207924,7 @@ ${indentMultiline(userMessage)}`;
206905
207924
  reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] run ${targetName} - waiting ${waitMs}ms`, void 0);
206906
207925
  }
206907
207926
  } : void 0;
206908
- const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
207927
+ const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts, executionContext?.signal) : await sendRequest(requestParsed, retryOpts, executionContext?.signal);
206909
207928
  addResponse(response);
206910
207929
  const stepResult = {
206911
207930
  type: "request",
@@ -206927,6 +207946,7 @@ ${indentMultiline(userMessage)}`;
206927
207946
  }
206928
207947
  }
206929
207948
  } catch (error2) {
207949
+ throwIfSequenceCancelled(executionContext?.signal);
206930
207950
  const userMessage = formatUserFacingError(
206931
207951
  error2,
206932
207952
  buildRequestValidationContext(stepIdx + 1, requestMethod || void 0, requestUrl || void 0, targetName)
@@ -207016,7 +208036,7 @@ ${indentMultiline(userMessage)}`;
207016
208036
  reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] var ${varName} = run ${sequenceName} - waiting ${waitMs}ms`, void 0);
207017
208037
  }
207018
208038
  } : void 0;
207019
- const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
208039
+ const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts, executionContext?.signal) : await sendRequest(requestParsed, retryOpts, executionContext?.signal);
207020
208040
  addResponse(response);
207021
208041
  responseIndexToVariable.set(responses.length, varName);
207022
208042
  const stepResult = {
@@ -207047,6 +208067,7 @@ ${indentMultiline(userMessage)}`;
207047
208067
  }
207048
208068
  }
207049
208069
  } catch (error2) {
208070
+ throwIfSequenceCancelled(executionContext?.signal);
207050
208071
  const userMessage = formatUserFacingError(
207051
208072
  error2,
207052
208073
  buildRequestValidationContext(stepIdx + 1, requestMethod || void 0, requestUrl || void 0, `${varName} = run ${sequenceName}`)
@@ -207132,7 +208153,8 @@ ${indentMultiline(userMessage)}`;
207132
208153
  agentEventLog,
207133
208154
  agentRootInvocationCounts,
207134
208155
  recordAgentRuns: executionContext?.recordAgentRuns,
207135
- knowledgeCoordinator
208156
+ knowledgeCoordinator,
208157
+ signal: executionContext?.signal
207136
208158
  };
207137
208159
  const subResult = await runSequenceWithJar(
207138
208160
  targetSequence.content,
@@ -207224,7 +208246,7 @@ ${indentMultiline(userMessage)}`;
207224
208246
  reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] ${requestDescription} - waiting ${waitMs}ms`, void 0);
207225
208247
  }
207226
208248
  } : void 0;
207227
- const response = cookieJar ? await sendRequestWithJar(parsed, cookieJar, retryOpts) : await sendRequest(parsed, retryOpts);
208249
+ const response = cookieJar ? await sendRequestWithJar(parsed, cookieJar, retryOpts, executionContext?.signal) : await sendRequest(parsed, retryOpts, executionContext?.signal);
207228
208250
  addResponse(response);
207229
208251
  const stepResult = {
207230
208252
  type: "request",
@@ -207247,6 +208269,7 @@ ${indentMultiline(userMessage)}`;
207247
208269
  }
207248
208270
  }
207249
208271
  } catch (error2) {
208272
+ throwIfSequenceCancelled(executionContext?.signal);
207250
208273
  const userMessage = formatUserFacingError(
207251
208274
  error2,
207252
208275
  buildRequestValidationContext(stepIdx + 1)
@@ -207272,7 +208295,9 @@ ${indentMultiline(userMessage)}`;
207272
208295
  }
207273
208296
  }
207274
208297
  } finally {
207275
- await emitAfterStep(step, stepIdx);
208298
+ if (!executionContext?.signal?.aborted) {
208299
+ await emitAfterStep(step, stepIdx);
208300
+ }
207276
208301
  }
207277
208302
  }
207278
208303
  return {
@@ -207288,16 +208313,23 @@ ${indentMultiline(userMessage)}`;
207288
208313
  agentTrace,
207289
208314
  completed: true
207290
208315
  };
208316
+ } catch (error2) {
208317
+ if (executionContext?.signal?.aborted) {
208318
+ throw new SequenceCancelledError();
208319
+ }
208320
+ throw error2;
207291
208321
  } finally {
208322
+ executionContext?.signal?.removeEventListener("abort", cancelMcpSessions);
208323
+ const wasCancelled = executionContext?.signal?.aborted === true;
207292
208324
  let recordingPath;
207293
- const shouldRecordAgentRun = nestingDepth === 0 && agentTrace.hops.length > 0 && (executionContext?.recordAgentRuns ?? executionContext?.agentProviderRegistry === void 0);
208325
+ const shouldRecordAgentRun = !wasCancelled && nestingDepth === 0 && agentTrace.hops.length > 0 && (executionContext?.recordAgentRuns ?? executionContext?.agentProviderRegistry === void 0);
207294
208326
  if (shouldRecordAgentRun && executionContext?.filePath) {
207295
208327
  try {
207296
208328
  recordingPath = writeAgentRunRecording({
207297
208329
  sourcePath: executionContext.filePath,
207298
208330
  sequenceName: executionContext.sequenceName || "sequence",
207299
208331
  ...executionContext.environment?.activeEnvironment ? { environmentName: executionContext.environment.activeEnvironment } : {},
207300
- success: errors.filter((error2) => !error2.startsWith("Warning")).length === 0 && contractViolations === 0 && agentFailures === 0,
208332
+ success: !wasCancelled && errors.filter((error2) => !error2.startsWith("Warning")).length === 0 && contractViolations === 0 && agentFailures === 0,
207301
208333
  durationMs: Date.now() - startTime,
207302
208334
  ...knowledgeCoordinator?.pinnedSnapshotId ? {
207303
208335
  runId: knowledgeCoordinator.runId,
@@ -207322,7 +208354,7 @@ ${indentMultiline(userMessage)}`;
207322
208354
  environmentName: executionContext.environment?.activeEnvironment,
207323
208355
  startedAt: new Date(startTime),
207324
208356
  durationMs: Date.now() - startTime,
207325
- success: errors.filter((error2) => !error2.startsWith("Warning")).length === 0 && contractViolations === 0 && agentFailures === 0,
208357
+ success: !wasCancelled && errors.filter((error2) => !error2.startsWith("Warning")).length === 0 && contractViolations === 0 && agentFailures === 0,
207326
208358
  recordingPath,
207327
208359
  secrets: resolveAgentRecordingSecretValues(
207328
208360
  executionContext.filePath,
@@ -207654,6 +208686,20 @@ function redactAgentRequestForJson(request, redaction) {
207654
208686
  agentSourcePath: redactString(request.agentSourcePath, redaction),
207655
208687
  sourcePath: request.sourcePath ? redactString(request.sourcePath, redaction) : void 0,
207656
208688
  sequenceName: request.sequenceName ? redactString(request.sequenceName, redaction) : void 0,
208689
+ knowledge: request.knowledge ? {
208690
+ ...request.knowledge,
208691
+ domains: request.knowledge.domains?.map((domain2) => ({
208692
+ ...domain2,
208693
+ declarationAlias: redactString(domain2.declarationAlias, redaction),
208694
+ sourcePath: redactString(domain2.sourcePath, redaction),
208695
+ domainPath: redactString(domain2.domainPath, redaction),
208696
+ topics: domain2.topics.map((topic) => ({
208697
+ ...topic,
208698
+ heading: topic.heading ? redactString(topic.heading, redaction) : void 0,
208699
+ itemPaths: topic.itemPaths.map((itemPath) => redactString(itemPath, redaction))
208700
+ }))
208701
+ }))
208702
+ } : void 0,
207657
208703
  promptHash: (0, import_crypto11.createHash)("sha256").update(systemPrompt, "utf8").digest("hex")
207658
208704
  };
207659
208705
  }
@@ -207673,6 +208719,10 @@ function redactAgentHopForJson(hop, redaction) {
207673
208719
  sourcePath: redactString(hop.turnLimit.sourcePath, redaction),
207674
208720
  ...hop.turnLimit.lastToolName ? { lastToolName: redactString(hop.turnLimit.lastToolName, redaction) } : {}
207675
208721
  } : void 0,
208722
+ inputTokenLimit: hop.inputTokenLimit ? {
208723
+ ...hop.inputTokenLimit,
208724
+ sourcePath: redactString(hop.inputTokenLimit.sourcePath, redaction)
208725
+ } : void 0,
207676
208726
  warnings: hop.warnings.map((warning) => redactString(warning, redaction)),
207677
208727
  accepts: redactAgentContractForJson(hop.accepts, redaction),
207678
208728
  returns: redactAgentContractForJson(hop.returns, redaction),
@@ -207689,7 +208739,7 @@ function redactAgentHopForJson(hop, redaction) {
207689
208739
  body: redactBody(hop.output.body, redaction),
207690
208740
  text: typeof redactedText === "string" ? redactedText : String(redactedText ?? ""),
207691
208741
  model: redactString(hop.output.model, redaction),
207692
- toolCalls: hop.output.toolCalls.map((toolCall) => ({
208742
+ toolCalls: hop.output.toolCalls.filter((toolCall) => toolCall.kind !== "context" && !(toolCall.kind === "knowledge" && toolCall.output && typeof toolCall.output === "object" && !Array.isArray(toolCall.output) && toolCall.output.alreadyLoaded === true)).map((toolCall) => ({
207693
208743
  ...toolCall,
207694
208744
  name: redactString(toolCall.name, redaction),
207695
208745
  input: redactBody(toolCall.input, redaction),
@@ -207929,6 +208979,9 @@ function isFailedToolResult(value) {
207929
208979
  const record2 = value;
207930
208980
  return Object.keys(record2).length === 1 && typeof record2.error === "string";
207931
208981
  }
208982
+ function isKnowledgeAlreadyLoaded(value) {
208983
+ return Boolean(value && typeof value === "object" && !Array.isArray(value) && value.alreadyLoaded === true);
208984
+ }
207932
208985
  function getReplyValue(hop) {
207933
208986
  if (hop.output.text.trim().length > 0) {
207934
208987
  return { present: true, value: hop.output.text };
@@ -208006,6 +209059,9 @@ function formatAgentHopAtDepth(hop, options, displayDepth) {
208006
209059
  const renderedChildren = /* @__PURE__ */ new Set();
208007
209060
  const childrenByInvocationId = new Map(hop.children.map((child) => [child.invocationId, child]));
208008
209061
  for (const toolCall of hop.output.toolCalls) {
209062
+ if (toolCall.kind === "context" || toolCall.kind === "knowledge" && isKnowledgeAlreadyLoaded(toolCall.output)) {
209063
+ continue;
209064
+ }
208009
209065
  if (toolCall.kind === "agent" || toolCall.childInvocationId) {
208010
209066
  const child = toolCall.childInvocationId ? childrenByInvocationId.get(toolCall.childInvocationId) : void 0;
208011
209067
  if (child) {
@@ -210854,6 +211910,42 @@ async function runK8sRunbooks(text, options = {}) {
210854
211910
  var import_util8 = require("util");
210855
211911
 
210856
211912
  // src/agents/agentHopPresentation.ts
211913
+ function buildAgentKnowledgePresentation(knowledge) {
211914
+ if (!knowledge) {
211915
+ return void 0;
211916
+ }
211917
+ const domains = knowledge.domains ? knowledge.domains.map((domain2) => ({
211918
+ domainId: domain2.domainId,
211919
+ declarationAlias: domain2.declarationAlias,
211920
+ sourcePath: domain2.sourcePath,
211921
+ domainPath: domain2.domainPath,
211922
+ disposition: domain2.disposition,
211923
+ estimatedTokens: domain2.estimatedTokens,
211924
+ topicCount: domain2.topicCount,
211925
+ topics: domain2.topics.map((topic) => ({
211926
+ topicHash: topic.topicHash,
211927
+ ...topic.heading ? { heading: topic.heading } : {},
211928
+ estimatedTokens: topic.estimatedTokens,
211929
+ itemPaths: [...topic.itemPaths]
211930
+ }))
211931
+ })) : knowledge.selectedDomainIds.map((domainId) => {
211932
+ const parts = decodeKnowledgeId(domainId);
211933
+ const domainPath = typeof parts?.[2] === "string" ? parts[2] : "Recorded domain";
211934
+ return {
211935
+ domainId,
211936
+ domainPath,
211937
+ disposition: "selected",
211938
+ topics: []
211939
+ };
211940
+ });
211941
+ return {
211942
+ routingRule: knowledge.routingRule,
211943
+ estimatedTokens: knowledge.estimatedTokens,
211944
+ estimatorVersion: knowledge.estimatorVersion,
211945
+ detailsComplete: knowledge.domains !== void 0,
211946
+ domains
211947
+ };
211948
+ }
210857
211949
  function isSameAgentContract(left, right) {
210858
211950
  return Boolean(
210859
211951
  left && right && left.kind === right.kind && left.path === right.path && left.assertion.passed === right.assertion.passed && left.assertion.expression === right.assertion.expression && left.assertion.error === right.assertion.error
@@ -210906,15 +211998,25 @@ function deriveAgentHopPresentation(hop) {
210906
211998
  ...hop.turnLimit.lastToolName ? { lastToolName: hop.turnLimit.lastToolName } : {}
210907
211999
  }
210908
212000
  } : {},
212001
+ ...hop.inputTokenLimit ? {
212002
+ inputTokenLimit: {
212003
+ maxInputTokens: hop.inputTokenLimit.maxInputTokens,
212004
+ usedInputTokens: hop.inputTokenLimit.usedInputTokens,
212005
+ ...hop.inputTokenLimit.estimatedNextInputTokens !== void 0 ? { estimatedNextInputTokens: hop.inputTokenLimit.estimatedNextInputTokens } : {}
212006
+ }
212007
+ } : {},
212008
+ ...hop.request?.maxInputTokens !== void 0 ? { maxInputTokens: hop.request.maxInputTokens } : {},
210909
212009
  ...hop.error ? { error: hop.error } : {},
210910
212010
  warnings: [...hop.warnings],
210911
212011
  ...hop.judge ? { judge: buildAgentJudgePresentation(hop.judge) } : {},
212012
+ ...hop.request?.knowledge ? { knowledge: buildAgentKnowledgePresentation(hop.request.knowledge) } : {},
210912
212013
  output: {
210913
212014
  model: hop.output.model,
210914
212015
  ms: hop.output.ms,
210915
212016
  usage: {
210916
212017
  inputTokens: hop.output.usage.inputTokens,
210917
- outputTokens: hop.output.usage.outputTokens
212018
+ outputTokens: hop.output.usage.outputTokens,
212019
+ ...hop.output.usage.turns ? { turns: hop.output.usage.turns.map((turn) => ({ ...turn })) } : {}
210918
212020
  },
210919
212021
  text: hop.output.text,
210920
212022
  ...Object.prototype.hasOwnProperty.call(hop.output, "body") && hop.output.body !== void 0 ? { body: hop.output.body } : {},
@@ -211008,7 +212110,13 @@ var ReplayAgentProviderAdapter = class {
211008
212110
  };
211009
212111
 
211010
212112
  // src/agents/agentReplay.ts
211011
- var PROVIDERS = ["openai", "anthropic", "google", "local"];
212113
+ var PROVIDERS = [
212114
+ "openai",
212115
+ "openai_compatible",
212116
+ "anthropic",
212117
+ "google",
212118
+ "local"
212119
+ ];
211012
212120
  function normalizedPath(value) {
211013
212121
  return value.replace(/\\/g, "/").toLowerCase();
211014
212122
  }
@@ -211034,7 +212142,10 @@ function recordedProviderResponse(hop, provider) {
211034
212142
  text: hop.output.text,
211035
212143
  ...hasOwn4(hop.output, "body") ? { parsed: structuredClone(hop.output.body) } : {},
211036
212144
  toolCalls: cloneToolCalls(hop.output.toolCalls),
211037
- usage: { ...hop.output.usage },
212145
+ usage: {
212146
+ ...hop.output.usage,
212147
+ ...hop.output.usage.turns ? { turns: hop.output.usage.turns.map((turn) => ({ ...turn })) } : {}
212148
+ },
211038
212149
  resolvedModelId: resolvedModelId(hop, provider)
211039
212150
  };
211040
212151
  }
@@ -211196,8 +212307,10 @@ var ReplayCoordinator = class {
211196
212307
  provider: hop.request.provider,
211197
212308
  modelId: hop.request.modelId,
211198
212309
  displayModel: hop.request.displayModel,
212310
+ ...hop.request.reasoningEffort ? { reasoningEffort: hop.request.reasoningEffort } : {},
211199
212311
  systemPrompt: hop.request.systemPrompt,
211200
212312
  maxTokens: hop.request.maxTokens,
212313
+ ...hop.request.maxInputTokens !== void 0 ? { maxInputTokens: hop.request.maxInputTokens } : {},
211201
212314
  tools: hop.request.tools.map((tool) => ({
211202
212315
  ...tool,
211203
212316
  inputSchema: structuredClone(tool.inputSchema)
@@ -211375,7 +212488,8 @@ var ReplayCoordinator = class {
211375
212488
  let actualOutput;
211376
212489
  let actualError;
211377
212490
  try {
211378
- actualOutput = await currentTool.execute(structuredClone(recordedCall.input));
212491
+ const executed = await currentTool.execute(structuredClone(recordedCall.input));
212492
+ actualOutput = normalizePreparedAgentToolResult(executed).recordedOutput;
211379
212493
  } catch (error2) {
211380
212494
  if (kind === "mcp") {
211381
212495
  throw error2;