norn-cli 3.3.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/cli.js +1396 -276
  2. package/dist/knowledgeIndexWorker.js +71 -35
  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 },
@@ -158665,15 +158674,6 @@ var MCP_DIRECTIVES = {
158665
158674
  };
158666
158675
  var MCP_DIRECTIVE_NAMES = Object.keys(MCP_DIRECTIVES);
158667
158676
  var MCP_SESSION_SCOPES = ["run", "agent", "call"];
158668
- function splitCommandParts(value) {
158669
- const parts = [];
158670
- const pattern = /"([^"]*)"|'([^']*)'|(\S+)/g;
158671
- let match2;
158672
- while ((match2 = pattern.exec(value)) !== null) {
158673
- parts.push(match2[1] ?? match2[2] ?? match2[3]);
158674
- }
158675
- return parts;
158676
- }
158677
158677
  function splitNamedDirective(value) {
158678
158678
  const match2 = value.trim().match(/^(\S+)\s+(.+)$/);
158679
158679
  if (!match2) {
@@ -158691,6 +158691,7 @@ function splitHeaderDirective(value) {
158691
158691
  }
158692
158692
  var LIMIT_DIRECTIVES = {
158693
158693
  max_tokens: "maxTokens",
158694
+ max_input_tokens: "maxInputTokens",
158694
158695
  max_depth: "maxDepth",
158695
158696
  max_invocations: "maxInvocations",
158696
158697
  max_turns: "maxTurns",
@@ -158997,7 +158998,7 @@ end mcp
158997
158998
  mcp.invalid = true;
158998
158999
  }
158999
159000
  }
159000
- if (resolvedTransport === "stdio" && mcp.commandTemplates.length === 0) {
159001
+ if (resolvedTransport === "stdio" && !mcp.commandTemplate) {
159001
159002
  errors.push(parseError(
159002
159003
  mcp.lineNumber,
159003
159004
  `MCP server '${mcp.alias}' uses transport stdio and requires a command directive.`,
@@ -159050,7 +159051,7 @@ end mcp
159050
159051
  mcpServers.set(mcp.alias.toLowerCase(), {
159051
159052
  alias: mcp.alias,
159052
159053
  transportTemplate: transport.value,
159053
- commandTemplates: mcp.commandTemplates,
159054
+ ...mcp.commandTemplate ? { commandTemplate: mcp.commandTemplate } : {},
159054
159055
  ...mcp.values.cwd ? { cwdTemplate: mcp.values.cwd.value } : {},
159055
159056
  ...mcp.envTemplates.length > 0 ? { envTemplates: mcp.envTemplates } : {},
159056
159057
  ...mcp.values.url ? { urlTemplate: mcp.values.url.value } : {},
@@ -159125,6 +159126,23 @@ end mcp
159125
159126
  ));
159126
159127
  model.invalid = true;
159127
159128
  }
159129
+ const reasoning = model.values.reasoning;
159130
+ if (reasoning && !reasoning.value.includes("{{") && !AGENT_REASONING_EFFORTS.some((effort) => effort === reasoning.value.toLowerCase())) {
159131
+ errors.push(parseError(
159132
+ reasoning.lineNumber,
159133
+ `Unknown reasoning effort '${reasoning.value}' in model '${model.alias}'. Expected one of: ${AGENT_REASONING_EFFORTS.join(", ")}.`,
159134
+ "agent-model-reasoning-invalid"
159135
+ ));
159136
+ model.invalid = true;
159137
+ }
159138
+ if (reasoning && provider && !provider.value.includes("{{") && provider.value.toLowerCase() !== "openai") {
159139
+ errors.push(parseError(
159140
+ reasoning.lineNumber,
159141
+ `Model '${model.alias}' can declare reasoning only with provider openai. Provider '${provider.value}' does not support this directive yet.`,
159142
+ "agent-model-reasoning-provider"
159143
+ ));
159144
+ model.invalid = true;
159145
+ }
159128
159146
  if (model.invalid || !provider || !name) {
159129
159147
  return;
159130
159148
  }
@@ -159133,6 +159151,7 @@ end mcp
159133
159151
  providerTemplate: provider.value,
159134
159152
  nameTemplate: name.value,
159135
159153
  targetTemplate: `${provider.value}/${name.value}`,
159154
+ ...reasoning ? { reasoningTemplate: reasoning.value } : {},
159136
159155
  ...model.values.apiKey ? { apiKeyTemplate: model.values.apiKey.value } : {},
159137
159156
  ...model.values.baseUrl ? { baseUrlTemplate: model.values.baseUrl.value } : {},
159138
159157
  lineNumber: model.lineNumber,
@@ -159172,6 +159191,13 @@ end mcp
159172
159191
  "agent-model-alias-unknown"
159173
159192
  ));
159174
159193
  pending.invalid = true;
159194
+ } else if (model.alias !== pending.modelAlias.value) {
159195
+ errors.push(parseError(
159196
+ pending.modelAlias.lineNumber,
159197
+ `Model alias '${pending.modelAlias.value}' in agent '${pending.name}' does not match the declared alias '${model.alias}'. Model aliases are case-sensitive.`,
159198
+ "agent-model-alias-case-mismatch"
159199
+ ));
159200
+ pending.invalid = true;
159175
159201
  }
159176
159202
  }
159177
159203
  }
@@ -159207,6 +159233,7 @@ end mcp
159207
159233
  accepts: pending.accepts,
159208
159234
  returns: pending.returns,
159209
159235
  maxTokens: pending.maxTokens?.value,
159236
+ maxInputTokens: pending.maxInputTokens?.value,
159210
159237
  maxDepth: pending.maxDepth?.value,
159211
159238
  maxInvocations: pending.maxInvocations?.value,
159212
159239
  maxTurns: pending.maxTurns?.value,
@@ -159360,7 +159387,7 @@ end mcp
159360
159387
  lineNumber: index
159361
159388
  };
159362
159389
  if (directive === "command") {
159363
- pendingMcp.commandTemplates = splitCommandParts(rawValue);
159390
+ pendingMcp.commandTemplate = rawValue;
159364
159391
  }
159365
159392
  continue;
159366
159393
  }
@@ -159495,7 +159522,6 @@ end model`,
159495
159522
  alias,
159496
159523
  lineNumber: index,
159497
159524
  values: {},
159498
- commandTemplates: [],
159499
159525
  envTemplates: [],
159500
159526
  headerTemplates: [],
159501
159527
  sawDirective: false,
@@ -159611,29 +159637,38 @@ end model`,
159611
159637
  }
159612
159638
  const knowledgeGrantMatch = trimmed.match(/^knowledge(?:\s+(.*))?$/i);
159613
159639
  if (knowledgeGrantMatch) {
159614
- const alias = (knowledgeGrantMatch[1] ?? "").trim();
159615
- if (!new RegExp(`^${IDENTIFIER}$`).test(alias)) {
159640
+ const rawValue = (knowledgeGrantMatch[1] ?? "").trim();
159641
+ const aliases = rawValue ? rawValue.split(",").map((part) => part.trim()) : [];
159642
+ if (aliases.length === 0 || aliases.some((alias) => !new RegExp(`^${IDENTIFIER}$`).test(alias))) {
159616
159643
  errors.push(parseError(
159617
159644
  index,
159618
- `Invalid knowledge grant '${trimmed}' in agent '${pending.name}'. Expected: knowledge <Alias>.`,
159645
+ `Invalid knowledge grant '${trimmed}' in agent '${pending.name}'. Expected: knowledge <Alias>[, <Alias>\u2026].`,
159619
159646
  "agent-knowledge-grant-invalid"
159620
159647
  ));
159621
159648
  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 {
159649
+ continue;
159650
+ }
159651
+ const granted = new Set(pending.knowledge.map((grant) => grant.alias.toLowerCase()));
159652
+ for (const alias of aliases) {
159653
+ if (granted.has(alias.toLowerCase())) {
159654
+ errors.push(parseError(
159655
+ index,
159656
+ `Agent '${pending.name}' grants knowledge '${alias}' more than once.`,
159657
+ "agent-knowledge-grant-duplicate"
159658
+ ));
159659
+ pending.invalid = true;
159660
+ continue;
159661
+ }
159662
+ if (!knowledgeBases.has(alias.toLowerCase())) {
159663
+ errors.push(parseError(
159664
+ index,
159665
+ `Unknown knowledge alias '${alias}' in agent '${pending.name}'.`,
159666
+ "agent-knowledge-alias-unknown"
159667
+ ));
159668
+ pending.invalid = true;
159669
+ continue;
159670
+ }
159671
+ granted.add(alias.toLowerCase());
159637
159672
  pending.knowledge.push({ alias, lineNumber: index });
159638
159673
  }
159639
159674
  continue;
@@ -159948,6 +159983,7 @@ function isNonNegativeInteger(value) {
159948
159983
  var AGENT_LIMIT_CONFIG_KEYS = /* @__PURE__ */ new Set([
159949
159984
  "_comment",
159950
159985
  "max_tokens",
159986
+ "max_input_tokens",
159951
159987
  "max_depth",
159952
159988
  "max_invocations",
159953
159989
  "max_turns",
@@ -159957,7 +159993,7 @@ function hasOnlyKeys(value, allowedKeys) {
159957
159993
  return Object.keys(value).every((key) => allowedKeys.has(key));
159958
159994
  }
159959
159995
  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));
159996
+ 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
159997
  }
159962
159998
  function isNornAgentProviderConfig(value) {
159963
159999
  if (!isObjectRecord(value)) {
@@ -159977,7 +160013,13 @@ function isNornAgentRecordingConfig(value) {
159977
160013
  }
159978
160014
  return hasOnlyKeys(value, /* @__PURE__ */ new Set(["_comment", "enabled"])) && (value.enabled === void 0 || typeof value.enabled === "boolean");
159979
160015
  }
159980
- var AGENT_PROVIDER_NAMES = /* @__PURE__ */ new Set(["openai", "anthropic", "google", "local"]);
160016
+ var AGENT_PROVIDER_NAMES = /* @__PURE__ */ new Set([
160017
+ "openai",
160018
+ "openai_compatible",
160019
+ "anthropic",
160020
+ "google",
160021
+ "local"
160022
+ ]);
159981
160023
  function isNornAgentsConfig(value) {
159982
160024
  if (value === void 0) {
159983
160025
  return true;
@@ -160419,7 +160461,7 @@ function extractImports(text) {
160419
160461
  }
160420
160462
  function sameMcpServerDeclaration(left, right) {
160421
160463
  const named = (entries) => (entries ?? []).map((entry) => `${entry.name}=${entry.valueTemplate}`).join("\0");
160422
- return left.transportTemplate === right.transportTemplate && left.commandTemplates.join("\0") === right.commandTemplates.join("\0") && left.cwdTemplate === right.cwdTemplate && left.urlTemplate === right.urlTemplate && left.timeoutMs === right.timeoutMs && left.session === right.session && named(left.envTemplates) === named(right.envTemplates) && named(left.headerTemplates) === named(right.headerTemplates);
160464
+ return left.transportTemplate === right.transportTemplate && left.commandTemplate === right.commandTemplate && left.cwdTemplate === right.cwdTemplate && left.urlTemplate === right.urlTemplate && left.timeoutMs === right.timeoutMs && left.session === right.session && named(left.envTemplates) === named(right.envTemplates) && named(left.headerTemplates) === named(right.headerTemplates);
160423
160465
  }
160424
160466
  async function resolveImports(text, baseDir, readFile4, alreadyImported = /* @__PURE__ */ new Set(), importStack = /* @__PURE__ */ new Set(), sourceFilePath = path4.resolve(baseDir, "__norn_root__.norn"), sqlImportedPathsBySource = /* @__PURE__ */ new Map(), sqlOperationsBySource = /* @__PURE__ */ new Map(), agentImportedPathsBySource = /* @__PURE__ */ new Map(), agentDefinitionsBySource = /* @__PURE__ */ new Map(), mcpServersBySource = /* @__PURE__ */ new Map()) {
160425
160467
  const imports = extractImports(text);
@@ -166666,6 +166708,68 @@ function encodeFormUrlEncodedBody(body) {
166666
166708
  };
166667
166709
  }
166668
166710
 
166711
+ // src/sequenceCancellation.ts
166712
+ var SEQUENCE_CANCELLED_MESSAGE = "Sequence run cancelled by user.";
166713
+ var SequenceCancelledError = class extends Error {
166714
+ constructor() {
166715
+ super(SEQUENCE_CANCELLED_MESSAGE);
166716
+ this.name = "SequenceCancelledError";
166717
+ }
166718
+ };
166719
+ function throwIfSequenceCancelled(signal) {
166720
+ if (signal?.aborted) {
166721
+ throw new SequenceCancelledError();
166722
+ }
166723
+ }
166724
+ function raceWithSequenceCancellation(operation, signal) {
166725
+ if (!signal) {
166726
+ return operation;
166727
+ }
166728
+ throwIfSequenceCancelled(signal);
166729
+ return new Promise((resolve28, reject) => {
166730
+ const cancel = () => {
166731
+ signal.removeEventListener("abort", cancel);
166732
+ reject(new SequenceCancelledError());
166733
+ };
166734
+ signal.addEventListener("abort", cancel, { once: true });
166735
+ if (signal.aborted) {
166736
+ cancel();
166737
+ return;
166738
+ }
166739
+ operation.then(
166740
+ (value) => {
166741
+ signal.removeEventListener("abort", cancel);
166742
+ resolve28(value);
166743
+ },
166744
+ (error2) => {
166745
+ signal.removeEventListener("abort", cancel);
166746
+ reject(error2);
166747
+ }
166748
+ );
166749
+ });
166750
+ }
166751
+ function cancellableDelay(ms, signal) {
166752
+ if (!signal) {
166753
+ return new Promise((resolve28) => setTimeout(resolve28, ms));
166754
+ }
166755
+ throwIfSequenceCancelled(signal);
166756
+ return new Promise((resolve28, reject) => {
166757
+ const timer = setTimeout(() => {
166758
+ signal.removeEventListener("abort", cancel);
166759
+ resolve28();
166760
+ }, ms);
166761
+ const cancel = () => {
166762
+ clearTimeout(timer);
166763
+ signal.removeEventListener("abort", cancel);
166764
+ reject(new SequenceCancelledError());
166765
+ };
166766
+ signal.addEventListener("abort", cancel, { once: true });
166767
+ if (signal.aborted) {
166768
+ cancel();
166769
+ }
166770
+ });
166771
+ }
166772
+
166669
166773
  // src/httpClient.ts
166670
166774
  var sharedCookieJar = new CookieJar();
166671
166775
  var insecureHttpsAgent = new https2.Agent({ rejectUnauthorized: false });
@@ -166688,11 +166792,8 @@ async function getAllCookies(jar) {
166688
166792
  secure: Boolean(c.secure)
166689
166793
  }));
166690
166794
  }
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));
166795
+ async function sendRequest(request, retryOptions, signal) {
166796
+ return sendRequestWithJar(request, sharedCookieJar, retryOptions, signal);
166696
166797
  }
166697
166798
  function shouldRetry(response, error2) {
166698
166799
  if (error2) {
@@ -166703,13 +166804,14 @@ function shouldRetry(response, error2) {
166703
166804
  }
166704
166805
  return false;
166705
166806
  }
166706
- async function sendRequestWithJar(request, jar, retryOptions) {
166807
+ async function sendRequestWithJar(request, jar, retryOptions, signal) {
166707
166808
  const totalAttempts = (retryOptions?.retryCount ?? 0) + 1;
166708
166809
  const backoffMs = retryOptions?.backoffMs ?? 1e3;
166709
166810
  const retriedErrors = [];
166710
166811
  let lastError = null;
166711
166812
  let attemptsMade = 0;
166712
166813
  for (let attempt = 1; attempt <= totalAttempts; attempt++) {
166814
+ throwIfSequenceCancelled(signal);
166713
166815
  attemptsMade = attempt;
166714
166816
  const startTime = Date.now();
166715
166817
  const maxRedirects = 10;
@@ -166722,6 +166824,7 @@ async function sendRequestWithJar(request, jar, retryOptions) {
166722
166824
  let sentRequestHeaders = {};
166723
166825
  try {
166724
166826
  while (redirectCount <= maxRedirects) {
166827
+ throwIfSequenceCancelled(signal);
166725
166828
  const existingCookies = await jar.getCookies(currentUrl);
166726
166829
  const cookieHeader = existingCookies.map((c) => `${c.key}=${c.value}`).join("; ");
166727
166830
  const headers = { ...request.headers };
@@ -166776,7 +166879,8 @@ async function sendRequestWithJar(request, jar, retryOptions) {
166776
166879
  timeout: getRequestTimeoutMs(),
166777
166880
  maxRedirects: 0,
166778
166881
  validateStatus: () => true,
166779
- httpsAgent: getHttpsAgent()
166882
+ httpsAgent: getHttpsAgent(),
166883
+ signal
166780
166884
  });
166781
166885
  const setCookieHeader = response.headers["set-cookie"];
166782
166886
  if (setCookieHeader) {
@@ -166811,7 +166915,7 @@ async function sendRequestWithJar(request, jar, retryOptions) {
166811
166915
  if (retryOptions?.onRetry) {
166812
166916
  retryOptions.onRetry(attempt, totalAttempts, errorMessage, waitMs);
166813
166917
  }
166814
- await sleep(waitMs);
166918
+ await cancellableDelay(waitMs, signal);
166815
166919
  continue;
166816
166920
  }
166817
166921
  const cookies = await getAllCookies(jar);
@@ -166834,6 +166938,9 @@ async function sendRequestWithJar(request, jar, retryOptions) {
166834
166938
  }
166835
166939
  return result;
166836
166940
  } catch (error2) {
166941
+ if (signal?.aborted) {
166942
+ throw new SequenceCancelledError();
166943
+ }
166837
166944
  const errorMessage = error2.message || String(error2);
166838
166945
  const effectiveUrl = currentUrl || request.url;
166839
166946
  const isUrlError = /invalid url/i.test(errorMessage) || /unsupported protocol/i.test(errorMessage);
@@ -166876,7 +166983,7 @@ URL used: ${effectiveUrl}`);
166876
166983
  if (retryOptions?.onRetry) {
166877
166984
  retryOptions.onRetry(attempt, totalAttempts, errorMessage, waitMs);
166878
166985
  }
166879
- await sleep(waitMs);
166986
+ await cancellableDelay(waitMs, signal);
166880
166987
  continue;
166881
166988
  }
166882
166989
  throw lastError;
@@ -167080,7 +167187,8 @@ function isRunCommand(line2) {
167080
167187
  const trimmed = line2.trim();
167081
167188
  return /^(var\s+[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*)?run\s+(bash|powershell|pwsh|js)\s+/i.test(trimmed);
167082
167189
  }
167083
- async function runScript(type, scriptPath, args, workingDir, variables = {}, captureVar) {
167190
+ async function runScript(type, scriptPath, args, workingDir, variables = {}, captureVar, signal) {
167191
+ throwIfSequenceCancelled(signal);
167084
167192
  const startTime = Date.now();
167085
167193
  const resolvedPath = path5.isAbsolute(scriptPath) ? scriptPath : path5.resolve(workingDir, scriptPath);
167086
167194
  let command;
@@ -167111,7 +167219,7 @@ async function runScript(type, scriptPath, args, workingDir, variables = {}, cap
167111
167219
  captureVar
167112
167220
  };
167113
167221
  }
167114
- return new Promise((resolve28) => {
167222
+ return new Promise((resolve28, reject) => {
167115
167223
  const env4 = {
167116
167224
  ...process.env,
167117
167225
  // Pass variables as environment variables with NORN_ prefix
@@ -167128,6 +167236,28 @@ async function runScript(type, scriptPath, args, workingDir, variables = {}, cap
167128
167236
  });
167129
167237
  let stdout = "";
167130
167238
  let stderr = "";
167239
+ let settled = false;
167240
+ const cancel = () => {
167241
+ if (settled) {
167242
+ return;
167243
+ }
167244
+ settled = true;
167245
+ signal?.removeEventListener("abort", cancel);
167246
+ child.kill("SIGTERM");
167247
+ reject(new SequenceCancelledError());
167248
+ };
167249
+ signal?.addEventListener("abort", cancel, { once: true });
167250
+ if (signal?.aborted) {
167251
+ cancel();
167252
+ }
167253
+ const finish = (result) => {
167254
+ if (settled) {
167255
+ return;
167256
+ }
167257
+ settled = true;
167258
+ signal?.removeEventListener("abort", cancel);
167259
+ resolve28(result);
167260
+ };
167131
167261
  child.stdout.on("data", (data) => {
167132
167262
  stdout += data.toString();
167133
167263
  });
@@ -167135,7 +167265,7 @@ async function runScript(type, scriptPath, args, workingDir, variables = {}, cap
167135
167265
  stderr += data.toString();
167136
167266
  });
167137
167267
  child.on("error", (err) => {
167138
- resolve28({
167268
+ finish({
167139
167269
  success: false,
167140
167270
  output: cleanScriptOutput(stdout),
167141
167271
  error: `Failed to execute script: ${err.message}`,
@@ -167149,7 +167279,7 @@ async function runScript(type, scriptPath, args, workingDir, variables = {}, cap
167149
167279
  child.on("close", (code) => {
167150
167280
  const exitCode = code ?? 0;
167151
167281
  const cleanedOutput = cleanScriptOutput(stdout);
167152
- resolve28({
167282
+ finish({
167153
167283
  success: exitCode === 0,
167154
167284
  output: cleanedOutput,
167155
167285
  error: stderr.trim(),
@@ -168730,7 +168860,8 @@ function buildAdapterError(message, stderr, stdout) {
168730
168860
  }
168731
168861
  return new Error(details.join("\n"));
168732
168862
  }
168733
- async function runExternalSqlAdapter(command, request, workingDir) {
168863
+ async function runExternalSqlAdapter(command, request, workingDir, signal) {
168864
+ throwIfSequenceCancelled(signal);
168734
168865
  if (!command.length) {
168735
168866
  throw new Error("SQL adapter command is empty");
168736
168867
  }
@@ -168743,11 +168874,25 @@ async function runExternalSqlAdapter(command, request, workingDir) {
168743
168874
  let stdout = "";
168744
168875
  let stderr = "";
168745
168876
  let settled = false;
168877
+ const cancel = () => {
168878
+ if (settled) {
168879
+ return;
168880
+ }
168881
+ settled = true;
168882
+ signal?.removeEventListener("abort", cancel);
168883
+ child.kill("SIGTERM");
168884
+ reject(new SequenceCancelledError());
168885
+ };
168886
+ signal?.addEventListener("abort", cancel, { once: true });
168887
+ if (signal?.aborted) {
168888
+ cancel();
168889
+ }
168746
168890
  const fail3 = (error2) => {
168747
168891
  if (settled) {
168748
168892
  return;
168749
168893
  }
168750
168894
  settled = true;
168895
+ signal?.removeEventListener("abort", cancel);
168751
168896
  reject(error2);
168752
168897
  };
168753
168898
  const succeed = (response) => {
@@ -168755,6 +168900,7 @@ async function runExternalSqlAdapter(command, request, workingDir) {
168755
168900
  return;
168756
168901
  }
168757
168902
  settled = true;
168903
+ signal?.removeEventListener("abort", cancel);
168758
168904
  resolve28(response);
168759
168905
  };
168760
168906
  child.stdout.on("data", (chunk) => {
@@ -168812,11 +168958,11 @@ async function runExternalSqlAdapter(command, request, workingDir) {
168812
168958
  }
168813
168959
  });
168814
168960
  }
168815
- async function runSqlAdapter(target, request, workingDir) {
168961
+ async function runSqlAdapter(target, request, workingDir, signal) {
168816
168962
  if (target.kind === "builtin") {
168817
- return runBuiltInSqlAdapter(target.adapterId, request);
168963
+ return raceWithSequenceCancellation(runBuiltInSqlAdapter(target.adapterId, request), signal);
168818
168964
  }
168819
- return runExternalSqlAdapter(target.command, request, workingDir);
168965
+ return runExternalSqlAdapter(target.command, request, workingDir, signal);
168820
168966
  }
168821
168967
 
168822
168968
  // src/mcpClient.ts
@@ -168831,10 +168977,10 @@ var util3;
168831
168977
  function assertIs2(_arg) {
168832
168978
  }
168833
168979
  util4.assertIs = assertIs2;
168834
- function assertNever5(_x) {
168980
+ function assertNever6(_x) {
168835
168981
  throw new Error();
168836
168982
  }
168837
- util4.assertNever = assertNever5;
168983
+ util4.assertNever = assertNever6;
168838
168984
  util4.arrayToEnum = (items) => {
168839
168985
  const obj = {};
168840
168986
  for (const item of items) {
@@ -184623,6 +184769,15 @@ function resolveTemplateString(value, envVariables) {
184623
184769
  const scopedVariables = attachEnvironmentScope({}, envVariables);
184624
184770
  return substituteVariables(value, scopedVariables);
184625
184771
  }
184772
+ function splitCommandLine(value) {
184773
+ const parts = [];
184774
+ const pattern = /"([^"]*)"|'([^']*)'|(\S+)/g;
184775
+ let match2;
184776
+ while ((match2 = pattern.exec(value)) !== null) {
184777
+ parts.push(match2[1] ?? match2[2] ?? match2[3]);
184778
+ }
184779
+ return parts;
184780
+ }
184626
184781
  function resolveCommandParts2(originPath, command, envVariables) {
184627
184782
  const configDir = path10.dirname(originPath);
184628
184783
  return command.map((part, index) => {
@@ -184751,9 +184906,16 @@ function resolveNamedTemplates(entries, declaration, directive, envVariables) {
184751
184906
  function resolveMcpServerDeclaration(declaration, envVariables) {
184752
184907
  const transport = resolveTemplate(declaration.transportTemplate, declaration, "transport", envVariables).toLowerCase();
184753
184908
  if (transport === "stdio") {
184754
- const command = declaration.commandTemplates.map(
184755
- (part) => resolveTemplate(part, declaration, "command", envVariables)
184909
+ const resolvedCommand = resolveTemplate(
184910
+ declaration.commandTemplate ?? "",
184911
+ declaration,
184912
+ "command",
184913
+ envVariables
184756
184914
  );
184915
+ const command = splitCommandLine(resolvedCommand);
184916
+ if (command.length === 0) {
184917
+ throw new Error(`MCP server '${declaration.alias}' resolved to an empty command.`);
184918
+ }
184757
184919
  return {
184758
184920
  transport: "stdio",
184759
184921
  // Reuse the config path's own resolution so a bare name stays a PATH lookup and a
@@ -186229,15 +186391,155 @@ function describeUnmetExpectations(verdict) {
186229
186391
 
186230
186392
  // src/agents/providers/providerUtils.ts
186231
186393
  var import_crypto6 = require("crypto");
186394
+
186395
+ // src/agents/toolResultContext.ts
186396
+ var MAX_INLINE_TOOL_RESULT_TOKENS = 2e4;
186397
+ var MAX_INLINE_TOOL_RESULT_CHARACTERS = MAX_INLINE_TOOL_RESULT_TOKENS * 4;
186398
+ var MAX_RETAINED_INLINE_TOOL_RESULTS = 3;
186399
+ var TOOL_RESULT_PREVIEW_CHARACTERS = 2e3;
186400
+ var TOOL_RESULT_READ_CHARACTERS = 12e3;
186401
+ function outputToText(output2) {
186402
+ if (typeof output2 === "string") {
186403
+ return output2;
186404
+ }
186405
+ if (output2 === void 0) {
186406
+ return "";
186407
+ }
186408
+ try {
186409
+ return JSON.stringify(output2);
186410
+ } catch {
186411
+ return String(output2);
186412
+ }
186413
+ }
186414
+ function referenceText(input2) {
186415
+ return JSON.stringify({
186416
+ nornToolResult: {
186417
+ status: input2.released ? "released_from_context" : "offloaded",
186418
+ handle: input2.handle,
186419
+ tool: input2.toolName,
186420
+ originalCharacters: input2.originalCharacters,
186421
+ estimatedTokens: input2.estimatedTokens,
186422
+ ...input2.preview !== void 0 ? { preview: input2.preview } : {},
186423
+ instruction: `Use norn_read_tool_result with handle '${input2.handle}' to read or search bounded portions.`
186424
+ }
186425
+ });
186426
+ }
186427
+ var PreparedAgentToolResult = class {
186428
+ constructor(recordedOutput, immediateOutput, immediateText, historyPlaceholder) {
186429
+ this.recordedOutput = recordedOutput;
186430
+ this.immediateOutput = immediateOutput;
186431
+ this.immediateText = immediateText;
186432
+ this.historyPlaceholder = historyPlaceholder;
186433
+ }
186434
+ };
186435
+ var AgentToolResultStore = class {
186436
+ results = /* @__PURE__ */ new Map();
186437
+ nextHandle = 1;
186438
+ prepare(toolName2, output2) {
186439
+ const text = outputToText(output2);
186440
+ const handle = `tool-result-${this.nextHandle++}`;
186441
+ const estimatedTokens = Math.ceil(text.length / 4);
186442
+ const stored = { handle, toolName: toolName2, text, estimatedTokens };
186443
+ this.results.set(handle, stored);
186444
+ const historyPlaceholder = referenceText({
186445
+ handle,
186446
+ toolName: toolName2,
186447
+ originalCharacters: text.length,
186448
+ estimatedTokens,
186449
+ released: true
186450
+ });
186451
+ if (text.length <= MAX_INLINE_TOOL_RESULT_CHARACTERS) {
186452
+ return new PreparedAgentToolResult(output2, output2, text, historyPlaceholder);
186453
+ }
186454
+ const offloadedReference = referenceText({
186455
+ handle,
186456
+ toolName: toolName2,
186457
+ originalCharacters: text.length,
186458
+ estimatedTokens,
186459
+ preview: text.slice(0, TOOL_RESULT_PREVIEW_CHARACTERS),
186460
+ released: false
186461
+ });
186462
+ return new PreparedAgentToolResult(output2, offloadedReference, offloadedReference, historyPlaceholder);
186463
+ }
186464
+ read(input2) {
186465
+ const handle = typeof input2.handle === "string" ? input2.handle : "";
186466
+ const stored = this.results.get(handle);
186467
+ if (!stored) {
186468
+ return { error: `Unknown or expired tool-result handle '${handle}'.` };
186469
+ }
186470
+ const query = typeof input2.query === "string" ? input2.query.trim() : "";
186471
+ if (query) {
186472
+ return this.search(stored, query);
186473
+ }
186474
+ const requestedOffset = typeof input2.offset === "number" && Number.isSafeInteger(input2.offset) ? input2.offset : 0;
186475
+ const offset = Math.max(0, Math.min(requestedOffset, stored.text.length));
186476
+ const text = stored.text.slice(offset, offset + TOOL_RESULT_READ_CHARACTERS);
186477
+ return {
186478
+ handle,
186479
+ tool: stored.toolName,
186480
+ offset,
186481
+ returnedCharacters: text.length,
186482
+ totalCharacters: stored.text.length,
186483
+ hasMore: offset + text.length < stored.text.length,
186484
+ text
186485
+ };
186486
+ }
186487
+ search(stored, query) {
186488
+ const haystack = stored.text.toLocaleLowerCase();
186489
+ const needle = query.toLocaleLowerCase();
186490
+ const matches = [];
186491
+ let from = 0;
186492
+ while (matches.length < 5) {
186493
+ const index = haystack.indexOf(needle, from);
186494
+ if (index < 0) {
186495
+ break;
186496
+ }
186497
+ const start = Math.max(0, index - 300);
186498
+ const end = Math.min(stored.text.length, index + query.length + 500);
186499
+ matches.push({ offset: start, text: stored.text.slice(start, end) });
186500
+ from = Math.max(index + needle.length, index + 1);
186501
+ }
186502
+ return {
186503
+ handle: stored.handle,
186504
+ tool: stored.toolName,
186505
+ query,
186506
+ totalCharacters: stored.text.length,
186507
+ matches
186508
+ };
186509
+ }
186510
+ };
186511
+ var AgentToolResultHistory = class {
186512
+ retained = [];
186513
+ retain(replaceWithReference) {
186514
+ this.retained.push(replaceWithReference);
186515
+ while (this.retained.length > MAX_RETAINED_INLINE_TOOL_RESULTS) {
186516
+ this.retained.shift()?.();
186517
+ }
186518
+ }
186519
+ };
186520
+ function normalizePreparedAgentToolResult(output2) {
186521
+ if (output2 instanceof PreparedAgentToolResult) {
186522
+ return output2;
186523
+ }
186524
+ return {
186525
+ recordedOutput: output2,
186526
+ immediateOutput: output2,
186527
+ immediateText: outputToText(output2),
186528
+ historyPlaceholder: "[Older tool result released from active context by Norn.]"
186529
+ };
186530
+ }
186531
+
186532
+ // src/agents/providers/providerUtils.ts
186232
186533
  var PROVIDER_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]{0,63}$/;
186233
186534
  var PROVIDER_NAME_MAX_LENGTH = 64;
186234
186535
  var AgentTurnLimitError = class extends Error {
186235
- constructor(provider, maxTurns, toolCallCount, lastToolName) {
186536
+ constructor(provider, maxTurns, toolCallCount, lastToolName, usage) {
186236
186537
  super(`${provider} tool loop reached its ${maxTurns}-turn limit before the model finished.`);
186237
186538
  this.provider = provider;
186238
186539
  this.maxTurns = maxTurns;
186239
186540
  this.toolCallCount = toolCallCount;
186240
186541
  this.lastToolName = lastToolName;
186542
+ this.usage = usage;
186241
186543
  this.name = "AgentTurnLimitError";
186242
186544
  }
186243
186545
  };
@@ -186310,19 +186612,6 @@ function agentInputToText(input2) {
186310
186612
  return String(input2);
186311
186613
  }
186312
186614
  }
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
186615
  function tryParseStructuredAgentOutput(text, hasSchema) {
186327
186616
  if (!hasSchema) {
186328
186617
  return void 0;
@@ -186405,7 +186694,70 @@ function createRecordedToolCall(records, name, input2, kind) {
186405
186694
 
186406
186695
  // src/agents/providers/anthropicAdapter.ts
186407
186696
  init_sdk();
186408
- init_json_schema();
186697
+
186698
+ // src/agents/agentTokenBudget.ts
186699
+ function estimateSerializedInputTokens(value) {
186700
+ let text;
186701
+ try {
186702
+ text = JSON.stringify(value) ?? "";
186703
+ } catch {
186704
+ text = String(value);
186705
+ }
186706
+ return Math.ceil(text.length / 3);
186707
+ }
186708
+ function formatAgentInputTokenLimitError(agentName, outcome) {
186709
+ const next = outcome.estimatedNextInputTokens === void 0 ? "" : ` The next request was estimated at ${outcome.estimatedNextInputTokens} input tokens.`;
186710
+ 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.`;
186711
+ }
186712
+ function sumAgentTurnUsage(turns) {
186713
+ return {
186714
+ inputTokens: turns.reduce((total, turn) => total + turn.inputTokens, 0),
186715
+ outputTokens: turns.reduce((total, turn) => total + turn.outputTokens, 0),
186716
+ turns: turns.map((turn) => ({ ...turn }))
186717
+ };
186718
+ }
186719
+ var AgentInputTokenLimitError = class extends Error {
186720
+ constructor(provider, maxInputTokens, usedInputTokens, usage, estimatedNextInputTokens) {
186721
+ const reason = estimatedNextInputTokens === void 0 ? `after using ${usedInputTokens}` : `before an estimated ${estimatedNextInputTokens}-token request with ${usedInputTokens} already used`;
186722
+ super(`${provider} agent input budget ${maxInputTokens} was reached ${reason}.`);
186723
+ this.provider = provider;
186724
+ this.maxInputTokens = maxInputTokens;
186725
+ this.usedInputTokens = usedInputTokens;
186726
+ this.usage = usage;
186727
+ this.estimatedNextInputTokens = estimatedNextInputTokens;
186728
+ this.name = "AgentInputTokenLimitError";
186729
+ }
186730
+ };
186731
+ function assertInputBudgetAllowsCall(provider, budget, estimatedNextInputTokens, turns) {
186732
+ if (!budget || budget.usedInputTokens + estimatedNextInputTokens <= budget.maxInputTokens) {
186733
+ return;
186734
+ }
186735
+ throw new AgentInputTokenLimitError(
186736
+ provider,
186737
+ budget.maxInputTokens,
186738
+ budget.usedInputTokens,
186739
+ sumAgentTurnUsage(turns),
186740
+ estimatedNextInputTokens
186741
+ );
186742
+ }
186743
+ function recordAgentTurnUsage(provider, budget, turns, usage) {
186744
+ const turn = { turn: turns.length + 1, ...usage };
186745
+ turns.push(turn);
186746
+ if (!budget) {
186747
+ return;
186748
+ }
186749
+ budget.usedInputTokens += turn.inputTokens;
186750
+ if (budget.usedInputTokens > budget.maxInputTokens) {
186751
+ throw new AgentInputTokenLimitError(
186752
+ provider,
186753
+ budget.maxInputTokens,
186754
+ budget.usedInputTokens,
186755
+ sumAgentTurnUsage(turns)
186756
+ );
186757
+ }
186758
+ }
186759
+
186760
+ // src/agents/providers/anthropicAdapter.ts
186409
186761
  function supportsAdaptiveThinking(modelId) {
186410
186762
  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
186763
  }
@@ -186487,67 +186839,136 @@ var AnthropicAgentAdapter = class {
186487
186839
  })),
186488
186840
  inputMessage
186489
186841
  ];
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({
186842
+ const turns = [];
186843
+ if (request.tools.length === 0) {
186844
+ const call = {
186521
186845
  model: request.modelId,
186522
186846
  max_tokens: request.maxTokens,
186523
186847
  system: request.systemPrompt,
186524
186848
  messages,
186525
- tools: runnableTools,
186526
- tool_choice: { type: "auto", disable_parallel_tool_use: true },
186527
186849
  output_config: outputConfig,
186528
- thinking,
186529
- max_iterations: request.maxTurns,
186530
- stream: false
186850
+ thinking
186851
+ };
186852
+ assertInputBudgetAllowsCall(
186853
+ this.provider,
186854
+ request.inputTokenBudget,
186855
+ estimateSerializedInputTokens(call),
186856
+ turns
186857
+ );
186858
+ const finalMessage = await client.messages.create(
186859
+ call,
186860
+ request.signal ? { signal: request.signal } : void 0
186861
+ );
186862
+ recordAgentTurnUsage(this.provider, request.inputTokenBudget, turns, {
186863
+ inputTokens: finalMessage.usage.input_tokens + (finalMessage.usage.cache_creation_input_tokens ?? 0) + (finalMessage.usage.cache_read_input_tokens ?? 0),
186864
+ outputTokens: finalMessage.usage.output_tokens,
186865
+ ...typeof finalMessage.usage.cache_read_input_tokens === "number" ? { cachedInputTokens: finalMessage.usage.cache_read_input_tokens } : {},
186866
+ ...typeof finalMessage.usage.cache_creation_input_tokens === "number" ? { cacheWriteInputTokens: finalMessage.usage.cache_creation_input_tokens } : {}
186531
186867
  });
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;
186868
+ return this.completeResponse(request, finalMessage, toolCalls, turns);
186869
+ }
186870
+ const preparedTools = prepareAgentProviderTools(request.tools);
186871
+ const betaMessages = messages;
186872
+ const toolsByProviderName = new Map(
186873
+ preparedTools.map((prepared) => [prepared.providerName, prepared.tool])
186874
+ );
186875
+ const tools = preparedTools.map(({ tool, providerName }) => {
186876
+ if (tool.inputSchema.type !== "object") {
186877
+ throw new Error(`Tool '${tool.name}' must expose an object input schema.`);
186537
186878
  }
186538
- finalMessage = latest ?? await runner.done();
186539
- } else {
186540
- finalMessage = await client.messages.create({
186879
+ return {
186880
+ name: providerName,
186881
+ description: tool.description || `Run ${tool.name}`,
186882
+ input_schema: tool.inputSchema
186883
+ };
186884
+ });
186885
+ const toolHistory = new AgentToolResultHistory();
186886
+ for (let turn = 1; turn <= request.maxTurns; turn++) {
186887
+ const call = {
186541
186888
  model: request.modelId,
186542
186889
  max_tokens: request.maxTokens,
186543
186890
  system: request.systemPrompt,
186544
- messages,
186891
+ messages: betaMessages,
186892
+ tools,
186893
+ tool_choice: { type: "auto", disable_parallel_tool_use: true },
186545
186894
  output_config: outputConfig,
186546
186895
  thinking
186896
+ };
186897
+ assertInputBudgetAllowsCall(
186898
+ this.provider,
186899
+ request.inputTokenBudget,
186900
+ estimateSerializedInputTokens(call),
186901
+ turns
186902
+ );
186903
+ const response = await client.beta.messages.create(
186904
+ call,
186905
+ request.signal ? { signal: request.signal } : void 0
186906
+ );
186907
+ recordAgentTurnUsage(this.provider, request.inputTokenBudget, turns, {
186908
+ inputTokens: response.usage.input_tokens + (response.usage.cache_creation_input_tokens ?? 0) + (response.usage.cache_read_input_tokens ?? 0),
186909
+ outputTokens: response.usage.output_tokens,
186910
+ ...typeof response.usage.cache_read_input_tokens === "number" ? { cachedInputTokens: response.usage.cache_read_input_tokens } : {},
186911
+ ...typeof response.usage.cache_creation_input_tokens === "number" ? { cacheWriteInputTokens: response.usage.cache_creation_input_tokens } : {}
186912
+ });
186913
+ const calls = response.content.filter((block) => block.type === "tool_use");
186914
+ if (calls.length === 0) {
186915
+ return this.completeResponse(request, response, toolCalls, turns);
186916
+ }
186917
+ if (turn >= request.maxTurns) {
186918
+ throw new AgentTurnLimitError(
186919
+ this.provider,
186920
+ request.maxTurns,
186921
+ toolCalls.length,
186922
+ toolCalls[toolCalls.length - 1]?.name,
186923
+ sumAgentTurnUsage(turns)
186924
+ );
186925
+ }
186926
+ betaMessages.push({
186927
+ role: "assistant",
186928
+ content: response.content
186547
186929
  });
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;
186930
+ const resultBlocks = [];
186931
+ for (const callBlock of calls) {
186932
+ const tool = toolsByProviderName.get(callBlock.name);
186933
+ if (!tool) {
186934
+ throw new Error(`Anthropic requested unknown tool '${callBlock.name}'.`);
186935
+ }
186936
+ const input2 = parseToolInput(callBlock.input);
186937
+ const recorded = createRecordedToolCall(toolCalls, tool.name, input2, tool.kind);
186938
+ let preparedOutput;
186939
+ try {
186940
+ const output2 = await tool.execute(input2);
186941
+ preparedOutput = normalizePreparedAgentToolResult(output2);
186942
+ recorded.complete(preparedOutput.recordedOutput);
186943
+ } catch (error2) {
186944
+ const output2 = { error: error2 instanceof Error ? error2.message : String(error2) };
186945
+ recorded.complete(output2);
186946
+ if (error2 instanceof AgentToolFatalError) {
186947
+ throw error2;
186948
+ }
186949
+ preparedOutput = normalizePreparedAgentToolResult(output2);
186950
+ }
186951
+ const resultBlock = {
186952
+ type: "tool_result",
186953
+ tool_use_id: callBlock.id,
186954
+ content: preparedOutput.immediateText
186955
+ };
186956
+ resultBlocks.push(resultBlock);
186957
+ toolHistory.retain(() => {
186958
+ resultBlock.content = preparedOutput.historyPlaceholder;
186959
+ });
186960
+ }
186961
+ betaMessages.push({ role: "user", content: resultBlocks });
186550
186962
  }
186963
+ throw new AgentTurnLimitError(
186964
+ this.provider,
186965
+ request.maxTurns,
186966
+ toolCalls.length,
186967
+ toolCalls[toolCalls.length - 1]?.name,
186968
+ sumAgentTurnUsage(turns)
186969
+ );
186970
+ }
186971
+ completeResponse(request, finalMessage, toolCalls, turns) {
186551
186972
  const text = getVisibleText(finalMessage, Boolean(request.outputSchema));
186552
186973
  if (finalMessage.stop_reason === "refusal") {
186553
186974
  throw new AgentProviderRefusalError(`Anthropic model refused the request${text ? `: ${text}` : "."}`, text);
@@ -186560,7 +186981,8 @@ var AnthropicAgentAdapter = class {
186560
186981
  this.provider,
186561
186982
  request.maxTurns,
186562
186983
  toolCalls.length,
186563
- toolCalls[toolCalls.length - 1]?.name
186984
+ toolCalls[toolCalls.length - 1]?.name,
186985
+ sumAgentTurnUsage(turns)
186564
186986
  );
186565
186987
  }
186566
186988
  if (finalMessage.stop_reason === "model_context_window_exceeded") {
@@ -186573,7 +186995,7 @@ var AnthropicAgentAdapter = class {
186573
186995
  text,
186574
186996
  parsed: tryParseStructuredAgentOutput(text, Boolean(request.outputSchema)),
186575
186997
  toolCalls,
186576
- usage: { inputTokens, outputTokens },
186998
+ usage: sumAgentTurnUsage(turns),
186577
186999
  resolvedModelId: finalMessage.model || request.modelId
186578
187000
  };
186579
187001
  }
@@ -186759,6 +187181,7 @@ var GoogleAgentAdapter = class {
186759
187181
  const config2 = {
186760
187182
  systemInstruction: request.systemPrompt,
186761
187183
  maxOutputTokens: request.maxTokens,
187184
+ ...request.signal ? { abortSignal: request.signal } : {},
186762
187185
  ...request.outputSchema ? {
186763
187186
  responseMimeType: "application/json",
186764
187187
  responseJsonSchema: request.outputSchema
@@ -186777,18 +187200,28 @@ var GoogleAgentAdapter = class {
186777
187200
  automaticFunctionCalling: { disable: true }
186778
187201
  } : {}
186779
187202
  };
186780
- let inputTokens = 0;
186781
- let outputTokens = 0;
186782
187203
  let resolvedModelId2 = request.modelId;
187204
+ const turns = [];
187205
+ const toolHistory = new AgentToolResultHistory();
186783
187206
  for (let turn = 0; turn < request.maxTurns; turn++) {
187207
+ const estimatedInputTokens = estimateSerializedInputTokens({ contents, config: config2 });
187208
+ assertInputBudgetAllowsCall(
187209
+ this.provider,
187210
+ request.inputTokenBudget,
187211
+ estimatedInputTokens,
187212
+ turns
187213
+ );
186784
187214
  const response = await client.models.generateContent({
186785
187215
  model: request.modelId,
186786
187216
  contents,
186787
187217
  config: config2
186788
187218
  });
186789
187219
  const usage = response.usageMetadata;
186790
- inputTokens += (usage?.promptTokenCount ?? 0) + (usage?.toolUsePromptTokenCount ?? 0);
186791
- outputTokens += (usage?.candidatesTokenCount ?? 0) + (usage?.thoughtsTokenCount ?? 0);
187220
+ recordAgentTurnUsage(this.provider, request.inputTokenBudget, turns, {
187221
+ inputTokens: usage ? (usage.promptTokenCount ?? 0) + (usage.toolUsePromptTokenCount ?? 0) : estimatedInputTokens,
187222
+ outputTokens: (usage?.candidatesTokenCount ?? 0) + (usage?.thoughtsTokenCount ?? 0),
187223
+ ...usage?.cachedContentTokenCount !== void 0 ? { cachedInputTokens: usage.cachedContentTokenCount } : {}
187224
+ });
186792
187225
  resolvedModelId2 = response.modelVersion || resolvedModelId2;
186793
187226
  const requestedCalls = getFunctionCalls(response);
186794
187227
  const text = getVisibleText2(
@@ -186803,7 +187236,7 @@ var GoogleAgentAdapter = class {
186803
187236
  text,
186804
187237
  parsed: tryParseStructuredAgentOutput(text, Boolean(request.outputSchema)),
186805
187238
  toolCalls,
186806
- usage: { inputTokens, outputTokens },
187239
+ usage: sumAgentTurnUsage(turns),
186807
187240
  resolvedModelId: resolvedModelId2
186808
187241
  };
186809
187242
  }
@@ -186817,7 +187250,8 @@ var GoogleAgentAdapter = class {
186817
187250
  this.provider,
186818
187251
  request.maxTurns,
186819
187252
  toolCalls.length,
186820
- toolCalls[toolCalls.length - 1]?.name
187253
+ toolCalls[toolCalls.length - 1]?.name,
187254
+ sumAgentTurnUsage(turns)
186821
187255
  );
186822
187256
  }
186823
187257
  const modelContent = getCandidate(response)?.content;
@@ -186838,8 +187272,12 @@ var GoogleAgentAdapter = class {
186838
187272
  let responseBody;
186839
187273
  try {
186840
187274
  const output2 = await tool.execute(input2);
186841
- recorded.complete(output2);
186842
- responseBody = { output: output2 };
187275
+ const preparedOutput = normalizePreparedAgentToolResult(output2);
187276
+ recorded.complete(preparedOutput.recordedOutput);
187277
+ responseBody = { output: preparedOutput.immediateOutput };
187278
+ toolHistory.retain(() => {
187279
+ responseBody.output = preparedOutput.historyPlaceholder;
187280
+ });
186843
187281
  } catch (error2) {
186844
187282
  const message = error2 instanceof Error ? error2.message : String(error2);
186845
187283
  const output2 = { error: message };
@@ -186864,7 +187302,8 @@ var GoogleAgentAdapter = class {
186864
187302
  this.provider,
186865
187303
  request.maxTurns,
186866
187304
  toolCalls.length,
186867
- toolCalls[toolCalls.length - 1]?.name
187305
+ toolCalls[toolCalls.length - 1]?.name,
187306
+ sumAgentTurnUsage(turns)
186868
187307
  );
186869
187308
  }
186870
187309
  };
@@ -187108,7 +187547,7 @@ var safeJSON2 = (text) => {
187108
187547
  };
187109
187548
 
187110
187549
  // node_modules/openai/internal/utils/sleep.mjs
187111
- var sleep4 = (ms) => new Promise((resolve28) => setTimeout(resolve28, ms));
187550
+ var sleep3 = (ms) => new Promise((resolve28) => setTimeout(resolve28, ms));
187112
187551
 
187113
187552
  // node_modules/openai/version.mjs
187114
187553
  var VERSION4 = "6.49.0";
@@ -194427,7 +194866,7 @@ var Runs = class extends APIResource2 {
194427
194866
  }
194428
194867
  }
194429
194868
  }
194430
- await sleep4(sleepInterval);
194869
+ await sleep3(sleepInterval);
194431
194870
  break;
194432
194871
  //We return the run in any terminal state.
194433
194872
  case "requires_action":
@@ -195036,7 +195475,7 @@ var Files4 = class extends APIResource2 {
195036
195475
  const start = Date.now();
195037
195476
  let file2 = await this.retrieve(id);
195038
195477
  while (!file2.status || !TERMINAL_STATES.has(file2.status)) {
195039
- await sleep4(pollInterval);
195478
+ await sleep3(pollInterval);
195040
195479
  file2 = await this.retrieve(id);
195041
195480
  if (Date.now() - start > maxWait) {
195042
195481
  throw new APIConnectionTimeoutError3({
@@ -196732,7 +197171,7 @@ var FileBatches = class extends APIResource2 {
196732
197171
  }
196733
197172
  }
196734
197173
  }
196735
- await sleep4(sleepInterval);
197174
+ await sleep3(sleepInterval);
196736
197175
  break;
196737
197176
  case "failed":
196738
197177
  case "cancelled":
@@ -196872,7 +197311,7 @@ var Files5 = class extends APIResource2 {
196872
197311
  }
196873
197312
  }
196874
197313
  }
196875
- await sleep4(sleepInterval);
197314
+ await sleep3(sleepInterval);
196876
197315
  break;
196877
197316
  case "failed":
196878
197317
  case "completed":
@@ -197648,7 +198087,7 @@ var OpenAI = class {
197648
198087
  const maxRetries = options.maxRetries ?? this.maxRetries;
197649
198088
  timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries);
197650
198089
  }
197651
- await sleep4(timeoutMillis);
198090
+ await sleep3(timeoutMillis);
197652
198091
  return this.makeRequest(options, retriesRemaining - 1, requestLogID);
197653
198092
  }
197654
198093
  calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries) {
@@ -197816,6 +198255,93 @@ function isUndiciDispatcherVersionMismatchError(error2) {
197816
198255
  return false;
197817
198256
  }
197818
198257
 
198258
+ // node_modules/openai/lib/responses/ResponseInputItems.mjs
198259
+ function toResponseInputItems(items) {
198260
+ const inputItems = [];
198261
+ for (const item of items) {
198262
+ const inputItem = toResponseInputItem(item);
198263
+ if (inputItem) {
198264
+ inputItems.push(inputItem);
198265
+ }
198266
+ }
198267
+ return inputItems;
198268
+ }
198269
+ function toResponseInputItem(item) {
198270
+ switch (item.type) {
198271
+ case "additional_tools": {
198272
+ if (item.role !== "developer") {
198273
+ return null;
198274
+ }
198275
+ return stripCreatedBy(item);
198276
+ }
198277
+ case "shell_call_output": {
198278
+ const output2 = item.output.map((chunk) => stripCreatedBy(chunk));
198279
+ return {
198280
+ ...stripCreatedBy(item),
198281
+ output: output2
198282
+ };
198283
+ }
198284
+ case "computer_call_output": {
198285
+ const { created_by: _createdBy, ...withoutCreatedBy } = item;
198286
+ if (withoutCreatedBy.status === "failed") {
198287
+ return null;
198288
+ }
198289
+ return withoutCreatedBy;
198290
+ }
198291
+ case "custom_tool_call_output": {
198292
+ if ("status" in item && item.status !== "completed") {
198293
+ return null;
198294
+ }
198295
+ const { created_by: _createdBy, status: _status, ...inputItem } = item;
198296
+ return inputItem;
198297
+ }
198298
+ case "apply_patch_call":
198299
+ case "apply_patch_call_output":
198300
+ case "code_interpreter_call":
198301
+ case "compaction":
198302
+ case "compaction_trigger":
198303
+ case "computer_call":
198304
+ case "custom_tool_call":
198305
+ case "file_search_call":
198306
+ case "function_call":
198307
+ case "function_call_output":
198308
+ case "image_generation_call":
198309
+ case "item_reference":
198310
+ case "local_shell_call":
198311
+ case "local_shell_call_output":
198312
+ case "mcp_approval_request":
198313
+ case "mcp_approval_response":
198314
+ case "mcp_call":
198315
+ case "mcp_list_tools":
198316
+ case "message":
198317
+ case "program":
198318
+ case "program_output":
198319
+ case "reasoning":
198320
+ case "shell_call":
198321
+ case "tool_search_call":
198322
+ case "tool_search_output":
198323
+ case "web_search_call":
198324
+ case null:
198325
+ case void 0: {
198326
+ return stripCreatedBy(item);
198327
+ }
198328
+ default: {
198329
+ return assertNever5(item);
198330
+ }
198331
+ }
198332
+ }
198333
+ function stripCreatedBy(item) {
198334
+ if (!("created_by" in item)) {
198335
+ return item;
198336
+ }
198337
+ const { created_by: _createdBy, ...rest } = item;
198338
+ return rest;
198339
+ }
198340
+ function assertNever5(value) {
198341
+ const type = value.type;
198342
+ throw new TypeError(`Unsupported response item type: ${String(type)}`);
198343
+ }
198344
+
197819
198345
  // src/agents/providers/openaiAdapter.ts
197820
198346
  function parseToolArguments(raw) {
197821
198347
  const parsed = JSON.parse(raw);
@@ -197825,7 +198351,251 @@ function parseToolArguments(raw) {
197825
198351
  return parsed;
197826
198352
  }
197827
198353
  var OpenAIAgentAdapter = class {
197828
- constructor(provider = "openai") {
198354
+ provider = "openai";
198355
+ createClient(request) {
198356
+ const apiKey = request.credentials?.apiKey ?? getScopedEnvironmentValue(request.environment, "OPENAI_API_KEY");
198357
+ const baseURL = request.credentials?.baseUrl ?? getScopedEnvironmentValue(request.environment, "OPENAI_BASE_URL");
198358
+ try {
198359
+ return new OpenAI({
198360
+ ...apiKey ? { apiKey } : {},
198361
+ ...baseURL ? { baseURL } : {}
198362
+ });
198363
+ } catch (error2) {
198364
+ if (error2 instanceof Error && error2.message.includes("Missing credentials")) {
198365
+ throw new Error(
198366
+ "OpenAI authentication failed. Add an apiKey directive to the model block, for example: apiKey {{$env.OPENAI_API_KEY}}.",
198367
+ { cause: error2 }
198368
+ );
198369
+ }
198370
+ throw error2;
198371
+ }
198372
+ }
198373
+ async run(request) {
198374
+ if (request.provider !== this.provider) {
198375
+ throw new Error(`The openai provider adapter cannot run a ${request.provider} request.`);
198376
+ }
198377
+ try {
198378
+ return await this.runRequest(request);
198379
+ } catch (error2) {
198380
+ if (error2 instanceof AgentProviderRefusalError) {
198381
+ throw error2;
198382
+ }
198383
+ if (error2 instanceof OpenAI.APIError) {
198384
+ if (isAgentProviderMaxTokensRejection(error2)) {
198385
+ throw new AgentProviderMaxTokensError(
198386
+ this.provider,
198387
+ request.maxTokens,
198388
+ error2.message,
198389
+ error2
198390
+ );
198391
+ }
198392
+ const status = error2.status ? ` (HTTP ${error2.status})` : "";
198393
+ const requestId = error2.requestID ? ` [request ${error2.requestID}]` : "";
198394
+ throw new Error(`OpenAI API request failed${status}: ${error2.message}${requestId}`, {
198395
+ cause: error2
198396
+ });
198397
+ }
198398
+ throw error2;
198399
+ }
198400
+ }
198401
+ async runRequest(request) {
198402
+ const client = this.createClient(request);
198403
+ const toolCalls = [];
198404
+ const input2 = [
198405
+ ...(request.conversation ?? []).map((message) => ({
198406
+ type: "message",
198407
+ role: message.role,
198408
+ content: message.content
198409
+ })),
198410
+ {
198411
+ type: "message",
198412
+ role: "user",
198413
+ content: agentInputToText(request.input)
198414
+ }
198415
+ ];
198416
+ const preparedTools = prepareAgentProviderTools(request.tools);
198417
+ const tools = preparedTools.map(({ tool, providerName }) => ({
198418
+ type: "function",
198419
+ name: providerName,
198420
+ description: tool.description || `Run ${tool.name}`,
198421
+ parameters: tool.inputSchema,
198422
+ strict: false
198423
+ }));
198424
+ const toolsByProviderName = new Map(
198425
+ preparedTools.map((prepared) => [prepared.providerName, prepared])
198426
+ );
198427
+ const text = request.outputSchema ? {
198428
+ format: {
198429
+ type: "json_schema",
198430
+ name: toProviderSchemaName(request.outputSchemaName),
198431
+ schema: request.outputSchema,
198432
+ strict: true
198433
+ }
198434
+ } : void 0;
198435
+ let resolvedModelId2 = request.modelId;
198436
+ const turns = [];
198437
+ const toolHistory = new AgentToolResultHistory();
198438
+ for (let turn = 1; turn <= request.maxTurns; turn++) {
198439
+ const estimatedInputTokens = estimateSerializedInputTokens({
198440
+ instructions: request.systemPrompt,
198441
+ input: input2,
198442
+ tools,
198443
+ text
198444
+ });
198445
+ assertInputBudgetAllowsCall(
198446
+ this.provider,
198447
+ request.inputTokenBudget,
198448
+ estimatedInputTokens,
198449
+ turns
198450
+ );
198451
+ const response = await client.responses.create({
198452
+ model: request.modelId,
198453
+ instructions: request.systemPrompt,
198454
+ input: input2,
198455
+ max_output_tokens: request.maxTokens,
198456
+ store: false,
198457
+ parallel_tool_calls: false,
198458
+ ...request.reasoningEffort ? { reasoning: { effort: request.reasoningEffort } } : {},
198459
+ ...tools.length > 0 ? {
198460
+ tools,
198461
+ tool_choice: "auto",
198462
+ include: ["reasoning.encrypted_content"]
198463
+ } : {},
198464
+ ...text ? { text } : {}
198465
+ }, request.signal ? { signal: request.signal } : void 0);
198466
+ resolvedModelId2 = response.model || request.modelId;
198467
+ const turnInputTokens = response.usage?.input_tokens ?? estimatedInputTokens;
198468
+ const turnOutputTokens = response.usage?.output_tokens ?? 0;
198469
+ recordAgentTurnUsage(this.provider, request.inputTokenBudget, turns, {
198470
+ inputTokens: turnInputTokens,
198471
+ outputTokens: turnOutputTokens,
198472
+ ...response.usage?.input_tokens_details?.cached_tokens !== void 0 ? { cachedInputTokens: response.usage.input_tokens_details.cached_tokens } : {},
198473
+ ...response.usage?.input_tokens_details?.cache_write_tokens !== void 0 ? { cacheWriteInputTokens: response.usage.input_tokens_details.cache_write_tokens } : {}
198474
+ });
198475
+ const refusal = response.output.filter((item) => item.type === "message").flatMap((message) => message.content).find((content) => content.type === "refusal");
198476
+ if (refusal?.type === "refusal") {
198477
+ throw new AgentProviderRefusalError(
198478
+ `OpenAI model refused the request: ${refusal.refusal}`,
198479
+ response.output_text || refusal.refusal
198480
+ );
198481
+ }
198482
+ if (response.error) {
198483
+ throw new Error(`OpenAI response failed: ${response.error.message}`);
198484
+ }
198485
+ const calls = response.output.filter((item) => item.type === "function_call");
198486
+ if (calls.length === 0) {
198487
+ this.assertCompleted(response, request);
198488
+ const responseText = response.output_text || "";
198489
+ return {
198490
+ text: responseText,
198491
+ parsed: tryParseStructuredAgentOutput(responseText, Boolean(request.outputSchema)),
198492
+ toolCalls,
198493
+ usage: sumAgentTurnUsage(turns),
198494
+ resolvedModelId: resolvedModelId2
198495
+ };
198496
+ }
198497
+ if (turn >= request.maxTurns) {
198498
+ throw new AgentTurnLimitError(
198499
+ this.provider,
198500
+ request.maxTurns,
198501
+ toolCalls.length,
198502
+ toolCalls[toolCalls.length - 1]?.name,
198503
+ sumAgentTurnUsage(turns)
198504
+ );
198505
+ }
198506
+ input2.push(...toResponseInputItems(response.output));
198507
+ for (const call of calls) {
198508
+ const prepared = toolsByProviderName.get(call.name);
198509
+ if (!prepared) {
198510
+ throw new Error(`OpenAI requested unknown agent tool '${call.name}'.`);
198511
+ }
198512
+ let toolInput = {};
198513
+ let output2;
198514
+ try {
198515
+ toolInput = parseToolArguments(call.arguments);
198516
+ const recorded = createRecordedToolCall(
198517
+ toolCalls,
198518
+ prepared.tool.name,
198519
+ toolInput,
198520
+ prepared.tool.kind
198521
+ );
198522
+ try {
198523
+ output2 = await prepared.tool.execute(toolInput);
198524
+ recorded.complete(normalizePreparedAgentToolResult(output2).recordedOutput);
198525
+ } catch (error2) {
198526
+ output2 = { error: error2 instanceof Error ? error2.message : String(error2) };
198527
+ recorded.complete(output2);
198528
+ if (error2 instanceof AgentToolFatalError) {
198529
+ throw error2;
198530
+ }
198531
+ }
198532
+ } catch (error2) {
198533
+ if (error2 instanceof AgentToolFatalError) {
198534
+ throw error2;
198535
+ }
198536
+ output2 = { error: error2 instanceof Error ? error2.message : String(error2) };
198537
+ const recorded = createRecordedToolCall(
198538
+ toolCalls,
198539
+ prepared.tool.name,
198540
+ toolInput,
198541
+ prepared.tool.kind
198542
+ );
198543
+ recorded.complete(output2);
198544
+ }
198545
+ const preparedOutput = normalizePreparedAgentToolResult(output2);
198546
+ const outputItem = {
198547
+ type: "function_call_output",
198548
+ call_id: call.call_id,
198549
+ output: preparedOutput.immediateText
198550
+ };
198551
+ input2.push(outputItem);
198552
+ toolHistory.retain(() => {
198553
+ outputItem.output = preparedOutput.historyPlaceholder;
198554
+ });
198555
+ }
198556
+ }
198557
+ throw new AgentTurnLimitError(
198558
+ this.provider,
198559
+ request.maxTurns,
198560
+ toolCalls.length,
198561
+ toolCalls[toolCalls.length - 1]?.name,
198562
+ sumAgentTurnUsage(turns)
198563
+ );
198564
+ }
198565
+ assertCompleted(response, request) {
198566
+ if (response.status === "incomplete") {
198567
+ if (response.incomplete_details?.reason === "content_filter") {
198568
+ throw new AgentProviderRefusalError(
198569
+ "OpenAI blocked the response through its content filter.",
198570
+ response.output_text || ""
198571
+ );
198572
+ }
198573
+ if (response.incomplete_details?.reason === "max_output_tokens") {
198574
+ throw new Error(
198575
+ `OpenAI model reached its ${request.maxTokens}-token completion limit before finishing.`
198576
+ );
198577
+ }
198578
+ throw new Error("OpenAI returned an incomplete response.");
198579
+ }
198580
+ if (response.status === "failed" || response.status === "cancelled") {
198581
+ throw new Error(`OpenAI response ended with status '${response.status}'.`);
198582
+ }
198583
+ if (response.status === "in_progress" || response.status === "queued") {
198584
+ throw new Error(`OpenAI returned unexpected non-terminal status '${response.status}'.`);
198585
+ }
198586
+ }
198587
+ };
198588
+
198589
+ // src/agents/providers/openaiCompatibleAdapter.ts
198590
+ function parseToolArguments2(raw) {
198591
+ const parsed = JSON.parse(raw);
198592
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
198593
+ throw new Error("Tool input must be a JSON object.");
198594
+ }
198595
+ return parsed;
198596
+ }
198597
+ var OpenAICompatibleAgentAdapter = class {
198598
+ constructor(provider = "openai_compatible") {
197829
198599
  this.provider = provider;
197830
198600
  }
197831
198601
  createClient(request) {
@@ -197839,17 +198609,19 @@ var OpenAIAgentAdapter = class {
197839
198609
  const apiKey2 = request.credentials?.apiKey ?? getEnvironmentValue(request.environment, "LOCAL_API_KEY") ?? "norn-local";
197840
198610
  return new OpenAI({ apiKey: apiKey2, baseURL: baseURL2 });
197841
198611
  }
197842
- const apiKey = request.credentials?.apiKey ?? getScopedEnvironmentValue(request.environment, "OPENAI_API_KEY");
197843
- const baseURL = request.credentials?.baseUrl ?? getScopedEnvironmentValue(request.environment, "OPENAI_BASE_URL");
198612
+ const apiKey = request.credentials?.apiKey ?? getScopedEnvironmentValue(request.environment, "OPENAI_COMPATIBLE_API_KEY") ?? "norn-openai-compatible";
198613
+ const baseURL = request.credentials?.baseUrl ?? getScopedEnvironmentValue(request.environment, "OPENAI_COMPATIBLE_BASE_URL");
198614
+ if (!baseURL) {
198615
+ throw new Error(
198616
+ "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."
198617
+ );
198618
+ }
197844
198619
  try {
197845
- return new OpenAI({
197846
- ...apiKey ? { apiKey } : {},
197847
- ...baseURL ? { baseURL } : {}
197848
- });
198620
+ return new OpenAI({ apiKey, baseURL });
197849
198621
  } catch (error2) {
197850
198622
  if (error2 instanceof Error && error2.message.includes("Missing credentials")) {
197851
198623
  throw new Error(
197852
- "OpenAI authentication failed. Add an apiKey directive to the model block, for example: apiKey {{$env.OPENAI_API_KEY}}.",
198624
+ "OpenAI-compatible authentication failed. Add an apiKey directive to the model block.",
197853
198625
  { cause: error2 }
197854
198626
  );
197855
198627
  }
@@ -197875,7 +198647,7 @@ var OpenAIAgentAdapter = class {
197875
198647
  error2
197876
198648
  );
197877
198649
  }
197878
- const providerLabel = this.provider === "local" ? "Local OpenAI-compatible" : "OpenAI";
198650
+ const providerLabel = this.provider === "local" ? "Local OpenAI-compatible" : "OpenAI-compatible";
197879
198651
  const status = error2.status ? ` (HTTP ${error2.status})` : "";
197880
198652
  const requestId = error2.requestID ? ` [request ${error2.requestID}]` : "";
197881
198653
  throw new Error(`${providerLabel} API request failed${status}: ${error2.message}${requestId}`, {
@@ -197904,82 +198676,128 @@ var OpenAIAgentAdapter = class {
197904
198676
  strict: true
197905
198677
  }
197906
198678
  } : void 0;
197907
- const tokenLimit = this.provider === "local" ? { max_tokens: request.maxTokens } : { max_completion_tokens: request.maxTokens };
197908
- let text = "";
198679
+ const tokenLimit = { max_tokens: request.maxTokens };
197909
198680
  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) => {
198681
+ const preparedTools = prepareAgentProviderTools(request.tools);
198682
+ const toolsByProviderName = new Map(
198683
+ preparedTools.map((prepared) => [prepared.providerName, prepared.tool])
198684
+ );
198685
+ const tools = preparedTools.map(({ tool, providerName }) => ({
198686
+ type: "function",
198687
+ function: {
198688
+ name: providerName,
198689
+ description: tool.description || `Run ${tool.name}`,
198690
+ parameters: tool.inputSchema
198691
+ }
198692
+ }));
198693
+ const turns = [];
198694
+ const toolHistory = new AgentToolResultHistory();
198695
+ for (let turn = 1; turn <= request.maxTurns; turn++) {
198696
+ const call = {
198697
+ model: request.modelId,
198698
+ messages,
198699
+ ...tools.length > 0 ? {
198700
+ tools,
198701
+ tool_choice: "auto",
198702
+ parallel_tool_calls: false
198703
+ } : {},
198704
+ response_format: responseFormat,
198705
+ ...tokenLimit
198706
+ };
198707
+ const estimatedInputTokens = estimateSerializedInputTokens(call);
198708
+ assertInputBudgetAllowsCall(
198709
+ this.provider,
198710
+ request.inputTokenBudget,
198711
+ estimatedInputTokens,
198712
+ turns
198713
+ );
198714
+ const completion = await client.chat.completions.create(
198715
+ call,
198716
+ request.signal ? { signal: request.signal } : void 0
198717
+ );
198718
+ const message = completion.choices[0]?.message;
198719
+ if (!message) {
198720
+ throw new Error("OpenAI returned no assistant message.");
198721
+ }
198722
+ resolvedModelId2 = completion.model || request.modelId;
198723
+ recordAgentTurnUsage(this.provider, request.inputTokenBudget, turns, {
198724
+ inputTokens: completion.usage?.prompt_tokens ?? estimatedInputTokens,
198725
+ outputTokens: completion.usage?.completion_tokens ?? 0,
198726
+ ...completion.usage?.prompt_tokens_details?.cached_tokens !== void 0 ? { cachedInputTokens: completion.usage.prompt_tokens_details.cached_tokens } : {}
198727
+ });
198728
+ const requestedCalls = message.tool_calls?.filter((call2) => call2.type === "function") ?? [];
198729
+ const text = message.content || "";
198730
+ if (requestedCalls.length === 0) {
198731
+ this.assertCompleted(message, completion.choices[0]?.finish_reason, text, request, toolCalls);
198732
+ return {
198733
+ text,
198734
+ parsed: tryParseStructuredAgentOutput(text, Boolean(request.outputSchema)),
198735
+ toolCalls,
198736
+ usage: sumAgentTurnUsage(turns),
198737
+ resolvedModelId: resolvedModelId2
198738
+ };
198739
+ }
198740
+ if (turn >= request.maxTurns) {
198741
+ throw new AgentTurnLimitError(
198742
+ this.provider,
198743
+ request.maxTurns,
198744
+ toolCalls.length,
198745
+ toolCalls[toolCalls.length - 1]?.name,
198746
+ sumAgentTurnUsage(turns)
198747
+ );
198748
+ }
198749
+ messages.push(message);
198750
+ for (const callItem of requestedCalls) {
198751
+ const tool = toolsByProviderName.get(callItem.function.name);
198752
+ let input2 = {};
198753
+ let preparedOutput;
198754
+ if (!tool) {
198755
+ preparedOutput = normalizePreparedAgentToolResult({
198756
+ error: `Unknown tool '${callItem.function.name}'.`
198757
+ });
198758
+ } else {
198759
+ try {
198760
+ input2 = parseToolArguments2(callItem.function.arguments);
197921
198761
  const recorded = createRecordedToolCall(toolCalls, tool.name, input2, tool.kind);
197922
198762
  try {
197923
198763
  const output2 = await tool.execute(input2);
197924
- recorded.complete(output2);
197925
- return providerToolOutputToText(output2);
198764
+ preparedOutput = normalizePreparedAgentToolResult(output2);
198765
+ recorded.complete(preparedOutput.recordedOutput);
197926
198766
  } catch (error2) {
197927
198767
  const output2 = { error: error2 instanceof Error ? error2.message : String(error2) };
197928
198768
  recorded.complete(output2);
197929
198769
  if (error2 instanceof AgentToolFatalError) {
197930
198770
  throw error2;
197931
198771
  }
197932
- return providerToolOutputToText(output2);
198772
+ preparedOutput = normalizePreparedAgentToolResult(output2);
197933
198773
  }
198774
+ } catch (error2) {
198775
+ if (error2 instanceof AgentToolFatalError) {
198776
+ throw error2;
198777
+ }
198778
+ preparedOutput = normalizePreparedAgentToolResult({
198779
+ error: error2 instanceof Error ? error2.message : String(error2)
198780
+ });
197934
198781
  }
197935
198782
  }
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.");
198783
+ const toolMessage = {
198784
+ role: "tool",
198785
+ tool_call_id: callItem.id,
198786
+ content: preparedOutput.immediateText
198787
+ };
198788
+ messages.push(toolMessage);
198789
+ toolHistory.retain(() => {
198790
+ toolMessage.content = preparedOutput.historyPlaceholder;
198791
+ });
197969
198792
  }
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
198793
  }
197976
- return {
197977
- text,
197978
- parsed: tryParseStructuredAgentOutput(text, Boolean(request.outputSchema)),
197979
- toolCalls,
197980
- usage: { inputTokens, outputTokens },
197981
- resolvedModelId: resolvedModelId2
197982
- };
198794
+ throw new AgentTurnLimitError(
198795
+ this.provider,
198796
+ request.maxTurns,
198797
+ toolCalls.length,
198798
+ toolCalls[toolCalls.length - 1]?.name,
198799
+ sumAgentTurnUsage(turns)
198800
+ );
197983
198801
  }
197984
198802
  assertCompleted(message, finishReason, rawText, request, toolCalls) {
197985
198803
  if (message.refusal) {
@@ -198020,8 +198838,9 @@ var StaticAgentProviderRegistry = class {
198020
198838
  };
198021
198839
  function createDefaultAgentProviderRegistry() {
198022
198840
  return new StaticAgentProviderRegistry([
198023
- new OpenAIAgentAdapter("openai"),
198024
- new OpenAIAgentAdapter("local"),
198841
+ new OpenAIAgentAdapter(),
198842
+ new OpenAICompatibleAgentAdapter("openai_compatible"),
198843
+ new OpenAICompatibleAgentAdapter("local"),
198025
198844
  new AnthropicAgentAdapter(),
198026
198845
  new GoogleAgentAdapter()
198027
198846
  ]);
@@ -198223,21 +199042,15 @@ function getContractDisplaySeverity(assertion) {
198223
199042
  // src/knowledge/knowledgeTypes.ts
198224
199043
  var KNOWLEDGE_TOKEN_ESTIMATOR_VERSION = "knowledge-token-estimate-v1";
198225
199044
 
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
199045
  // src/agents/agentConfig.ts
198235
199046
  function resolveAgentRuntimeLimits(startPath, provider, agent) {
198236
199047
  const loadedConfig = startPath && findNearestConfigFile(startPath, NORN_CONFIG_FILENAME) ? loadNornConfig(startPath).config.agents : void 0;
198237
199048
  const providerConfig = loadedConfig?.providers?.[provider];
198238
199049
  const defaultMaxTokens = provider === "local" ? DEFAULT_LOCAL_AGENT_MAX_TOKENS : DEFAULT_AGENT_MAX_TOKENS;
199050
+ const maxInputTokens = agent.maxInputTokens ?? providerConfig?.max_input_tokens ?? loadedConfig?.max_input_tokens;
198239
199051
  return {
198240
199052
  maxTokens: agent.maxTokens ?? providerConfig?.max_tokens ?? loadedConfig?.max_tokens ?? defaultMaxTokens,
199053
+ ...maxInputTokens !== void 0 ? { maxInputTokens } : {},
198241
199054
  maxDepth: agent.maxDepth ?? providerConfig?.max_depth ?? loadedConfig?.max_depth ?? DEFAULT_AGENT_MAX_DEPTH,
198242
199055
  maxInvocations: agent.maxInvocations ?? providerConfig?.max_invocations ?? loadedConfig?.max_invocations ?? DEFAULT_AGENT_MAX_INVOCATIONS,
198243
199056
  maxTurns: agent.maxTurns ?? providerConfig?.max_turns ?? loadedConfig?.max_turns ?? DEFAULT_AGENT_MAX_TURNS,
@@ -198421,7 +199234,7 @@ function adaptAgentSchemaForGeneration(original, provider) {
198421
199234
  if (provider === "anthropic" && containsRecursiveReference(original)) {
198422
199235
  throw new AgentSchemaAdaptationError("Anthropic structured outputs do not support recursive JSON Schemas.");
198423
199236
  }
198424
- if ((provider === "openai" || provider === "local") && getMaximumObjectDepth(original) > 5) {
199237
+ if ((provider === "openai" || provider === "openai_compatible" || provider === "local") && getMaximumObjectDepth(original) > 5) {
198425
199238
  throw new AgentSchemaAdaptationError("Norn limits OpenAI generation schemas to at most 5 nested object levels.");
198426
199239
  }
198427
199240
  const strippedKeywords = /* @__PURE__ */ new Set();
@@ -198477,7 +199290,7 @@ function adaptAgentSchemaForGeneration(original, provider) {
198477
199290
  transformed.additionalProperties = false;
198478
199291
  additionalPropertiesChanges++;
198479
199292
  }
198480
- if ((provider === "openai" || provider === "local") && properties) {
199293
+ if ((provider === "openai" || provider === "openai_compatible" || provider === "local") && properties) {
198481
199294
  const authoredRequired = new Set(
198482
199295
  Array.isArray(schemaValue.required) ? schemaValue.required.filter((entry) => typeof entry === "string") : []
198483
199296
  );
@@ -198531,6 +199344,19 @@ var DEFAULT_TOOL_INPUT_SCHEMA = {
198531
199344
  required: ["input"],
198532
199345
  additionalProperties: false
198533
199346
  };
199347
+ function mergeAgentUsage(left, right) {
199348
+ const leftTurns = left.turns ?? [];
199349
+ const rightTurns = right.turns ?? [];
199350
+ const turns = [...leftTurns, ...rightTurns].map((turn, index) => ({
199351
+ ...turn,
199352
+ turn: index + 1
199353
+ }));
199354
+ return {
199355
+ inputTokens: left.inputTokens + right.inputTokens,
199356
+ outputTokens: left.outputTokens + right.outputTokens,
199357
+ ...turns.length > 0 ? { turns } : {}
199358
+ };
199359
+ }
198534
199360
  function emptyAgentResult(hasReturns = false) {
198535
199361
  return {
198536
199362
  ...hasReturns ? { body: void 0 } : {},
@@ -198546,16 +199372,36 @@ function resolveAgentModelTarget(template, runtimeVariables) {
198546
199372
  if (/\{\{[^}]+\}\}/.test(resolved)) {
198547
199373
  throw new Error(`Could not resolve model target '${template}'.`);
198548
199374
  }
198549
- const match2 = resolved.match(/^(openai|anthropic|google|local)\/(.+)$/i);
199375
+ const match2 = resolved.match(/^(openai|openai_compatible|anthropic|google|local)\/(.+)$/i);
198550
199376
  if (!match2 || !match2[2].trim()) {
198551
199377
  throw new Error(
198552
- `Invalid resolved model '${resolved}'. Expected openai/<model>, anthropic/<model>, google/<model>, or local/<model>.`
199378
+ `Invalid resolved model '${resolved}'. Expected openai/<model>, openai_compatible/<model>, anthropic/<model>, google/<model>, or local/<model>.`
198553
199379
  );
198554
199380
  }
198555
199381
  const provider = match2[1].toLowerCase();
198556
199382
  const modelId = match2[2].trim();
198557
199383
  return { provider, modelId, displayName: `${provider}/${modelId}` };
198558
199384
  }
199385
+ function resolveAgentReasoningEffort(model, provider, runtimeVariables) {
199386
+ if (model.reasoningTemplate === void 0) {
199387
+ return void 0;
199388
+ }
199389
+ const resolved = substituteVariables(model.reasoningTemplate, runtimeVariables).trim().toLowerCase();
199390
+ if (/\{\{[^}]+\}\}/.test(resolved)) {
199391
+ throw new Error(`Could not resolve reasoning for model '${model.alias}'.`);
199392
+ }
199393
+ if (provider !== "openai") {
199394
+ throw new Error(
199395
+ `Model '${model.alias}' can declare reasoning only with provider openai; the resolved provider is '${provider}'.`
199396
+ );
199397
+ }
199398
+ if (!AGENT_REASONING_EFFORTS.some((effort) => effort === resolved)) {
199399
+ throw new Error(
199400
+ `Invalid resolved reasoning effort '${resolved}' for model '${model.alias}'. Expected one of: ${AGENT_REASONING_EFFORTS.join(", ")}.`
199401
+ );
199402
+ }
199403
+ return resolved;
199404
+ }
198559
199405
  function resolveAgentModelCredentials(model, runtimeVariables) {
198560
199406
  const resolveField = (template, directive) => {
198561
199407
  if (template === void 0) {
@@ -198660,8 +199506,11 @@ var AgentInvocationRunner = class {
198660
199506
  definitions = /* @__PURE__ */ new Map();
198661
199507
  conversations = /* @__PURE__ */ new Map();
198662
199508
  invocationBudget;
199509
+ inputTokenBudget;
198663
199510
  invocationCount = 0;
199511
+ toolResultStore = new AgentToolResultStore();
198664
199512
  async run(agent, input2) {
199513
+ throwIfSequenceCancelled(this.options.signal);
198665
199514
  const rootInvocationIndex2 = this.options.rootInvocationIndex ?? 1;
198666
199515
  if (!Number.isSafeInteger(rootInvocationIndex2) || rootInvocationIndex2 < 1) {
198667
199516
  throw new Error(`Agent root invocation index must be a positive integer, received '${rootInvocationIndex2}'.`);
@@ -198750,6 +199599,7 @@ var AgentInvocationRunner = class {
198750
199599
  provider: request.provider,
198751
199600
  modelId: request.modelId,
198752
199601
  displayModel: request.displayModel,
199602
+ ...request.reasoningEffort ? { reasoningEffort: request.reasoningEffort } : {},
198753
199603
  systemPrompt: request.systemPrompt,
198754
199604
  input: cloneSnapshotValue(request.input, null),
198755
199605
  conversation: (request.conversation ?? []).map((message) => ({ ...message })),
@@ -198757,6 +199607,7 @@ var AgentInvocationRunner = class {
198757
199607
  ...request.outputSchema ? { outputSchema: cloneSnapshotValue(request.outputSchema, {}) } : {},
198758
199608
  ...request.outputSchemaName ? { outputSchemaName: request.outputSchemaName } : {},
198759
199609
  maxTokens: request.maxTokens,
199610
+ ...request.inputTokenBudget ? { maxInputTokens: request.inputTokenBudget.maxInputTokens } : {},
198760
199611
  maxTurns: request.maxTurns,
198761
199612
  ...mcpServerGrants?.length ? { mcpServerGrants: mcpServerGrants.map((grant) => ({
198762
199613
  alias: grant.alias,
@@ -198775,7 +199626,25 @@ var AgentInvocationRunner = class {
198775
199626
  routingRule: knowledge.routing.rule,
198776
199627
  selectedDomainIds: knowledge.assembly.selectedDomainIds,
198777
199628
  estimatedTokens: knowledge.assembly.estimatedTokens,
198778
- estimatorVersion: KNOWLEDGE_TOKEN_ESTIMATOR_VERSION
199629
+ estimatorVersion: KNOWLEDGE_TOKEN_ESTIMATOR_VERSION,
199630
+ domains: knowledge.projection.domains.map((domain2) => {
199631
+ const selected = knowledge.assembly.selectedDomainIds.includes(domain2.encodedId);
199632
+ return {
199633
+ domainId: domain2.encodedId,
199634
+ declarationAlias: domain2.declarationAlias,
199635
+ sourcePath: domain2.sourcePath,
199636
+ domainPath: domain2.ref.domainPath,
199637
+ disposition: selected ? domain2.estimatedTokens <= knowledge.config.domainInlineTokenLimit ? "inline" : "index" : "manifest",
199638
+ estimatedTokens: domain2.estimatedTokens,
199639
+ topicCount: domain2.topics.length,
199640
+ topics: domain2.topics.map((topic) => ({
199641
+ topicHash: topic.topicHash,
199642
+ ...topic.headingText ? { heading: topic.headingText } : {},
199643
+ estimatedTokens: topic.estimatedTokens,
199644
+ itemPaths: [...new Set(topic.occurrences.map((occurrence) => occurrence.ref.itemPath))].sort()
199645
+ }))
199646
+ };
199647
+ })
198779
199648
  } } : {}
198780
199649
  };
198781
199650
  }
@@ -198796,7 +199665,7 @@ var AgentInvocationRunner = class {
198796
199665
  try {
198797
199666
  const output2 = await execute(input2, context);
198798
199667
  record2.output = cloneSnapshotValue(output2, null);
198799
- return output2;
199668
+ return definition.kind === "context" ? output2 : this.toolResultStore.prepare(definition.name, output2);
198800
199669
  } catch (error2) {
198801
199670
  record2.output = getToolErrorOutput(error2);
198802
199671
  throw error2;
@@ -198839,6 +199708,7 @@ var AgentInvocationRunner = class {
198839
199708
  return hop;
198840
199709
  }
198841
199710
  async invokeAgent(agent, input2, depth, parentInvocationId, ancestors, conversation, hopPath) {
199711
+ throwIfSequenceCancelled(this.options.signal);
198842
199712
  const startedAt = Date.now();
198843
199713
  const invocationId = this.nextInvocationId();
198844
199714
  const hop = {
@@ -198869,6 +199739,7 @@ var AgentInvocationRunner = class {
198869
199739
  let resolvedModel;
198870
199740
  let fatalToolError;
198871
199741
  let preparedKnowledge;
199742
+ let accumulatedProviderUsage = { inputTokens: 0, outputTokens: 0 };
198872
199743
  try {
198873
199744
  const requestOverride = await this.options.resolveRequestOverride?.(hopPath);
198874
199745
  const authoredModel = requestOverride?.provider && requestOverride.modelId ? void 0 : resolveAgentModelTarget(
@@ -198883,6 +199754,7 @@ var AgentInvocationRunner = class {
198883
199754
  modelId,
198884
199755
  displayName: requestOverride?.displayModel ?? `${provider}/${modelId}`
198885
199756
  };
199757
+ const reasoningEffort = requestOverride ? requestOverride.reasoningEffort : resolveAgentReasoningEffort(agent.model, provider, this.options.runtimeVariables);
198886
199758
  hop.output.model = resolvedModel.displayName;
198887
199759
  const configuredLimits = resolveAgentRuntimeLimits(
198888
199760
  this.options.startPath,
@@ -198891,7 +199763,8 @@ var AgentInvocationRunner = class {
198891
199763
  );
198892
199764
  const limits = {
198893
199765
  ...configuredLimits,
198894
- ...requestOverride?.maxTokens !== void 0 ? { maxTokens: requestOverride.maxTokens } : {}
199766
+ ...requestOverride?.maxTokens !== void 0 ? { maxTokens: requestOverride.maxTokens } : {},
199767
+ ...requestOverride?.maxInputTokens !== void 0 ? { maxInputTokens: requestOverride.maxInputTokens } : {}
198895
199768
  };
198896
199769
  if (!this.invocationBudget) {
198897
199770
  this.invocationBudget = {
@@ -198899,6 +199772,12 @@ var AgentInvocationRunner = class {
198899
199772
  maxInvocations: limits.maxInvocations
198900
199773
  };
198901
199774
  }
199775
+ if (depth === 0 && limits.maxInputTokens !== void 0) {
199776
+ this.inputTokenBudget = {
199777
+ maxInputTokens: limits.maxInputTokens,
199778
+ usedInputTokens: 0
199779
+ };
199780
+ }
198902
199781
  this.invocationCount++;
198903
199782
  this.addDefinition(agent, resolvedModel);
198904
199783
  const authoredSystemPrompt = requestOverride?.systemPrompt ?? substituteVariables(
@@ -198954,6 +199833,7 @@ var AgentInvocationRunner = class {
198954
199833
  provider: resolvedModel.provider,
198955
199834
  modelId: resolvedModel.modelId,
198956
199835
  displayModel: resolvedModel.displayName,
199836
+ ...reasoningEffort ? { reasoningEffort } : {},
198957
199837
  systemPrompt,
198958
199838
  input: input2,
198959
199839
  conversation,
@@ -198961,9 +199841,11 @@ var AgentInvocationRunner = class {
198961
199841
  outputSchema,
198962
199842
  outputSchemaName,
198963
199843
  maxTokens: limits.maxTokens,
199844
+ ...this.inputTokenBudget ? { inputTokenBudget: this.inputTokenBudget } : {},
198964
199845
  maxTurns: limits.maxTurns,
198965
199846
  environment: this.options.environment,
198966
- ...credentials ? { credentials } : {}
199847
+ ...credentials ? { credentials } : {},
199848
+ ...this.options.signal ? { signal: this.options.signal } : {}
198967
199849
  };
198968
199850
  hop.request = this.createRequestSnapshot(
198969
199851
  providerRequest,
@@ -198985,6 +199867,10 @@ var AgentInvocationRunner = class {
198985
199867
  ...providerRequest,
198986
199868
  conversation: correctionConversation
198987
199869
  });
199870
+ accumulatedProviderUsage = mergeAgentUsage(
199871
+ accumulatedProviderUsage,
199872
+ providerResponse.usage
199873
+ );
198988
199874
  hop.providerCompleted = true;
198989
199875
  if (fatalToolError) {
198990
199876
  throw fatalToolError;
@@ -198995,7 +199881,7 @@ var AgentInvocationRunner = class {
198995
199881
  text: providerResponse.text,
198996
199882
  toolCalls,
198997
199883
  model: `${resolvedModel.provider}/${providerResponse.resolvedModelId}`,
198998
- usage: providerResponse.usage,
199884
+ usage: accumulatedProviderUsage,
198999
199885
  ms: Date.now() - startedAt
199000
199886
  };
199001
199887
  const returnValue = providerResponse.parsed !== void 0 ? providerResponse.parsed : providerResponse.text;
@@ -199033,10 +199919,32 @@ var AgentInvocationRunner = class {
199033
199919
  }
199034
199920
  hop.success = true;
199035
199921
  } catch (error2) {
199922
+ throwIfSequenceCancelled(this.options.signal);
199036
199923
  const reportedError = fatalToolError ?? error2;
199037
199924
  if (reportedError instanceof AgentProviderMaxTokensError) {
199038
199925
  hop.error = formatAgentProviderMaxTokensError(agent.name, reportedError);
199926
+ } else if (reportedError instanceof AgentInputTokenLimitError) {
199927
+ accumulatedProviderUsage = mergeAgentUsage(
199928
+ accumulatedProviderUsage,
199929
+ reportedError.usage
199930
+ );
199931
+ hop.output.usage = accumulatedProviderUsage;
199932
+ hop.inputTokenLimit = {
199933
+ maxInputTokens: reportedError.maxInputTokens,
199934
+ usedInputTokens: reportedError.usedInputTokens,
199935
+ ...reportedError.estimatedNextInputTokens !== void 0 ? { estimatedNextInputTokens: reportedError.estimatedNextInputTokens } : {},
199936
+ sourcePath: agent.sourcePath,
199937
+ lineNumber: agent.lineNumber
199938
+ };
199939
+ hop.error = formatAgentInputTokenLimitError(agent.name, hop.inputTokenLimit);
199039
199940
  } else if (reportedError instanceof AgentTurnLimitError) {
199941
+ if (reportedError.usage) {
199942
+ accumulatedProviderUsage = mergeAgentUsage(
199943
+ accumulatedProviderUsage,
199944
+ reportedError.usage
199945
+ );
199946
+ hop.output.usage = accumulatedProviderUsage;
199947
+ }
199040
199948
  hop.turnLimit = {
199041
199949
  maxTurns: reportedError.maxTurns,
199042
199950
  toolCallCount: reportedError.toolCallCount,
@@ -199097,10 +200005,12 @@ var AgentInvocationRunner = class {
199097
200005
  overrideKeys.add(key);
199098
200006
  }
199099
200007
  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.");
200008
+ if (agent.callableAgents.some((reference) => ["load_knowledge", "norn_read_tool_result"].includes(reference.name.toLowerCase()))) {
200009
+ throw new Error(
200010
+ "Agent names 'load_knowledge' and 'norn_read_tool_result' are reserved by Norn."
200011
+ );
199102
200012
  }
199103
- if (knowledge) {
200013
+ if (knowledge?.hasLoadableKnowledge) {
199104
200014
  const offeredOverride = requestToolsOverride?.find((tool) => tool.kind === "knowledge");
199105
200015
  providerTools.push(this.createRecordedProviderTool(parentHop, {
199106
200016
  name: "load_knowledge",
@@ -199403,6 +200313,25 @@ var AgentInvocationRunner = class {
199403
200313
  return Object.prototype.hasOwnProperty.call(child.output, "body") ? child.output.body : child.output.text;
199404
200314
  }));
199405
200315
  }
200316
+ const offeredContext = requestToolsOverride?.find((tool) => tool.kind === "context");
200317
+ if (!requestToolsOverride && providerTools.length > 0 || offeredContext) {
200318
+ providerTools.push(this.createRecordedProviderTool(parentHop, {
200319
+ name: "norn_read_tool_result",
200320
+ providerName: offeredContext?.providerName ?? "norn_read_tool_result",
200321
+ kind: "context",
200322
+ description: offeredContext?.description ?? "Read or search a bounded portion of a tool result that Norn offloaded from active context.",
200323
+ inputSchema: offeredContext?.inputSchema ?? {
200324
+ type: "object",
200325
+ properties: {
200326
+ handle: { type: "string" },
200327
+ offset: { type: "integer", minimum: 0 },
200328
+ query: { type: "string" }
200329
+ },
200330
+ required: ["handle"],
200331
+ additionalProperties: false
200332
+ }
200333
+ }, async (input2) => this.toolResultStore.read(input2)));
200334
+ }
199406
200335
  if (requestToolsOverride) {
199407
200336
  const currentKeys = new Set(providerTools.map(
199408
200337
  (tool) => `${tool.kind ?? ""}\0${tool.name.toLowerCase()}`
@@ -200542,6 +201471,18 @@ function normalizeHopForDisk(hop, projectRoot) {
200542
201471
  ...extended.request !== void 0 ? {
200543
201472
  request: relativizeTopLevelSourcePaths(extended.request, projectRoot, true)
200544
201473
  } : {},
201474
+ ...hop.turnLimit ? {
201475
+ turnLimit: {
201476
+ ...hop.turnLimit,
201477
+ sourcePath: path26.isAbsolute(hop.turnLimit.sourcePath) ? projectRelativePath(projectRoot, hop.turnLimit.sourcePath) : hop.turnLimit.sourcePath
201478
+ }
201479
+ } : {},
201480
+ ...hop.inputTokenLimit ? {
201481
+ inputTokenLimit: {
201482
+ ...hop.inputTokenLimit,
201483
+ sourcePath: path26.isAbsolute(hop.inputTokenLimit.sourcePath) ? projectRelativePath(projectRoot, hop.inputTokenLimit.sourcePath) : hop.inputTokenLimit.sourcePath
201484
+ }
201485
+ } : {},
200545
201486
  children: hop.children.map((child) => normalizeHopForDisk(child, projectRoot))
200546
201487
  };
200547
201488
  return normalized;
@@ -200709,28 +201650,35 @@ function isSafeProjectRelativePath(value) {
200709
201650
  return isRelativePath(value) && !normalizePathSeparators(value).split("/").includes("..");
200710
201651
  }
200711
201652
  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");
201653
+ 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");
201654
+ }
201655
+ function isAgentKnowledgeTopicSnapshot(value) {
201656
+ return isObjectRecord2(value) && typeof value.topicHash === "string" && (value.heading === void 0 || typeof value.heading === "string") && isFiniteNonNegativeNumber(value.estimatedTokens) && isStringArray(value.itemPaths);
201657
+ }
201658
+ function isAgentKnowledgeDomainSnapshot(value) {
201659
+ 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
201660
  }
200714
201661
  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))) {
201662
+ 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
201663
  return false;
200717
201664
  }
200718
201665
  const validConversation = value.conversation.every(
200719
201666
  (message) => isObjectRecord2(message) && (message.role === "user" || message.role === "assistant") && typeof message.content === "string"
200720
201667
  );
200721
201668
  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)
201669
+ (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
201670
  );
200724
201671
  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
201672
  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));
201673
+ ) && 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));
201674
+ 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
201675
  }
200729
201676
  function isAgentResult(value) {
200730
201677
  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
201678
  return false;
200732
201679
  }
200733
- return isFiniteNonNegativeNumber(value.usage.inputTokens) && isFiniteNonNegativeNumber(value.usage.outputTokens);
201680
+ 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)));
201681
+ return isFiniteNonNegativeNumber(value.usage.inputTokens) && isFiniteNonNegativeNumber(value.usage.outputTokens) && validTurns;
200734
201682
  }
200735
201683
  function isAgentContract(value) {
200736
201684
  return isObjectRecord2(value) && (value.kind === "accepts" || value.kind === "returns") && typeof value.path === "string" && isObjectRecord2(value.assertion);
@@ -200742,7 +201690,7 @@ function isAgentHop(value) {
200742
201690
  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
201691
  return false;
200744
201692
  }
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);
201693
+ 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
201694
  }
200747
201695
  function isAgentTrace(value) {
200748
201696
  if (!isObjectRecord2(value) || !Array.isArray(value.agents) || !Array.isArray(value.hops)) {
@@ -202959,6 +203907,14 @@ function readKnowledgeSnapshot(startPath, snapshotId, options = {}) {
202959
203907
  function encodeKnowledgeId(parts) {
202960
203908
  return Buffer.from(canonicalJson(parts), "utf8").toString("base64url");
202961
203909
  }
203910
+ function decodeKnowledgeId(value) {
203911
+ try {
203912
+ const parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
203913
+ return Array.isArray(parsed) ? parsed : void 0;
203914
+ } catch {
203915
+ return void 0;
203916
+ }
203917
+ }
202962
203918
 
202963
203919
  // src/knowledge/knowledgeAssembly.ts
202964
203920
  function estimateKnowledgeTokens(rendered) {
@@ -202998,10 +203954,10 @@ function renderKnowledgeTopicIndex(domain2) {
202998
203954
  }))
202999
203955
  };
203000
203956
  }
203001
- function renderKnowledgeDomainBodies(domain2) {
203957
+ function renderKnowledgeDomainBodies(domain2, topics = domain2.topics) {
203002
203958
  return {
203003
203959
  domainId: domain2.encodedId,
203004
- topics: domain2.topics.map((topic) => ({
203960
+ topics: topics.map((topic) => ({
203005
203961
  topicHash: topic.topicHash,
203006
203962
  heading: topic.headingText,
203007
203963
  body: topic.body,
@@ -203363,20 +204319,30 @@ var KnowledgeRunCoordinator = class {
203363
204319
  const routing = routeKnowledge(this.projectRoot, input2, projection, config2, agent.name);
203364
204320
  const assembly = assembleKnowledgePrompt(authoredSystemPrompt, projection, routing, config2, agent.name);
203365
204321
  const loadEvents = [];
204322
+ const loadedTopicHashes = new Set(assembly.initiallyLoadedTopics.map((topic) => topic.topicHash));
204323
+ const loadedTopicIndexes = new Set(projection.domains.filter((domain2) => assembly.selectedDomainIds.includes(domain2.encodedId) && domain2.estimatedTokens > config2.domainInlineTokenLimit).map((domain2) => domain2.encodedId));
204324
+ const loadableDomains = projection.domains.filter(
204325
+ (domain2) => domain2.topics.some((topic) => !loadedTopicHashes.has(topic.topicHash)) || domain2.estimatedTokens > config2.domainInlineTokenLimit && !loadedTopicIndexes.has(domain2.encodedId)
204326
+ );
204327
+ const loadableTopicHashes = [...new Set(loadableDomains.flatMap((domain2) => domain2.topics).filter((topic) => !loadedTopicHashes.has(topic.topicHash)).map((topic) => topic.topicHash))];
203366
204328
  let usedTokens = 0;
203367
204329
  const tool = {
203368
204330
  name: "load_knowledge",
203369
204331
  providerName: "norn_load_knowledge",
203370
204332
  kind: "knowledge",
203371
- description: "Load one granted knowledge domain or a topic occurrence by the exact IDs in the manifest.",
204333
+ 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
204334
  inputSchema: { type: "object", properties: {
203373
- domainId: { type: "string" },
203374
- topicHash: { type: "string" },
204335
+ domainId: { type: "string", enum: loadableDomains.map((domain2) => domain2.encodedId) },
204336
+ topicHash: { type: "string", enum: loadableTopicHashes },
203375
204337
  occurrenceId: { type: "string" }
203376
204338
  }, required: ["domainId"], additionalProperties: false },
203377
204339
  execute: async (raw) => {
203378
204340
  const request = raw;
203379
204341
  const before = usedTokens;
204342
+ const alreadyLoaded = () => ({
204343
+ alreadyLoaded: true,
204344
+ message: "This knowledge has already been loaded."
204345
+ });
203380
204346
  const refuse = (code, message) => {
203381
204347
  loadEvents.push({
203382
204348
  order: loadEvents.length + 1,
@@ -203402,6 +204368,8 @@ var KnowledgeRunCoordinator = class {
203402
204368
  return refuse("unknown_or_ungranted_domain", "The requested domain ID is unknown or not granted.");
203403
204369
  }
203404
204370
  let value;
204371
+ let newlyLoadedTopicHashes = [];
204372
+ let loadedTopicIndex = false;
203405
204373
  if (request.topicHash) {
203406
204374
  const topic = domain2.topics.find((item) => item.topicHash === request.topicHash);
203407
204375
  if (!topic) {
@@ -203423,6 +204391,9 @@ var KnowledgeRunCoordinator = class {
203423
204391
  return refuse("unknown_or_ungranted_occurrence", "The occurrence ID is not exposed by this domain.");
203424
204392
  }
203425
204393
  }
204394
+ if (loadedTopicHashes.has(topic.topicHash)) {
204395
+ return alreadyLoaded();
204396
+ }
203426
204397
  value = { domainId: domain2.encodedId, topic: {
203427
204398
  topicHash: topic.topicHash,
203428
204399
  heading: topic.headingText,
@@ -203434,8 +204405,22 @@ var KnowledgeRunCoordinator = class {
203434
204405
  useWhen: item.metadata.useWhen?.value
203435
204406
  }))
203436
204407
  } };
204408
+ newlyLoadedTopicHashes = [topic.topicHash];
203437
204409
  } else {
203438
- value = domain2.estimatedTokens <= config2.domainInlineTokenLimit ? renderKnowledgeDomainBodies(domain2) : renderKnowledgeTopicIndex(domain2);
204410
+ if (domain2.estimatedTokens <= config2.domainInlineTokenLimit) {
204411
+ const unloadedTopics = domain2.topics.filter((topic) => !loadedTopicHashes.has(topic.topicHash));
204412
+ if (!unloadedTopics.length) {
204413
+ return alreadyLoaded();
204414
+ }
204415
+ value = renderKnowledgeDomainBodies(domain2, unloadedTopics);
204416
+ newlyLoadedTopicHashes = unloadedTopics.map((topic) => topic.topicHash);
204417
+ } else {
204418
+ if (loadedTopicIndexes.has(domain2.encodedId)) {
204419
+ return alreadyLoaded();
204420
+ }
204421
+ value = renderKnowledgeTopicIndex(domain2);
204422
+ loadedTopicIndex = true;
204423
+ }
203439
204424
  }
203440
204425
  const output2 = escapedCanonicalKnowledgeJson(value);
203441
204426
  const estimatedTokens = estimateKnowledgeTokens(output2);
@@ -203443,11 +204428,18 @@ var KnowledgeRunCoordinator = class {
203443
204428
  return refuse("tool_token_limit", `The request would exceed knowledge.toolTokenLimit=${config2.toolTokenLimit}.`);
203444
204429
  }
203445
204430
  usedTokens += estimatedTokens;
204431
+ for (const topicHash of newlyLoadedTopicHashes) {
204432
+ loadedTopicHashes.add(topicHash);
204433
+ }
204434
+ if (loadedTopicIndex) {
204435
+ loadedTopicIndexes.add(domain2.encodedId);
204436
+ }
203446
204437
  loadEvents.push({
203447
204438
  order: loadEvents.length + 1,
203448
204439
  request,
203449
204440
  accepted: true,
203450
204441
  estimatedTokens,
204442
+ loadedTopicHashes: newlyLoadedTopicHashes,
203451
204443
  budgetBefore: before,
203452
204444
  budgetAfter: usedTokens
203453
204445
  });
@@ -203455,7 +204447,17 @@ var KnowledgeRunCoordinator = class {
203455
204447
  }
203456
204448
  };
203457
204449
  void invocationId;
203458
- return { runId: this.runId, snapshotId, projection, routing, assembly, tool, loadEvents, config: config2 };
204450
+ return {
204451
+ runId: this.runId,
204452
+ snapshotId,
204453
+ projection,
204454
+ routing,
204455
+ assembly,
204456
+ tool,
204457
+ hasLoadableKnowledge: loadableDomains.length > 0,
204458
+ loadEvents,
204459
+ config: config2
204460
+ };
203459
204461
  }
203460
204462
  recordInvocation(agent, hop, knowledge) {
203461
204463
  this.invocations.push({ agent, hop, knowledge });
@@ -203510,7 +204512,8 @@ var KnowledgeRunCoordinator = class {
203510
204512
  accepts: item.hop.accepts,
203511
204513
  returns: item.hop.returns,
203512
204514
  judge: item.hop.judge,
203513
- turnLimit: item.hop.turnLimit
204515
+ turnLimit: item.hop.turnLimit,
204516
+ inputTokenLimit: item.hop.inputTokenLimit
203514
204517
  })
203515
204518
  );
203516
204519
  if (!item.knowledge) {
@@ -203614,7 +204617,11 @@ var KnowledgeRunCoordinator = class {
203614
204617
  event.estimatedTokens
203615
204618
  );
203616
204619
  } else if (event.accepted) {
204620
+ const loadedTopicHashes = event.loadedTopicHashes ? new Set(event.loadedTopicHashes) : void 0;
203617
204621
  for (const topic of domain2.topics) {
204622
+ if (loadedTopicHashes && !loadedTopicHashes.has(topic.topicHash)) {
204623
+ continue;
204624
+ }
203618
204625
  for (const occurrence of topic.occurrences) {
203619
204626
  index.run(
203620
204627
  `INSERT OR REPLACE INTO run_topics(run_id, invocation_id,
@@ -204824,9 +205831,6 @@ function valueToString2(value) {
204824
205831
  }
204825
205832
  return String(value);
204826
205833
  }
204827
- function sleep5(ms) {
204828
- return new Promise((resolve28) => setTimeout(resolve28, ms));
204829
- }
204830
205834
  function isIfCommand(line2) {
204831
205835
  return /^if\s+.+$/i.test(line2.trim());
204832
205836
  }
@@ -205333,6 +206337,10 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205333
206337
  };
205334
206338
  const runtimeVariables = copyEnvironmentScope({ ...fileVariables, ...sequenceArgs }, fileVariables);
205335
206339
  const mcpSessionManager = executionContext?.mcpSessionManager ?? new McpSessionManager();
206340
+ const cancelMcpSessions = () => {
206341
+ void mcpSessionManager.closeAll();
206342
+ };
206343
+ executionContext?.signal?.addEventListener("abort", cancelMcpSessions, { once: true });
205336
206344
  const agentProviderRegistry = executionContext?.agentProviderRegistry ?? createDefaultAgentProviderRegistry();
205337
206345
  const agentWarningKeys = executionContext?.agentWarningKeys ?? /* @__PURE__ */ new Set();
205338
206346
  const agentEventLog = executionContext?.agentEventLog ?? [];
@@ -205547,6 +206555,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205547
206555
  }
205548
206556
  };
205549
206557
  for (const step of steps) {
206558
+ throwIfSequenceCancelled(executionContext?.signal);
205550
206559
  if (step.type === "judge") {
205551
206560
  const parsedJudge = parseJudgeStep(step);
205552
206561
  const blockingError = parsedJudge.errors[0];
@@ -205644,6 +206653,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205644
206653
  try {
205645
206654
  await preflightAgentGraph(definition, scope);
205646
206655
  } catch (error2) {
206656
+ throwIfSequenceCancelled(executionContext?.signal);
205647
206657
  await mcpSessionManager.closeScope(MCP_PREFLIGHT_SCOPE);
205648
206658
  const message = `Agent '${definition.name}' tool preflight failed: ${error2 instanceof Error ? error2.message : String(error2)}`;
205649
206659
  errors.push(message);
@@ -205663,8 +206673,10 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205663
206673
  await mcpSessionManager.closeScope(MCP_PREFLIGHT_SCOPE);
205664
206674
  }
205665
206675
  for (let stepIdx = 0; stepIdx < steps.length; stepIdx++) {
206676
+ throwIfSequenceCancelled(executionContext?.signal);
205666
206677
  const step = steps[stepIdx];
205667
206678
  await emitBeforeStep(step, stepIdx);
206679
+ throwIfSequenceCancelled(executionContext?.signal);
205668
206680
  const stepStartTime = Date.now();
205669
206681
  try {
205670
206682
  if (step.type === "if") {
@@ -205752,7 +206764,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205752
206764
  if (step.type === "wait") {
205753
206765
  const durationMs = parseWaitCommand(step.content);
205754
206766
  reportProgress(stepIdx, "wait", `wait: ${durationMs}ms`);
205755
- await sleep5(durationMs);
206767
+ await cancellableDelay(durationMs, executionContext?.signal);
205756
206768
  const stepResult = createPrintStepResult(
205757
206769
  stepIdx,
205758
206770
  stepStartTime,
@@ -205954,6 +206966,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
205954
206966
  runLineNumber: debugLocation.absoluteLine,
205955
206967
  rootInvocationIndex: rootInvocationIndex2,
205956
206968
  knowledgeCoordinator,
206969
+ signal: executionContext?.signal,
205957
206970
  expandMcpServerGrants: (_hopPath, grantedAgent) => mcpGrantExpansions.get(agentDefinitionKey(grantedAgent)) ?? expandMcpGrantsLive(grantedAgent),
205958
206971
  onInvocationProgress: (agentEvent) => {
205959
206972
  const recordedEvent = cloneSerializableValue(agentEvent);
@@ -206012,6 +207025,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
206012
207025
  }
206013
207026
  }
206014
207027
  } catch (error2) {
207028
+ throwIfSequenceCancelled(executionContext?.signal);
206015
207029
  execution.error = error2 instanceof Error ? error2.message : String(error2);
206016
207030
  }
206017
207031
  const stepResult = {
@@ -206090,6 +207104,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206090
207104
  runLineNumber: debugLocation.absoluteLine,
206091
207105
  rootInvocationIndex: rootInvocationIndex2,
206092
207106
  knowledgeCoordinator,
207107
+ signal: executionContext?.signal,
206093
207108
  expandMcpServerGrants: (_hopPath, grantedAgent) => mcpGrantExpansions.get(agentDefinitionKey(grantedAgent)) ?? expandMcpGrantsLive(grantedAgent),
206094
207109
  onInvocationProgress: (agentEvent) => {
206095
207110
  const recordedEvent = cloneSerializableValue(agentEvent);
@@ -206116,6 +207131,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206116
207131
  addTraceDefinition(definition);
206117
207132
  }
206118
207133
  } catch (error2) {
207134
+ throwIfSequenceCancelled(executionContext?.signal);
206119
207135
  const message = error2 instanceof Error ? error2.message : String(error2);
206120
207136
  if (agent?.returns) {
206121
207137
  output2 = { ...output2, body: void 0 };
@@ -206314,7 +207330,8 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206314
207330
  workingDir: adapterWorkingDir
206315
207331
  }
206316
207332
  },
206317
- adapterWorkingDir
207333
+ adapterWorkingDir,
207334
+ executionContext?.signal
206318
207335
  );
206319
207336
  let storedValue;
206320
207337
  let sqlResult;
@@ -206361,6 +207378,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206361
207378
  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
207379
  reportProgress(stepIdx, "sql", description, stepResult);
206363
207380
  } catch (error2) {
207381
+ throwIfSequenceCancelled(executionContext?.signal);
206364
207382
  errors.push(`SQL execution failed for '${operation.name}': ${error2.message || String(error2)}`);
206365
207383
  await emitFailure(`SQL execution failed for '${operation.name}'`, step, stepIdx);
206366
207384
  return {
@@ -206414,6 +207432,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206414
207432
  orderedSteps.push(stepResult);
206415
207433
  reportProgress(stepIdx, "mcp", `run mcp list ${parsed.serverAlias} \u2192 ${listResult.tools.length} tool(s)`, stepResult);
206416
207434
  } catch (error2) {
207435
+ throwIfSequenceCancelled(executionContext?.signal);
206417
207436
  const message = `MCP list failed for '${parsed.serverAlias}': ${error2?.message || String(error2)}`;
206418
207437
  errors.push(message);
206419
207438
  await emitFailure(message, step, stepIdx);
@@ -206465,6 +207484,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206465
207484
  parsed.toolName
206466
207485
  );
206467
207486
  } catch (error2) {
207487
+ throwIfSequenceCancelled(executionContext?.signal);
206468
207488
  const message = `MCP call failed for '${parsed.serverAlias}.${parsed.toolName}': ${error2?.message || String(error2)}`;
206469
207489
  errors.push(message);
206470
207490
  await emitFailure(message, step, stepIdx);
@@ -206534,6 +207554,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206534
207554
  };
206535
207555
  }
206536
207556
  } catch (error2) {
207557
+ throwIfSequenceCancelled(executionContext?.signal);
206537
207558
  const message = `MCP call failed for '${parsed.serverAlias}.${parsed.toolName}': ${error2?.message || String(error2)}`;
206538
207559
  errors.push(message);
206539
207560
  await emitFailure(message, step, stepIdx);
@@ -206596,7 +207617,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206596
207617
  reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] ${requestDescription} - waiting ${waitMs}ms`, void 0);
206597
207618
  }
206598
207619
  } : void 0;
206599
- const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
207620
+ const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts, executionContext?.signal) : await sendRequest(requestParsed, retryOpts, executionContext?.signal);
206600
207621
  addResponse(response);
206601
207622
  runContractValidation(preparedRequest.endpointName, response, step, stepIdx);
206602
207623
  responseIndexToVariable.set(responses.length, parsed.varName);
@@ -206622,6 +207643,7 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
206622
207643
  }
206623
207644
  }
206624
207645
  } catch (error2) {
207646
+ throwIfSequenceCancelled(executionContext?.signal);
206625
207647
  const userMessage = formatUserFacingError(
206626
207648
  error2,
206627
207649
  buildRequestValidationContext(stepIdx + 1, void 0, void 0, `var ${parsed.varName}`)
@@ -206674,7 +207696,8 @@ ${indentMultiline(userMessage)}`;
206674
207696
  substitutedArgs,
206675
207697
  workingDir || process.cwd(),
206676
207698
  runtimeVariables,
206677
- parsed.captureAs
207699
+ parsed.captureAs,
207700
+ executionContext?.signal
206678
207701
  );
206679
207702
  scriptResults.push(result);
206680
207703
  const stepResult = {
@@ -206707,6 +207730,7 @@ ${indentMultiline(userMessage)}`;
206707
207730
  runtimeVariables[parsed.captureAs] = outputValue;
206708
207731
  }
206709
207732
  } catch (error2) {
207733
+ throwIfSequenceCancelled(executionContext?.signal);
206710
207734
  errors.push(`Script execution error: ${error2.message}`);
206711
207735
  return {
206712
207736
  name: "",
@@ -206812,7 +207836,8 @@ ${indentMultiline(userMessage)}`;
206812
207836
  agentEventLog,
206813
207837
  agentRootInvocationCounts,
206814
207838
  recordAgentRuns: executionContext?.recordAgentRuns,
206815
- knowledgeCoordinator
207839
+ knowledgeCoordinator,
207840
+ signal: executionContext?.signal
206816
207841
  };
206817
207842
  const subResult = await runSequenceWithJar(
206818
207843
  targetSequence.content,
@@ -206905,7 +207930,7 @@ ${indentMultiline(userMessage)}`;
206905
207930
  reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] run ${targetName} - waiting ${waitMs}ms`, void 0);
206906
207931
  }
206907
207932
  } : void 0;
206908
- const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
207933
+ const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts, executionContext?.signal) : await sendRequest(requestParsed, retryOpts, executionContext?.signal);
206909
207934
  addResponse(response);
206910
207935
  const stepResult = {
206911
207936
  type: "request",
@@ -206927,6 +207952,7 @@ ${indentMultiline(userMessage)}`;
206927
207952
  }
206928
207953
  }
206929
207954
  } catch (error2) {
207955
+ throwIfSequenceCancelled(executionContext?.signal);
206930
207956
  const userMessage = formatUserFacingError(
206931
207957
  error2,
206932
207958
  buildRequestValidationContext(stepIdx + 1, requestMethod || void 0, requestUrl || void 0, targetName)
@@ -207016,7 +208042,7 @@ ${indentMultiline(userMessage)}`;
207016
208042
  reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] var ${varName} = run ${sequenceName} - waiting ${waitMs}ms`, void 0);
207017
208043
  }
207018
208044
  } : void 0;
207019
- const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
208045
+ const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts, executionContext?.signal) : await sendRequest(requestParsed, retryOpts, executionContext?.signal);
207020
208046
  addResponse(response);
207021
208047
  responseIndexToVariable.set(responses.length, varName);
207022
208048
  const stepResult = {
@@ -207047,6 +208073,7 @@ ${indentMultiline(userMessage)}`;
207047
208073
  }
207048
208074
  }
207049
208075
  } catch (error2) {
208076
+ throwIfSequenceCancelled(executionContext?.signal);
207050
208077
  const userMessage = formatUserFacingError(
207051
208078
  error2,
207052
208079
  buildRequestValidationContext(stepIdx + 1, requestMethod || void 0, requestUrl || void 0, `${varName} = run ${sequenceName}`)
@@ -207132,7 +208159,8 @@ ${indentMultiline(userMessage)}`;
207132
208159
  agentEventLog,
207133
208160
  agentRootInvocationCounts,
207134
208161
  recordAgentRuns: executionContext?.recordAgentRuns,
207135
- knowledgeCoordinator
208162
+ knowledgeCoordinator,
208163
+ signal: executionContext?.signal
207136
208164
  };
207137
208165
  const subResult = await runSequenceWithJar(
207138
208166
  targetSequence.content,
@@ -207224,7 +208252,7 @@ ${indentMultiline(userMessage)}`;
207224
208252
  reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] ${requestDescription} - waiting ${waitMs}ms`, void 0);
207225
208253
  }
207226
208254
  } : void 0;
207227
- const response = cookieJar ? await sendRequestWithJar(parsed, cookieJar, retryOpts) : await sendRequest(parsed, retryOpts);
208255
+ const response = cookieJar ? await sendRequestWithJar(parsed, cookieJar, retryOpts, executionContext?.signal) : await sendRequest(parsed, retryOpts, executionContext?.signal);
207228
208256
  addResponse(response);
207229
208257
  const stepResult = {
207230
208258
  type: "request",
@@ -207247,6 +208275,7 @@ ${indentMultiline(userMessage)}`;
207247
208275
  }
207248
208276
  }
207249
208277
  } catch (error2) {
208278
+ throwIfSequenceCancelled(executionContext?.signal);
207250
208279
  const userMessage = formatUserFacingError(
207251
208280
  error2,
207252
208281
  buildRequestValidationContext(stepIdx + 1)
@@ -207272,7 +208301,9 @@ ${indentMultiline(userMessage)}`;
207272
208301
  }
207273
208302
  }
207274
208303
  } finally {
207275
- await emitAfterStep(step, stepIdx);
208304
+ if (!executionContext?.signal?.aborted) {
208305
+ await emitAfterStep(step, stepIdx);
208306
+ }
207276
208307
  }
207277
208308
  }
207278
208309
  return {
@@ -207288,16 +208319,23 @@ ${indentMultiline(userMessage)}`;
207288
208319
  agentTrace,
207289
208320
  completed: true
207290
208321
  };
208322
+ } catch (error2) {
208323
+ if (executionContext?.signal?.aborted) {
208324
+ throw new SequenceCancelledError();
208325
+ }
208326
+ throw error2;
207291
208327
  } finally {
208328
+ executionContext?.signal?.removeEventListener("abort", cancelMcpSessions);
208329
+ const wasCancelled = executionContext?.signal?.aborted === true;
207292
208330
  let recordingPath;
207293
- const shouldRecordAgentRun = nestingDepth === 0 && agentTrace.hops.length > 0 && (executionContext?.recordAgentRuns ?? executionContext?.agentProviderRegistry === void 0);
208331
+ const shouldRecordAgentRun = !wasCancelled && nestingDepth === 0 && agentTrace.hops.length > 0 && (executionContext?.recordAgentRuns ?? executionContext?.agentProviderRegistry === void 0);
207294
208332
  if (shouldRecordAgentRun && executionContext?.filePath) {
207295
208333
  try {
207296
208334
  recordingPath = writeAgentRunRecording({
207297
208335
  sourcePath: executionContext.filePath,
207298
208336
  sequenceName: executionContext.sequenceName || "sequence",
207299
208337
  ...executionContext.environment?.activeEnvironment ? { environmentName: executionContext.environment.activeEnvironment } : {},
207300
- success: errors.filter((error2) => !error2.startsWith("Warning")).length === 0 && contractViolations === 0 && agentFailures === 0,
208338
+ success: !wasCancelled && errors.filter((error2) => !error2.startsWith("Warning")).length === 0 && contractViolations === 0 && agentFailures === 0,
207301
208339
  durationMs: Date.now() - startTime,
207302
208340
  ...knowledgeCoordinator?.pinnedSnapshotId ? {
207303
208341
  runId: knowledgeCoordinator.runId,
@@ -207322,7 +208360,7 @@ ${indentMultiline(userMessage)}`;
207322
208360
  environmentName: executionContext.environment?.activeEnvironment,
207323
208361
  startedAt: new Date(startTime),
207324
208362
  durationMs: Date.now() - startTime,
207325
- success: errors.filter((error2) => !error2.startsWith("Warning")).length === 0 && contractViolations === 0 && agentFailures === 0,
208363
+ success: !wasCancelled && errors.filter((error2) => !error2.startsWith("Warning")).length === 0 && contractViolations === 0 && agentFailures === 0,
207326
208364
  recordingPath,
207327
208365
  secrets: resolveAgentRecordingSecretValues(
207328
208366
  executionContext.filePath,
@@ -207654,6 +208692,20 @@ function redactAgentRequestForJson(request, redaction) {
207654
208692
  agentSourcePath: redactString(request.agentSourcePath, redaction),
207655
208693
  sourcePath: request.sourcePath ? redactString(request.sourcePath, redaction) : void 0,
207656
208694
  sequenceName: request.sequenceName ? redactString(request.sequenceName, redaction) : void 0,
208695
+ knowledge: request.knowledge ? {
208696
+ ...request.knowledge,
208697
+ domains: request.knowledge.domains?.map((domain2) => ({
208698
+ ...domain2,
208699
+ declarationAlias: redactString(domain2.declarationAlias, redaction),
208700
+ sourcePath: redactString(domain2.sourcePath, redaction),
208701
+ domainPath: redactString(domain2.domainPath, redaction),
208702
+ topics: domain2.topics.map((topic) => ({
208703
+ ...topic,
208704
+ heading: topic.heading ? redactString(topic.heading, redaction) : void 0,
208705
+ itemPaths: topic.itemPaths.map((itemPath) => redactString(itemPath, redaction))
208706
+ }))
208707
+ }))
208708
+ } : void 0,
207657
208709
  promptHash: (0, import_crypto11.createHash)("sha256").update(systemPrompt, "utf8").digest("hex")
207658
208710
  };
207659
208711
  }
@@ -207673,6 +208725,10 @@ function redactAgentHopForJson(hop, redaction) {
207673
208725
  sourcePath: redactString(hop.turnLimit.sourcePath, redaction),
207674
208726
  ...hop.turnLimit.lastToolName ? { lastToolName: redactString(hop.turnLimit.lastToolName, redaction) } : {}
207675
208727
  } : void 0,
208728
+ inputTokenLimit: hop.inputTokenLimit ? {
208729
+ ...hop.inputTokenLimit,
208730
+ sourcePath: redactString(hop.inputTokenLimit.sourcePath, redaction)
208731
+ } : void 0,
207676
208732
  warnings: hop.warnings.map((warning) => redactString(warning, redaction)),
207677
208733
  accepts: redactAgentContractForJson(hop.accepts, redaction),
207678
208734
  returns: redactAgentContractForJson(hop.returns, redaction),
@@ -207689,7 +208745,7 @@ function redactAgentHopForJson(hop, redaction) {
207689
208745
  body: redactBody(hop.output.body, redaction),
207690
208746
  text: typeof redactedText === "string" ? redactedText : String(redactedText ?? ""),
207691
208747
  model: redactString(hop.output.model, redaction),
207692
- toolCalls: hop.output.toolCalls.map((toolCall) => ({
208748
+ 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
208749
  ...toolCall,
207694
208750
  name: redactString(toolCall.name, redaction),
207695
208751
  input: redactBody(toolCall.input, redaction),
@@ -207929,6 +208985,9 @@ function isFailedToolResult(value) {
207929
208985
  const record2 = value;
207930
208986
  return Object.keys(record2).length === 1 && typeof record2.error === "string";
207931
208987
  }
208988
+ function isKnowledgeAlreadyLoaded(value) {
208989
+ return Boolean(value && typeof value === "object" && !Array.isArray(value) && value.alreadyLoaded === true);
208990
+ }
207932
208991
  function getReplyValue(hop) {
207933
208992
  if (hop.output.text.trim().length > 0) {
207934
208993
  return { present: true, value: hop.output.text };
@@ -208006,6 +209065,9 @@ function formatAgentHopAtDepth(hop, options, displayDepth) {
208006
209065
  const renderedChildren = /* @__PURE__ */ new Set();
208007
209066
  const childrenByInvocationId = new Map(hop.children.map((child) => [child.invocationId, child]));
208008
209067
  for (const toolCall of hop.output.toolCalls) {
209068
+ if (toolCall.kind === "context" || toolCall.kind === "knowledge" && isKnowledgeAlreadyLoaded(toolCall.output)) {
209069
+ continue;
209070
+ }
208009
209071
  if (toolCall.kind === "agent" || toolCall.childInvocationId) {
208010
209072
  const child = toolCall.childInvocationId ? childrenByInvocationId.get(toolCall.childInvocationId) : void 0;
208011
209073
  if (child) {
@@ -210854,6 +211916,42 @@ async function runK8sRunbooks(text, options = {}) {
210854
211916
  var import_util8 = require("util");
210855
211917
 
210856
211918
  // src/agents/agentHopPresentation.ts
211919
+ function buildAgentKnowledgePresentation(knowledge) {
211920
+ if (!knowledge) {
211921
+ return void 0;
211922
+ }
211923
+ const domains = knowledge.domains ? knowledge.domains.map((domain2) => ({
211924
+ domainId: domain2.domainId,
211925
+ declarationAlias: domain2.declarationAlias,
211926
+ sourcePath: domain2.sourcePath,
211927
+ domainPath: domain2.domainPath,
211928
+ disposition: domain2.disposition,
211929
+ estimatedTokens: domain2.estimatedTokens,
211930
+ topicCount: domain2.topicCount,
211931
+ topics: domain2.topics.map((topic) => ({
211932
+ topicHash: topic.topicHash,
211933
+ ...topic.heading ? { heading: topic.heading } : {},
211934
+ estimatedTokens: topic.estimatedTokens,
211935
+ itemPaths: [...topic.itemPaths]
211936
+ }))
211937
+ })) : knowledge.selectedDomainIds.map((domainId) => {
211938
+ const parts = decodeKnowledgeId(domainId);
211939
+ const domainPath = typeof parts?.[2] === "string" ? parts[2] : "Recorded domain";
211940
+ return {
211941
+ domainId,
211942
+ domainPath,
211943
+ disposition: "selected",
211944
+ topics: []
211945
+ };
211946
+ });
211947
+ return {
211948
+ routingRule: knowledge.routingRule,
211949
+ estimatedTokens: knowledge.estimatedTokens,
211950
+ estimatorVersion: knowledge.estimatorVersion,
211951
+ detailsComplete: knowledge.domains !== void 0,
211952
+ domains
211953
+ };
211954
+ }
210857
211955
  function isSameAgentContract(left, right) {
210858
211956
  return Boolean(
210859
211957
  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 +212004,25 @@ function deriveAgentHopPresentation(hop) {
210906
212004
  ...hop.turnLimit.lastToolName ? { lastToolName: hop.turnLimit.lastToolName } : {}
210907
212005
  }
210908
212006
  } : {},
212007
+ ...hop.inputTokenLimit ? {
212008
+ inputTokenLimit: {
212009
+ maxInputTokens: hop.inputTokenLimit.maxInputTokens,
212010
+ usedInputTokens: hop.inputTokenLimit.usedInputTokens,
212011
+ ...hop.inputTokenLimit.estimatedNextInputTokens !== void 0 ? { estimatedNextInputTokens: hop.inputTokenLimit.estimatedNextInputTokens } : {}
212012
+ }
212013
+ } : {},
212014
+ ...hop.request?.maxInputTokens !== void 0 ? { maxInputTokens: hop.request.maxInputTokens } : {},
210909
212015
  ...hop.error ? { error: hop.error } : {},
210910
212016
  warnings: [...hop.warnings],
210911
212017
  ...hop.judge ? { judge: buildAgentJudgePresentation(hop.judge) } : {},
212018
+ ...hop.request?.knowledge ? { knowledge: buildAgentKnowledgePresentation(hop.request.knowledge) } : {},
210912
212019
  output: {
210913
212020
  model: hop.output.model,
210914
212021
  ms: hop.output.ms,
210915
212022
  usage: {
210916
212023
  inputTokens: hop.output.usage.inputTokens,
210917
- outputTokens: hop.output.usage.outputTokens
212024
+ outputTokens: hop.output.usage.outputTokens,
212025
+ ...hop.output.usage.turns ? { turns: hop.output.usage.turns.map((turn) => ({ ...turn })) } : {}
210918
212026
  },
210919
212027
  text: hop.output.text,
210920
212028
  ...Object.prototype.hasOwnProperty.call(hop.output, "body") && hop.output.body !== void 0 ? { body: hop.output.body } : {},
@@ -211008,7 +212116,13 @@ var ReplayAgentProviderAdapter = class {
211008
212116
  };
211009
212117
 
211010
212118
  // src/agents/agentReplay.ts
211011
- var PROVIDERS = ["openai", "anthropic", "google", "local"];
212119
+ var PROVIDERS = [
212120
+ "openai",
212121
+ "openai_compatible",
212122
+ "anthropic",
212123
+ "google",
212124
+ "local"
212125
+ ];
211012
212126
  function normalizedPath(value) {
211013
212127
  return value.replace(/\\/g, "/").toLowerCase();
211014
212128
  }
@@ -211034,7 +212148,10 @@ function recordedProviderResponse(hop, provider) {
211034
212148
  text: hop.output.text,
211035
212149
  ...hasOwn4(hop.output, "body") ? { parsed: structuredClone(hop.output.body) } : {},
211036
212150
  toolCalls: cloneToolCalls(hop.output.toolCalls),
211037
- usage: { ...hop.output.usage },
212151
+ usage: {
212152
+ ...hop.output.usage,
212153
+ ...hop.output.usage.turns ? { turns: hop.output.usage.turns.map((turn) => ({ ...turn })) } : {}
212154
+ },
211038
212155
  resolvedModelId: resolvedModelId(hop, provider)
211039
212156
  };
211040
212157
  }
@@ -211196,8 +212313,10 @@ var ReplayCoordinator = class {
211196
212313
  provider: hop.request.provider,
211197
212314
  modelId: hop.request.modelId,
211198
212315
  displayModel: hop.request.displayModel,
212316
+ ...hop.request.reasoningEffort ? { reasoningEffort: hop.request.reasoningEffort } : {},
211199
212317
  systemPrompt: hop.request.systemPrompt,
211200
212318
  maxTokens: hop.request.maxTokens,
212319
+ ...hop.request.maxInputTokens !== void 0 ? { maxInputTokens: hop.request.maxInputTokens } : {},
211201
212320
  tools: hop.request.tools.map((tool) => ({
211202
212321
  ...tool,
211203
212322
  inputSchema: structuredClone(tool.inputSchema)
@@ -211375,7 +212494,8 @@ var ReplayCoordinator = class {
211375
212494
  let actualOutput;
211376
212495
  let actualError;
211377
212496
  try {
211378
- actualOutput = await currentTool.execute(structuredClone(recordedCall.input));
212497
+ const executed = await currentTool.execute(structuredClone(recordedCall.input));
212498
+ actualOutput = normalizePreparedAgentToolResult(executed).recordedOutput;
211379
212499
  } catch (error2) {
211380
212500
  if (kind === "mcp") {
211381
212501
  throw error2;