@theokit/sdk 2.20.0 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/dist/a2a/index.cjs +673 -317
  3. package/dist/a2a/index.cjs.map +1 -1
  4. package/dist/a2a/index.d.cts +2 -1
  5. package/dist/a2a/index.d.ts +2 -1
  6. package/dist/a2a/index.js +674 -318
  7. package/dist/a2a/index.js.map +1 -1
  8. package/dist/a2a/subagent.d.cts +70 -1
  9. package/dist/a2a/subagent.d.ts +70 -1
  10. package/dist/a2a/types.d.cts +7 -0
  11. package/dist/a2a/types.d.ts +7 -0
  12. package/dist/agent-builder.d.ts +2 -1
  13. package/dist/agent-generate.d.ts +12 -0
  14. package/dist/client/index.cjs.map +1 -1
  15. package/dist/client/index.d.cts +5 -0
  16. package/dist/client/index.d.ts +5 -0
  17. package/dist/client/index.js.map +1 -1
  18. package/dist/client/theokit-client.d.cts +6 -0
  19. package/dist/client/theokit-client.d.ts +6 -0
  20. package/dist/client/types.d.cts +2 -0
  21. package/dist/client/types.d.ts +2 -0
  22. package/dist/{cron-B8fqui49.d.cts → cron-7CruUd_0.d.ts} +53 -3
  23. package/dist/{cron-D2yLOrk2.d.ts → cron-Bpt_1khA.d.cts} +53 -3
  24. package/dist/cron.cjs +654 -389
  25. package/dist/cron.cjs.map +1 -1
  26. package/dist/cron.d.cts +3 -2
  27. package/dist/cron.d.ts +3 -2
  28. package/dist/cron.js +655 -390
  29. package/dist/cron.js.map +1 -1
  30. package/dist/{errors-5lj1EWgs.d.ts → errors-BuwwkrAk.d.ts} +2 -2
  31. package/dist/{errors-CE-lMBi2.d.cts → errors-CkCaIqVP.d.cts} +2 -2
  32. package/dist/errors.d.cts +3 -2
  33. package/dist/eval.cjs +654 -389
  34. package/dist/eval.cjs.map +1 -1
  35. package/dist/eval.js +654 -389
  36. package/dist/eval.js.map +1 -1
  37. package/dist/generate-object.d.ts +4 -3
  38. package/dist/index.cjs +800 -392
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +259 -17
  41. package/dist/index.d.ts +259 -17
  42. package/dist/index.js +798 -394
  43. package/dist/index.js.map +1 -1
  44. package/dist/internal/agent-loop/loop-types.d.ts +2 -0
  45. package/dist/internal/agent-loop/tool-result-guard.d.ts +1 -0
  46. package/dist/internal/llm/tool-result-content.d.ts +40 -0
  47. package/dist/internal/llm/types.d.ts +6 -1
  48. package/dist/internal/persistence/conversation-storage-fs.d.cts +3 -1
  49. package/dist/internal/persistence/conversation-storage-fs.d.ts +3 -1
  50. package/dist/internal/persistence/conversation-storage-memory.d.cts +3 -1
  51. package/dist/internal/persistence/conversation-storage-memory.d.ts +3 -1
  52. package/dist/internal/persistence/session-meta.d.cts +13 -0
  53. package/dist/internal/persistence/session-meta.d.ts +13 -0
  54. package/dist/internal/runtime/model-selection.d.ts +14 -0
  55. package/dist/internal/structured-output-helpers.d.ts +1 -1
  56. package/dist/permission-engine.d.ts +32 -1
  57. package/dist/{run-BgfBWX-z.d.cts → run-CrIulPF7.d.cts} +282 -19
  58. package/dist/{run-BgfBWX-z.d.ts → run-CrIulPF7.d.ts} +282 -19
  59. package/dist/stream-object.d.ts +2 -1
  60. package/dist/tool-error.d.ts +30 -0
  61. package/dist/types/agent-prims.d.ts +30 -8
  62. package/dist/types/agent.d.ts +13 -2
  63. package/dist/types/content-blocks.d.ts +47 -0
  64. package/dist/types/conversation-storage.d.ts +38 -0
  65. package/dist/types/index.d.ts +1 -0
  66. package/dist/types/messages.d.ts +2 -9
  67. package/dist/types/run-events.d.ts +84 -0
  68. package/dist/types/run.d.ts +115 -1
  69. package/dist/types/session.d.ts +99 -0
  70. package/dist/types/workflow.d.ts +7 -0
  71. package/dist/workflow.cjs +7 -2
  72. package/dist/workflow.cjs.map +1 -1
  73. package/dist/workflow.d.cts +2 -0
  74. package/dist/workflow.d.ts +2 -0
  75. package/dist/workflow.js +7 -2
  76. package/dist/workflow.js.map +1 -1
  77. package/package.json +14 -34
  78. package/dist/internal/observability/context.d.cts +0 -23
  79. package/dist/internal/observability/context.d.ts +0 -23
  80. package/dist/internal/observability/index.cjs +0 -38
  81. package/dist/internal/observability/index.cjs.map +0 -1
  82. package/dist/internal/observability/index.d.cts +0 -8
  83. package/dist/internal/observability/index.d.ts +0 -8
  84. package/dist/internal/observability/index.js +0 -33
  85. package/dist/internal/observability/index.js.map +0 -1
  86. package/dist/internal/plugins/index.cjs +0 -363
  87. package/dist/internal/plugins/index.cjs.map +0 -1
  88. package/dist/internal/plugins/index.js +0 -357
  89. package/dist/internal/plugins/index.js.map +0 -1
  90. package/dist/internal/workflow/index.d.ts +0 -12
  91. package/dist/server/adapter/index.d.cts +0 -8
  92. package/dist/server/adapter/index.d.ts +0 -8
package/dist/cron.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
+ var zod = require('zod');
4
+ var module$1 = require('module');
3
5
  var crypto = require('crypto');
4
6
  var promises = require('fs/promises');
5
7
  var path = require('path');
6
- var zod = require('zod');
7
8
  var fs = require('fs');
8
9
  var async_hooks = require('async_hooks');
9
- var module$1 = require('module');
10
10
  var os = require('os');
11
11
  var child_process = require('child_process');
12
12
  var url = require('url');
@@ -513,6 +513,256 @@ var init_errors = __esm({
513
513
  };
514
514
  }
515
515
  });
516
+ function toJsonSchema(schema, options = { unrepresentable: "any" }) {
517
+ return zod.toJSONSchema(schema, options);
518
+ }
519
+ var init_to_json_schema = __esm({
520
+ "src/internal/zod/to-json-schema.ts"() {
521
+ }
522
+ });
523
+ function requireZod() {
524
+ if (cachedZ !== void 0) return cachedZ;
525
+ const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cron.cjs', document.baseURI).href)));
526
+ let mod;
527
+ try {
528
+ mod = r("zod");
529
+ } catch (cause) {
530
+ throw new ConfigurationError(
531
+ 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
532
+ { code: "zod_not_installed", cause }
533
+ );
534
+ }
535
+ cachedZ = mod.z ?? mod;
536
+ return cachedZ;
537
+ }
538
+ function makeOutputTool(jsonSchema, onCapture) {
539
+ return {
540
+ name: "output",
541
+ description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
542
+ inputSchema: jsonSchema,
543
+ handler: (input) => {
544
+ const out = onCapture(input);
545
+ if (typeof out === "string") return out;
546
+ if (out instanceof Promise) return out;
547
+ return "";
548
+ }
549
+ };
550
+ }
551
+ function buildTransientAgentOptions(params) {
552
+ return {
553
+ model: params.model,
554
+ local: params.local,
555
+ tools: [params.outputTool],
556
+ systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
557
+ ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
558
+ ...params.providers !== void 0 ? { providers: params.providers } : {}
559
+ };
560
+ }
561
+ function extractUsage(result) {
562
+ const u = result.usage;
563
+ if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
564
+ return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
565
+ }
566
+ function buildToolPrompt(prompt) {
567
+ return `${prompt}
568
+
569
+ Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
570
+ }
571
+ function setupStructuredOutput(schema, maxRetries) {
572
+ const z6 = requireZod();
573
+ const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
574
+ return {
575
+ z: z6,
576
+ jsonSchema,
577
+ maxRetries: maxRetries ?? 1,
578
+ initialUsage: { inputTokens: 0, outputTokens: 0 }
579
+ };
580
+ }
581
+ async function disposeAndDeleteTransient(agent, deletor) {
582
+ await agent.dispose();
583
+ try {
584
+ await deletor(agent.agentId);
585
+ } catch {
586
+ }
587
+ }
588
+ var cachedZ;
589
+ var init_structured_output_helpers = __esm({
590
+ "src/internal/structured-output-helpers.ts"() {
591
+ init_errors();
592
+ init_to_json_schema();
593
+ }
594
+ });
595
+
596
+ // src/generate-object.ts
597
+ var generate_object_exports = {};
598
+ __export(generate_object_exports, {
599
+ GenerateObjectError: () => GenerateObjectError,
600
+ generateObjectImpl: () => generateObjectImpl
601
+ });
602
+ function salvagePartial(schema, raw) {
603
+ if (typeof raw !== "object" || raw === null) return raw;
604
+ const shape = schema.shape;
605
+ if (shape === void 0 || typeof shape !== "object") return raw;
606
+ const rawObj = raw;
607
+ const out = {};
608
+ for (const [key, fieldSchema] of Object.entries(shape)) {
609
+ if (typeof fieldSchema?.safeParse !== "function") continue;
610
+ const parsed = fieldSchema.safeParse(rawObj[key]);
611
+ if (parsed.success) out[key] = parsed.data;
612
+ }
613
+ return out;
614
+ }
615
+ async function runReasoningPhase(options, deps) {
616
+ const reasoningOptions = {
617
+ model: options.model,
618
+ local: options.local,
619
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
620
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
621
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
622
+ };
623
+ const reasoningAgent = await deps.create(reasoningOptions);
624
+ try {
625
+ const run = await reasoningAgent.send(options.prompt);
626
+ const result = await run.wait();
627
+ const text = result.result;
628
+ return typeof text === "string" ? text : options.prompt;
629
+ } finally {
630
+ await disposeAndDeleteTransient(reasoningAgent, deps.delete);
631
+ }
632
+ }
633
+ async function generateObjectImpl(options, deps) {
634
+ const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
635
+ options.schema,
636
+ options.maxRetries
637
+ );
638
+ let capturedRaw;
639
+ let lastUsage = initialUsage;
640
+ const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
641
+ class CaptureSentinel extends Error {
642
+ constructor(captured) {
643
+ super("generate-object-capture");
644
+ this.captured = captured;
645
+ }
646
+ captured;
647
+ [sentinel] = true;
648
+ }
649
+ const outputTool = makeOutputTool(jsonSchema, (input) => {
650
+ if (capturedRaw === void 0) {
651
+ capturedRaw = input;
652
+ }
653
+ throw new CaptureSentinel(input);
654
+ });
655
+ const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
656
+ const structuringModel = options.structuringModel ?? options.model;
657
+ const structuringPrompt = reasoningText ?? options.prompt;
658
+ const agentOptions = buildTransientAgentOptions({
659
+ model: structuringModel,
660
+ local: options.local,
661
+ outputTool,
662
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
663
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
664
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
665
+ });
666
+ const agent = await deps.create(agentOptions);
667
+ try {
668
+ const userMessage = buildToolPrompt(structuringPrompt);
669
+ let lastParseError;
670
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
671
+ capturedRaw = void 0;
672
+ const run = await agent.send(userMessage);
673
+ const result = await run.wait();
674
+ lastUsage = extractUsage(result);
675
+ if (capturedRaw === void 0) {
676
+ if (result.status === "error" && result.error !== void 0) {
677
+ throw new GenerateObjectError(
678
+ "no_tool_call",
679
+ `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
680
+ result.error
681
+ );
682
+ }
683
+ if (attempt === maxRetries) {
684
+ throw new GenerateObjectError(
685
+ "no_tool_call",
686
+ "The model returned text instead of calling the `output` tool."
687
+ );
688
+ }
689
+ continue;
690
+ }
691
+ const parsed = options.schema.safeParse(capturedRaw);
692
+ if (parsed.success) {
693
+ return {
694
+ object: parsed.data,
695
+ raw: capturedRaw,
696
+ usage: lastUsage,
697
+ finishReason: "tool_use"
698
+ };
699
+ }
700
+ lastParseError = parsed.error;
701
+ }
702
+ if (options.errorStrategy === "return-raw") {
703
+ return {
704
+ object: capturedRaw,
705
+ raw: capturedRaw,
706
+ usage: lastUsage,
707
+ finishReason: "tool_use"
708
+ };
709
+ }
710
+ if (options.errorStrategy === "return-partial") {
711
+ return {
712
+ object: salvagePartial(options.schema, capturedRaw),
713
+ raw: capturedRaw,
714
+ usage: lastUsage,
715
+ finishReason: "tool_use"
716
+ };
717
+ }
718
+ throw new GenerateObjectError(
719
+ "parse_failed",
720
+ "Schema parse failed after all retries.",
721
+ lastParseError
722
+ );
723
+ } finally {
724
+ await disposeAndDeleteTransient(agent, deps.delete);
725
+ }
726
+ }
727
+ var GenerateObjectError;
728
+ var init_generate_object = __esm({
729
+ "src/generate-object.ts"() {
730
+ init_structured_output_helpers();
731
+ GenerateObjectError = class extends Error {
732
+ name = "GenerateObjectError";
733
+ code;
734
+ cause;
735
+ constructor(code, message, cause) {
736
+ super(message);
737
+ this.code = code;
738
+ if (cause !== void 0) this.cause = cause;
739
+ }
740
+ };
741
+ }
742
+ });
743
+
744
+ // src/internal/runtime/registry/agent-factory-registry.ts
745
+ var agent_factory_registry_exports = {};
746
+ __export(agent_factory_registry_exports, {
747
+ getAgentFacade: () => getAgentFacade,
748
+ setAgentFacade: () => setAgentFacade
749
+ });
750
+ function setAgentFacade(facade) {
751
+ registered = facade;
752
+ }
753
+ function getAgentFacade() {
754
+ if (registered === void 0) {
755
+ throw new Error(
756
+ "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
757
+ );
758
+ }
759
+ return registered;
760
+ }
761
+ var registered;
762
+ var init_agent_factory_registry = __esm({
763
+ "src/internal/runtime/registry/agent-factory-registry.ts"() {
764
+ }
765
+ });
516
766
 
517
767
  // src/internal/persistence/cwd-mutex.ts
518
768
  function withCwdMutex(key, fn) {
@@ -1430,29 +1680,6 @@ var init_judge_call = __esm({
1430
1680
  }
1431
1681
  });
1432
1682
 
1433
- // src/internal/runtime/registry/agent-factory-registry.ts
1434
- var agent_factory_registry_exports = {};
1435
- __export(agent_factory_registry_exports, {
1436
- getAgentFacade: () => getAgentFacade,
1437
- setAgentFacade: () => setAgentFacade
1438
- });
1439
- function setAgentFacade(facade) {
1440
- registered3 = facade;
1441
- }
1442
- function getAgentFacade() {
1443
- if (registered3 === void 0) {
1444
- throw new Error(
1445
- "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
1446
- );
1447
- }
1448
- return registered3;
1449
- }
1450
- var registered3;
1451
- var init_agent_factory_registry = __esm({
1452
- "src/internal/runtime/registry/agent-factory-registry.ts"() {
1453
- }
1454
- });
1455
-
1456
1683
  // src/internal/runtime/lifecycle/run-to-completion.ts
1457
1684
  var run_to_completion_exports = {};
1458
1685
  __export(run_to_completion_exports, {
@@ -1617,14 +1844,14 @@ async function forkAgentImpl(parent, options, deps) {
1617
1844
  return {
1618
1845
  result: result.result,
1619
1846
  toolCalls: [],
1620
- usage: extractUsage(result)
1847
+ usage: extractUsage2(result)
1621
1848
  };
1622
1849
  });
1623
1850
  } finally {
1624
1851
  await fork.dispose();
1625
1852
  }
1626
1853
  }
1627
- function extractUsage(result) {
1854
+ function extractUsage2(result) {
1628
1855
  if (typeof result !== "object" || result === null) {
1629
1856
  return { inputTokens: 0, outputTokens: 0 };
1630
1857
  }
@@ -2060,342 +2287,115 @@ async function runWorkAndFinalize(taskId, aborter, work) {
2060
2287
  type: "progress",
2061
2288
  taskId,
2062
2289
  at: Date.now(),
2063
- payload
2064
- });
2065
- }
2066
- };
2067
- try {
2068
- const result = await reentryAls.run(true, () => Promise.resolve().then(() => work(ctx)));
2069
- if (aborter.signal.aborted) {
2070
- const cancelledAt = Date.now();
2071
- await transition(taskId, "cancelled", { cancelledAt });
2072
- emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2073
- return;
2074
- }
2075
- const finishedAt = Date.now();
2076
- await transition(taskId, "finished", { finishedAt, result });
2077
- emitToSubscribers(taskId, { type: "finished", taskId, finishedAt, result });
2078
- } catch (err) {
2079
- if (aborter.signal.aborted) {
2080
- const cancelledAt = Date.now();
2081
- await transition(taskId, "cancelled", { cancelledAt });
2082
- emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2083
- return;
2084
- }
2085
- const erroredAt = Date.now();
2086
- const error = err instanceof Error ? { code: err.code ?? "work_threw", message: err.message } : { code: "work_threw", message: String(err) };
2087
- await transition(taskId, "error", { erroredAt, error });
2088
- emitToSubscribers(taskId, { type: "errored", taskId, erroredAt, error });
2089
- }
2090
- }
2091
- async function checkCancelRequestedAt(taskId, aborter) {
2092
- if (aborter.signal.aborted) return;
2093
- try {
2094
- const handle = await state.store.get(taskId);
2095
- if (handle?.cancelRequested === true) {
2096
- aborter.abort("cancelRequested");
2097
- }
2098
- } catch {
2099
- }
2100
- }
2101
- async function acquireSlot() {
2102
- if (reentryAls.getStore() === true) {
2103
- return () => {
2104
- };
2105
- }
2106
- return state.semaphore.acquire();
2107
- }
2108
- async function submit(internal) {
2109
- state.firstSubmitSeen = true;
2110
- startEvictTimerIfNeeded();
2111
- const resolvedId = resolveIdInternal(internal);
2112
- const submitSpan = startTaskSubmitSpan({ taskId: resolvedId, kind: internal.kind });
2113
- const existing = await state.store.get(resolvedId);
2114
- if (existing !== void 0) {
2115
- submitSpan.end();
2116
- return existing;
2117
- }
2118
- const queuedHandle = await buildAndInsertQueued(internal, resolvedId);
2119
- submitSpan.end();
2120
- if (internal.signal?.aborted === true) {
2121
- return shortCircuitAborted(queuedHandle, internal.signal);
2122
- }
2123
- const aborter = new AbortController();
2124
- state.aborters.set(resolvedId, aborter);
2125
- if (internal.signal !== void 0) {
2126
- internal.signal.addEventListener("abort", () => aborter.abort(internal.signal?.reason), {
2127
- once: true
2128
- });
2129
- }
2130
- const release = await acquireSlot();
2131
- void (async () => {
2132
- try {
2133
- await checkCancelRequestedAt(resolvedId, aborter);
2134
- if (aborter.signal.aborted) {
2135
- const cancelledAt = Date.now();
2136
- await transition(resolvedId, "cancelled", { cancelledAt });
2137
- emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
2138
- return;
2139
- }
2140
- const startedAt = Date.now();
2141
- await transition(resolvedId, "running", { startedAt });
2142
- emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
2143
- await runWorkAndFinalize(resolvedId, aborter, internal.work);
2144
- } finally {
2145
- release();
2146
- state.aborters.delete(resolvedId);
2147
- }
2148
- })();
2149
- return queuedHandle;
2150
- }
2151
- async function get(id) {
2152
- if (!isValidTaskId(id, true)) return void 0;
2153
- return state.store.get(id);
2154
- }
2155
- var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state;
2156
- var init_registry = __esm({
2157
- "src/internal/task/registry.ts"() {
2158
- init_errors();
2159
- init_task();
2160
- init_async_semaphore();
2161
- init_ring_buffer();
2162
- init_store();
2163
- init_subscribe();
2164
- init_telemetry();
2165
- DEFAULT_CONCURRENCY = 8;
2166
- DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
2167
- EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
2168
- RING_CAP = 64;
2169
- reentryAls = new async_hooks.AsyncLocalStorage();
2170
- state = buildState({});
2171
- }
2172
- });
2173
- function toJsonSchema(schema, options = { unrepresentable: "any" }) {
2174
- return zod.toJSONSchema(schema, options);
2175
- }
2176
- var init_to_json_schema = __esm({
2177
- "src/internal/zod/to-json-schema.ts"() {
2178
- }
2179
- });
2180
- function requireZod() {
2181
- if (cachedZ !== void 0) return cachedZ;
2182
- const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cron.cjs', document.baseURI).href)));
2183
- let mod;
2184
- try {
2185
- mod = r("zod");
2186
- } catch (cause) {
2187
- throw new ConfigurationError(
2188
- 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
2189
- { code: "zod_not_installed", cause }
2190
- );
2191
- }
2192
- cachedZ = mod.z ?? mod;
2193
- return cachedZ;
2194
- }
2195
- function makeOutputTool(jsonSchema, onCapture) {
2196
- return {
2197
- name: "output",
2198
- description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
2199
- inputSchema: jsonSchema,
2200
- handler: (input) => {
2201
- const out = onCapture(input);
2202
- if (typeof out === "string") return out;
2203
- if (out instanceof Promise) return out;
2204
- return "";
2205
- }
2206
- };
2207
- }
2208
- function buildTransientAgentOptions(params) {
2209
- return {
2210
- model: params.model,
2211
- local: params.local,
2212
- tools: [params.outputTool],
2213
- systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
2214
- ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
2215
- ...params.providers !== void 0 ? { providers: params.providers } : {}
2216
- };
2217
- }
2218
- function extractUsage2(result) {
2219
- const u = result.usage;
2220
- if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
2221
- return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
2222
- }
2223
- function buildToolPrompt(prompt) {
2224
- return `${prompt}
2225
-
2226
- Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
2227
- }
2228
- function setupStructuredOutput(schema, maxRetries) {
2229
- const z6 = requireZod();
2230
- const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
2231
- return {
2232
- z: z6,
2233
- jsonSchema,
2234
- maxRetries: maxRetries ?? 1,
2235
- initialUsage: { inputTokens: 0, outputTokens: 0 }
2236
- };
2237
- }
2238
- async function disposeAndDeleteTransient(agent, deletor) {
2239
- await agent.dispose();
2240
- try {
2241
- await deletor(agent.agentId);
2242
- } catch {
2243
- }
2244
- }
2245
- var cachedZ;
2246
- var init_structured_output_helpers = __esm({
2247
- "src/internal/structured-output-helpers.ts"() {
2248
- init_errors();
2249
- init_to_json_schema();
2250
- }
2251
- });
2252
-
2253
- // src/generate-object.ts
2254
- var generate_object_exports = {};
2255
- __export(generate_object_exports, {
2256
- GenerateObjectError: () => GenerateObjectError,
2257
- generateObjectImpl: () => generateObjectImpl
2258
- });
2259
- function salvagePartial(schema, raw) {
2260
- if (typeof raw !== "object" || raw === null) return raw;
2261
- const shape = schema.shape;
2262
- if (shape === void 0 || typeof shape !== "object") return raw;
2263
- const rawObj = raw;
2264
- const out = {};
2265
- for (const [key, fieldSchema] of Object.entries(shape)) {
2266
- if (typeof fieldSchema?.safeParse !== "function") continue;
2267
- const parsed = fieldSchema.safeParse(rawObj[key]);
2268
- if (parsed.success) out[key] = parsed.data;
2269
- }
2270
- return out;
2271
- }
2272
- async function runReasoningPhase(options, deps) {
2273
- const reasoningOptions = {
2274
- model: options.model,
2275
- local: options.local,
2276
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
2277
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
2278
- ...options.providers !== void 0 ? { providers: options.providers } : {}
2279
- };
2280
- const reasoningAgent = await deps.create(reasoningOptions);
2281
- try {
2282
- const run = await reasoningAgent.send(options.prompt);
2283
- const result = await run.wait();
2284
- const text = result.result;
2285
- return typeof text === "string" ? text : options.prompt;
2286
- } finally {
2287
- await disposeAndDeleteTransient(reasoningAgent, deps.delete);
2288
- }
2289
- }
2290
- async function generateObjectImpl(options, deps) {
2291
- const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
2292
- options.schema,
2293
- options.maxRetries
2294
- );
2295
- let capturedRaw;
2296
- let lastUsage = initialUsage;
2297
- const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
2298
- class CaptureSentinel extends Error {
2299
- constructor(captured) {
2300
- super("generate-object-capture");
2301
- this.captured = captured;
2302
- }
2303
- captured;
2304
- [sentinel] = true;
2305
- }
2306
- const outputTool = makeOutputTool(jsonSchema, (input) => {
2307
- if (capturedRaw === void 0) {
2308
- capturedRaw = input;
2309
- }
2310
- throw new CaptureSentinel(input);
2311
- });
2312
- const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
2313
- const structuringModel = options.structuringModel ?? options.model;
2314
- const structuringPrompt = reasoningText ?? options.prompt;
2315
- const agentOptions = buildTransientAgentOptions({
2316
- model: structuringModel,
2317
- local: options.local,
2318
- outputTool,
2319
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
2320
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
2321
- ...options.providers !== void 0 ? { providers: options.providers } : {}
2322
- });
2323
- const agent = await deps.create(agentOptions);
2290
+ payload
2291
+ });
2292
+ }
2293
+ };
2324
2294
  try {
2325
- const userMessage = buildToolPrompt(structuringPrompt);
2326
- let lastParseError;
2327
- for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
2328
- capturedRaw = void 0;
2329
- const run = await agent.send(userMessage);
2330
- const result = await run.wait();
2331
- lastUsage = extractUsage2(result);
2332
- if (capturedRaw === void 0) {
2333
- if (result.status === "error" && result.error !== void 0) {
2334
- throw new GenerateObjectError(
2335
- "no_tool_call",
2336
- `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
2337
- result.error
2338
- );
2339
- }
2340
- if (attempt === maxRetries) {
2341
- throw new GenerateObjectError(
2342
- "no_tool_call",
2343
- "The model returned text instead of calling the `output` tool."
2344
- );
2345
- }
2346
- continue;
2347
- }
2348
- const parsed = options.schema.safeParse(capturedRaw);
2349
- if (parsed.success) {
2350
- return {
2351
- object: parsed.data,
2352
- raw: capturedRaw,
2353
- usage: lastUsage,
2354
- finishReason: "tool_use"
2355
- };
2356
- }
2357
- lastParseError = parsed.error;
2295
+ const result = await reentryAls.run(true, () => Promise.resolve().then(() => work(ctx)));
2296
+ if (aborter.signal.aborted) {
2297
+ const cancelledAt = Date.now();
2298
+ await transition(taskId, "cancelled", { cancelledAt });
2299
+ emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2300
+ return;
2358
2301
  }
2359
- if (options.errorStrategy === "return-raw") {
2360
- return {
2361
- object: capturedRaw,
2362
- raw: capturedRaw,
2363
- usage: lastUsage,
2364
- finishReason: "tool_use"
2365
- };
2302
+ const finishedAt = Date.now();
2303
+ await transition(taskId, "finished", { finishedAt, result });
2304
+ emitToSubscribers(taskId, { type: "finished", taskId, finishedAt, result });
2305
+ } catch (err) {
2306
+ if (aborter.signal.aborted) {
2307
+ const cancelledAt = Date.now();
2308
+ await transition(taskId, "cancelled", { cancelledAt });
2309
+ emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2310
+ return;
2366
2311
  }
2367
- if (options.errorStrategy === "return-partial") {
2368
- return {
2369
- object: salvagePartial(options.schema, capturedRaw),
2370
- raw: capturedRaw,
2371
- usage: lastUsage,
2372
- finishReason: "tool_use"
2373
- };
2312
+ const erroredAt = Date.now();
2313
+ const error = err instanceof Error ? { code: err.code ?? "work_threw", message: err.message } : { code: "work_threw", message: String(err) };
2314
+ await transition(taskId, "error", { erroredAt, error });
2315
+ emitToSubscribers(taskId, { type: "errored", taskId, erroredAt, error });
2316
+ }
2317
+ }
2318
+ async function checkCancelRequestedAt(taskId, aborter) {
2319
+ if (aborter.signal.aborted) return;
2320
+ try {
2321
+ const handle = await state.store.get(taskId);
2322
+ if (handle?.cancelRequested === true) {
2323
+ aborter.abort("cancelRequested");
2374
2324
  }
2375
- throw new GenerateObjectError(
2376
- "parse_failed",
2377
- "Schema parse failed after all retries.",
2378
- lastParseError
2379
- );
2380
- } finally {
2381
- await disposeAndDeleteTransient(agent, deps.delete);
2325
+ } catch {
2382
2326
  }
2383
2327
  }
2384
- var GenerateObjectError;
2385
- var init_generate_object = __esm({
2386
- "src/generate-object.ts"() {
2387
- init_structured_output_helpers();
2388
- GenerateObjectError = class extends Error {
2389
- name = "GenerateObjectError";
2390
- code;
2391
- cause;
2392
- constructor(code, message, cause) {
2393
- super(message);
2394
- this.code = code;
2395
- if (cause !== void 0) this.cause = cause;
2396
- }
2328
+ async function acquireSlot() {
2329
+ if (reentryAls.getStore() === true) {
2330
+ return () => {
2397
2331
  };
2398
2332
  }
2333
+ return state.semaphore.acquire();
2334
+ }
2335
+ async function submit(internal) {
2336
+ state.firstSubmitSeen = true;
2337
+ startEvictTimerIfNeeded();
2338
+ const resolvedId = resolveIdInternal(internal);
2339
+ const submitSpan = startTaskSubmitSpan({ taskId: resolvedId, kind: internal.kind });
2340
+ const existing = await state.store.get(resolvedId);
2341
+ if (existing !== void 0) {
2342
+ submitSpan.end();
2343
+ return existing;
2344
+ }
2345
+ const queuedHandle = await buildAndInsertQueued(internal, resolvedId);
2346
+ submitSpan.end();
2347
+ if (internal.signal?.aborted === true) {
2348
+ return shortCircuitAborted(queuedHandle, internal.signal);
2349
+ }
2350
+ const aborter = new AbortController();
2351
+ state.aborters.set(resolvedId, aborter);
2352
+ if (internal.signal !== void 0) {
2353
+ internal.signal.addEventListener("abort", () => aborter.abort(internal.signal?.reason), {
2354
+ once: true
2355
+ });
2356
+ }
2357
+ const release = await acquireSlot();
2358
+ void (async () => {
2359
+ try {
2360
+ await checkCancelRequestedAt(resolvedId, aborter);
2361
+ if (aborter.signal.aborted) {
2362
+ const cancelledAt = Date.now();
2363
+ await transition(resolvedId, "cancelled", { cancelledAt });
2364
+ emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
2365
+ return;
2366
+ }
2367
+ const startedAt = Date.now();
2368
+ await transition(resolvedId, "running", { startedAt });
2369
+ emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
2370
+ await runWorkAndFinalize(resolvedId, aborter, internal.work);
2371
+ } finally {
2372
+ release();
2373
+ state.aborters.delete(resolvedId);
2374
+ }
2375
+ })();
2376
+ return queuedHandle;
2377
+ }
2378
+ async function get(id) {
2379
+ if (!isValidTaskId(id, true)) return void 0;
2380
+ return state.store.get(id);
2381
+ }
2382
+ var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state;
2383
+ var init_registry = __esm({
2384
+ "src/internal/task/registry.ts"() {
2385
+ init_errors();
2386
+ init_task();
2387
+ init_async_semaphore();
2388
+ init_ring_buffer();
2389
+ init_store();
2390
+ init_subscribe();
2391
+ init_telemetry();
2392
+ DEFAULT_CONCURRENCY = 8;
2393
+ DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
2394
+ EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
2395
+ RING_CAP = 64;
2396
+ reentryAls = new async_hooks.AsyncLocalStorage();
2397
+ state = buildState({});
2398
+ }
2399
2399
  });
2400
2400
 
2401
2401
  // src/stream-object.ts
@@ -2452,7 +2452,7 @@ async function* streamObjectImpl(options, deps) {
2452
2452
  }
2453
2453
  }
2454
2454
  const result = await run.wait();
2455
- lastUsage = extractUsage2(result);
2455
+ lastUsage = extractUsage(result);
2456
2456
  if (capturedRaw === void 0) {
2457
2457
  if (result.status === "error" && result.error !== void 0) {
2458
2458
  throw new StreamObjectError(
@@ -2798,6 +2798,7 @@ var AgentBuilder = class {
2798
2798
  constructor(deps) {
2799
2799
  this.deps = deps;
2800
2800
  }
2801
+ /** SE8 — accepts a bare-string id shorthand (`"openai/gpt-4o-mini"`) or a {@link ModelSelection}. */
2801
2802
  model(m) {
2802
2803
  this.opts.model = m;
2803
2804
  return this;
@@ -3151,6 +3152,69 @@ function isLocalAgentId(id) {
3151
3152
  return id.startsWith(PREFIX.localAgent);
3152
3153
  }
3153
3154
 
3155
+ // src/internal/runtime/config/default-model.ts
3156
+ var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
3157
+
3158
+ // src/agent-generate.ts
3159
+ async function agentGenerate(agent, agentModel, agentApiKey, agentLocal, input, options) {
3160
+ const { output, maxRetries, errorStrategy, ...sendOptions } = options;
3161
+ const run = await agent.send(input, sendOptions);
3162
+ const result = await run.wait();
3163
+ const { generateObjectImpl: generateObjectImpl2, GenerateObjectError: GenerateObjectError2 } = await Promise.resolve().then(() => (init_generate_object(), generate_object_exports));
3164
+ const answer = structurableAnswerOrThrow(result, GenerateObjectError2);
3165
+ const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
3166
+ const facade = getAgentFacade2();
3167
+ const structured = await generateObjectImpl2(
3168
+ {
3169
+ prompt: answer,
3170
+ schema: output,
3171
+ // `agentModel` arrives already normalized (`this.model` passed through
3172
+ // `normalizeModel` in the LocalAgent/CloudAgent constructor), so the widened
3173
+ // `string | ModelSelection` field only ever receives a `ModelSelection` here.
3174
+ model: agentModel ?? { id: DEFAULT_AGENTIC_MODEL_ID },
3175
+ // `local` is required to keep the transient structuring agent local-only;
3176
+ // default to the process cwd when the agent had no explicit local config.
3177
+ local: agentLocal ?? { cwd: process.cwd() },
3178
+ ...agentApiKey !== void 0 ? { apiKey: agentApiKey } : {},
3179
+ ...maxRetries !== void 0 ? { maxRetries } : {},
3180
+ ...errorStrategy !== void 0 ? { errorStrategy } : {}
3181
+ },
3182
+ {
3183
+ create: (opts) => facade.create(opts),
3184
+ delete: (agentId) => facade.delete(agentId)
3185
+ }
3186
+ );
3187
+ return {
3188
+ object: structured.object,
3189
+ result,
3190
+ raw: structured.raw,
3191
+ usage: structured.usage
3192
+ };
3193
+ }
3194
+ function structurableAnswerOrThrow(result, GenerateObjectError2) {
3195
+ if (result.status === "error") {
3196
+ throw new GenerateObjectError2(
3197
+ "no_tool_call",
3198
+ `Agent run failed before an answer could be structured: ${result.error?.message ?? "unknown error"} [${result.error?.code ?? "?"}]`,
3199
+ result.error
3200
+ );
3201
+ }
3202
+ if (result.status === "cancelled") {
3203
+ throw new GenerateObjectError2(
3204
+ "no_tool_call",
3205
+ "Agent run was cancelled before an answer could be structured."
3206
+ );
3207
+ }
3208
+ const answer = result.result;
3209
+ if (answer === void 0 || answer.length === 0) {
3210
+ throw new GenerateObjectError2(
3211
+ "no_tool_call",
3212
+ "The tool loop finished with no text answer (tool-only completion) \u2014 there is nothing to structure."
3213
+ );
3214
+ }
3215
+ return answer;
3216
+ }
3217
+
3154
3218
  // src/internal/runtime/cloud/cloud-agent.ts
3155
3219
  init_errors();
3156
3220
  init_cwd_mutex();
@@ -3301,8 +3365,19 @@ function safeFilenameForId(id, options) {
3301
3365
  return `h-${crypto.createHash("sha256").update(id).digest("hex").slice(0, 16)}`;
3302
3366
  }
3303
3367
 
3304
- // src/internal/runtime/config/default-model.ts
3305
- var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
3368
+ // src/internal/runtime/model-selection.ts
3369
+ init_errors();
3370
+ function normalizeModel(m) {
3371
+ if (m === void 0 || typeof m !== "string") return m;
3372
+ const id = m.trim();
3373
+ if (id.length === 0) {
3374
+ throw new ConfigurationError(
3375
+ 'model must be a non-empty "provider/model" id string or a ModelSelection object',
3376
+ { code: "invalid_model_selection" }
3377
+ );
3378
+ }
3379
+ return { id };
3380
+ }
3306
3381
 
3307
3382
  // src/internal/runtime/registry/agent-registry-store.ts
3308
3383
  init_cwd_mutex();
@@ -3379,7 +3454,10 @@ var REGISTRY_RELATIVE_PATH = path.join(".theokit", "agents", "registry.json");
3379
3454
  function stripSecretsFromOptions(options) {
3380
3455
  return assignDefined({
3381
3456
  name: options.name,
3382
- model: options.model !== void 0 ? { id: options.model.id } : void 0,
3457
+ model: (() => {
3458
+ const m = normalizeModel(options.model);
3459
+ return m !== void 0 ? { id: m.id } : void 0;
3460
+ })(),
3383
3461
  systemPrompt: typeof options.systemPrompt === "string" ? options.systemPrompt : void 0,
3384
3462
  local: serializeLocal(options.local),
3385
3463
  cloud: serializeCloud(options.cloud),
@@ -3664,7 +3742,8 @@ function serializeCloudAgentConfig(options) {
3664
3742
  cloud: serializeCloud2(options.cloud)
3665
3743
  };
3666
3744
  if (typeof options.agentId === "string") payload.agentId = options.agentId;
3667
- if (options.model !== void 0) payload.model = { id: options.model.id };
3745
+ const cfgModel = normalizeModel(options.model);
3746
+ if (cfgModel !== void 0) payload.model = { id: cfgModel.id };
3668
3747
  if (typeof options.systemPrompt === "string") payload.systemPrompt = options.systemPrompt;
3669
3748
  const skills = serializeSkills(options.skills);
3670
3749
  if (skills !== void 0) payload.skills = skills;
@@ -4560,6 +4639,7 @@ function applyScriptMetrics(base, script) {
4560
4639
  if (script.cost !== void 0) base.cost = script.cost;
4561
4640
  if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
4562
4641
  if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
4642
+ if (script.origin !== void 0) base.origin = script.origin;
4563
4643
  }
4564
4644
 
4565
4645
  // src/internal/runtime/cloud/cloud-run.ts
@@ -4915,7 +4995,7 @@ var CloudAgent = class {
4915
4995
  disposed = false;
4916
4996
  constructor(options, providedAgentId) {
4917
4997
  this.agentId = providedAgentId ?? options.agentId ?? generateCloudAgentId();
4918
- this.model = options.model;
4998
+ this.model = normalizeModel(options.model);
4919
4999
  this.options = options;
4920
5000
  this.cloudPayload = serializeCloudAgentConfig({ ...options, agentId: this.agentId });
4921
5001
  const repoUrls = (options.cloud?.repos ?? []).map((repo) => repo.url);
@@ -4945,6 +5025,17 @@ var CloudAgent = class {
4945
5025
  const apiKey = resolveApiKey(this.options.apiKey);
4946
5026
  return isFixtureApiKey(apiKey) && getConfiguredBaseUrl() === void 0;
4947
5027
  }
5028
+ // SE9 — integrated structured output; delegates to the shared helper.
5029
+ generate(message, options) {
5030
+ return agentGenerate(
5031
+ this,
5032
+ this.model,
5033
+ this.options.apiKey,
5034
+ this.options.local,
5035
+ message,
5036
+ options
5037
+ );
5038
+ }
4948
5039
  async send(message, options = {}) {
4949
5040
  if (options.tools !== void 0 && options.tools.length > 0) {
4950
5041
  throw new ConfigurationError(
@@ -4975,7 +5066,7 @@ var CloudAgent = class {
4975
5066
  });
4976
5067
  }
4977
5068
  async sendLocked(message, options) {
4978
- const overrideModel = options.model;
5069
+ const overrideModel = normalizeModel(options.model);
4979
5070
  if (overrideModel !== void 0) {
4980
5071
  this.model = overrideModel;
4981
5072
  updateRegisteredAgent(this.agentId, { model: overrideModel });
@@ -5889,18 +5980,18 @@ var ALL_ADAPTERS = [
5889
5980
  arizeAdapter,
5890
5981
  braintrustAdapter
5891
5982
  ];
5892
- var registered = /* @__PURE__ */ new Set();
5983
+ var registered2 = /* @__PURE__ */ new Set();
5893
5984
  function tryAutoRegisterAdapters(settings) {
5894
5985
  if (settings?.enabled !== true) return;
5895
5986
  if (settings.autoDetect === false) return;
5896
5987
  const disabled = new Set(settings.disable ?? []);
5897
5988
  for (const adapter of ALL_ADAPTERS) {
5898
- if (registered.has(adapter.moduleName)) continue;
5989
+ if (registered2.has(adapter.moduleName)) continue;
5899
5990
  if (disabled.has(adapter.displayName.toLowerCase())) continue;
5900
5991
  if (!adapter.detect()) continue;
5901
5992
  try {
5902
5993
  adapter.register();
5903
- registered.add(adapter.moduleName);
5994
+ registered2.add(adapter.moduleName);
5904
5995
  process.stderr.write(`[theokit-sdk] telemetry: ${adapter.displayName} auto-instrumented.
5905
5996
  `);
5906
5997
  } catch (cause) {
@@ -6463,6 +6554,26 @@ function paginate(items, opts) {
6463
6554
  return items.slice(start, end);
6464
6555
  }
6465
6556
 
6557
+ // src/internal/persistence/session-meta.ts
6558
+ function applyMetaPatch(current, patch) {
6559
+ const next = {};
6560
+ if (current.title !== void 0) next.title = current.title;
6561
+ if (current.tag !== void 0) next.tag = current.tag;
6562
+ if (patch.title === null) delete next.title;
6563
+ else if (patch.title !== void 0) next.title = patch.title;
6564
+ if (patch.tag === null) delete next.tag;
6565
+ else if (patch.tag !== void 0) next.tag = patch.tag;
6566
+ return next;
6567
+ }
6568
+ function coerceSessionMeta(raw) {
6569
+ if (typeof raw !== "object" || raw === null) return void 0;
6570
+ const obj = raw;
6571
+ const meta = {};
6572
+ if (typeof obj.title === "string") meta.title = obj.title;
6573
+ if (typeof obj.tag === "string") meta.tag = obj.tag;
6574
+ return meta.title === void 0 && meta.tag === void 0 ? void 0 : meta;
6575
+ }
6576
+
6466
6577
  // src/internal/persistence/conversation-storage-fs.ts
6467
6578
  var FileSystemConversationStorage = class {
6468
6579
  #root;
@@ -6513,6 +6624,37 @@ var FileSystemConversationStorage = class {
6513
6624
  async compact(conversationId, maxTurns) {
6514
6625
  await compactSessionFile(this.#root, conversationId, maxTurns);
6515
6626
  }
6627
+ // SE4 — session metadata persisted as a per-conversation sidecar
6628
+ // `<root>/.theokit/agents/<safeId>/session.json` (same sanitized perimeter as
6629
+ // the transcript). Kept separate from messages.jsonl so a title/tag write does
6630
+ // not rewrite the append-only log.
6631
+ #metaPath(conversationId) {
6632
+ const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
6633
+ return safePathJoin(this.#root, ".theokit", "agents", safe2, "session.json");
6634
+ }
6635
+ async getSessionMeta(conversationId) {
6636
+ try {
6637
+ const raw = await promises.readFile(this.#metaPath(conversationId), "utf8");
6638
+ return coerceSessionMeta(JSON.parse(raw));
6639
+ } catch (cause) {
6640
+ if (cause.code === "ENOENT") return void 0;
6641
+ throw cause;
6642
+ }
6643
+ }
6644
+ async setSessionMeta(conversationId, patch) {
6645
+ const metaPath = this.#metaPath(conversationId);
6646
+ await promises.mkdir(path.dirname(metaPath), { recursive: true });
6647
+ await withFileLock(metaPath, async () => {
6648
+ let current = {};
6649
+ try {
6650
+ current = coerceSessionMeta(JSON.parse(await promises.readFile(metaPath, "utf8"))) ?? {};
6651
+ } catch (cause) {
6652
+ if (cause.code !== "ENOENT") throw cause;
6653
+ }
6654
+ const next = applyMetaPatch(current, patch);
6655
+ await promises.writeFile(metaPath, redactSecrets(JSON.stringify(next)), "utf8");
6656
+ });
6657
+ }
6516
6658
  async dispose() {
6517
6659
  }
6518
6660
  };
@@ -8432,7 +8574,7 @@ function bootstrapSubmanagers(args) {
8432
8574
  const providerCount = (args.options.providers?.routes?.length ?? 0) + enabledPluginNames(args.options.plugins).length;
8433
8575
  if (providerCount > 0 || args.options.providers !== void 0) {
8434
8576
  out.providers = new ProvidersManagerImpl(
8435
- args.options.model,
8577
+ normalizeModel(args.options.model),
8436
8578
  args.options.providers,
8437
8579
  args.options.plugins
8438
8580
  );
@@ -8502,6 +8644,7 @@ function createLocalRun(options) {
8502
8644
  projectMcpServers: options.projectMcpServers,
8503
8645
  ...options.persistMemoryFact !== void 0 ? { persistMemoryFact: options.persistMemoryFact } : {}
8504
8646
  });
8647
+ if (options.sendOptions.origin !== void 0) script.origin = options.sendOptions.origin;
8505
8648
  const handle = new LocalRun({
8506
8649
  id,
8507
8650
  agentId: options.agentId,
@@ -9150,6 +9293,15 @@ async function emitReasoningDeltaCallback(inputs, text) {
9150
9293
  );
9151
9294
  }
9152
9295
 
9296
+ // src/types/run-events.ts
9297
+ function emitRunEvent(sink, event) {
9298
+ if (sink === void 0) return;
9299
+ try {
9300
+ sink(event);
9301
+ } catch {
9302
+ }
9303
+ }
9304
+
9153
9305
  // src/internal/agent-loop/tool-dispatch.ts
9154
9306
  init_async_local_storage();
9155
9307
 
@@ -9247,6 +9399,22 @@ function coerceArgsToSchema(args, schema) {
9247
9399
  return { value: out, changed };
9248
9400
  }
9249
9401
 
9402
+ // src/tool-error.ts
9403
+ init_errors();
9404
+ var ToolError = class extends TheokitAgentError {
9405
+ name = "ToolError";
9406
+ /** The error content surfaced to the model: a string, or text/image blocks. */
9407
+ content;
9408
+ constructor(content, options = {}) {
9409
+ super(renderToolErrorMessage(content), { ...options, isRetryable: false });
9410
+ this.content = content;
9411
+ }
9412
+ };
9413
+ function renderToolErrorMessage(content) {
9414
+ if (typeof content === "string") return content;
9415
+ return content.map((block) => block.type === "text" ? block.text : `[${block.source.media_type} image]`).join("\n");
9416
+ }
9417
+
9250
9418
  // src/internal/runtime/tools/shell-tool.ts
9251
9419
  async function runShell(options) {
9252
9420
  if (options.sandbox === true && isObviouslyUnsafe(options.command)) {
@@ -9286,23 +9454,27 @@ async function executeTool(inputs, resolved, call) {
9286
9454
  if (resolved.origin === "shell") return runShellTool(inputs, call);
9287
9455
  if (resolved.origin === "memory") return runMemoryTool(resolved, call, inputs.context);
9288
9456
  if (resolved.origin === "custom")
9289
- return runCustomTool(resolved, call, inputs.signal, inputs.context);
9457
+ return runCustomTool(resolved, call, inputs.signal, inputs.context, inputs.messages);
9290
9458
  return runMcpTool(inputs, resolved, call);
9291
9459
  }
9292
9460
  async function runMemoryTool(resolved, call, context) {
9293
9461
  return runHandlerTool("memory", resolved.memoryHandler, call, void 0, context);
9294
9462
  }
9295
- async function runCustomTool(resolved, call, signal, context) {
9296
- return runHandlerTool("custom", resolved.customHandler, call, signal, context);
9463
+ async function runCustomTool(resolved, call, signal, context, messages) {
9464
+ return runHandlerTool("custom", resolved.customHandler, call, signal, context, messages);
9297
9465
  }
9298
- async function runHandlerTool(kind, handler, call, signal, context) {
9466
+ async function runHandlerTool(kind, handler, call, signal, context, messages) {
9299
9467
  if (handler === void 0) {
9300
9468
  return { stdout: "", stderr: `${kind} tool ${call.name} has no handler`, exitCode: 127 };
9301
9469
  }
9302
9470
  try {
9303
- const stdout = await handler(call.input, { signal, context });
9304
- return { stdout, stderr: "", exitCode: 0 };
9471
+ const out = await handler(call.input, { signal, context, messages });
9472
+ if (typeof out !== "string") return { stdout: "", stderr: "", exitCode: 0, content: out };
9473
+ return { stdout: out, stderr: "", exitCode: 0 };
9305
9474
  } catch (cause) {
9475
+ if (cause instanceof ToolError) {
9476
+ return { stdout: "", stderr: cause.message, exitCode: 1, content: cause.content };
9477
+ }
9306
9478
  const message = cause instanceof Error ? cause.message : String(cause);
9307
9479
  return { stdout: "", stderr: message, exitCode: 1 };
9308
9480
  }
@@ -9421,6 +9593,11 @@ async function dispatchSingleCall(inputs, tools, call, events, parentSpan) {
9421
9593
  toolSpan?.end();
9422
9594
  return fileVeto;
9423
9595
  }
9596
+ emitRunEvent(inputs.runEventSink, {
9597
+ type: "tool_progress",
9598
+ toolName: workingCall.name,
9599
+ toolCallId: callId
9600
+ });
9424
9601
  const result = await runToolWithLifecycle(inputs, resolved, workingCall, callId);
9425
9602
  await inputs.pluginManager?.runPostToolCallHooks({
9426
9603
  name: workingCall.name,
@@ -9447,6 +9624,13 @@ function applyRepairAndExtractCall(tools, call) {
9447
9624
  function vetoFromForkWhitelist(inputs, call, callId, events) {
9448
9625
  const whitelistDecision = checkToolWhitelist(call.name);
9449
9626
  if (whitelistDecision.allowed) return void 0;
9627
+ emitRunEvent(inputs.runEventSink, {
9628
+ type: "permission_denied",
9629
+ toolName: call.name,
9630
+ toolCallId: callId,
9631
+ source: "fork_whitelist",
9632
+ message: whitelistDecision.reason ?? "tool not available in fork"
9633
+ });
9450
9634
  events.push(buildToolUseRunning(inputs, callId, call));
9451
9635
  events.push(
9452
9636
  buildToolUseCompleted(inputs, callId, call, {
@@ -9483,6 +9667,13 @@ async function vetoFromPluginPreHook(inputs, call, callId, events) {
9483
9667
  runId: inputs.runId
9484
9668
  });
9485
9669
  if (pluginVeto === void 0) return void 0;
9670
+ emitRunEvent(inputs.runEventSink, {
9671
+ type: "permission_denied",
9672
+ toolName: call.name,
9673
+ toolCallId: callId,
9674
+ source: "plugin",
9675
+ message: pluginVeto.message
9676
+ });
9486
9677
  events.push(
9487
9678
  buildToolUseCompleted(inputs, callId, call, {
9488
9679
  stdout: "",
@@ -9505,6 +9696,13 @@ async function vetoFromFileHookPreDecision(inputs, call, callId, events) {
9505
9696
  runId: inputs.runId
9506
9697
  });
9507
9698
  if (!preDecision.blocked) return void 0;
9699
+ emitRunEvent(inputs.runEventSink, {
9700
+ type: "permission_denied",
9701
+ toolName: call.name,
9702
+ toolCallId: callId,
9703
+ source: "file_hook",
9704
+ message: preDecision.reason ?? "blocked by hook"
9705
+ });
9508
9706
  events.push(
9509
9707
  buildToolUseCompleted(inputs, callId, call, {
9510
9708
  stdout: "",
@@ -9548,7 +9746,9 @@ async function runToolWithLifecycle(inputs, resolved, call, callId) {
9548
9746
  await safeEmitToolHook(inputs.onToolEnd, {
9549
9747
  toolName: call.name,
9550
9748
  args: call.input,
9551
- result: result.stdout,
9749
+ // SE7 — a block-returning handler has empty `stdout`; surface its structured
9750
+ // content to the hook instead of an empty string.
9751
+ result: result.content !== void 0 ? result.content : result.stdout,
9552
9752
  conversationId: inputs.agentId,
9553
9753
  callId,
9554
9754
  durationMs
@@ -9570,7 +9770,10 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
9570
9770
  output: {
9571
9771
  stdout: result.stdout,
9572
9772
  stderr: result.stderr,
9573
- exitCode: result.exitCode ?? 0
9773
+ exitCode: result.exitCode ?? 0,
9774
+ // SE7 — structured content (text + image blocks) when the handler
9775
+ // returned/threw blocks; absent for the string path.
9776
+ ...result.content !== void 0 ? { content: result.content } : {}
9574
9777
  },
9575
9778
  agentId: inputs.agentId,
9576
9779
  runId: inputs.runId
@@ -9583,7 +9786,9 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
9583
9786
  return {
9584
9787
  type: "tool_result",
9585
9788
  toolUseId: call.id,
9586
- content: renderToolResult(result),
9789
+ // SE7 — structured content blocks (handler-returned or ToolError-carried)
9790
+ // are authoritative; otherwise the legacy string rendering.
9791
+ content: result.content !== void 0 ? result.content : renderToolResult(result),
9587
9792
  ...result.exitCode !== 0 && result.exitCode !== void 0 ? { isError: true } : {}
9588
9793
  };
9589
9794
  }
@@ -9632,6 +9837,29 @@ function buildToolUseCompleted(inputs, callId, call, result) {
9632
9837
  };
9633
9838
  }
9634
9839
 
9840
+ // src/internal/llm/tool-result-content.ts
9841
+ init_errors();
9842
+ function toBlockToolResultContent(content) {
9843
+ return content;
9844
+ }
9845
+ function toStringToolResultContent(content, providerName) {
9846
+ if (typeof content === "string") return content;
9847
+ if (content.some((block) => block.type === "image")) {
9848
+ throw new ConfigurationError(
9849
+ `provider "${providerName}" does not support image content in tool results`
9850
+ );
9851
+ }
9852
+ return content.filter(
9853
+ (block) => block.type === "text"
9854
+ ).map((block) => block.text).join("\n");
9855
+ }
9856
+ function renderToolResultContentText(content, fn) {
9857
+ if (typeof content === "string") return fn(content);
9858
+ return content.map(
9859
+ (block) => block.type === "text" ? { type: "text", text: fn(block.text) } : block
9860
+ );
9861
+ }
9862
+
9635
9863
  // src/internal/agent-loop/tool-result-guard.ts
9636
9864
  var OPEN = "<untrusted-tool-output>";
9637
9865
  var CLOSE = "</untrusted-tool-output>";
@@ -9657,7 +9885,11 @@ ${CLOSE}`;
9657
9885
  function applyToolResultGuard(parts, opts) {
9658
9886
  if (opts.delimit !== true && opts.redactPii !== true) return parts;
9659
9887
  return parts.map(
9660
- (p) => p.type === "tool_result" ? { ...p, content: guardText(p.content, opts) } : p
9888
+ (p) => (
9889
+ // SE7 — guard the TEXT of a tool result (string or the text blocks of a
9890
+ // structured content); image blocks pass through untouched.
9891
+ p.type === "tool_result" ? { ...p, content: renderToolResultContentText(p.content, (t) => guardText(t, opts)) } : p
9892
+ )
9661
9893
  );
9662
9894
  }
9663
9895
 
@@ -9996,6 +10228,14 @@ function computeUsageCost(inputs, usage) {
9996
10228
  }
9997
10229
 
9998
10230
  // src/internal/agent-loop/loop.ts
10231
+ function projectToolContextMessages(messages) {
10232
+ const projected = [];
10233
+ for (const m of messages) {
10234
+ const content = m.content.flatMap((p) => p.type === "text" ? [p.text] : []).join("");
10235
+ if (content !== "") projected.push({ role: m.role, content });
10236
+ }
10237
+ return projected;
10238
+ }
9999
10239
  var MAX_NUDGE_ATTEMPTS = 2;
10000
10240
  var MAX_STOP_FEEDBACK_ATTEMPTS = 2;
10001
10241
  async function runAgentLoop(inputs) {
@@ -10238,7 +10478,9 @@ async function continueOrTerminate(inputs, ctx, llmOutput) {
10238
10478
  const outText = await transformLlmOutputText(inputs, llmOutput.text, tCtx);
10239
10479
  ctx.messages.push(buildAssistantTurn(outText, llmOutput.toolCalls));
10240
10480
  const rawResults = await dispatchTools(
10241
- inputs,
10481
+ // SE12 — forward a read-only text projection of the transcript-so-far to tool
10482
+ // handlers via `ctx.messages` (consumed by defineSubAgent's messageFilter).
10483
+ { ...inputs, messages: projectToolContextMessages(ctx.messages) },
10242
10484
  ctx.tools,
10243
10485
  llmOutput.toolCalls,
10244
10486
  ctx.events,
@@ -10574,10 +10816,10 @@ function stripVertexPrefix(modelId) {
10574
10816
  }
10575
10817
 
10576
10818
  // src/internal/providers/builtin/index.ts
10577
- var registered2 = false;
10819
+ var registered3 = false;
10578
10820
  function registerBuiltins() {
10579
- if (registered2) return;
10580
- registered2 = true;
10821
+ if (registered3) return;
10822
+ registered3 = true;
10581
10823
  registerProvider(ANTHROPIC);
10582
10824
  registerProvider(OPENAI);
10583
10825
  registerProvider(OPENROUTER);
@@ -10805,7 +11047,9 @@ function toAnthropicWireMessage(message) {
10805
11047
  return {
10806
11048
  type: "tool_result",
10807
11049
  tool_use_id: part.toolUseId,
10808
- content: part.content,
11050
+ // SE7 — this wire is block-capable: strings pass through, structured
11051
+ // text/image blocks are forwarded natively.
11052
+ content: toBlockToolResultContent(part.content),
10809
11053
  ...part.isError === true ? { is_error: true } : {}
10810
11054
  };
10811
11055
  });
@@ -11597,7 +11841,8 @@ function toOllamaMessages(message) {
11597
11841
  if (part.type === "tool_result") {
11598
11842
  out.push({
11599
11843
  role: "tool",
11600
- content: part.content,
11844
+ // SE7 — string-only tool role: text blocks flatten; image fails fast.
11845
+ content: toStringToolResultContent(part.content, "ollama"),
11601
11846
  tool_name: part.toolUseId
11602
11847
  });
11603
11848
  }
@@ -12134,7 +12379,9 @@ function userOrToolMessages(message) {
12134
12379
  out.push({
12135
12380
  role: "tool",
12136
12381
  tool_call_id: part.toolUseId,
12137
- content: part.content
12382
+ // SE7 — this wire's tool role is string-only: text blocks flatten; an
12383
+ // image block fails fast (ConfigurationError).
12384
+ content: toStringToolResultContent(part.content, "openai")
12138
12385
  });
12139
12386
  }
12140
12387
  }
@@ -13063,18 +13310,20 @@ function levenshtein(a, b) {
13063
13310
  function createRealLocalRun(options) {
13064
13311
  const { userText, id, startTime } = prepareRunContext(options.message);
13065
13312
  const supported = /* @__PURE__ */ new Set(["stream", "wait", "cancel", "conversation"]);
13066
- const unusedFixtureScript = {
13313
+ const runScript = {
13067
13314
  events: [],
13068
13315
  finalStatus: "running",
13069
13316
  cancellable: false,
13070
- conversation: []
13317
+ conversation: [],
13318
+ // SE3 — carry the turn's provenance so buildResult forwards it onto RunResult.origin.
13319
+ ...options.sendOptions.origin !== void 0 ? { origin: options.sendOptions.origin } : {}
13071
13320
  };
13072
13321
  const handle = new RealLocalRun(
13073
13322
  {
13074
13323
  id,
13075
13324
  agentId: options.agentId,
13076
13325
  model: options.model,
13077
- script: unusedFixtureScript,
13326
+ script: runScript,
13078
13327
  supportedOps: supported,
13079
13328
  startTime
13080
13329
  },
@@ -13186,6 +13435,8 @@ function buildLoopInputs(options, runId, userText) {
13186
13435
  // M7 — forward SendOptions.context to the loop so every tool handler receives
13187
13436
  // it on `ctx.context` (shared run config set once, e.g. projectRoot).
13188
13437
  ...options.sendOptions.context !== void 0 ? { context: options.sendOptions.context } : {},
13438
+ // SE2 — forward the opt-in typed runtime-event sink to the loop.
13439
+ ...options.sendOptions.onRunEvent !== void 0 ? { runEventSink: options.sendOptions.onRunEvent } : {},
13189
13440
  // #58 / #57 — forward the per-tool timeout + tool-result guard so a consumer
13190
13441
  // can enable them via SendOptions (not only internal AgentLoopInputs).
13191
13442
  ...options.sendOptions.perToolTimeoutMs !== void 0 ? { perToolTimeoutMs: options.sendOptions.perToolTimeoutMs } : {},
@@ -15878,7 +16129,7 @@ function wrapRunWithPostReplyHook(args) {
15878
16129
  async function executeSendLocked(inputs, message, options) {
15879
16130
  if (inputs.disposed) throw new AgentDisposedError(inputs.agentId);
15880
16131
  await consumePending(inputs.agentId, inputs.invalidationPending, inputs.clearInvalidation, inputs.reload);
15881
- inputs.applyModelOverride(options.model);
16132
+ inputs.applyModelOverride(normalizeModel(options.model));
15882
16133
  const userText = typeof message === "string" ? message : message.text;
15883
16134
  if (inputs.options.onBeforeSend !== void 0) {
15884
16135
  await inputs.options.onBeforeSend({
@@ -16030,7 +16281,7 @@ var LocalAgent = class {
16030
16281
  constructor(options) {
16031
16282
  this.agentId = options.agentId ?? generateLocalAgentId();
16032
16283
  this._telemetry = createTelemetry(options.telemetry);
16033
- this.model = options.model;
16284
+ this.model = normalizeModel(options.model);
16034
16285
  this.options = options;
16035
16286
  this.workspaceCwd = resolveCwd(options.local?.cwd);
16036
16287
  this.conversationStorage = options.conversationStorage;
@@ -16097,6 +16348,11 @@ var LocalAgent = class {
16097
16348
  hooks() {
16098
16349
  return this.hooksExecutor;
16099
16350
  }
16351
+ // SE9 — integrated structured output; delegates to the shared helper.
16352
+ generate(message, options) {
16353
+ const { apiKey, local } = this.options;
16354
+ return agentGenerate(this, this.model, apiKey, local, message, options);
16355
+ }
16100
16356
  async send(message, options = {}) {
16101
16357
  if (options.tools !== void 0 && options.tools.length > 0) {
16102
16358
  validateToolCatalog(options.tools);
@@ -16336,6 +16592,7 @@ function includesSetting(options, source) {
16336
16592
 
16337
16593
  // src/agent-helpers.ts
16338
16594
  async function runCreateUnderSpan(options, span) {
16595
+ options = { ...options, model: normalizeModel(options.model) };
16339
16596
  validateAgentOptions(options);
16340
16597
  validateCloudToolParity(options);
16341
16598
  await guardAgainstIdCollision(options);
@@ -16409,7 +16666,10 @@ async function createLocalAgent(options) {
16409
16666
  const willFlowToProvider = !isFixtureApiKey(apiKey) && !shouldUseRealLocalRuntime(apiKey);
16410
16667
  const shape = validateApiKeyShape(apiKey, {
16411
16668
  strict: willFlowToProvider,
16412
- ...willFlowToProvider ? { provider: providerFromModelId(options.model?.id) } : {}
16669
+ // `options.model` is already normalized by `runCreateUnderSpan`; `normalizeModel`
16670
+ // here is the idempotent narrowing of the widened public type (`{id}` passes
16671
+ // through by reference) — cleaner than an `as` cast.
16672
+ ...willFlowToProvider ? { provider: providerFromModelId(normalizeModel(options.model)?.id) } : {}
16413
16673
  });
16414
16674
  if (shape.malformed) {
16415
16675
  throw new AuthenticationError(shape.message, { code: "malformed_api_key" });
@@ -16475,6 +16735,10 @@ async function rehydrateExistingAgent(agentId, existing, options) {
16475
16735
  ...existing.options,
16476
16736
  ...options,
16477
16737
  ...mergedLocal !== void 0 ? { local: mergedLocal } : {},
16738
+ // SE8 — normalize a caller-supplied bare-string model override at this resume
16739
+ // boundary (Agent.resume never flows through runCreateUnderSpan). Cloud resume
16740
+ // honors it; local resume forces the persisted model below.
16741
+ model: normalizeModel(options.model ?? existing.options.model),
16478
16742
  mcpServers: void 0,
16479
16743
  agentId
16480
16744
  };
@@ -16841,6 +17105,7 @@ async function getOrCreateUncached(agentId, options) {
16841
17105
  }
16842
17106
  setAgentFacade({
16843
17107
  create: (options) => Agent.create(options),
17108
+ delete: (agentId) => Agent.delete(agentId),
16844
17109
  prompt: (message, options) => Agent.prompt(message, options),
16845
17110
  get: (agentId) => Agent.get(agentId),
16846
17111
  resume: (agentId, options) => Agent.resume(agentId, options),