@theokit/sdk 2.20.0 → 2.21.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 (88) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dist/a2a/index.cjs +592 -290
  3. package/dist/a2a/index.cjs.map +1 -1
  4. package/dist/a2a/index.js +593 -291
  5. package/dist/a2a/index.js.map +1 -1
  6. package/dist/a2a/types.d.cts +7 -0
  7. package/dist/a2a/types.d.ts +7 -0
  8. package/dist/agent-builder.d.ts +2 -1
  9. package/dist/agent-generate.d.ts +12 -0
  10. package/dist/client/index.cjs.map +1 -1
  11. package/dist/client/index.d.cts +5 -0
  12. package/dist/client/index.d.ts +5 -0
  13. package/dist/client/index.js.map +1 -1
  14. package/dist/client/theokit-client.d.cts +6 -0
  15. package/dist/client/theokit-client.d.ts +6 -0
  16. package/dist/client/types.d.cts +2 -0
  17. package/dist/client/types.d.ts +2 -0
  18. package/dist/{cron-D2yLOrk2.d.ts → cron-YrmsszEN.d.cts} +53 -3
  19. package/dist/{cron-B8fqui49.d.cts → cron-dpvtRoro.d.ts} +53 -3
  20. package/dist/cron.cjs +642 -387
  21. package/dist/cron.cjs.map +1 -1
  22. package/dist/cron.d.cts +3 -2
  23. package/dist/cron.d.ts +3 -2
  24. package/dist/cron.js +643 -388
  25. package/dist/cron.js.map +1 -1
  26. package/dist/{errors-5lj1EWgs.d.ts → errors-C4vZPqXf.d.ts} +2 -2
  27. package/dist/{errors-CE-lMBi2.d.cts → errors-DrcpYVfZ.d.cts} +2 -2
  28. package/dist/errors.d.cts +3 -2
  29. package/dist/eval.cjs +642 -387
  30. package/dist/eval.cjs.map +1 -1
  31. package/dist/eval.js +642 -387
  32. package/dist/eval.js.map +1 -1
  33. package/dist/generate-object.d.ts +4 -3
  34. package/dist/index.cjs +788 -390
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +259 -17
  37. package/dist/index.d.ts +259 -17
  38. package/dist/index.js +786 -392
  39. package/dist/index.js.map +1 -1
  40. package/dist/internal/agent-loop/loop-types.d.ts +2 -0
  41. package/dist/internal/agent-loop/tool-result-guard.d.ts +1 -0
  42. package/dist/internal/llm/tool-result-content.d.ts +40 -0
  43. package/dist/internal/llm/types.d.ts +6 -1
  44. package/dist/internal/persistence/conversation-storage-fs.d.cts +3 -1
  45. package/dist/internal/persistence/conversation-storage-fs.d.ts +3 -1
  46. package/dist/internal/persistence/conversation-storage-memory.d.cts +3 -1
  47. package/dist/internal/persistence/conversation-storage-memory.d.ts +3 -1
  48. package/dist/internal/persistence/session-meta.d.cts +13 -0
  49. package/dist/internal/persistence/session-meta.d.ts +13 -0
  50. package/dist/internal/runtime/model-selection.d.ts +14 -0
  51. package/dist/internal/structured-output-helpers.d.ts +1 -1
  52. package/dist/permission-engine.d.ts +32 -1
  53. package/dist/{run-BgfBWX-z.d.cts → run-BMo8yRwK.d.cts} +262 -19
  54. package/dist/{run-BgfBWX-z.d.ts → run-BMo8yRwK.d.ts} +262 -19
  55. package/dist/stream-object.d.ts +2 -1
  56. package/dist/tool-error.d.ts +30 -0
  57. package/dist/types/agent-prims.d.ts +10 -8
  58. package/dist/types/agent.d.ts +12 -1
  59. package/dist/types/content-blocks.d.ts +47 -0
  60. package/dist/types/conversation-storage.d.ts +38 -0
  61. package/dist/types/index.d.ts +1 -0
  62. package/dist/types/messages.d.ts +2 -9
  63. package/dist/types/run-events.d.ts +84 -0
  64. package/dist/types/run.d.ts +115 -1
  65. package/dist/types/session.d.ts +99 -0
  66. package/dist/types/workflow.d.ts +7 -0
  67. package/dist/workflow.cjs +7 -2
  68. package/dist/workflow.cjs.map +1 -1
  69. package/dist/workflow.d.cts +2 -0
  70. package/dist/workflow.d.ts +2 -0
  71. package/dist/workflow.js +7 -2
  72. package/dist/workflow.js.map +1 -1
  73. package/package.json +14 -34
  74. package/dist/internal/observability/context.d.cts +0 -23
  75. package/dist/internal/observability/context.d.ts +0 -23
  76. package/dist/internal/observability/index.cjs +0 -38
  77. package/dist/internal/observability/index.cjs.map +0 -1
  78. package/dist/internal/observability/index.d.cts +0 -8
  79. package/dist/internal/observability/index.d.ts +0 -8
  80. package/dist/internal/observability/index.js +0 -33
  81. package/dist/internal/observability/index.js.map +0 -1
  82. package/dist/internal/plugins/index.cjs +0 -363
  83. package/dist/internal/plugins/index.cjs.map +0 -1
  84. package/dist/internal/plugins/index.js +0 -357
  85. package/dist/internal/plugins/index.js.map +0 -1
  86. package/dist/internal/workflow/index.d.ts +0 -12
  87. package/dist/server/adapter/index.d.cts +0 -8
  88. package/dist/server/adapter/index.d.ts +0 -8
package/dist/eval.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');
@@ -512,6 +512,256 @@ var init_errors = __esm({
512
512
  };
513
513
  }
514
514
  });
515
+ function toJsonSchema(schema, options = { unrepresentable: "any" }) {
516
+ return zod.toJSONSchema(schema, options);
517
+ }
518
+ var init_to_json_schema = __esm({
519
+ "src/internal/zod/to-json-schema.ts"() {
520
+ }
521
+ });
522
+ function requireZod() {
523
+ if (cachedZ !== void 0) return cachedZ;
524
+ const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('eval.cjs', document.baseURI).href)));
525
+ let mod;
526
+ try {
527
+ mod = r("zod");
528
+ } catch (cause) {
529
+ throw new ConfigurationError(
530
+ 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
531
+ { code: "zod_not_installed", cause }
532
+ );
533
+ }
534
+ cachedZ = mod.z ?? mod;
535
+ return cachedZ;
536
+ }
537
+ function makeOutputTool(jsonSchema, onCapture) {
538
+ return {
539
+ name: "output",
540
+ description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
541
+ inputSchema: jsonSchema,
542
+ handler: (input) => {
543
+ const out = onCapture(input);
544
+ if (typeof out === "string") return out;
545
+ if (out instanceof Promise) return out;
546
+ return "";
547
+ }
548
+ };
549
+ }
550
+ function buildTransientAgentOptions(params) {
551
+ return {
552
+ model: params.model,
553
+ local: params.local,
554
+ tools: [params.outputTool],
555
+ systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
556
+ ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
557
+ ...params.providers !== void 0 ? { providers: params.providers } : {}
558
+ };
559
+ }
560
+ function extractUsage(result) {
561
+ const u = result.usage;
562
+ if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
563
+ return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
564
+ }
565
+ function buildToolPrompt(prompt) {
566
+ return `${prompt}
567
+
568
+ Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
569
+ }
570
+ function setupStructuredOutput(schema, maxRetries) {
571
+ const z7 = requireZod();
572
+ const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
573
+ return {
574
+ z: z7,
575
+ jsonSchema,
576
+ maxRetries: maxRetries ?? 1,
577
+ initialUsage: { inputTokens: 0, outputTokens: 0 }
578
+ };
579
+ }
580
+ async function disposeAndDeleteTransient(agent, deletor) {
581
+ await agent.dispose();
582
+ try {
583
+ await deletor(agent.agentId);
584
+ } catch {
585
+ }
586
+ }
587
+ var cachedZ;
588
+ var init_structured_output_helpers = __esm({
589
+ "src/internal/structured-output-helpers.ts"() {
590
+ init_errors();
591
+ init_to_json_schema();
592
+ }
593
+ });
594
+
595
+ // src/generate-object.ts
596
+ var generate_object_exports = {};
597
+ __export(generate_object_exports, {
598
+ GenerateObjectError: () => GenerateObjectError,
599
+ generateObjectImpl: () => generateObjectImpl
600
+ });
601
+ function salvagePartial(schema, raw) {
602
+ if (typeof raw !== "object" || raw === null) return raw;
603
+ const shape = schema.shape;
604
+ if (shape === void 0 || typeof shape !== "object") return raw;
605
+ const rawObj = raw;
606
+ const out = {};
607
+ for (const [key, fieldSchema] of Object.entries(shape)) {
608
+ if (typeof fieldSchema?.safeParse !== "function") continue;
609
+ const parsed = fieldSchema.safeParse(rawObj[key]);
610
+ if (parsed.success) out[key] = parsed.data;
611
+ }
612
+ return out;
613
+ }
614
+ async function runReasoningPhase(options, deps) {
615
+ const reasoningOptions = {
616
+ model: options.model,
617
+ local: options.local,
618
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
619
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
620
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
621
+ };
622
+ const reasoningAgent = await deps.create(reasoningOptions);
623
+ try {
624
+ const run = await reasoningAgent.send(options.prompt);
625
+ const result = await run.wait();
626
+ const text = result.result;
627
+ return typeof text === "string" ? text : options.prompt;
628
+ } finally {
629
+ await disposeAndDeleteTransient(reasoningAgent, deps.delete);
630
+ }
631
+ }
632
+ async function generateObjectImpl(options, deps) {
633
+ const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
634
+ options.schema,
635
+ options.maxRetries
636
+ );
637
+ let capturedRaw;
638
+ let lastUsage = initialUsage;
639
+ const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
640
+ class CaptureSentinel extends Error {
641
+ constructor(captured) {
642
+ super("generate-object-capture");
643
+ this.captured = captured;
644
+ }
645
+ captured;
646
+ [sentinel] = true;
647
+ }
648
+ const outputTool = makeOutputTool(jsonSchema, (input) => {
649
+ if (capturedRaw === void 0) {
650
+ capturedRaw = input;
651
+ }
652
+ throw new CaptureSentinel(input);
653
+ });
654
+ const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
655
+ const structuringModel = options.structuringModel ?? options.model;
656
+ const structuringPrompt = reasoningText ?? options.prompt;
657
+ const agentOptions = buildTransientAgentOptions({
658
+ model: structuringModel,
659
+ local: options.local,
660
+ outputTool,
661
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
662
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
663
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
664
+ });
665
+ const agent = await deps.create(agentOptions);
666
+ try {
667
+ const userMessage = buildToolPrompt(structuringPrompt);
668
+ let lastParseError;
669
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
670
+ capturedRaw = void 0;
671
+ const run = await agent.send(userMessage);
672
+ const result = await run.wait();
673
+ lastUsage = extractUsage(result);
674
+ if (capturedRaw === void 0) {
675
+ if (result.status === "error" && result.error !== void 0) {
676
+ throw new GenerateObjectError(
677
+ "no_tool_call",
678
+ `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
679
+ result.error
680
+ );
681
+ }
682
+ if (attempt === maxRetries) {
683
+ throw new GenerateObjectError(
684
+ "no_tool_call",
685
+ "The model returned text instead of calling the `output` tool."
686
+ );
687
+ }
688
+ continue;
689
+ }
690
+ const parsed = options.schema.safeParse(capturedRaw);
691
+ if (parsed.success) {
692
+ return {
693
+ object: parsed.data,
694
+ raw: capturedRaw,
695
+ usage: lastUsage,
696
+ finishReason: "tool_use"
697
+ };
698
+ }
699
+ lastParseError = parsed.error;
700
+ }
701
+ if (options.errorStrategy === "return-raw") {
702
+ return {
703
+ object: capturedRaw,
704
+ raw: capturedRaw,
705
+ usage: lastUsage,
706
+ finishReason: "tool_use"
707
+ };
708
+ }
709
+ if (options.errorStrategy === "return-partial") {
710
+ return {
711
+ object: salvagePartial(options.schema, capturedRaw),
712
+ raw: capturedRaw,
713
+ usage: lastUsage,
714
+ finishReason: "tool_use"
715
+ };
716
+ }
717
+ throw new GenerateObjectError(
718
+ "parse_failed",
719
+ "Schema parse failed after all retries.",
720
+ lastParseError
721
+ );
722
+ } finally {
723
+ await disposeAndDeleteTransient(agent, deps.delete);
724
+ }
725
+ }
726
+ var GenerateObjectError;
727
+ var init_generate_object = __esm({
728
+ "src/generate-object.ts"() {
729
+ init_structured_output_helpers();
730
+ GenerateObjectError = class extends Error {
731
+ name = "GenerateObjectError";
732
+ code;
733
+ cause;
734
+ constructor(code, message, cause) {
735
+ super(message);
736
+ this.code = code;
737
+ if (cause !== void 0) this.cause = cause;
738
+ }
739
+ };
740
+ }
741
+ });
742
+
743
+ // src/internal/runtime/registry/agent-factory-registry.ts
744
+ var agent_factory_registry_exports = {};
745
+ __export(agent_factory_registry_exports, {
746
+ getAgentFacade: () => getAgentFacade,
747
+ setAgentFacade: () => setAgentFacade
748
+ });
749
+ function setAgentFacade(facade) {
750
+ registered = facade;
751
+ }
752
+ function getAgentFacade() {
753
+ if (registered === void 0) {
754
+ throw new Error(
755
+ "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
756
+ );
757
+ }
758
+ return registered;
759
+ }
760
+ var registered;
761
+ var init_agent_factory_registry = __esm({
762
+ "src/internal/runtime/registry/agent-factory-registry.ts"() {
763
+ }
764
+ });
515
765
 
516
766
  // src/internal/persistence/cwd-mutex.ts
517
767
  function withCwdMutex(key, fn) {
@@ -1429,29 +1679,6 @@ var init_judge_call = __esm({
1429
1679
  }
1430
1680
  });
1431
1681
 
1432
- // src/internal/runtime/registry/agent-factory-registry.ts
1433
- var agent_factory_registry_exports = {};
1434
- __export(agent_factory_registry_exports, {
1435
- getAgentFacade: () => getAgentFacade,
1436
- setAgentFacade: () => setAgentFacade
1437
- });
1438
- function setAgentFacade(facade) {
1439
- registered3 = facade;
1440
- }
1441
- function getAgentFacade() {
1442
- if (registered3 === void 0) {
1443
- throw new Error(
1444
- "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
1445
- );
1446
- }
1447
- return registered3;
1448
- }
1449
- var registered3;
1450
- var init_agent_factory_registry = __esm({
1451
- "src/internal/runtime/registry/agent-factory-registry.ts"() {
1452
- }
1453
- });
1454
-
1455
1682
  // src/internal/runtime/lifecycle/run-to-completion.ts
1456
1683
  var run_to_completion_exports = {};
1457
1684
  __export(run_to_completion_exports, {
@@ -1616,14 +1843,14 @@ async function forkAgentImpl(parent, options, deps) {
1616
1843
  return {
1617
1844
  result: result.result,
1618
1845
  toolCalls: [],
1619
- usage: extractUsage(result)
1846
+ usage: extractUsage2(result)
1620
1847
  };
1621
1848
  });
1622
1849
  } finally {
1623
1850
  await fork.dispose();
1624
1851
  }
1625
1852
  }
1626
- function extractUsage(result) {
1853
+ function extractUsage2(result) {
1627
1854
  if (typeof result !== "object" || result === null) {
1628
1855
  return { inputTokens: 0, outputTokens: 0 };
1629
1856
  }
@@ -2056,345 +2283,118 @@ async function runWorkAndFinalize(taskId, aborter, work) {
2056
2283
  emit(payload) {
2057
2284
  void checkCancelRequestedAt(taskId, aborter);
2058
2285
  emitToSubscribers(taskId, {
2059
- type: "progress",
2060
- taskId,
2061
- at: Date.now(),
2062
- payload
2063
- });
2064
- }
2065
- };
2066
- try {
2067
- const result = await reentryAls.run(true, () => Promise.resolve().then(() => work(ctx)));
2068
- if (aborter.signal.aborted) {
2069
- const cancelledAt = Date.now();
2070
- await transition(taskId, "cancelled", { cancelledAt });
2071
- emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2072
- return;
2073
- }
2074
- const finishedAt = Date.now();
2075
- await transition(taskId, "finished", { finishedAt, result });
2076
- emitToSubscribers(taskId, { type: "finished", taskId, finishedAt, result });
2077
- } catch (err) {
2078
- if (aborter.signal.aborted) {
2079
- const cancelledAt = Date.now();
2080
- await transition(taskId, "cancelled", { cancelledAt });
2081
- emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2082
- return;
2083
- }
2084
- const erroredAt = Date.now();
2085
- const error = err instanceof Error ? { code: err.code ?? "work_threw", message: err.message } : { code: "work_threw", message: String(err) };
2086
- await transition(taskId, "error", { erroredAt, error });
2087
- emitToSubscribers(taskId, { type: "errored", taskId, erroredAt, error });
2088
- }
2089
- }
2090
- async function checkCancelRequestedAt(taskId, aborter) {
2091
- if (aborter.signal.aborted) return;
2092
- try {
2093
- const handle = await state.store.get(taskId);
2094
- if (handle?.cancelRequested === true) {
2095
- aborter.abort("cancelRequested");
2096
- }
2097
- } catch {
2098
- }
2099
- }
2100
- async function acquireSlot() {
2101
- if (reentryAls.getStore() === true) {
2102
- return () => {
2103
- };
2104
- }
2105
- return state.semaphore.acquire();
2106
- }
2107
- async function submit(internal) {
2108
- state.firstSubmitSeen = true;
2109
- startEvictTimerIfNeeded();
2110
- const resolvedId = resolveIdInternal(internal);
2111
- const submitSpan = startTaskSubmitSpan({ taskId: resolvedId, kind: internal.kind });
2112
- const existing = await state.store.get(resolvedId);
2113
- if (existing !== void 0) {
2114
- submitSpan.end();
2115
- return existing;
2116
- }
2117
- const queuedHandle = await buildAndInsertQueued(internal, resolvedId);
2118
- submitSpan.end();
2119
- if (internal.signal?.aborted === true) {
2120
- return shortCircuitAborted(queuedHandle, internal.signal);
2121
- }
2122
- const aborter = new AbortController();
2123
- state.aborters.set(resolvedId, aborter);
2124
- if (internal.signal !== void 0) {
2125
- internal.signal.addEventListener("abort", () => aborter.abort(internal.signal?.reason), {
2126
- once: true
2127
- });
2128
- }
2129
- const release = await acquireSlot();
2130
- void (async () => {
2131
- try {
2132
- await checkCancelRequestedAt(resolvedId, aborter);
2133
- if (aborter.signal.aborted) {
2134
- const cancelledAt = Date.now();
2135
- await transition(resolvedId, "cancelled", { cancelledAt });
2136
- emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
2137
- return;
2138
- }
2139
- const startedAt = Date.now();
2140
- await transition(resolvedId, "running", { startedAt });
2141
- emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
2142
- await runWorkAndFinalize(resolvedId, aborter, internal.work);
2143
- } finally {
2144
- release();
2145
- state.aborters.delete(resolvedId);
2146
- }
2147
- })();
2148
- return queuedHandle;
2149
- }
2150
- async function get(id) {
2151
- if (!isValidTaskId(id, true)) return void 0;
2152
- return state.store.get(id);
2153
- }
2154
- var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state;
2155
- var init_registry = __esm({
2156
- "src/internal/task/registry.ts"() {
2157
- init_errors();
2158
- init_task();
2159
- init_async_semaphore();
2160
- init_ring_buffer();
2161
- init_store();
2162
- init_subscribe();
2163
- init_telemetry();
2164
- DEFAULT_CONCURRENCY = 8;
2165
- DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
2166
- EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
2167
- RING_CAP = 64;
2168
- reentryAls = new async_hooks.AsyncLocalStorage();
2169
- state = buildState({});
2170
- }
2171
- });
2172
- function toJsonSchema(schema, options = { unrepresentable: "any" }) {
2173
- return zod.toJSONSchema(schema, options);
2174
- }
2175
- var init_to_json_schema = __esm({
2176
- "src/internal/zod/to-json-schema.ts"() {
2177
- }
2178
- });
2179
- function requireZod() {
2180
- if (cachedZ !== void 0) return cachedZ;
2181
- const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('eval.cjs', document.baseURI).href)));
2182
- let mod;
2183
- try {
2184
- mod = r("zod");
2185
- } catch (cause) {
2186
- throw new ConfigurationError(
2187
- 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
2188
- { code: "zod_not_installed", cause }
2189
- );
2190
- }
2191
- cachedZ = mod.z ?? mod;
2192
- return cachedZ;
2193
- }
2194
- function makeOutputTool(jsonSchema, onCapture) {
2195
- return {
2196
- name: "output",
2197
- description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
2198
- inputSchema: jsonSchema,
2199
- handler: (input) => {
2200
- const out = onCapture(input);
2201
- if (typeof out === "string") return out;
2202
- if (out instanceof Promise) return out;
2203
- return "";
2204
- }
2205
- };
2206
- }
2207
- function buildTransientAgentOptions(params) {
2208
- return {
2209
- model: params.model,
2210
- local: params.local,
2211
- tools: [params.outputTool],
2212
- systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
2213
- ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
2214
- ...params.providers !== void 0 ? { providers: params.providers } : {}
2215
- };
2216
- }
2217
- function extractUsage2(result) {
2218
- const u = result.usage;
2219
- if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
2220
- return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
2221
- }
2222
- function buildToolPrompt(prompt) {
2223
- return `${prompt}
2224
-
2225
- Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
2226
- }
2227
- function setupStructuredOutput(schema, maxRetries) {
2228
- const z7 = requireZod();
2229
- const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
2230
- return {
2231
- z: z7,
2232
- jsonSchema,
2233
- maxRetries: maxRetries ?? 1,
2234
- initialUsage: { inputTokens: 0, outputTokens: 0 }
2235
- };
2236
- }
2237
- async function disposeAndDeleteTransient(agent, deletor) {
2238
- await agent.dispose();
2239
- try {
2240
- await deletor(agent.agentId);
2241
- } catch {
2242
- }
2243
- }
2244
- var cachedZ;
2245
- var init_structured_output_helpers = __esm({
2246
- "src/internal/structured-output-helpers.ts"() {
2247
- init_errors();
2248
- init_to_json_schema();
2249
- }
2250
- });
2251
-
2252
- // src/generate-object.ts
2253
- var generate_object_exports = {};
2254
- __export(generate_object_exports, {
2255
- GenerateObjectError: () => GenerateObjectError,
2256
- generateObjectImpl: () => generateObjectImpl
2257
- });
2258
- function salvagePartial(schema, raw) {
2259
- if (typeof raw !== "object" || raw === null) return raw;
2260
- const shape = schema.shape;
2261
- if (shape === void 0 || typeof shape !== "object") return raw;
2262
- const rawObj = raw;
2263
- const out = {};
2264
- for (const [key, fieldSchema] of Object.entries(shape)) {
2265
- if (typeof fieldSchema?.safeParse !== "function") continue;
2266
- const parsed = fieldSchema.safeParse(rawObj[key]);
2267
- if (parsed.success) out[key] = parsed.data;
2268
- }
2269
- return out;
2270
- }
2271
- async function runReasoningPhase(options, deps) {
2272
- const reasoningOptions = {
2273
- model: options.model,
2274
- local: options.local,
2275
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
2276
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
2277
- ...options.providers !== void 0 ? { providers: options.providers } : {}
2278
- };
2279
- const reasoningAgent = await deps.create(reasoningOptions);
2280
- try {
2281
- const run = await reasoningAgent.send(options.prompt);
2282
- const result = await run.wait();
2283
- const text = result.result;
2284
- return typeof text === "string" ? text : options.prompt;
2285
- } finally {
2286
- await disposeAndDeleteTransient(reasoningAgent, deps.delete);
2287
- }
2288
- }
2289
- async function generateObjectImpl(options, deps) {
2290
- const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
2291
- options.schema,
2292
- options.maxRetries
2293
- );
2294
- let capturedRaw;
2295
- let lastUsage = initialUsage;
2296
- const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
2297
- class CaptureSentinel extends Error {
2298
- constructor(captured) {
2299
- super("generate-object-capture");
2300
- this.captured = captured;
2301
- }
2302
- captured;
2303
- [sentinel] = true;
2304
- }
2305
- const outputTool = makeOutputTool(jsonSchema, (input) => {
2306
- if (capturedRaw === void 0) {
2307
- capturedRaw = input;
2308
- }
2309
- throw new CaptureSentinel(input);
2310
- });
2311
- const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
2312
- const structuringModel = options.structuringModel ?? options.model;
2313
- const structuringPrompt = reasoningText ?? options.prompt;
2314
- const agentOptions = buildTransientAgentOptions({
2315
- model: structuringModel,
2316
- local: options.local,
2317
- outputTool,
2318
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
2319
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
2320
- ...options.providers !== void 0 ? { providers: options.providers } : {}
2321
- });
2322
- const agent = await deps.create(agentOptions);
2286
+ type: "progress",
2287
+ taskId,
2288
+ at: Date.now(),
2289
+ payload
2290
+ });
2291
+ }
2292
+ };
2323
2293
  try {
2324
- const userMessage = buildToolPrompt(structuringPrompt);
2325
- let lastParseError;
2326
- for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
2327
- capturedRaw = void 0;
2328
- const run = await agent.send(userMessage);
2329
- const result = await run.wait();
2330
- lastUsage = extractUsage2(result);
2331
- if (capturedRaw === void 0) {
2332
- if (result.status === "error" && result.error !== void 0) {
2333
- throw new GenerateObjectError(
2334
- "no_tool_call",
2335
- `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
2336
- result.error
2337
- );
2338
- }
2339
- if (attempt === maxRetries) {
2340
- throw new GenerateObjectError(
2341
- "no_tool_call",
2342
- "The model returned text instead of calling the `output` tool."
2343
- );
2344
- }
2345
- continue;
2346
- }
2347
- const parsed = options.schema.safeParse(capturedRaw);
2348
- if (parsed.success) {
2349
- return {
2350
- object: parsed.data,
2351
- raw: capturedRaw,
2352
- usage: lastUsage,
2353
- finishReason: "tool_use"
2354
- };
2355
- }
2356
- lastParseError = parsed.error;
2294
+ const result = await reentryAls.run(true, () => Promise.resolve().then(() => work(ctx)));
2295
+ if (aborter.signal.aborted) {
2296
+ const cancelledAt = Date.now();
2297
+ await transition(taskId, "cancelled", { cancelledAt });
2298
+ emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2299
+ return;
2357
2300
  }
2358
- if (options.errorStrategy === "return-raw") {
2359
- return {
2360
- object: capturedRaw,
2361
- raw: capturedRaw,
2362
- usage: lastUsage,
2363
- finishReason: "tool_use"
2364
- };
2301
+ const finishedAt = Date.now();
2302
+ await transition(taskId, "finished", { finishedAt, result });
2303
+ emitToSubscribers(taskId, { type: "finished", taskId, finishedAt, result });
2304
+ } catch (err) {
2305
+ if (aborter.signal.aborted) {
2306
+ const cancelledAt = Date.now();
2307
+ await transition(taskId, "cancelled", { cancelledAt });
2308
+ emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2309
+ return;
2365
2310
  }
2366
- if (options.errorStrategy === "return-partial") {
2367
- return {
2368
- object: salvagePartial(options.schema, capturedRaw),
2369
- raw: capturedRaw,
2370
- usage: lastUsage,
2371
- finishReason: "tool_use"
2372
- };
2311
+ const erroredAt = Date.now();
2312
+ const error = err instanceof Error ? { code: err.code ?? "work_threw", message: err.message } : { code: "work_threw", message: String(err) };
2313
+ await transition(taskId, "error", { erroredAt, error });
2314
+ emitToSubscribers(taskId, { type: "errored", taskId, erroredAt, error });
2315
+ }
2316
+ }
2317
+ async function checkCancelRequestedAt(taskId, aborter) {
2318
+ if (aborter.signal.aborted) return;
2319
+ try {
2320
+ const handle = await state.store.get(taskId);
2321
+ if (handle?.cancelRequested === true) {
2322
+ aborter.abort("cancelRequested");
2373
2323
  }
2374
- throw new GenerateObjectError(
2375
- "parse_failed",
2376
- "Schema parse failed after all retries.",
2377
- lastParseError
2378
- );
2379
- } finally {
2380
- await disposeAndDeleteTransient(agent, deps.delete);
2324
+ } catch {
2381
2325
  }
2382
2326
  }
2383
- var GenerateObjectError;
2384
- var init_generate_object = __esm({
2385
- "src/generate-object.ts"() {
2386
- init_structured_output_helpers();
2387
- GenerateObjectError = class extends Error {
2388
- name = "GenerateObjectError";
2389
- code;
2390
- cause;
2391
- constructor(code, message, cause) {
2392
- super(message);
2393
- this.code = code;
2394
- if (cause !== void 0) this.cause = cause;
2395
- }
2327
+ async function acquireSlot() {
2328
+ if (reentryAls.getStore() === true) {
2329
+ return () => {
2396
2330
  };
2397
2331
  }
2332
+ return state.semaphore.acquire();
2333
+ }
2334
+ async function submit(internal) {
2335
+ state.firstSubmitSeen = true;
2336
+ startEvictTimerIfNeeded();
2337
+ const resolvedId = resolveIdInternal(internal);
2338
+ const submitSpan = startTaskSubmitSpan({ taskId: resolvedId, kind: internal.kind });
2339
+ const existing = await state.store.get(resolvedId);
2340
+ if (existing !== void 0) {
2341
+ submitSpan.end();
2342
+ return existing;
2343
+ }
2344
+ const queuedHandle = await buildAndInsertQueued(internal, resolvedId);
2345
+ submitSpan.end();
2346
+ if (internal.signal?.aborted === true) {
2347
+ return shortCircuitAborted(queuedHandle, internal.signal);
2348
+ }
2349
+ const aborter = new AbortController();
2350
+ state.aborters.set(resolvedId, aborter);
2351
+ if (internal.signal !== void 0) {
2352
+ internal.signal.addEventListener("abort", () => aborter.abort(internal.signal?.reason), {
2353
+ once: true
2354
+ });
2355
+ }
2356
+ const release = await acquireSlot();
2357
+ void (async () => {
2358
+ try {
2359
+ await checkCancelRequestedAt(resolvedId, aborter);
2360
+ if (aborter.signal.aborted) {
2361
+ const cancelledAt = Date.now();
2362
+ await transition(resolvedId, "cancelled", { cancelledAt });
2363
+ emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
2364
+ return;
2365
+ }
2366
+ const startedAt = Date.now();
2367
+ await transition(resolvedId, "running", { startedAt });
2368
+ emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
2369
+ await runWorkAndFinalize(resolvedId, aborter, internal.work);
2370
+ } finally {
2371
+ release();
2372
+ state.aborters.delete(resolvedId);
2373
+ }
2374
+ })();
2375
+ return queuedHandle;
2376
+ }
2377
+ async function get(id) {
2378
+ if (!isValidTaskId(id, true)) return void 0;
2379
+ return state.store.get(id);
2380
+ }
2381
+ var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state;
2382
+ var init_registry = __esm({
2383
+ "src/internal/task/registry.ts"() {
2384
+ init_errors();
2385
+ init_task();
2386
+ init_async_semaphore();
2387
+ init_ring_buffer();
2388
+ init_store();
2389
+ init_subscribe();
2390
+ init_telemetry();
2391
+ DEFAULT_CONCURRENCY = 8;
2392
+ DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
2393
+ EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
2394
+ RING_CAP = 64;
2395
+ reentryAls = new async_hooks.AsyncLocalStorage();
2396
+ state = buildState({});
2397
+ }
2398
2398
  });
2399
2399
 
2400
2400
  // src/stream-object.ts
@@ -2451,7 +2451,7 @@ async function* streamObjectImpl(options, deps) {
2451
2451
  }
2452
2452
  }
2453
2453
  const result = await run.wait();
2454
- lastUsage = extractUsage2(result);
2454
+ lastUsage = extractUsage(result);
2455
2455
  if (capturedRaw === void 0) {
2456
2456
  if (result.status === "error" && result.error !== void 0) {
2457
2457
  throw new StreamObjectError(
@@ -2797,6 +2797,7 @@ var AgentBuilder = class {
2797
2797
  constructor(deps) {
2798
2798
  this.deps = deps;
2799
2799
  }
2800
+ /** SE8 — accepts a bare-string id shorthand (`"openai/gpt-4o-mini"`) or a {@link ModelSelection}. */
2800
2801
  model(m) {
2801
2802
  this.opts.model = m;
2802
2803
  return this;
@@ -3146,6 +3147,69 @@ function isLocalAgentId(id) {
3146
3147
  return id.startsWith(PREFIX.localAgent);
3147
3148
  }
3148
3149
 
3150
+ // src/internal/runtime/config/default-model.ts
3151
+ var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
3152
+
3153
+ // src/agent-generate.ts
3154
+ async function agentGenerate(agent, agentModel, agentApiKey, agentLocal, input, options) {
3155
+ const { output, maxRetries, errorStrategy, ...sendOptions } = options;
3156
+ const run = await agent.send(input, sendOptions);
3157
+ const result = await run.wait();
3158
+ const { generateObjectImpl: generateObjectImpl2, GenerateObjectError: GenerateObjectError2 } = await Promise.resolve().then(() => (init_generate_object(), generate_object_exports));
3159
+ const answer = structurableAnswerOrThrow(result, GenerateObjectError2);
3160
+ const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
3161
+ const facade = getAgentFacade2();
3162
+ const structured = await generateObjectImpl2(
3163
+ {
3164
+ prompt: answer,
3165
+ schema: output,
3166
+ // `agentModel` arrives already normalized (`this.model` passed through
3167
+ // `normalizeModel` in the LocalAgent/CloudAgent constructor), so the widened
3168
+ // `string | ModelSelection` field only ever receives a `ModelSelection` here.
3169
+ model: agentModel ?? { id: DEFAULT_AGENTIC_MODEL_ID },
3170
+ // `local` is required to keep the transient structuring agent local-only;
3171
+ // default to the process cwd when the agent had no explicit local config.
3172
+ local: agentLocal ?? { cwd: process.cwd() },
3173
+ ...agentApiKey !== void 0 ? { apiKey: agentApiKey } : {},
3174
+ ...maxRetries !== void 0 ? { maxRetries } : {},
3175
+ ...errorStrategy !== void 0 ? { errorStrategy } : {}
3176
+ },
3177
+ {
3178
+ create: (opts) => facade.create(opts),
3179
+ delete: (agentId) => facade.delete(agentId)
3180
+ }
3181
+ );
3182
+ return {
3183
+ object: structured.object,
3184
+ result,
3185
+ raw: structured.raw,
3186
+ usage: structured.usage
3187
+ };
3188
+ }
3189
+ function structurableAnswerOrThrow(result, GenerateObjectError2) {
3190
+ if (result.status === "error") {
3191
+ throw new GenerateObjectError2(
3192
+ "no_tool_call",
3193
+ `Agent run failed before an answer could be structured: ${result.error?.message ?? "unknown error"} [${result.error?.code ?? "?"}]`,
3194
+ result.error
3195
+ );
3196
+ }
3197
+ if (result.status === "cancelled") {
3198
+ throw new GenerateObjectError2(
3199
+ "no_tool_call",
3200
+ "Agent run was cancelled before an answer could be structured."
3201
+ );
3202
+ }
3203
+ const answer = result.result;
3204
+ if (answer === void 0 || answer.length === 0) {
3205
+ throw new GenerateObjectError2(
3206
+ "no_tool_call",
3207
+ "The tool loop finished with no text answer (tool-only completion) \u2014 there is nothing to structure."
3208
+ );
3209
+ }
3210
+ return answer;
3211
+ }
3212
+
3149
3213
  // src/internal/runtime/cloud/cloud-agent.ts
3150
3214
  init_errors();
3151
3215
  init_cwd_mutex();
@@ -3296,8 +3360,19 @@ function safeFilenameForId(id, options) {
3296
3360
  return `h-${crypto.createHash("sha256").update(id).digest("hex").slice(0, 16)}`;
3297
3361
  }
3298
3362
 
3299
- // src/internal/runtime/config/default-model.ts
3300
- var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
3363
+ // src/internal/runtime/model-selection.ts
3364
+ init_errors();
3365
+ function normalizeModel(m) {
3366
+ if (m === void 0 || typeof m !== "string") return m;
3367
+ const id = m.trim();
3368
+ if (id.length === 0) {
3369
+ throw new ConfigurationError(
3370
+ 'model must be a non-empty "provider/model" id string or a ModelSelection object',
3371
+ { code: "invalid_model_selection" }
3372
+ );
3373
+ }
3374
+ return { id };
3375
+ }
3301
3376
 
3302
3377
  // src/internal/runtime/registry/agent-registry-store.ts
3303
3378
  init_cwd_mutex();
@@ -3374,7 +3449,10 @@ var REGISTRY_RELATIVE_PATH = path.join(".theokit", "agents", "registry.json");
3374
3449
  function stripSecretsFromOptions(options) {
3375
3450
  return assignDefined({
3376
3451
  name: options.name,
3377
- model: options.model !== void 0 ? { id: options.model.id } : void 0,
3452
+ model: (() => {
3453
+ const m = normalizeModel(options.model);
3454
+ return m !== void 0 ? { id: m.id } : void 0;
3455
+ })(),
3378
3456
  systemPrompt: typeof options.systemPrompt === "string" ? options.systemPrompt : void 0,
3379
3457
  local: serializeLocal(options.local),
3380
3458
  cloud: serializeCloud(options.cloud),
@@ -3659,7 +3737,8 @@ function serializeCloudAgentConfig(options) {
3659
3737
  cloud: serializeCloud2(options.cloud)
3660
3738
  };
3661
3739
  if (typeof options.agentId === "string") payload.agentId = options.agentId;
3662
- if (options.model !== void 0) payload.model = { id: options.model.id };
3740
+ const cfgModel = normalizeModel(options.model);
3741
+ if (cfgModel !== void 0) payload.model = { id: cfgModel.id };
3663
3742
  if (typeof options.systemPrompt === "string") payload.systemPrompt = options.systemPrompt;
3664
3743
  const skills = serializeSkills(options.skills);
3665
3744
  if (skills !== void 0) payload.skills = skills;
@@ -4555,6 +4634,7 @@ function applyScriptMetrics(base, script) {
4555
4634
  if (script.cost !== void 0) base.cost = script.cost;
4556
4635
  if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
4557
4636
  if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
4637
+ if (script.origin !== void 0) base.origin = script.origin;
4558
4638
  }
4559
4639
 
4560
4640
  // src/internal/runtime/cloud/cloud-run.ts
@@ -4910,7 +4990,7 @@ var CloudAgent = class {
4910
4990
  disposed = false;
4911
4991
  constructor(options, providedAgentId) {
4912
4992
  this.agentId = providedAgentId ?? options.agentId ?? generateCloudAgentId();
4913
- this.model = options.model;
4993
+ this.model = normalizeModel(options.model);
4914
4994
  this.options = options;
4915
4995
  this.cloudPayload = serializeCloudAgentConfig({ ...options, agentId: this.agentId });
4916
4996
  const repoUrls = (options.cloud?.repos ?? []).map((repo) => repo.url);
@@ -4940,6 +5020,17 @@ var CloudAgent = class {
4940
5020
  const apiKey = resolveApiKey(this.options.apiKey);
4941
5021
  return isFixtureApiKey(apiKey) && getConfiguredBaseUrl() === void 0;
4942
5022
  }
5023
+ // SE9 — integrated structured output; delegates to the shared helper.
5024
+ generate(message, options) {
5025
+ return agentGenerate(
5026
+ this,
5027
+ this.model,
5028
+ this.options.apiKey,
5029
+ this.options.local,
5030
+ message,
5031
+ options
5032
+ );
5033
+ }
4943
5034
  async send(message, options = {}) {
4944
5035
  if (options.tools !== void 0 && options.tools.length > 0) {
4945
5036
  throw new ConfigurationError(
@@ -4970,7 +5061,7 @@ var CloudAgent = class {
4970
5061
  });
4971
5062
  }
4972
5063
  async sendLocked(message, options) {
4973
- const overrideModel = options.model;
5064
+ const overrideModel = normalizeModel(options.model);
4974
5065
  if (overrideModel !== void 0) {
4975
5066
  this.model = overrideModel;
4976
5067
  updateRegisteredAgent(this.agentId, { model: overrideModel });
@@ -5884,18 +5975,18 @@ var ALL_ADAPTERS = [
5884
5975
  arizeAdapter,
5885
5976
  braintrustAdapter
5886
5977
  ];
5887
- var registered = /* @__PURE__ */ new Set();
5978
+ var registered2 = /* @__PURE__ */ new Set();
5888
5979
  function tryAutoRegisterAdapters(settings) {
5889
5980
  if (settings?.enabled !== true) return;
5890
5981
  if (settings.autoDetect === false) return;
5891
5982
  const disabled = new Set(settings.disable ?? []);
5892
5983
  for (const adapter of ALL_ADAPTERS) {
5893
- if (registered.has(adapter.moduleName)) continue;
5984
+ if (registered2.has(adapter.moduleName)) continue;
5894
5985
  if (disabled.has(adapter.displayName.toLowerCase())) continue;
5895
5986
  if (!adapter.detect()) continue;
5896
5987
  try {
5897
5988
  adapter.register();
5898
- registered.add(adapter.moduleName);
5989
+ registered2.add(adapter.moduleName);
5899
5990
  process.stderr.write(`[theokit-sdk] telemetry: ${adapter.displayName} auto-instrumented.
5900
5991
  `);
5901
5992
  } catch (cause) {
@@ -6458,6 +6549,26 @@ function paginate(items, opts) {
6458
6549
  return items.slice(start, end);
6459
6550
  }
6460
6551
 
6552
+ // src/internal/persistence/session-meta.ts
6553
+ function applyMetaPatch(current, patch) {
6554
+ const next = {};
6555
+ if (current.title !== void 0) next.title = current.title;
6556
+ if (current.tag !== void 0) next.tag = current.tag;
6557
+ if (patch.title === null) delete next.title;
6558
+ else if (patch.title !== void 0) next.title = patch.title;
6559
+ if (patch.tag === null) delete next.tag;
6560
+ else if (patch.tag !== void 0) next.tag = patch.tag;
6561
+ return next;
6562
+ }
6563
+ function coerceSessionMeta(raw) {
6564
+ if (typeof raw !== "object" || raw === null) return void 0;
6565
+ const obj = raw;
6566
+ const meta = {};
6567
+ if (typeof obj.title === "string") meta.title = obj.title;
6568
+ if (typeof obj.tag === "string") meta.tag = obj.tag;
6569
+ return meta.title === void 0 && meta.tag === void 0 ? void 0 : meta;
6570
+ }
6571
+
6461
6572
  // src/internal/persistence/conversation-storage-fs.ts
6462
6573
  var FileSystemConversationStorage = class {
6463
6574
  #root;
@@ -6508,6 +6619,37 @@ var FileSystemConversationStorage = class {
6508
6619
  async compact(conversationId, maxTurns) {
6509
6620
  await compactSessionFile(this.#root, conversationId, maxTurns);
6510
6621
  }
6622
+ // SE4 — session metadata persisted as a per-conversation sidecar
6623
+ // `<root>/.theokit/agents/<safeId>/session.json` (same sanitized perimeter as
6624
+ // the transcript). Kept separate from messages.jsonl so a title/tag write does
6625
+ // not rewrite the append-only log.
6626
+ #metaPath(conversationId) {
6627
+ const safe3 = sanitizeIdentifier(conversationId, { maxLen: 128 });
6628
+ return safePathJoin(this.#root, ".theokit", "agents", safe3, "session.json");
6629
+ }
6630
+ async getSessionMeta(conversationId) {
6631
+ try {
6632
+ const raw = await promises.readFile(this.#metaPath(conversationId), "utf8");
6633
+ return coerceSessionMeta(JSON.parse(raw));
6634
+ } catch (cause) {
6635
+ if (cause.code === "ENOENT") return void 0;
6636
+ throw cause;
6637
+ }
6638
+ }
6639
+ async setSessionMeta(conversationId, patch) {
6640
+ const metaPath = this.#metaPath(conversationId);
6641
+ await promises.mkdir(path.dirname(metaPath), { recursive: true });
6642
+ await withFileLock(metaPath, async () => {
6643
+ let current = {};
6644
+ try {
6645
+ current = coerceSessionMeta(JSON.parse(await promises.readFile(metaPath, "utf8"))) ?? {};
6646
+ } catch (cause) {
6647
+ if (cause.code !== "ENOENT") throw cause;
6648
+ }
6649
+ const next = applyMetaPatch(current, patch);
6650
+ await promises.writeFile(metaPath, redactSecrets(JSON.stringify(next)), "utf8");
6651
+ });
6652
+ }
6511
6653
  async dispose() {
6512
6654
  }
6513
6655
  };
@@ -8427,7 +8569,7 @@ function bootstrapSubmanagers(args) {
8427
8569
  const providerCount = (args.options.providers?.routes?.length ?? 0) + enabledPluginNames(args.options.plugins).length;
8428
8570
  if (providerCount > 0 || args.options.providers !== void 0) {
8429
8571
  out.providers = new ProvidersManagerImpl(
8430
- args.options.model,
8572
+ normalizeModel(args.options.model),
8431
8573
  args.options.providers,
8432
8574
  args.options.plugins
8433
8575
  );
@@ -8497,6 +8639,7 @@ function createLocalRun(options) {
8497
8639
  projectMcpServers: options.projectMcpServers,
8498
8640
  ...options.persistMemoryFact !== void 0 ? { persistMemoryFact: options.persistMemoryFact } : {}
8499
8641
  });
8642
+ if (options.sendOptions.origin !== void 0) script.origin = options.sendOptions.origin;
8500
8643
  const handle = new LocalRun({
8501
8644
  id,
8502
8645
  agentId: options.agentId,
@@ -9145,6 +9288,15 @@ async function emitReasoningDeltaCallback(inputs, text) {
9145
9288
  );
9146
9289
  }
9147
9290
 
9291
+ // src/types/run-events.ts
9292
+ function emitRunEvent(sink, event) {
9293
+ if (sink === void 0) return;
9294
+ try {
9295
+ sink(event);
9296
+ } catch {
9297
+ }
9298
+ }
9299
+
9148
9300
  // src/internal/agent-loop/tool-dispatch.ts
9149
9301
  init_async_local_storage();
9150
9302
 
@@ -9242,6 +9394,22 @@ function coerceArgsToSchema(args, schema) {
9242
9394
  return { value: out, changed };
9243
9395
  }
9244
9396
 
9397
+ // src/tool-error.ts
9398
+ init_errors();
9399
+ var ToolError = class extends TheokitAgentError {
9400
+ name = "ToolError";
9401
+ /** The error content surfaced to the model: a string, or text/image blocks. */
9402
+ content;
9403
+ constructor(content, options = {}) {
9404
+ super(renderToolErrorMessage(content), { ...options, isRetryable: false });
9405
+ this.content = content;
9406
+ }
9407
+ };
9408
+ function renderToolErrorMessage(content) {
9409
+ if (typeof content === "string") return content;
9410
+ return content.map((block) => block.type === "text" ? block.text : `[${block.source.media_type} image]`).join("\n");
9411
+ }
9412
+
9245
9413
  // src/internal/runtime/tools/shell-tool.ts
9246
9414
  async function runShell(options) {
9247
9415
  if (options.sandbox === true && isObviouslyUnsafe(options.command)) {
@@ -9295,9 +9463,13 @@ async function runHandlerTool(kind, handler, call, signal, context) {
9295
9463
  return { stdout: "", stderr: `${kind} tool ${call.name} has no handler`, exitCode: 127 };
9296
9464
  }
9297
9465
  try {
9298
- const stdout = await handler(call.input, { signal, context });
9299
- return { stdout, stderr: "", exitCode: 0 };
9466
+ const out = await handler(call.input, { signal, context });
9467
+ if (typeof out !== "string") return { stdout: "", stderr: "", exitCode: 0, content: out };
9468
+ return { stdout: out, stderr: "", exitCode: 0 };
9300
9469
  } catch (cause) {
9470
+ if (cause instanceof ToolError) {
9471
+ return { stdout: "", stderr: cause.message, exitCode: 1, content: cause.content };
9472
+ }
9301
9473
  const message = cause instanceof Error ? cause.message : String(cause);
9302
9474
  return { stdout: "", stderr: message, exitCode: 1 };
9303
9475
  }
@@ -9416,6 +9588,11 @@ async function dispatchSingleCall(inputs, tools, call, events, parentSpan) {
9416
9588
  toolSpan?.end();
9417
9589
  return fileVeto;
9418
9590
  }
9591
+ emitRunEvent(inputs.runEventSink, {
9592
+ type: "tool_progress",
9593
+ toolName: workingCall.name,
9594
+ toolCallId: callId
9595
+ });
9419
9596
  const result = await runToolWithLifecycle(inputs, resolved, workingCall, callId);
9420
9597
  await inputs.pluginManager?.runPostToolCallHooks({
9421
9598
  name: workingCall.name,
@@ -9442,6 +9619,13 @@ function applyRepairAndExtractCall(tools, call) {
9442
9619
  function vetoFromForkWhitelist(inputs, call, callId, events) {
9443
9620
  const whitelistDecision = checkToolWhitelist(call.name);
9444
9621
  if (whitelistDecision.allowed) return void 0;
9622
+ emitRunEvent(inputs.runEventSink, {
9623
+ type: "permission_denied",
9624
+ toolName: call.name,
9625
+ toolCallId: callId,
9626
+ source: "fork_whitelist",
9627
+ message: whitelistDecision.reason ?? "tool not available in fork"
9628
+ });
9445
9629
  events.push(buildToolUseRunning(inputs, callId, call));
9446
9630
  events.push(
9447
9631
  buildToolUseCompleted(inputs, callId, call, {
@@ -9478,6 +9662,13 @@ async function vetoFromPluginPreHook(inputs, call, callId, events) {
9478
9662
  runId: inputs.runId
9479
9663
  });
9480
9664
  if (pluginVeto === void 0) return void 0;
9665
+ emitRunEvent(inputs.runEventSink, {
9666
+ type: "permission_denied",
9667
+ toolName: call.name,
9668
+ toolCallId: callId,
9669
+ source: "plugin",
9670
+ message: pluginVeto.message
9671
+ });
9481
9672
  events.push(
9482
9673
  buildToolUseCompleted(inputs, callId, call, {
9483
9674
  stdout: "",
@@ -9500,6 +9691,13 @@ async function vetoFromFileHookPreDecision(inputs, call, callId, events) {
9500
9691
  runId: inputs.runId
9501
9692
  });
9502
9693
  if (!preDecision.blocked) return void 0;
9694
+ emitRunEvent(inputs.runEventSink, {
9695
+ type: "permission_denied",
9696
+ toolName: call.name,
9697
+ toolCallId: callId,
9698
+ source: "file_hook",
9699
+ message: preDecision.reason ?? "blocked by hook"
9700
+ });
9503
9701
  events.push(
9504
9702
  buildToolUseCompleted(inputs, callId, call, {
9505
9703
  stdout: "",
@@ -9543,7 +9741,9 @@ async function runToolWithLifecycle(inputs, resolved, call, callId) {
9543
9741
  await safeEmitToolHook(inputs.onToolEnd, {
9544
9742
  toolName: call.name,
9545
9743
  args: call.input,
9546
- result: result.stdout,
9744
+ // SE7 — a block-returning handler has empty `stdout`; surface its structured
9745
+ // content to the hook instead of an empty string.
9746
+ result: result.content !== void 0 ? result.content : result.stdout,
9547
9747
  conversationId: inputs.agentId,
9548
9748
  callId,
9549
9749
  durationMs
@@ -9565,7 +9765,10 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
9565
9765
  output: {
9566
9766
  stdout: result.stdout,
9567
9767
  stderr: result.stderr,
9568
- exitCode: result.exitCode ?? 0
9768
+ exitCode: result.exitCode ?? 0,
9769
+ // SE7 — structured content (text + image blocks) when the handler
9770
+ // returned/threw blocks; absent for the string path.
9771
+ ...result.content !== void 0 ? { content: result.content } : {}
9569
9772
  },
9570
9773
  agentId: inputs.agentId,
9571
9774
  runId: inputs.runId
@@ -9578,7 +9781,9 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
9578
9781
  return {
9579
9782
  type: "tool_result",
9580
9783
  toolUseId: call.id,
9581
- content: renderToolResult(result),
9784
+ // SE7 — structured content blocks (handler-returned or ToolError-carried)
9785
+ // are authoritative; otherwise the legacy string rendering.
9786
+ content: result.content !== void 0 ? result.content : renderToolResult(result),
9582
9787
  ...result.exitCode !== 0 && result.exitCode !== void 0 ? { isError: true } : {}
9583
9788
  };
9584
9789
  }
@@ -9627,6 +9832,29 @@ function buildToolUseCompleted(inputs, callId, call, result) {
9627
9832
  };
9628
9833
  }
9629
9834
 
9835
+ // src/internal/llm/tool-result-content.ts
9836
+ init_errors();
9837
+ function toBlockToolResultContent(content) {
9838
+ return content;
9839
+ }
9840
+ function toStringToolResultContent(content, providerName) {
9841
+ if (typeof content === "string") return content;
9842
+ if (content.some((block) => block.type === "image")) {
9843
+ throw new ConfigurationError(
9844
+ `provider "${providerName}" does not support image content in tool results`
9845
+ );
9846
+ }
9847
+ return content.filter(
9848
+ (block) => block.type === "text"
9849
+ ).map((block) => block.text).join("\n");
9850
+ }
9851
+ function renderToolResultContentText(content, fn) {
9852
+ if (typeof content === "string") return fn(content);
9853
+ return content.map(
9854
+ (block) => block.type === "text" ? { type: "text", text: fn(block.text) } : block
9855
+ );
9856
+ }
9857
+
9630
9858
  // src/internal/agent-loop/tool-result-guard.ts
9631
9859
  var OPEN = "<untrusted-tool-output>";
9632
9860
  var CLOSE = "</untrusted-tool-output>";
@@ -9652,7 +9880,11 @@ ${CLOSE}`;
9652
9880
  function applyToolResultGuard(parts, opts) {
9653
9881
  if (opts.delimit !== true && opts.redactPii !== true) return parts;
9654
9882
  return parts.map(
9655
- (p) => p.type === "tool_result" ? { ...p, content: guardText(p.content, opts) } : p
9883
+ (p) => (
9884
+ // SE7 — guard the TEXT of a tool result (string or the text blocks of a
9885
+ // structured content); image blocks pass through untouched.
9886
+ p.type === "tool_result" ? { ...p, content: renderToolResultContentText(p.content, (t) => guardText(t, opts)) } : p
9887
+ )
9656
9888
  );
9657
9889
  }
9658
9890
 
@@ -10569,10 +10801,10 @@ function stripVertexPrefix(modelId) {
10569
10801
  }
10570
10802
 
10571
10803
  // src/internal/providers/builtin/index.ts
10572
- var registered2 = false;
10804
+ var registered3 = false;
10573
10805
  function registerBuiltins() {
10574
- if (registered2) return;
10575
- registered2 = true;
10806
+ if (registered3) return;
10807
+ registered3 = true;
10576
10808
  registerProvider(ANTHROPIC);
10577
10809
  registerProvider(OPENAI);
10578
10810
  registerProvider(OPENROUTER);
@@ -10800,7 +11032,9 @@ function toAnthropicWireMessage(message) {
10800
11032
  return {
10801
11033
  type: "tool_result",
10802
11034
  tool_use_id: part.toolUseId,
10803
- content: part.content,
11035
+ // SE7 — this wire is block-capable: strings pass through, structured
11036
+ // text/image blocks are forwarded natively.
11037
+ content: toBlockToolResultContent(part.content),
10804
11038
  ...part.isError === true ? { is_error: true } : {}
10805
11039
  };
10806
11040
  });
@@ -11592,7 +11826,8 @@ function toOllamaMessages(message) {
11592
11826
  if (part.type === "tool_result") {
11593
11827
  out.push({
11594
11828
  role: "tool",
11595
- content: part.content,
11829
+ // SE7 — string-only tool role: text blocks flatten; image fails fast.
11830
+ content: toStringToolResultContent(part.content, "ollama"),
11596
11831
  tool_name: part.toolUseId
11597
11832
  });
11598
11833
  }
@@ -12129,7 +12364,9 @@ function userOrToolMessages(message) {
12129
12364
  out.push({
12130
12365
  role: "tool",
12131
12366
  tool_call_id: part.toolUseId,
12132
- content: part.content
12367
+ // SE7 — this wire's tool role is string-only: text blocks flatten; an
12368
+ // image block fails fast (ConfigurationError).
12369
+ content: toStringToolResultContent(part.content, "openai")
12133
12370
  });
12134
12371
  }
12135
12372
  }
@@ -13058,18 +13295,20 @@ function levenshtein(a, b) {
13058
13295
  function createRealLocalRun(options) {
13059
13296
  const { userText, id, startTime } = prepareRunContext(options.message);
13060
13297
  const supported = /* @__PURE__ */ new Set(["stream", "wait", "cancel", "conversation"]);
13061
- const unusedFixtureScript = {
13298
+ const runScript = {
13062
13299
  events: [],
13063
13300
  finalStatus: "running",
13064
13301
  cancellable: false,
13065
- conversation: []
13302
+ conversation: [],
13303
+ // SE3 — carry the turn's provenance so buildResult forwards it onto RunResult.origin.
13304
+ ...options.sendOptions.origin !== void 0 ? { origin: options.sendOptions.origin } : {}
13066
13305
  };
13067
13306
  const handle = new RealLocalRun(
13068
13307
  {
13069
13308
  id,
13070
13309
  agentId: options.agentId,
13071
13310
  model: options.model,
13072
- script: unusedFixtureScript,
13311
+ script: runScript,
13073
13312
  supportedOps: supported,
13074
13313
  startTime
13075
13314
  },
@@ -13181,6 +13420,8 @@ function buildLoopInputs(options, runId, userText) {
13181
13420
  // M7 — forward SendOptions.context to the loop so every tool handler receives
13182
13421
  // it on `ctx.context` (shared run config set once, e.g. projectRoot).
13183
13422
  ...options.sendOptions.context !== void 0 ? { context: options.sendOptions.context } : {},
13423
+ // SE2 — forward the opt-in typed runtime-event sink to the loop.
13424
+ ...options.sendOptions.onRunEvent !== void 0 ? { runEventSink: options.sendOptions.onRunEvent } : {},
13184
13425
  // #58 / #57 — forward the per-tool timeout + tool-result guard so a consumer
13185
13426
  // can enable them via SendOptions (not only internal AgentLoopInputs).
13186
13427
  ...options.sendOptions.perToolTimeoutMs !== void 0 ? { perToolTimeoutMs: options.sendOptions.perToolTimeoutMs } : {},
@@ -15873,7 +16114,7 @@ function wrapRunWithPostReplyHook(args) {
15873
16114
  async function executeSendLocked(inputs, message, options) {
15874
16115
  if (inputs.disposed) throw new AgentDisposedError(inputs.agentId);
15875
16116
  await consumePending(inputs.agentId, inputs.invalidationPending, inputs.clearInvalidation, inputs.reload);
15876
- inputs.applyModelOverride(options.model);
16117
+ inputs.applyModelOverride(normalizeModel(options.model));
15877
16118
  const userText = typeof message === "string" ? message : message.text;
15878
16119
  if (inputs.options.onBeforeSend !== void 0) {
15879
16120
  await inputs.options.onBeforeSend({
@@ -16025,7 +16266,7 @@ var LocalAgent = class {
16025
16266
  constructor(options) {
16026
16267
  this.agentId = options.agentId ?? generateLocalAgentId();
16027
16268
  this._telemetry = createTelemetry(options.telemetry);
16028
- this.model = options.model;
16269
+ this.model = normalizeModel(options.model);
16029
16270
  this.options = options;
16030
16271
  this.workspaceCwd = resolveCwd(options.local?.cwd);
16031
16272
  this.conversationStorage = options.conversationStorage;
@@ -16092,6 +16333,11 @@ var LocalAgent = class {
16092
16333
  hooks() {
16093
16334
  return this.hooksExecutor;
16094
16335
  }
16336
+ // SE9 — integrated structured output; delegates to the shared helper.
16337
+ generate(message, options) {
16338
+ const { apiKey, local } = this.options;
16339
+ return agentGenerate(this, this.model, apiKey, local, message, options);
16340
+ }
16095
16341
  async send(message, options = {}) {
16096
16342
  if (options.tools !== void 0 && options.tools.length > 0) {
16097
16343
  validateToolCatalog(options.tools);
@@ -16331,6 +16577,7 @@ function includesSetting(options, source) {
16331
16577
 
16332
16578
  // src/agent-helpers.ts
16333
16579
  async function runCreateUnderSpan(options, span) {
16580
+ options = { ...options, model: normalizeModel(options.model) };
16334
16581
  validateAgentOptions(options);
16335
16582
  validateCloudToolParity(options);
16336
16583
  await guardAgainstIdCollision(options);
@@ -16404,7 +16651,10 @@ async function createLocalAgent(options) {
16404
16651
  const willFlowToProvider = !isFixtureApiKey(apiKey) && !shouldUseRealLocalRuntime(apiKey);
16405
16652
  const shape = validateApiKeyShape(apiKey, {
16406
16653
  strict: willFlowToProvider,
16407
- ...willFlowToProvider ? { provider: providerFromModelId(options.model?.id) } : {}
16654
+ // `options.model` is already normalized by `runCreateUnderSpan`; `normalizeModel`
16655
+ // here is the idempotent narrowing of the widened public type (`{id}` passes
16656
+ // through by reference) — cleaner than an `as` cast.
16657
+ ...willFlowToProvider ? { provider: providerFromModelId(normalizeModel(options.model)?.id) } : {}
16408
16658
  });
16409
16659
  if (shape.malformed) {
16410
16660
  throw new AuthenticationError(shape.message, { code: "malformed_api_key" });
@@ -16470,6 +16720,10 @@ async function rehydrateExistingAgent(agentId, existing, options) {
16470
16720
  ...existing.options,
16471
16721
  ...options,
16472
16722
  ...mergedLocal !== void 0 ? { local: mergedLocal } : {},
16723
+ // SE8 — normalize a caller-supplied bare-string model override at this resume
16724
+ // boundary (Agent.resume never flows through runCreateUnderSpan). Cloud resume
16725
+ // honors it; local resume forces the persisted model below.
16726
+ model: normalizeModel(options.model ?? existing.options.model),
16473
16727
  mcpServers: void 0,
16474
16728
  agentId
16475
16729
  };
@@ -16836,6 +17090,7 @@ async function getOrCreateUncached(agentId, options) {
16836
17090
  }
16837
17091
  setAgentFacade({
16838
17092
  create: (options) => Agent.create(options),
17093
+ delete: (agentId) => Agent.delete(agentId),
16839
17094
  prompt: (message, options) => Agent.prompt(message, options),
16840
17095
  get: (agentId) => Agent.get(agentId),
16841
17096
  resume: (agentId, options) => Agent.resume(agentId, options),