@theokit/agents 0.30.2 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,19 +21,19 @@ import {
21
21
  getProjectContextConfig,
22
22
  getSkillsConfig,
23
23
  getSubAgents
24
- } from "./chunk-FI6ZG2YP.js";
24
+ } from "./chunk-3AX6M5TF.js";
25
25
  import {
26
26
  __name
27
27
  } from "./chunk-7QVYU63E.js";
28
28
 
29
29
  // src/bridge/agent-execution-context.ts
30
- function createAgentExecutionContext(base, agent, run, toolCall) {
30
+ function createAgentExecutionContext(base, agent2, run, toolCall) {
31
31
  return {
32
32
  getRequest: /* @__PURE__ */ __name(() => base.getRequest(), "getRequest"),
33
33
  getUrl: /* @__PURE__ */ __name(() => base.getUrl(), "getUrl"),
34
34
  getClass: /* @__PURE__ */ __name(() => base.getClass(), "getClass"),
35
35
  getMethodName: /* @__PURE__ */ __name(() => base.getMethodName(), "getMethodName"),
36
- getAgent: /* @__PURE__ */ __name(() => agent, "getAgent"),
36
+ getAgent: /* @__PURE__ */ __name(() => agent2, "getAgent"),
37
37
  getRun: /* @__PURE__ */ __name(() => run, "getRun"),
38
38
  getToolCall: /* @__PURE__ */ __name(() => toolCall ?? null, "getToolCall"),
39
39
  isAgentContext: /* @__PURE__ */ __name(() => true, "isAgentContext")
@@ -982,7 +982,8 @@ async function loadSdkRuntime() {
982
982
  InMemoryConversationStorage: InMemory,
983
983
  FileSystemConversationStorage: "FileSystemConversationStorage" in sdk ? sdk.FileSystemConversationStorage : InMemory
984
984
  };
985
- } catch {
985
+ } catch (err) {
986
+ console.warn("[theokit] @theokit/sdk import failed:", err);
986
987
  return null;
987
988
  }
988
989
  }
@@ -1124,15 +1125,34 @@ function hasZodInputSchema(schema) {
1124
1125
  return typeof schema?.parse === "function";
1125
1126
  }
1126
1127
  __name(hasZodInputSchema, "hasZodInputSchema");
1127
- function buildSdkTools(compiledTools, defineTool, extraSdkTools = []) {
1128
+ function withRunContext(handler, runContext) {
1129
+ return (input, ctx) => handler(input, {
1130
+ signal: ctx?.signal,
1131
+ context: runContext
1132
+ });
1133
+ }
1134
+ __name(withRunContext, "withRunContext");
1135
+ function buildSdkTools(compiledTools, defineTool, extraSdkTools = [], runContext) {
1136
+ const has = runContext !== void 0;
1128
1137
  return [
1129
- ...compiledTools.map((t) => hasZodInputSchema(t.inputSchema) ? defineTool({
1130
- name: t.name,
1131
- description: t.description,
1132
- inputSchema: t.inputSchema,
1133
- handler: t.handler
1134
- }) : t),
1135
- ...extraSdkTools
1138
+ ...compiledTools.map((t) => {
1139
+ if (hasZodInputSchema(t.inputSchema)) {
1140
+ return defineTool({
1141
+ name: t.name,
1142
+ description: t.description,
1143
+ inputSchema: t.inputSchema,
1144
+ handler: has ? withRunContext(t.handler, runContext) : t.handler
1145
+ });
1146
+ }
1147
+ return has ? {
1148
+ ...t,
1149
+ handler: withRunContext(t.handler, runContext)
1150
+ } : t;
1151
+ }),
1152
+ ...extraSdkTools.map((t) => has ? {
1153
+ ...t,
1154
+ handler: withRunContext(t.handler, runContext)
1155
+ } : t)
1136
1156
  ];
1137
1157
  }
1138
1158
  __name(buildSdkTools, "buildSdkTools");
@@ -1140,6 +1160,7 @@ function createSdkAgentStream(compiled, compiledTools, apiKey, overrides = {}) {
1140
1160
  const model = overrides.model ?? compiled.model ?? "openai/gpt-4o-mini";
1141
1161
  const reasoningEffort = overrides.reasoningEffort ?? compiled.reasoningEffort;
1142
1162
  const { parseThinkTags, stripToolDialect } = resolveTextTransformFlags(compiled, overrides);
1163
+ const runContext = overrides.runContext ?? compiled.runContext;
1143
1164
  let storage = overrides.conversationStorage;
1144
1165
  return (message, sessionId, factoryOpts) => ({
1145
1166
  async *[Symbol.asyncIterator]() {
@@ -1155,51 +1176,35 @@ function createSdkAgentStream(compiled, compiledTools, apiKey, overrides = {}) {
1155
1176
  };
1156
1177
  return;
1157
1178
  }
1158
- const { Agent, defineTool, InMemoryConversationStorage, FileSystemConversationStorage } = rt;
1159
- const sdkTools = buildSdkTools(compiledTools, defineTool, overrides.sdkTools);
1160
- let agent;
1179
+ const { InMemoryConversationStorage, FileSystemConversationStorage } = rt;
1180
+ const sdkTools = buildSdkTools(compiledTools, rt.defineTool, overrides.sdkTools, runContext);
1181
+ let runContextSource;
1182
+ if (overrides.runContext !== void 0) {
1183
+ runContextSource = "per-run";
1184
+ } else if (compiled.runContext !== void 0) {
1185
+ runContextSource = "agent-level";
1186
+ } else {
1187
+ runContextSource = "none";
1188
+ }
1189
+ console.debug("[THEO_AGENT_M7_RUN_CONTEXT]", {
1190
+ source: runContextSource,
1191
+ keys: runContext !== void 0 ? Object.keys(runContext) : []
1192
+ });
1193
+ storage ??= newConversationStorage(compiled, InMemoryConversationStorage, FileSystemConversationStorage);
1161
1194
  try {
1162
- const { options: m8, applied } = assembleM8CreateOptions(compiled);
1163
- if (overrides.cwd !== void 0) m8.local = {
1164
- ...m8.local,
1165
- cwd: overrides.cwd
1166
- };
1167
- const extra = buildExtraCreateOptions(overrides, compiled);
1168
- storage ??= newConversationStorage(compiled, InMemoryConversationStorage, FileSystemConversationStorage);
1169
- if (applied.length > 0) {
1170
- console.debug("[THEO_AGENT_M8_RUNTIME_APPLIED]", {
1171
- skills: applied.includes("skills"),
1172
- context: applied.includes("context"),
1173
- projectContext: applied.includes("projectContext")
1174
- });
1175
- }
1176
- agent = await Agent.getOrCreate(sessionId, {
1195
+ yield* streamSdkAgent(rt, compiled, sdkTools, storage, {
1177
1196
  apiKey,
1178
- model: buildModelSelection(model, reasoningEffort),
1179
- tools: sdkTools,
1180
- ...m8,
1181
- ...extra,
1182
- conversationStorage: storage
1183
- });
1184
- const queue = createAsyncQueue();
1185
- const { state, onDelta } = createDeltaSink(queue);
1186
- const sendPromise = agent.send(message, factoryOpts?.disableTools === true ? {
1187
- onDelta,
1188
- toolChoice: "none"
1189
- } : {
1190
- onDelta
1191
- });
1192
- const openStream = /* @__PURE__ */ __name(async () => (await sendPromise).stream(), "openStream");
1193
- const merged = mergeDeltaStream(queue, openStream, runId, state);
1194
- for await (const event of applyTextTransforms(merged, {
1197
+ model,
1198
+ reasoningEffort,
1199
+ overrides,
1195
1200
  parseThinkTags,
1196
- stripToolDialect
1197
- })) {
1198
- yield event;
1199
- }
1200
- if (!state.sawError) {
1201
- yield realUsageDone(await (await sendPromise).wait(), t0);
1202
- }
1201
+ stripToolDialect,
1202
+ sessionId,
1203
+ message,
1204
+ factoryOpts,
1205
+ runId,
1206
+ t0
1207
+ });
1203
1208
  } catch (err) {
1204
1209
  yield {
1205
1210
  type: "error",
@@ -1207,13 +1212,59 @@ function createSdkAgentStream(compiled, compiledTools, apiKey, overrides = {}) {
1207
1212
  message: err instanceof Error ? err.message : "SDK agent error",
1208
1213
  retryable: false
1209
1214
  };
1210
- } finally {
1211
- await agent?.dispose();
1212
1215
  }
1213
1216
  }
1214
1217
  });
1215
1218
  }
1216
1219
  __name(createSdkAgentStream, "createSdkAgentStream");
1220
+ async function* streamSdkAgent(rt, compiled, sdkTools, storage, opts) {
1221
+ const { Agent } = rt;
1222
+ const { apiKey, model, reasoningEffort, overrides, parseThinkTags, stripToolDialect, sessionId, message, factoryOpts, runId, t0 } = opts;
1223
+ const { options: m8, applied } = assembleM8CreateOptions(compiled);
1224
+ if (overrides.cwd !== void 0) m8.local = {
1225
+ ...m8.local,
1226
+ cwd: overrides.cwd
1227
+ };
1228
+ const extra = buildExtraCreateOptions(overrides, compiled);
1229
+ if (applied.length > 0) {
1230
+ console.debug("[THEO_AGENT_M8_RUNTIME_APPLIED]", {
1231
+ skills: applied.includes("skills"),
1232
+ contextWindow: applied.includes("context"),
1233
+ projectContext: applied.includes("projectContext")
1234
+ });
1235
+ }
1236
+ const agent2 = await Agent.getOrCreate(sessionId, {
1237
+ apiKey,
1238
+ model: buildModelSelection(model, reasoningEffort),
1239
+ tools: sdkTools,
1240
+ ...m8,
1241
+ ...extra,
1242
+ conversationStorage: storage
1243
+ });
1244
+ try {
1245
+ const queue = createAsyncQueue();
1246
+ const { state, onDelta } = createDeltaSink(queue);
1247
+ const sendOptions = {
1248
+ onDelta
1249
+ };
1250
+ if (factoryOpts?.disableTools === true) sendOptions.toolChoice = "none";
1251
+ const sendPromise = agent2.send(message, sendOptions);
1252
+ const openStream = /* @__PURE__ */ __name(async () => (await sendPromise).stream(), "openStream");
1253
+ const merged = mergeDeltaStream(queue, openStream, runId, state);
1254
+ for await (const event of applyTextTransforms(merged, {
1255
+ parseThinkTags,
1256
+ stripToolDialect
1257
+ })) {
1258
+ yield event;
1259
+ }
1260
+ if (!state.sawError) {
1261
+ yield realUsageDone(await (await sendPromise).wait(), t0);
1262
+ }
1263
+ } finally {
1264
+ await agent2.dispose();
1265
+ }
1266
+ }
1267
+ __name(streamSdkAgent, "streamSdkAgent");
1217
1268
 
1218
1269
  // src/bridge/ui-message-stream-translator.ts
1219
1270
  function* closeOpenBlock(state, textId) {
@@ -1390,11 +1441,12 @@ function isAgentDefinition(value) {
1390
1441
  }
1391
1442
  __name(isAgentDefinition, "isAgentDefinition");
1392
1443
  function toCompiledTool(tool) {
1444
+ const handler = tool.handler;
1393
1445
  return {
1394
1446
  name: tool.name,
1395
1447
  description: tool.description,
1396
1448
  inputSchema: tool.inputSchema,
1397
- handler: /* @__PURE__ */ __name((input) => tool.handler(input), "handler")
1449
+ handler: /* @__PURE__ */ __name((input, ctx) => handler(input, ctx), "handler")
1398
1450
  };
1399
1451
  }
1400
1452
  __name(toCompiledTool, "toCompiledTool");
@@ -1405,15 +1457,108 @@ function compileAgentDefinition(def) {
1405
1457
  systemPrompt: def.system,
1406
1458
  tools: (def.tools ?? []).map(toCompiledTool),
1407
1459
  agents: {},
1408
- stream: true
1460
+ stream: true,
1461
+ // M7 — run-context flows to CompiledAgentOptions.runContext (distinct from the
1462
+ // context-window `context` field the decorator path uses); absent ⇒ no key.
1463
+ ...def.context !== void 0 ? {
1464
+ runContext: def.context
1465
+ } : {},
1466
+ // M9 — guardrails flow through unchanged; the runner applies them at the input boundary.
1467
+ ...def.guardrails !== void 0 ? {
1468
+ guardrails: def.guardrails
1469
+ } : {},
1470
+ // M14 — HITL approvals compile into the same `hitl` map the decorator path produces.
1471
+ ...def.approvals !== void 0 ? {
1472
+ hitl: compileApprovals(def)
1473
+ } : {},
1474
+ // M13 — skills: a static list → SDK skills.enabled; a resolver → carried for the request path.
1475
+ ...compileSkillsSelection(def.skills)
1409
1476
  };
1410
1477
  }
1411
1478
  __name(compileAgentDefinition, "compileAgentDefinition");
1479
+ function compileSkillsSelection(skills) {
1480
+ if (skills === void 0) return {};
1481
+ if (typeof skills === "function") return {
1482
+ skillsResolver: skills
1483
+ };
1484
+ return {
1485
+ skills: {
1486
+ enabled: [
1487
+ ...skills
1488
+ ],
1489
+ autoInject: true
1490
+ }
1491
+ };
1492
+ }
1493
+ __name(compileSkillsSelection, "compileSkillsSelection");
1494
+ function compileApprovals(def) {
1495
+ const toolNames = new Set((def.tools ?? []).map((t) => t.name));
1496
+ const gates = /* @__PURE__ */ new Map();
1497
+ for (const [toolName, options] of Object.entries(def.approvals ?? {})) {
1498
+ if (!toolNames.has(toolName)) {
1499
+ throw new Error(`[@theokit/agents] defineAgent approval references unknown tool "${toolName}". Declared tools: ${[
1500
+ ...toolNames
1501
+ ].join(", ") || "(none)"}.`);
1502
+ }
1503
+ gates.set(toolName, options);
1504
+ }
1505
+ return gates;
1506
+ }
1507
+ __name(compileApprovals, "compileApprovals");
1508
+
1509
+ // src/bridge/agent-builder.ts
1510
+ function contextualTool(tool, _requiredContext) {
1511
+ return tool;
1512
+ }
1513
+ __name(contextualTool, "contextualTool");
1514
+ function makeBuilder(config) {
1515
+ const runtime = {
1516
+ input: /* @__PURE__ */ __name((schema) => makeBuilder({
1517
+ ...config,
1518
+ input: schema
1519
+ }), "input"),
1520
+ model: /* @__PURE__ */ __name((id) => makeBuilder({
1521
+ ...config,
1522
+ model: id
1523
+ }), "model"),
1524
+ system: /* @__PURE__ */ __name((prompt) => makeBuilder({
1525
+ ...config,
1526
+ system: prompt
1527
+ }), "system"),
1528
+ reasoningEffort: /* @__PURE__ */ __name((effort) => makeBuilder({
1529
+ ...config,
1530
+ reasoningEffort: effort
1531
+ }), "reasoningEffort"),
1532
+ context: /* @__PURE__ */ __name((value) => makeBuilder({
1533
+ ...config,
1534
+ context: value
1535
+ }), "context"),
1536
+ tool: /* @__PURE__ */ __name((tool) => makeBuilder({
1537
+ ...config,
1538
+ tools: [
1539
+ ...config.tools ?? [],
1540
+ tool
1541
+ ]
1542
+ }), "tool"),
1543
+ use: /* @__PURE__ */ __name((preset) => preset(runtime), "use"),
1544
+ build: /* @__PURE__ */ __name(() => defineAgent(config), "build")
1545
+ };
1546
+ return runtime;
1547
+ }
1548
+ __name(makeBuilder, "makeBuilder");
1549
+ function agent() {
1550
+ return makeBuilder({});
1551
+ }
1552
+ __name(agent, "agent");
1412
1553
 
1413
1554
  // src/bridge/hitl-plugin.ts
1414
1555
  function createHitlPlugin(wiring) {
1415
1556
  return {
1416
1557
  name: "theokit-hitl",
1558
+ version: "1.0.0",
1559
+ // `kind: 'general'` is load-bearing — without it the SDK's isCodePlugin() drops this plugin and
1560
+ // the HITL veto never fires (the run would proceed WITHOUT waiting for human approval).
1561
+ kind: "general",
1417
1562
  register(ctx) {
1418
1563
  ctx.on("pre_tool_call", async (c) => {
1419
1564
  const opts = wiring.gated.get(c.name);
@@ -1426,12 +1571,25 @@ function createHitlPlugin(wiring) {
1426
1571
  question: opts.question,
1427
1572
  input: c.args,
1428
1573
  callbackUrl: `approve/${approvalId}`,
1429
- timeoutMs: opts.timeout ?? 3e5
1574
+ timeoutMs: opts.timeout ?? 3e5,
1575
+ // M20 — carry the declared custom-payload schema so the UI knows what to collect.
1576
+ ...opts.payloadSchema !== void 0 ? {
1577
+ payloadSchema: opts.payloadSchema
1578
+ } : {}
1430
1579
  });
1431
- const approved = await wiring.awaitApproval(approvalId, opts);
1432
- return approved ? void 0 : {
1580
+ const raw = await wiring.awaitApproval(approvalId, opts, c.name);
1581
+ const decision = typeof raw === "boolean" ? {
1582
+ approved: raw
1583
+ } : raw;
1584
+ if (decision.approved) return void 0;
1585
+ let message = `Tool '${c.name}' denied by human approver`;
1586
+ if (decision.reason) message += `: ${decision.reason}`;
1587
+ if (decision.payload !== void 0) {
1588
+ message += ` (payload: ${JSON.stringify(decision.payload)})`;
1589
+ }
1590
+ return {
1433
1591
  block: true,
1434
- message: `Tool '${c.name}' denied by human approver`
1592
+ message
1435
1593
  };
1436
1594
  });
1437
1595
  }
@@ -1580,6 +1738,198 @@ function streamAgentUIMessages(compiled, apiKey, input) {
1580
1738
  }
1581
1739
  __name(streamAgentUIMessages, "streamAgentUIMessages");
1582
1740
 
1741
+ // src/guardrails/types.ts
1742
+ var GuardrailViolationError = class extends Error {
1743
+ static {
1744
+ __name(this, "GuardrailViolationError");
1745
+ }
1746
+ guardName;
1747
+ phase;
1748
+ reason;
1749
+ constructor(guardName, phase, reason) {
1750
+ super(`Guardrail "${guardName}" blocked ${phase}: ${reason}`), this.guardName = guardName, this.phase = phase, this.reason = reason;
1751
+ this.name = "GuardrailViolationError";
1752
+ }
1753
+ };
1754
+ var CostBudgetExceededError = class extends Error {
1755
+ static {
1756
+ __name(this, "CostBudgetExceededError");
1757
+ }
1758
+ usedTokens;
1759
+ maxTokens;
1760
+ constructor(usedTokens, maxTokens) {
1761
+ super(`Cost budget exceeded: ${usedTokens} > ${maxTokens} tokens`), this.usedTokens = usedTokens, this.maxTokens = maxTokens;
1762
+ this.name = "CostBudgetExceededError";
1763
+ }
1764
+ };
1765
+
1766
+ // src/guardrails/detectors.ts
1767
+ function estimateTokens(text) {
1768
+ return Math.ceil(text.length / 4);
1769
+ }
1770
+ __name(estimateTokens, "estimateTokens");
1771
+ var INJECTION_PHRASES = [
1772
+ "previous instructions",
1773
+ "disregard the above",
1774
+ "you are now dan",
1775
+ "system prompt",
1776
+ "no restrictions",
1777
+ "without restrictions",
1778
+ "no rules",
1779
+ "override your"
1780
+ ];
1781
+ function normalizeForMatch(text) {
1782
+ return text.toLowerCase().replace(/\s+/g, " ");
1783
+ }
1784
+ __name(normalizeForMatch, "normalizeForMatch");
1785
+ function promptInjectionDetector(options = {}) {
1786
+ const phrases = [
1787
+ ...INJECTION_PHRASES,
1788
+ ...(options.extra ?? []).map((p) => p.toLowerCase())
1789
+ ];
1790
+ return {
1791
+ name: "prompt-injection",
1792
+ checkInput(text) {
1793
+ const normalized = normalizeForMatch(text);
1794
+ for (const phrase of phrases) {
1795
+ if (normalized.includes(phrase)) {
1796
+ return {
1797
+ action: "block",
1798
+ reason: `prompt injection phrase matched: "${phrase}"`
1799
+ };
1800
+ }
1801
+ }
1802
+ return {
1803
+ action: "allow"
1804
+ };
1805
+ }
1806
+ };
1807
+ }
1808
+ __name(promptInjectionDetector, "promptInjectionDetector");
1809
+ var CPF = /\b\d{3}\.?\d{3}\.?\d{3}-?\d{2}\b/g;
1810
+ var EMAIL = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g;
1811
+ var PHONE = /\+?\d[\d\s()-]{7,13}\d/g;
1812
+ function piiDetector(options = {}) {
1813
+ const placeholder = options.placeholder ?? "[REDACTED]";
1814
+ return {
1815
+ name: "pii",
1816
+ checkInput(text) {
1817
+ let redacted = text;
1818
+ redacted = redacted.replace(EMAIL, placeholder);
1819
+ redacted = redacted.replace(CPF, placeholder);
1820
+ redacted = redacted.replace(PHONE, placeholder);
1821
+ if (redacted === text) return {
1822
+ action: "allow"
1823
+ };
1824
+ return {
1825
+ action: "redact",
1826
+ text: redacted,
1827
+ reason: "PII detected and redacted"
1828
+ };
1829
+ }
1830
+ };
1831
+ }
1832
+ __name(piiDetector, "piiDetector");
1833
+ var OBFUSCATION_CHARS = /[\u200B-\u200D\uFEFF\u202A-\u202E\u2066-\u2069]/g;
1834
+ function unicodeNormalizer() {
1835
+ return {
1836
+ name: "unicode-normalizer",
1837
+ checkInput(text) {
1838
+ const cleaned = text.normalize("NFKC").replace(OBFUSCATION_CHARS, "");
1839
+ if (cleaned === text) return {
1840
+ action: "allow"
1841
+ };
1842
+ return {
1843
+ action: "redact",
1844
+ text: cleaned,
1845
+ reason: "obfuscation characters normalized"
1846
+ };
1847
+ }
1848
+ };
1849
+ }
1850
+ __name(unicodeNormalizer, "unicodeNormalizer");
1851
+ function costGuard(options) {
1852
+ let used = 0;
1853
+ return {
1854
+ name: "cost-guard",
1855
+ // Returns a Promise explicitly (not `async`) so a budget breach REJECTS uniformly — never a
1856
+ // sync throw — safe for direct callers and for the pipeline's `await g.checkInput()`.
1857
+ checkInput(text) {
1858
+ used += estimateTokens(text);
1859
+ if (used > options.maxTokens) {
1860
+ return Promise.reject(new CostBudgetExceededError(used, options.maxTokens));
1861
+ }
1862
+ return Promise.resolve({
1863
+ action: "allow"
1864
+ });
1865
+ }
1866
+ };
1867
+ }
1868
+ __name(costGuard, "costGuard");
1869
+ function outputModeration(options) {
1870
+ return {
1871
+ name: "output-moderation",
1872
+ async checkOutput(text) {
1873
+ const flagged = await options.moderate(text);
1874
+ return flagged ? {
1875
+ action: "block",
1876
+ reason: "output flagged by moderation predicate"
1877
+ } : {
1878
+ action: "allow"
1879
+ };
1880
+ }
1881
+ };
1882
+ }
1883
+ __name(outputModeration, "outputModeration");
1884
+
1885
+ // src/guardrails/pipeline.ts
1886
+ async function runInputGuards(text, guards) {
1887
+ let current = text;
1888
+ for (const g of guards) {
1889
+ if (!g.checkInput) continue;
1890
+ const r = await g.checkInput(current);
1891
+ if (r.action === "block") {
1892
+ throw new GuardrailViolationError(g.name, "input", r.reason ?? "blocked");
1893
+ }
1894
+ if (r.action === "redact" && r.text !== void 0) current = r.text;
1895
+ }
1896
+ return current;
1897
+ }
1898
+ __name(runInputGuards, "runInputGuards");
1899
+ async function runOutputGuards(text, guards) {
1900
+ let current = text;
1901
+ for (const g of guards) {
1902
+ if (!g.checkOutput) continue;
1903
+ const r = await g.checkOutput(current);
1904
+ if (r.action === "block") {
1905
+ throw new GuardrailViolationError(g.name, "output", r.reason ?? "blocked");
1906
+ }
1907
+ if (r.action === "redact" && r.text !== void 0) current = r.text;
1908
+ }
1909
+ return current;
1910
+ }
1911
+ __name(runOutputGuards, "runOutputGuards");
1912
+
1913
+ // src/guardrails/stream.ts
1914
+ async function* moderateOutputStream(inner, guards, extractText) {
1915
+ const hasOutputGuard = guards.some((g) => g.checkOutput != null);
1916
+ if (!hasOutputGuard) return yield* inner;
1917
+ const buffered = [];
1918
+ let accumulated = "";
1919
+ let step = await inner.next();
1920
+ while (!step.done) {
1921
+ const event = step.value;
1922
+ const text = extractText(event);
1923
+ if (text !== void 0) accumulated += text;
1924
+ buffered.push(event);
1925
+ step = await inner.next();
1926
+ }
1927
+ await runOutputGuards(accumulated, guards);
1928
+ for (const event of buffered) yield event;
1929
+ return step.value;
1930
+ }
1931
+ __name(moderateOutputStream, "moderateOutputStream");
1932
+
1583
1933
  // src/loop/compaction-strategy.ts
1584
1934
  import { compactTranscript } from "@theokit/sdk/compaction";
1585
1935
  import { z } from "zod";
@@ -2003,6 +2353,18 @@ var AgentRunner = class {
2003
2353
  * builder set `.stream(false)`, callers should use {@link run} instead.
2004
2354
  */
2005
2355
  stream(message, opts) {
2356
+ const guardrails = this.compiled.guardrails;
2357
+ if (guardrails && guardrails.length > 0) {
2358
+ const runUnguarded = /* @__PURE__ */ __name((m) => this.streamUnguarded(m, opts), "runUnguarded");
2359
+ return (/* @__PURE__ */ __name(async function* guarded() {
2360
+ const safe = await runInputGuards(message, guardrails);
2361
+ return yield* moderateOutputStream(runUnguarded(safe), guardrails, (e) => e.type === "text_delta" && typeof e.content === "string" ? e.content : void 0);
2362
+ }, "guarded"))();
2363
+ }
2364
+ return this.streamUnguarded(message, opts);
2365
+ }
2366
+ /** The core stream path, after input guardrails have run (M9). */
2367
+ streamUnguarded(message, opts) {
2006
2368
  const tools = opts.tools ? [
2007
2369
  ...opts.tools
2008
2370
  ] : this.compiled.tools;
@@ -2117,6 +2479,10 @@ function mergeTools(parentTools, subTools) {
2117
2479
  __name(mergeTools, "mergeTools");
2118
2480
  async function delegate(SubAgentClass, message, opts = {}) {
2119
2481
  const apiKey = requireApiKey(opts, SubAgentClass.name);
2482
+ const effectiveMessage = opts.onDelegationStart ? await opts.onDelegationStart({
2483
+ subAgent: SubAgentClass.name,
2484
+ input: message
2485
+ }) : message;
2120
2486
  const walk = walkAgentMetadata(SubAgentClass, []);
2121
2487
  const toolboxInstances = new Map(walk.toolboxes.map((tb) => [
2122
2488
  tb.class,
@@ -2125,7 +2491,7 @@ async function delegate(SubAgentClass, message, opts = {}) {
2125
2491
  const compiled = compileAgent(walk, toolboxInstances);
2126
2492
  const allTools = mergeTools(opts.parentTools ?? [], compiled.tools);
2127
2493
  const budget = Math.min(opts.budget ?? Infinity, opts.parentBudgetRemaining ?? Infinity);
2128
- const streamFactory = createSdkAgentStream(compiled, allTools, apiKey, {
2494
+ const streamFactory = opts.streamFactory ?? createSdkAgentStream(compiled, allTools, apiKey, {
2129
2495
  model: opts.model ?? walk.agentConfig.model,
2130
2496
  cwd: opts.cwd,
2131
2497
  plugins: opts.plugins,
@@ -2138,7 +2504,7 @@ async function delegate(SubAgentClass, message, opts = {}) {
2138
2504
  const sessionId = opts.sessionId ?? `sub-${crypto.randomUUID()}`;
2139
2505
  const loopStrategy = resolveLoopStrategy(walk.mainLoop.strategy, opts.maxIterations ?? walk.mainLoop.maxIterations);
2140
2506
  const reflection = opts.reflection ?? (loopStrategy.name === "plan-act-reflect" ? ladderReflectionStrategy : noopReflectionStrategy);
2141
- return runReflectiveLoop(streamFactory, message, sessionId, {
2507
+ const result = await runReflectiveLoop(streamFactory, effectiveMessage, sessionId, {
2142
2508
  loop: loopStrategy,
2143
2509
  reflection,
2144
2510
  budget,
@@ -2146,9 +2512,213 @@ async function delegate(SubAgentClass, message, opts = {}) {
2146
2512
  signal: opts.signal,
2147
2513
  retry: opts.retry
2148
2514
  });
2515
+ if (opts.onDelegationComplete) {
2516
+ return await opts.onDelegationComplete({
2517
+ subAgent: SubAgentClass.name,
2518
+ result
2519
+ });
2520
+ }
2521
+ return result;
2149
2522
  }
2150
2523
  __name(delegate, "delegate");
2151
2524
 
2525
+ // src/bridge/tool-hooks-plugin.ts
2526
+ function createToolHooksPlugin(hooks) {
2527
+ return {
2528
+ name: "theokit-tool-hooks",
2529
+ version: "1.0.0",
2530
+ // `kind: 'general'` is load-bearing — without it the SDK's isCodePlugin() drops this plugin and
2531
+ // no hook fires (M10/M19 latent bug, proven via a real OpenRouter run).
2532
+ kind: "general",
2533
+ register(ctx) {
2534
+ const { beforeToolCall, afterToolCall, beforeLLMCall, afterLLMCall, processInput } = hooks;
2535
+ if (beforeToolCall) {
2536
+ ctx.on("pre_tool_call", (c) => beforeToolCall({
2537
+ name: c.name ?? "",
2538
+ args: c.args ?? {}
2539
+ }));
2540
+ }
2541
+ if (afterToolCall) {
2542
+ ctx.on("post_tool_call", (c) => afterToolCall({
2543
+ name: c.name ?? "",
2544
+ result: c.result
2545
+ }));
2546
+ }
2547
+ if (beforeLLMCall) {
2548
+ ctx.on("pre_llm_call", (c) => beforeLLMCall({
2549
+ agentId: c.agentId,
2550
+ runId: c.runId,
2551
+ iteration: c.iteration
2552
+ }));
2553
+ }
2554
+ if (afterLLMCall) {
2555
+ ctx.on("post_llm_call", (c) => afterLLMCall({
2556
+ agentId: c.agentId,
2557
+ runId: c.runId,
2558
+ iteration: c.iteration
2559
+ }));
2560
+ }
2561
+ if (processInput) {
2562
+ ctx.on("pre_user_send", async (c) => {
2563
+ const injected = await processInput({
2564
+ prompt: c.prompt ?? "",
2565
+ agentId: c.agentId,
2566
+ runId: c.runId
2567
+ });
2568
+ return injected !== void 0 && injected.length > 0 ? {
2569
+ recalledContext: injected
2570
+ } : void 0;
2571
+ });
2572
+ }
2573
+ }
2574
+ };
2575
+ }
2576
+ __name(createToolHooksPlugin, "createToolHooksPlugin");
2577
+
2578
+ // src/bridge/api-error-handler.ts
2579
+ var DEFAULT_MAX_ATTEMPTS = 3;
2580
+ async function runWithApiErrorHandling(thunk, policy) {
2581
+ const maxAttempts = policy.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
2582
+ let attempt = 0;
2583
+ for (; ; ) {
2584
+ attempt += 1;
2585
+ try {
2586
+ return await thunk();
2587
+ } catch (error) {
2588
+ const decision = await policy.processApiError({
2589
+ error,
2590
+ attempt
2591
+ });
2592
+ if (decision.retry && attempt < maxAttempts) continue;
2593
+ if (!decision.retry && "fallback" in decision && decision.fallback !== void 0) {
2594
+ return decision.fallback;
2595
+ }
2596
+ throw error;
2597
+ }
2598
+ }
2599
+ }
2600
+ __name(runWithApiErrorHandling, "runWithApiErrorHandling");
2601
+ function createApiErrorHandler(policy) {
2602
+ return (thunk) => runWithApiErrorHandling(thunk, policy);
2603
+ }
2604
+ __name(createApiErrorHandler, "createApiErrorHandler");
2605
+
2606
+ // src/bridge/delegation-scoring.ts
2607
+ function delegateBackground(subAgent, message, opts = {}) {
2608
+ const { delegateFn = delegate, ...delegateOpts } = opts;
2609
+ let isSettled = false;
2610
+ const promise = delegateFn(subAgent, message, delegateOpts).finally(() => {
2611
+ isSettled = true;
2612
+ });
2613
+ promise.catch(() => void 0);
2614
+ return {
2615
+ wait: /* @__PURE__ */ __name(() => promise, "wait"),
2616
+ settled: /* @__PURE__ */ __name(() => isSettled, "settled")
2617
+ };
2618
+ }
2619
+ __name(delegateBackground, "delegateBackground");
2620
+ var DEFAULT_MAX_ROUNDS = 3;
2621
+ function defaultFeedbackTemplate(message, feedback) {
2622
+ return `${message}
2623
+
2624
+ Feedback from the reviewer (address this): ${feedback}`;
2625
+ }
2626
+ __name(defaultFeedbackTemplate, "defaultFeedbackTemplate");
2627
+ async function delegateWithScoring(subAgent, message, opts) {
2628
+ const { scorer, maxRounds: maxRoundsOpt = DEFAULT_MAX_ROUNDS, delegateFn = delegate, feedbackTemplate = defaultFeedbackTemplate, ...delegateOpts } = opts;
2629
+ const maxRounds = Math.max(1, maxRoundsOpt);
2630
+ const verdicts = [];
2631
+ let currentMessage = message;
2632
+ let lastResult;
2633
+ for (let round = 1; round <= maxRounds; round++) {
2634
+ const result = await delegateFn(subAgent, currentMessage, delegateOpts);
2635
+ lastResult = result;
2636
+ const verdict = await scorer(result);
2637
+ verdicts.push(verdict);
2638
+ if (verdict.pass) {
2639
+ return {
2640
+ result,
2641
+ rounds: round,
2642
+ passed: true,
2643
+ verdicts
2644
+ };
2645
+ }
2646
+ if (verdict.feedback) currentMessage = feedbackTemplate(message, verdict.feedback);
2647
+ }
2648
+ return {
2649
+ result: lastResult,
2650
+ rounds: maxRounds,
2651
+ passed: false,
2652
+ verdicts
2653
+ };
2654
+ }
2655
+ __name(delegateWithScoring, "delegateWithScoring");
2656
+
2657
+ // src/bridge/mcp-resolver.ts
2658
+ async function resolveMcpServers(selection, ctx) {
2659
+ if (selection === void 0) return void 0;
2660
+ if (typeof selection !== "function") return selection;
2661
+ const resolved = await selection(ctx);
2662
+ if (typeof resolved !== "object" || resolved === null) {
2663
+ throw new Error("[@theokit/agents] MCP resolver must return an McpServersMap object");
2664
+ }
2665
+ return resolved;
2666
+ }
2667
+ __name(resolveMcpServers, "resolveMcpServers");
2668
+ function mcpRegistry(config) {
2669
+ const registry = config.registry;
2670
+ if (registry === "composio") {
2671
+ const apps = config.apps ?? [];
2672
+ return {
2673
+ composio: {
2674
+ command: "npx",
2675
+ args: [
2676
+ "-y",
2677
+ "@composio/mcp",
2678
+ ...apps.length > 0 ? [
2679
+ "--apps",
2680
+ apps.join(",")
2681
+ ] : []
2682
+ ],
2683
+ env: {
2684
+ COMPOSIO_API_KEY: config.apiKey
2685
+ }
2686
+ }
2687
+ };
2688
+ }
2689
+ if (registry === "mcp.run") {
2690
+ return {
2691
+ "mcp.run": {
2692
+ command: "npx",
2693
+ args: [
2694
+ "-y",
2695
+ "@mcp.run/cli",
2696
+ "serve",
2697
+ ...config.profile ? [
2698
+ "--profile",
2699
+ config.profile
2700
+ ] : []
2701
+ ],
2702
+ env: {
2703
+ MCP_RUN_API_KEY: config.apiKey
2704
+ }
2705
+ }
2706
+ };
2707
+ }
2708
+ throw new Error(`mcpRegistry: unknown registry ${JSON.stringify(registry)} (supported: 'composio', 'mcp.run').`);
2709
+ }
2710
+ __name(mcpRegistry, "mcpRegistry");
2711
+ function mcpToolApprovals(specs) {
2712
+ const out = {};
2713
+ for (const [tool, spec] of Object.entries(specs)) {
2714
+ out[tool] = typeof spec === "string" ? {
2715
+ question: spec
2716
+ } : spec;
2717
+ }
2718
+ return out;
2719
+ }
2720
+ __name(mcpToolApprovals, "mcpToolApprovals");
2721
+
2152
2722
  // src/manifest/agent-manifest.ts
2153
2723
  function generateAgentManifest(walkResults) {
2154
2724
  return {
@@ -2304,9 +2874,22 @@ export {
2304
2874
  defineAgent,
2305
2875
  isAgentDefinition,
2306
2876
  compileAgentDefinition,
2877
+ contextualTool,
2878
+ agent,
2307
2879
  AgentDefinitionError,
2308
2880
  compileAgentModule,
2309
2881
  streamAgentUIMessages,
2882
+ GuardrailViolationError,
2883
+ CostBudgetExceededError,
2884
+ estimateTokens,
2885
+ promptInjectionDetector,
2886
+ piiDetector,
2887
+ unicodeNormalizer,
2888
+ costGuard,
2889
+ outputModeration,
2890
+ runInputGuards,
2891
+ runOutputGuards,
2892
+ moderateOutputStream,
2310
2893
  DEFAULT_KEEP_TOKENS,
2311
2894
  compactionStrategyConfigSchema,
2312
2895
  resolveCompactionStrategy,
@@ -2322,7 +2905,15 @@ export {
2322
2905
  AgentRunner,
2323
2906
  AgentRunnerBuilder,
2324
2907
  delegate,
2908
+ createToolHooksPlugin,
2909
+ runWithApiErrorHandling,
2910
+ createApiErrorHandler,
2911
+ delegateBackground,
2912
+ delegateWithScoring,
2913
+ resolveMcpServers,
2914
+ mcpRegistry,
2915
+ mcpToolApprovals,
2325
2916
  generateAgentManifest,
2326
2917
  agentsPlugin
2327
2918
  };
2328
- //# sourceMappingURL=chunk-QJKU2YMC.js.map
2919
+ //# sourceMappingURL=chunk-5VIRIPVV.js.map