@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.js CHANGED
@@ -1,10 +1,10 @@
1
+ import { z, toJSONSchema } from 'zod';
2
+ import { createRequire } from 'module';
1
3
  import { randomUUID, randomBytes, createHash } from 'crypto';
2
4
  import { mkdir, writeFile, readFile, unlink, open, rename, statfs, stat, rm, readdir, appendFile, access } from 'fs/promises';
3
5
  import { join, dirname, resolve, sep, relative, isAbsolute } from 'path';
4
- import { z, toJSONSchema } from 'zod';
5
6
  import { readFileSync, mkdirSync, appendFileSync, readdirSync, existsSync, realpathSync, lstatSync, readlinkSync } from 'fs';
6
7
  import { AsyncLocalStorage } from 'async_hooks';
7
- import { createRequire } from 'module';
8
8
  import { homedir } from 'os';
9
9
  import { execFile, spawn } from 'child_process';
10
10
  import { fileURLToPath } from 'url';
@@ -509,6 +509,256 @@ var init_errors = __esm({
509
509
  };
510
510
  }
511
511
  });
512
+ function toJsonSchema(schema, options = { unrepresentable: "any" }) {
513
+ return toJSONSchema(schema, options);
514
+ }
515
+ var init_to_json_schema = __esm({
516
+ "src/internal/zod/to-json-schema.ts"() {
517
+ }
518
+ });
519
+ function requireZod() {
520
+ if (cachedZ !== void 0) return cachedZ;
521
+ const r = createRequire(import.meta.url);
522
+ let mod;
523
+ try {
524
+ mod = r("zod");
525
+ } catch (cause) {
526
+ throw new ConfigurationError(
527
+ 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
528
+ { code: "zod_not_installed", cause }
529
+ );
530
+ }
531
+ cachedZ = mod.z ?? mod;
532
+ return cachedZ;
533
+ }
534
+ function makeOutputTool(jsonSchema, onCapture) {
535
+ return {
536
+ name: "output",
537
+ description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
538
+ inputSchema: jsonSchema,
539
+ handler: (input) => {
540
+ const out = onCapture(input);
541
+ if (typeof out === "string") return out;
542
+ if (out instanceof Promise) return out;
543
+ return "";
544
+ }
545
+ };
546
+ }
547
+ function buildTransientAgentOptions(params) {
548
+ return {
549
+ model: params.model,
550
+ local: params.local,
551
+ tools: [params.outputTool],
552
+ systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
553
+ ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
554
+ ...params.providers !== void 0 ? { providers: params.providers } : {}
555
+ };
556
+ }
557
+ function extractUsage(result) {
558
+ const u = result.usage;
559
+ if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
560
+ return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
561
+ }
562
+ function buildToolPrompt(prompt) {
563
+ return `${prompt}
564
+
565
+ Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
566
+ }
567
+ function setupStructuredOutput(schema, maxRetries) {
568
+ const z7 = requireZod();
569
+ const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
570
+ return {
571
+ z: z7,
572
+ jsonSchema,
573
+ maxRetries: maxRetries ?? 1,
574
+ initialUsage: { inputTokens: 0, outputTokens: 0 }
575
+ };
576
+ }
577
+ async function disposeAndDeleteTransient(agent, deletor) {
578
+ await agent.dispose();
579
+ try {
580
+ await deletor(agent.agentId);
581
+ } catch {
582
+ }
583
+ }
584
+ var cachedZ;
585
+ var init_structured_output_helpers = __esm({
586
+ "src/internal/structured-output-helpers.ts"() {
587
+ init_errors();
588
+ init_to_json_schema();
589
+ }
590
+ });
591
+
592
+ // src/generate-object.ts
593
+ var generate_object_exports = {};
594
+ __export(generate_object_exports, {
595
+ GenerateObjectError: () => GenerateObjectError,
596
+ generateObjectImpl: () => generateObjectImpl
597
+ });
598
+ function salvagePartial(schema, raw) {
599
+ if (typeof raw !== "object" || raw === null) return raw;
600
+ const shape = schema.shape;
601
+ if (shape === void 0 || typeof shape !== "object") return raw;
602
+ const rawObj = raw;
603
+ const out = {};
604
+ for (const [key, fieldSchema] of Object.entries(shape)) {
605
+ if (typeof fieldSchema?.safeParse !== "function") continue;
606
+ const parsed = fieldSchema.safeParse(rawObj[key]);
607
+ if (parsed.success) out[key] = parsed.data;
608
+ }
609
+ return out;
610
+ }
611
+ async function runReasoningPhase(options, deps) {
612
+ const reasoningOptions = {
613
+ model: options.model,
614
+ local: options.local,
615
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
616
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
617
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
618
+ };
619
+ const reasoningAgent = await deps.create(reasoningOptions);
620
+ try {
621
+ const run = await reasoningAgent.send(options.prompt);
622
+ const result = await run.wait();
623
+ const text = result.result;
624
+ return typeof text === "string" ? text : options.prompt;
625
+ } finally {
626
+ await disposeAndDeleteTransient(reasoningAgent, deps.delete);
627
+ }
628
+ }
629
+ async function generateObjectImpl(options, deps) {
630
+ const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
631
+ options.schema,
632
+ options.maxRetries
633
+ );
634
+ let capturedRaw;
635
+ let lastUsage = initialUsage;
636
+ const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
637
+ class CaptureSentinel extends Error {
638
+ constructor(captured) {
639
+ super("generate-object-capture");
640
+ this.captured = captured;
641
+ }
642
+ captured;
643
+ [sentinel] = true;
644
+ }
645
+ const outputTool = makeOutputTool(jsonSchema, (input) => {
646
+ if (capturedRaw === void 0) {
647
+ capturedRaw = input;
648
+ }
649
+ throw new CaptureSentinel(input);
650
+ });
651
+ const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
652
+ const structuringModel = options.structuringModel ?? options.model;
653
+ const structuringPrompt = reasoningText ?? options.prompt;
654
+ const agentOptions = buildTransientAgentOptions({
655
+ model: structuringModel,
656
+ local: options.local,
657
+ outputTool,
658
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
659
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
660
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
661
+ });
662
+ const agent = await deps.create(agentOptions);
663
+ try {
664
+ const userMessage = buildToolPrompt(structuringPrompt);
665
+ let lastParseError;
666
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
667
+ capturedRaw = void 0;
668
+ const run = await agent.send(userMessage);
669
+ const result = await run.wait();
670
+ lastUsage = extractUsage(result);
671
+ if (capturedRaw === void 0) {
672
+ if (result.status === "error" && result.error !== void 0) {
673
+ throw new GenerateObjectError(
674
+ "no_tool_call",
675
+ `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
676
+ result.error
677
+ );
678
+ }
679
+ if (attempt === maxRetries) {
680
+ throw new GenerateObjectError(
681
+ "no_tool_call",
682
+ "The model returned text instead of calling the `output` tool."
683
+ );
684
+ }
685
+ continue;
686
+ }
687
+ const parsed = options.schema.safeParse(capturedRaw);
688
+ if (parsed.success) {
689
+ return {
690
+ object: parsed.data,
691
+ raw: capturedRaw,
692
+ usage: lastUsage,
693
+ finishReason: "tool_use"
694
+ };
695
+ }
696
+ lastParseError = parsed.error;
697
+ }
698
+ if (options.errorStrategy === "return-raw") {
699
+ return {
700
+ object: capturedRaw,
701
+ raw: capturedRaw,
702
+ usage: lastUsage,
703
+ finishReason: "tool_use"
704
+ };
705
+ }
706
+ if (options.errorStrategy === "return-partial") {
707
+ return {
708
+ object: salvagePartial(options.schema, capturedRaw),
709
+ raw: capturedRaw,
710
+ usage: lastUsage,
711
+ finishReason: "tool_use"
712
+ };
713
+ }
714
+ throw new GenerateObjectError(
715
+ "parse_failed",
716
+ "Schema parse failed after all retries.",
717
+ lastParseError
718
+ );
719
+ } finally {
720
+ await disposeAndDeleteTransient(agent, deps.delete);
721
+ }
722
+ }
723
+ var GenerateObjectError;
724
+ var init_generate_object = __esm({
725
+ "src/generate-object.ts"() {
726
+ init_structured_output_helpers();
727
+ GenerateObjectError = class extends Error {
728
+ name = "GenerateObjectError";
729
+ code;
730
+ cause;
731
+ constructor(code, message, cause) {
732
+ super(message);
733
+ this.code = code;
734
+ if (cause !== void 0) this.cause = cause;
735
+ }
736
+ };
737
+ }
738
+ });
739
+
740
+ // src/internal/runtime/registry/agent-factory-registry.ts
741
+ var agent_factory_registry_exports = {};
742
+ __export(agent_factory_registry_exports, {
743
+ getAgentFacade: () => getAgentFacade,
744
+ setAgentFacade: () => setAgentFacade
745
+ });
746
+ function setAgentFacade(facade) {
747
+ registered = facade;
748
+ }
749
+ function getAgentFacade() {
750
+ if (registered === void 0) {
751
+ throw new Error(
752
+ "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
753
+ );
754
+ }
755
+ return registered;
756
+ }
757
+ var registered;
758
+ var init_agent_factory_registry = __esm({
759
+ "src/internal/runtime/registry/agent-factory-registry.ts"() {
760
+ }
761
+ });
512
762
 
513
763
  // src/internal/persistence/cwd-mutex.ts
514
764
  function withCwdMutex(key, fn) {
@@ -1426,29 +1676,6 @@ var init_judge_call = __esm({
1426
1676
  }
1427
1677
  });
1428
1678
 
1429
- // src/internal/runtime/registry/agent-factory-registry.ts
1430
- var agent_factory_registry_exports = {};
1431
- __export(agent_factory_registry_exports, {
1432
- getAgentFacade: () => getAgentFacade,
1433
- setAgentFacade: () => setAgentFacade
1434
- });
1435
- function setAgentFacade(facade) {
1436
- registered3 = facade;
1437
- }
1438
- function getAgentFacade() {
1439
- if (registered3 === void 0) {
1440
- throw new Error(
1441
- "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
1442
- );
1443
- }
1444
- return registered3;
1445
- }
1446
- var registered3;
1447
- var init_agent_factory_registry = __esm({
1448
- "src/internal/runtime/registry/agent-factory-registry.ts"() {
1449
- }
1450
- });
1451
-
1452
1679
  // src/internal/runtime/lifecycle/run-to-completion.ts
1453
1680
  var run_to_completion_exports = {};
1454
1681
  __export(run_to_completion_exports, {
@@ -1613,14 +1840,14 @@ async function forkAgentImpl(parent, options, deps) {
1613
1840
  return {
1614
1841
  result: result.result,
1615
1842
  toolCalls: [],
1616
- usage: extractUsage(result)
1843
+ usage: extractUsage2(result)
1617
1844
  };
1618
1845
  });
1619
1846
  } finally {
1620
1847
  await fork.dispose();
1621
1848
  }
1622
1849
  }
1623
- function extractUsage(result) {
1850
+ function extractUsage2(result) {
1624
1851
  if (typeof result !== "object" || result === null) {
1625
1852
  return { inputTokens: 0, outputTokens: 0 };
1626
1853
  }
@@ -2053,345 +2280,118 @@ async function runWorkAndFinalize(taskId, aborter, work) {
2053
2280
  emit(payload) {
2054
2281
  void checkCancelRequestedAt(taskId, aborter);
2055
2282
  emitToSubscribers(taskId, {
2056
- type: "progress",
2057
- taskId,
2058
- at: Date.now(),
2059
- payload
2060
- });
2061
- }
2062
- };
2063
- try {
2064
- const result = await reentryAls.run(true, () => Promise.resolve().then(() => work(ctx)));
2065
- if (aborter.signal.aborted) {
2066
- const cancelledAt = Date.now();
2067
- await transition(taskId, "cancelled", { cancelledAt });
2068
- emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2069
- return;
2070
- }
2071
- const finishedAt = Date.now();
2072
- await transition(taskId, "finished", { finishedAt, result });
2073
- emitToSubscribers(taskId, { type: "finished", taskId, finishedAt, result });
2074
- } catch (err) {
2075
- if (aborter.signal.aborted) {
2076
- const cancelledAt = Date.now();
2077
- await transition(taskId, "cancelled", { cancelledAt });
2078
- emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2079
- return;
2080
- }
2081
- const erroredAt = Date.now();
2082
- const error = err instanceof Error ? { code: err.code ?? "work_threw", message: err.message } : { code: "work_threw", message: String(err) };
2083
- await transition(taskId, "error", { erroredAt, error });
2084
- emitToSubscribers(taskId, { type: "errored", taskId, erroredAt, error });
2085
- }
2086
- }
2087
- async function checkCancelRequestedAt(taskId, aborter) {
2088
- if (aborter.signal.aborted) return;
2089
- try {
2090
- const handle = await state.store.get(taskId);
2091
- if (handle?.cancelRequested === true) {
2092
- aborter.abort("cancelRequested");
2093
- }
2094
- } catch {
2095
- }
2096
- }
2097
- async function acquireSlot() {
2098
- if (reentryAls.getStore() === true) {
2099
- return () => {
2100
- };
2101
- }
2102
- return state.semaphore.acquire();
2103
- }
2104
- async function submit(internal) {
2105
- state.firstSubmitSeen = true;
2106
- startEvictTimerIfNeeded();
2107
- const resolvedId = resolveIdInternal(internal);
2108
- const submitSpan = startTaskSubmitSpan({ taskId: resolvedId, kind: internal.kind });
2109
- const existing = await state.store.get(resolvedId);
2110
- if (existing !== void 0) {
2111
- submitSpan.end();
2112
- return existing;
2113
- }
2114
- const queuedHandle = await buildAndInsertQueued(internal, resolvedId);
2115
- submitSpan.end();
2116
- if (internal.signal?.aborted === true) {
2117
- return shortCircuitAborted(queuedHandle, internal.signal);
2118
- }
2119
- const aborter = new AbortController();
2120
- state.aborters.set(resolvedId, aborter);
2121
- if (internal.signal !== void 0) {
2122
- internal.signal.addEventListener("abort", () => aborter.abort(internal.signal?.reason), {
2123
- once: true
2124
- });
2125
- }
2126
- const release = await acquireSlot();
2127
- void (async () => {
2128
- try {
2129
- await checkCancelRequestedAt(resolvedId, aborter);
2130
- if (aborter.signal.aborted) {
2131
- const cancelledAt = Date.now();
2132
- await transition(resolvedId, "cancelled", { cancelledAt });
2133
- emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
2134
- return;
2135
- }
2136
- const startedAt = Date.now();
2137
- await transition(resolvedId, "running", { startedAt });
2138
- emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
2139
- await runWorkAndFinalize(resolvedId, aborter, internal.work);
2140
- } finally {
2141
- release();
2142
- state.aborters.delete(resolvedId);
2143
- }
2144
- })();
2145
- return queuedHandle;
2146
- }
2147
- async function get(id) {
2148
- if (!isValidTaskId(id, true)) return void 0;
2149
- return state.store.get(id);
2150
- }
2151
- var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state;
2152
- var init_registry = __esm({
2153
- "src/internal/task/registry.ts"() {
2154
- init_errors();
2155
- init_task();
2156
- init_async_semaphore();
2157
- init_ring_buffer();
2158
- init_store();
2159
- init_subscribe();
2160
- init_telemetry();
2161
- DEFAULT_CONCURRENCY = 8;
2162
- DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
2163
- EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
2164
- RING_CAP = 64;
2165
- reentryAls = new AsyncLocalStorage();
2166
- state = buildState({});
2167
- }
2168
- });
2169
- function toJsonSchema(schema, options = { unrepresentable: "any" }) {
2170
- return toJSONSchema(schema, options);
2171
- }
2172
- var init_to_json_schema = __esm({
2173
- "src/internal/zod/to-json-schema.ts"() {
2174
- }
2175
- });
2176
- function requireZod() {
2177
- if (cachedZ !== void 0) return cachedZ;
2178
- const r = createRequire(import.meta.url);
2179
- let mod;
2180
- try {
2181
- mod = r("zod");
2182
- } catch (cause) {
2183
- throw new ConfigurationError(
2184
- 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
2185
- { code: "zod_not_installed", cause }
2186
- );
2187
- }
2188
- cachedZ = mod.z ?? mod;
2189
- return cachedZ;
2190
- }
2191
- function makeOutputTool(jsonSchema, onCapture) {
2192
- return {
2193
- name: "output",
2194
- description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
2195
- inputSchema: jsonSchema,
2196
- handler: (input) => {
2197
- const out = onCapture(input);
2198
- if (typeof out === "string") return out;
2199
- if (out instanceof Promise) return out;
2200
- return "";
2201
- }
2202
- };
2203
- }
2204
- function buildTransientAgentOptions(params) {
2205
- return {
2206
- model: params.model,
2207
- local: params.local,
2208
- tools: [params.outputTool],
2209
- systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
2210
- ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
2211
- ...params.providers !== void 0 ? { providers: params.providers } : {}
2212
- };
2213
- }
2214
- function extractUsage2(result) {
2215
- const u = result.usage;
2216
- if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
2217
- return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
2218
- }
2219
- function buildToolPrompt(prompt) {
2220
- return `${prompt}
2221
-
2222
- Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
2223
- }
2224
- function setupStructuredOutput(schema, maxRetries) {
2225
- const z7 = requireZod();
2226
- const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
2227
- return {
2228
- z: z7,
2229
- jsonSchema,
2230
- maxRetries: maxRetries ?? 1,
2231
- initialUsage: { inputTokens: 0, outputTokens: 0 }
2232
- };
2233
- }
2234
- async function disposeAndDeleteTransient(agent, deletor) {
2235
- await agent.dispose();
2236
- try {
2237
- await deletor(agent.agentId);
2238
- } catch {
2239
- }
2240
- }
2241
- var cachedZ;
2242
- var init_structured_output_helpers = __esm({
2243
- "src/internal/structured-output-helpers.ts"() {
2244
- init_errors();
2245
- init_to_json_schema();
2246
- }
2247
- });
2248
-
2249
- // src/generate-object.ts
2250
- var generate_object_exports = {};
2251
- __export(generate_object_exports, {
2252
- GenerateObjectError: () => GenerateObjectError,
2253
- generateObjectImpl: () => generateObjectImpl
2254
- });
2255
- function salvagePartial(schema, raw) {
2256
- if (typeof raw !== "object" || raw === null) return raw;
2257
- const shape = schema.shape;
2258
- if (shape === void 0 || typeof shape !== "object") return raw;
2259
- const rawObj = raw;
2260
- const out = {};
2261
- for (const [key, fieldSchema] of Object.entries(shape)) {
2262
- if (typeof fieldSchema?.safeParse !== "function") continue;
2263
- const parsed = fieldSchema.safeParse(rawObj[key]);
2264
- if (parsed.success) out[key] = parsed.data;
2265
- }
2266
- return out;
2267
- }
2268
- async function runReasoningPhase(options, deps) {
2269
- const reasoningOptions = {
2270
- model: options.model,
2271
- local: options.local,
2272
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
2273
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
2274
- ...options.providers !== void 0 ? { providers: options.providers } : {}
2275
- };
2276
- const reasoningAgent = await deps.create(reasoningOptions);
2277
- try {
2278
- const run = await reasoningAgent.send(options.prompt);
2279
- const result = await run.wait();
2280
- const text = result.result;
2281
- return typeof text === "string" ? text : options.prompt;
2282
- } finally {
2283
- await disposeAndDeleteTransient(reasoningAgent, deps.delete);
2284
- }
2285
- }
2286
- async function generateObjectImpl(options, deps) {
2287
- const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
2288
- options.schema,
2289
- options.maxRetries
2290
- );
2291
- let capturedRaw;
2292
- let lastUsage = initialUsage;
2293
- const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
2294
- class CaptureSentinel extends Error {
2295
- constructor(captured) {
2296
- super("generate-object-capture");
2297
- this.captured = captured;
2298
- }
2299
- captured;
2300
- [sentinel] = true;
2301
- }
2302
- const outputTool = makeOutputTool(jsonSchema, (input) => {
2303
- if (capturedRaw === void 0) {
2304
- capturedRaw = input;
2305
- }
2306
- throw new CaptureSentinel(input);
2307
- });
2308
- const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
2309
- const structuringModel = options.structuringModel ?? options.model;
2310
- const structuringPrompt = reasoningText ?? options.prompt;
2311
- const agentOptions = buildTransientAgentOptions({
2312
- model: structuringModel,
2313
- local: options.local,
2314
- outputTool,
2315
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
2316
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
2317
- ...options.providers !== void 0 ? { providers: options.providers } : {}
2318
- });
2319
- const agent = await deps.create(agentOptions);
2283
+ type: "progress",
2284
+ taskId,
2285
+ at: Date.now(),
2286
+ payload
2287
+ });
2288
+ }
2289
+ };
2320
2290
  try {
2321
- const userMessage = buildToolPrompt(structuringPrompt);
2322
- let lastParseError;
2323
- for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
2324
- capturedRaw = void 0;
2325
- const run = await agent.send(userMessage);
2326
- const result = await run.wait();
2327
- lastUsage = extractUsage2(result);
2328
- if (capturedRaw === void 0) {
2329
- if (result.status === "error" && result.error !== void 0) {
2330
- throw new GenerateObjectError(
2331
- "no_tool_call",
2332
- `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
2333
- result.error
2334
- );
2335
- }
2336
- if (attempt === maxRetries) {
2337
- throw new GenerateObjectError(
2338
- "no_tool_call",
2339
- "The model returned text instead of calling the `output` tool."
2340
- );
2341
- }
2342
- continue;
2343
- }
2344
- const parsed = options.schema.safeParse(capturedRaw);
2345
- if (parsed.success) {
2346
- return {
2347
- object: parsed.data,
2348
- raw: capturedRaw,
2349
- usage: lastUsage,
2350
- finishReason: "tool_use"
2351
- };
2352
- }
2353
- lastParseError = parsed.error;
2291
+ const result = await reentryAls.run(true, () => Promise.resolve().then(() => work(ctx)));
2292
+ if (aborter.signal.aborted) {
2293
+ const cancelledAt = Date.now();
2294
+ await transition(taskId, "cancelled", { cancelledAt });
2295
+ emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2296
+ return;
2354
2297
  }
2355
- if (options.errorStrategy === "return-raw") {
2356
- return {
2357
- object: capturedRaw,
2358
- raw: capturedRaw,
2359
- usage: lastUsage,
2360
- finishReason: "tool_use"
2361
- };
2298
+ const finishedAt = Date.now();
2299
+ await transition(taskId, "finished", { finishedAt, result });
2300
+ emitToSubscribers(taskId, { type: "finished", taskId, finishedAt, result });
2301
+ } catch (err) {
2302
+ if (aborter.signal.aborted) {
2303
+ const cancelledAt = Date.now();
2304
+ await transition(taskId, "cancelled", { cancelledAt });
2305
+ emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2306
+ return;
2362
2307
  }
2363
- if (options.errorStrategy === "return-partial") {
2364
- return {
2365
- object: salvagePartial(options.schema, capturedRaw),
2366
- raw: capturedRaw,
2367
- usage: lastUsage,
2368
- finishReason: "tool_use"
2369
- };
2308
+ const erroredAt = Date.now();
2309
+ const error = err instanceof Error ? { code: err.code ?? "work_threw", message: err.message } : { code: "work_threw", message: String(err) };
2310
+ await transition(taskId, "error", { erroredAt, error });
2311
+ emitToSubscribers(taskId, { type: "errored", taskId, erroredAt, error });
2312
+ }
2313
+ }
2314
+ async function checkCancelRequestedAt(taskId, aborter) {
2315
+ if (aborter.signal.aborted) return;
2316
+ try {
2317
+ const handle = await state.store.get(taskId);
2318
+ if (handle?.cancelRequested === true) {
2319
+ aborter.abort("cancelRequested");
2370
2320
  }
2371
- throw new GenerateObjectError(
2372
- "parse_failed",
2373
- "Schema parse failed after all retries.",
2374
- lastParseError
2375
- );
2376
- } finally {
2377
- await disposeAndDeleteTransient(agent, deps.delete);
2321
+ } catch {
2378
2322
  }
2379
2323
  }
2380
- var GenerateObjectError;
2381
- var init_generate_object = __esm({
2382
- "src/generate-object.ts"() {
2383
- init_structured_output_helpers();
2384
- GenerateObjectError = class extends Error {
2385
- name = "GenerateObjectError";
2386
- code;
2387
- cause;
2388
- constructor(code, message, cause) {
2389
- super(message);
2390
- this.code = code;
2391
- if (cause !== void 0) this.cause = cause;
2392
- }
2324
+ async function acquireSlot() {
2325
+ if (reentryAls.getStore() === true) {
2326
+ return () => {
2393
2327
  };
2394
2328
  }
2329
+ return state.semaphore.acquire();
2330
+ }
2331
+ async function submit(internal) {
2332
+ state.firstSubmitSeen = true;
2333
+ startEvictTimerIfNeeded();
2334
+ const resolvedId = resolveIdInternal(internal);
2335
+ const submitSpan = startTaskSubmitSpan({ taskId: resolvedId, kind: internal.kind });
2336
+ const existing = await state.store.get(resolvedId);
2337
+ if (existing !== void 0) {
2338
+ submitSpan.end();
2339
+ return existing;
2340
+ }
2341
+ const queuedHandle = await buildAndInsertQueued(internal, resolvedId);
2342
+ submitSpan.end();
2343
+ if (internal.signal?.aborted === true) {
2344
+ return shortCircuitAborted(queuedHandle, internal.signal);
2345
+ }
2346
+ const aborter = new AbortController();
2347
+ state.aborters.set(resolvedId, aborter);
2348
+ if (internal.signal !== void 0) {
2349
+ internal.signal.addEventListener("abort", () => aborter.abort(internal.signal?.reason), {
2350
+ once: true
2351
+ });
2352
+ }
2353
+ const release = await acquireSlot();
2354
+ void (async () => {
2355
+ try {
2356
+ await checkCancelRequestedAt(resolvedId, aborter);
2357
+ if (aborter.signal.aborted) {
2358
+ const cancelledAt = Date.now();
2359
+ await transition(resolvedId, "cancelled", { cancelledAt });
2360
+ emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
2361
+ return;
2362
+ }
2363
+ const startedAt = Date.now();
2364
+ await transition(resolvedId, "running", { startedAt });
2365
+ emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
2366
+ await runWorkAndFinalize(resolvedId, aborter, internal.work);
2367
+ } finally {
2368
+ release();
2369
+ state.aborters.delete(resolvedId);
2370
+ }
2371
+ })();
2372
+ return queuedHandle;
2373
+ }
2374
+ async function get(id) {
2375
+ if (!isValidTaskId(id, true)) return void 0;
2376
+ return state.store.get(id);
2377
+ }
2378
+ var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state;
2379
+ var init_registry = __esm({
2380
+ "src/internal/task/registry.ts"() {
2381
+ init_errors();
2382
+ init_task();
2383
+ init_async_semaphore();
2384
+ init_ring_buffer();
2385
+ init_store();
2386
+ init_subscribe();
2387
+ init_telemetry();
2388
+ DEFAULT_CONCURRENCY = 8;
2389
+ DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
2390
+ EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
2391
+ RING_CAP = 64;
2392
+ reentryAls = new AsyncLocalStorage();
2393
+ state = buildState({});
2394
+ }
2395
2395
  });
2396
2396
 
2397
2397
  // src/stream-object.ts
@@ -2448,7 +2448,7 @@ async function* streamObjectImpl(options, deps) {
2448
2448
  }
2449
2449
  }
2450
2450
  const result = await run.wait();
2451
- lastUsage = extractUsage2(result);
2451
+ lastUsage = extractUsage(result);
2452
2452
  if (capturedRaw === void 0) {
2453
2453
  if (result.status === "error" && result.error !== void 0) {
2454
2454
  throw new StreamObjectError(
@@ -2794,6 +2794,7 @@ var AgentBuilder = class {
2794
2794
  constructor(deps) {
2795
2795
  this.deps = deps;
2796
2796
  }
2797
+ /** SE8 — accepts a bare-string id shorthand (`"openai/gpt-4o-mini"`) or a {@link ModelSelection}. */
2797
2798
  model(m) {
2798
2799
  this.opts.model = m;
2799
2800
  return this;
@@ -3143,6 +3144,69 @@ function isLocalAgentId(id) {
3143
3144
  return id.startsWith(PREFIX.localAgent);
3144
3145
  }
3145
3146
 
3147
+ // src/internal/runtime/config/default-model.ts
3148
+ var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
3149
+
3150
+ // src/agent-generate.ts
3151
+ async function agentGenerate(agent, agentModel, agentApiKey, agentLocal, input, options) {
3152
+ const { output, maxRetries, errorStrategy, ...sendOptions } = options;
3153
+ const run = await agent.send(input, sendOptions);
3154
+ const result = await run.wait();
3155
+ const { generateObjectImpl: generateObjectImpl2, GenerateObjectError: GenerateObjectError2 } = await Promise.resolve().then(() => (init_generate_object(), generate_object_exports));
3156
+ const answer = structurableAnswerOrThrow(result, GenerateObjectError2);
3157
+ const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
3158
+ const facade = getAgentFacade2();
3159
+ const structured = await generateObjectImpl2(
3160
+ {
3161
+ prompt: answer,
3162
+ schema: output,
3163
+ // `agentModel` arrives already normalized (`this.model` passed through
3164
+ // `normalizeModel` in the LocalAgent/CloudAgent constructor), so the widened
3165
+ // `string | ModelSelection` field only ever receives a `ModelSelection` here.
3166
+ model: agentModel ?? { id: DEFAULT_AGENTIC_MODEL_ID },
3167
+ // `local` is required to keep the transient structuring agent local-only;
3168
+ // default to the process cwd when the agent had no explicit local config.
3169
+ local: agentLocal ?? { cwd: process.cwd() },
3170
+ ...agentApiKey !== void 0 ? { apiKey: agentApiKey } : {},
3171
+ ...maxRetries !== void 0 ? { maxRetries } : {},
3172
+ ...errorStrategy !== void 0 ? { errorStrategy } : {}
3173
+ },
3174
+ {
3175
+ create: (opts) => facade.create(opts),
3176
+ delete: (agentId) => facade.delete(agentId)
3177
+ }
3178
+ );
3179
+ return {
3180
+ object: structured.object,
3181
+ result,
3182
+ raw: structured.raw,
3183
+ usage: structured.usage
3184
+ };
3185
+ }
3186
+ function structurableAnswerOrThrow(result, GenerateObjectError2) {
3187
+ if (result.status === "error") {
3188
+ throw new GenerateObjectError2(
3189
+ "no_tool_call",
3190
+ `Agent run failed before an answer could be structured: ${result.error?.message ?? "unknown error"} [${result.error?.code ?? "?"}]`,
3191
+ result.error
3192
+ );
3193
+ }
3194
+ if (result.status === "cancelled") {
3195
+ throw new GenerateObjectError2(
3196
+ "no_tool_call",
3197
+ "Agent run was cancelled before an answer could be structured."
3198
+ );
3199
+ }
3200
+ const answer = result.result;
3201
+ if (answer === void 0 || answer.length === 0) {
3202
+ throw new GenerateObjectError2(
3203
+ "no_tool_call",
3204
+ "The tool loop finished with no text answer (tool-only completion) \u2014 there is nothing to structure."
3205
+ );
3206
+ }
3207
+ return answer;
3208
+ }
3209
+
3146
3210
  // src/internal/runtime/cloud/cloud-agent.ts
3147
3211
  init_errors();
3148
3212
  init_cwd_mutex();
@@ -3293,8 +3357,19 @@ function safeFilenameForId(id, options) {
3293
3357
  return `h-${createHash("sha256").update(id).digest("hex").slice(0, 16)}`;
3294
3358
  }
3295
3359
 
3296
- // src/internal/runtime/config/default-model.ts
3297
- var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
3360
+ // src/internal/runtime/model-selection.ts
3361
+ init_errors();
3362
+ function normalizeModel(m) {
3363
+ if (m === void 0 || typeof m !== "string") return m;
3364
+ const id = m.trim();
3365
+ if (id.length === 0) {
3366
+ throw new ConfigurationError(
3367
+ 'model must be a non-empty "provider/model" id string or a ModelSelection object',
3368
+ { code: "invalid_model_selection" }
3369
+ );
3370
+ }
3371
+ return { id };
3372
+ }
3298
3373
 
3299
3374
  // src/internal/runtime/registry/agent-registry-store.ts
3300
3375
  init_cwd_mutex();
@@ -3371,7 +3446,10 @@ var REGISTRY_RELATIVE_PATH = join(".theokit", "agents", "registry.json");
3371
3446
  function stripSecretsFromOptions(options) {
3372
3447
  return assignDefined({
3373
3448
  name: options.name,
3374
- model: options.model !== void 0 ? { id: options.model.id } : void 0,
3449
+ model: (() => {
3450
+ const m = normalizeModel(options.model);
3451
+ return m !== void 0 ? { id: m.id } : void 0;
3452
+ })(),
3375
3453
  systemPrompt: typeof options.systemPrompt === "string" ? options.systemPrompt : void 0,
3376
3454
  local: serializeLocal(options.local),
3377
3455
  cloud: serializeCloud(options.cloud),
@@ -3656,7 +3734,8 @@ function serializeCloudAgentConfig(options) {
3656
3734
  cloud: serializeCloud2(options.cloud)
3657
3735
  };
3658
3736
  if (typeof options.agentId === "string") payload.agentId = options.agentId;
3659
- if (options.model !== void 0) payload.model = { id: options.model.id };
3737
+ const cfgModel = normalizeModel(options.model);
3738
+ if (cfgModel !== void 0) payload.model = { id: cfgModel.id };
3660
3739
  if (typeof options.systemPrompt === "string") payload.systemPrompt = options.systemPrompt;
3661
3740
  const skills = serializeSkills(options.skills);
3662
3741
  if (skills !== void 0) payload.skills = skills;
@@ -4552,6 +4631,7 @@ function applyScriptMetrics(base, script) {
4552
4631
  if (script.cost !== void 0) base.cost = script.cost;
4553
4632
  if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
4554
4633
  if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
4634
+ if (script.origin !== void 0) base.origin = script.origin;
4555
4635
  }
4556
4636
 
4557
4637
  // src/internal/runtime/cloud/cloud-run.ts
@@ -4907,7 +4987,7 @@ var CloudAgent = class {
4907
4987
  disposed = false;
4908
4988
  constructor(options, providedAgentId) {
4909
4989
  this.agentId = providedAgentId ?? options.agentId ?? generateCloudAgentId();
4910
- this.model = options.model;
4990
+ this.model = normalizeModel(options.model);
4911
4991
  this.options = options;
4912
4992
  this.cloudPayload = serializeCloudAgentConfig({ ...options, agentId: this.agentId });
4913
4993
  const repoUrls = (options.cloud?.repos ?? []).map((repo) => repo.url);
@@ -4937,6 +5017,17 @@ var CloudAgent = class {
4937
5017
  const apiKey = resolveApiKey(this.options.apiKey);
4938
5018
  return isFixtureApiKey(apiKey) && getConfiguredBaseUrl() === void 0;
4939
5019
  }
5020
+ // SE9 — integrated structured output; delegates to the shared helper.
5021
+ generate(message, options) {
5022
+ return agentGenerate(
5023
+ this,
5024
+ this.model,
5025
+ this.options.apiKey,
5026
+ this.options.local,
5027
+ message,
5028
+ options
5029
+ );
5030
+ }
4940
5031
  async send(message, options = {}) {
4941
5032
  if (options.tools !== void 0 && options.tools.length > 0) {
4942
5033
  throw new ConfigurationError(
@@ -4967,7 +5058,7 @@ var CloudAgent = class {
4967
5058
  });
4968
5059
  }
4969
5060
  async sendLocked(message, options) {
4970
- const overrideModel = options.model;
5061
+ const overrideModel = normalizeModel(options.model);
4971
5062
  if (overrideModel !== void 0) {
4972
5063
  this.model = overrideModel;
4973
5064
  updateRegisteredAgent(this.agentId, { model: overrideModel });
@@ -5881,18 +5972,18 @@ var ALL_ADAPTERS = [
5881
5972
  arizeAdapter,
5882
5973
  braintrustAdapter
5883
5974
  ];
5884
- var registered = /* @__PURE__ */ new Set();
5975
+ var registered2 = /* @__PURE__ */ new Set();
5885
5976
  function tryAutoRegisterAdapters(settings) {
5886
5977
  if (settings?.enabled !== true) return;
5887
5978
  if (settings.autoDetect === false) return;
5888
5979
  const disabled = new Set(settings.disable ?? []);
5889
5980
  for (const adapter of ALL_ADAPTERS) {
5890
- if (registered.has(adapter.moduleName)) continue;
5981
+ if (registered2.has(adapter.moduleName)) continue;
5891
5982
  if (disabled.has(adapter.displayName.toLowerCase())) continue;
5892
5983
  if (!adapter.detect()) continue;
5893
5984
  try {
5894
5985
  adapter.register();
5895
- registered.add(adapter.moduleName);
5986
+ registered2.add(adapter.moduleName);
5896
5987
  process.stderr.write(`[theokit-sdk] telemetry: ${adapter.displayName} auto-instrumented.
5897
5988
  `);
5898
5989
  } catch (cause) {
@@ -6455,6 +6546,26 @@ function paginate(items, opts) {
6455
6546
  return items.slice(start, end);
6456
6547
  }
6457
6548
 
6549
+ // src/internal/persistence/session-meta.ts
6550
+ function applyMetaPatch(current, patch) {
6551
+ const next = {};
6552
+ if (current.title !== void 0) next.title = current.title;
6553
+ if (current.tag !== void 0) next.tag = current.tag;
6554
+ if (patch.title === null) delete next.title;
6555
+ else if (patch.title !== void 0) next.title = patch.title;
6556
+ if (patch.tag === null) delete next.tag;
6557
+ else if (patch.tag !== void 0) next.tag = patch.tag;
6558
+ return next;
6559
+ }
6560
+ function coerceSessionMeta(raw) {
6561
+ if (typeof raw !== "object" || raw === null) return void 0;
6562
+ const obj = raw;
6563
+ const meta = {};
6564
+ if (typeof obj.title === "string") meta.title = obj.title;
6565
+ if (typeof obj.tag === "string") meta.tag = obj.tag;
6566
+ return meta.title === void 0 && meta.tag === void 0 ? void 0 : meta;
6567
+ }
6568
+
6458
6569
  // src/internal/persistence/conversation-storage-fs.ts
6459
6570
  var FileSystemConversationStorage = class {
6460
6571
  #root;
@@ -6505,6 +6616,37 @@ var FileSystemConversationStorage = class {
6505
6616
  async compact(conversationId, maxTurns) {
6506
6617
  await compactSessionFile(this.#root, conversationId, maxTurns);
6507
6618
  }
6619
+ // SE4 — session metadata persisted as a per-conversation sidecar
6620
+ // `<root>/.theokit/agents/<safeId>/session.json` (same sanitized perimeter as
6621
+ // the transcript). Kept separate from messages.jsonl so a title/tag write does
6622
+ // not rewrite the append-only log.
6623
+ #metaPath(conversationId) {
6624
+ const safe3 = sanitizeIdentifier(conversationId, { maxLen: 128 });
6625
+ return safePathJoin(this.#root, ".theokit", "agents", safe3, "session.json");
6626
+ }
6627
+ async getSessionMeta(conversationId) {
6628
+ try {
6629
+ const raw = await readFile(this.#metaPath(conversationId), "utf8");
6630
+ return coerceSessionMeta(JSON.parse(raw));
6631
+ } catch (cause) {
6632
+ if (cause.code === "ENOENT") return void 0;
6633
+ throw cause;
6634
+ }
6635
+ }
6636
+ async setSessionMeta(conversationId, patch) {
6637
+ const metaPath = this.#metaPath(conversationId);
6638
+ await mkdir(dirname(metaPath), { recursive: true });
6639
+ await withFileLock(metaPath, async () => {
6640
+ let current = {};
6641
+ try {
6642
+ current = coerceSessionMeta(JSON.parse(await readFile(metaPath, "utf8"))) ?? {};
6643
+ } catch (cause) {
6644
+ if (cause.code !== "ENOENT") throw cause;
6645
+ }
6646
+ const next = applyMetaPatch(current, patch);
6647
+ await writeFile(metaPath, redactSecrets(JSON.stringify(next)), "utf8");
6648
+ });
6649
+ }
6508
6650
  async dispose() {
6509
6651
  }
6510
6652
  };
@@ -8424,7 +8566,7 @@ function bootstrapSubmanagers(args) {
8424
8566
  const providerCount = (args.options.providers?.routes?.length ?? 0) + enabledPluginNames(args.options.plugins).length;
8425
8567
  if (providerCount > 0 || args.options.providers !== void 0) {
8426
8568
  out.providers = new ProvidersManagerImpl(
8427
- args.options.model,
8569
+ normalizeModel(args.options.model),
8428
8570
  args.options.providers,
8429
8571
  args.options.plugins
8430
8572
  );
@@ -8494,6 +8636,7 @@ function createLocalRun(options) {
8494
8636
  projectMcpServers: options.projectMcpServers,
8495
8637
  ...options.persistMemoryFact !== void 0 ? { persistMemoryFact: options.persistMemoryFact } : {}
8496
8638
  });
8639
+ if (options.sendOptions.origin !== void 0) script.origin = options.sendOptions.origin;
8497
8640
  const handle = new LocalRun({
8498
8641
  id,
8499
8642
  agentId: options.agentId,
@@ -9142,6 +9285,15 @@ async function emitReasoningDeltaCallback(inputs, text) {
9142
9285
  );
9143
9286
  }
9144
9287
 
9288
+ // src/types/run-events.ts
9289
+ function emitRunEvent(sink, event) {
9290
+ if (sink === void 0) return;
9291
+ try {
9292
+ sink(event);
9293
+ } catch {
9294
+ }
9295
+ }
9296
+
9145
9297
  // src/internal/agent-loop/tool-dispatch.ts
9146
9298
  init_async_local_storage();
9147
9299
 
@@ -9239,6 +9391,22 @@ function coerceArgsToSchema(args, schema) {
9239
9391
  return { value: out, changed };
9240
9392
  }
9241
9393
 
9394
+ // src/tool-error.ts
9395
+ init_errors();
9396
+ var ToolError = class extends TheokitAgentError {
9397
+ name = "ToolError";
9398
+ /** The error content surfaced to the model: a string, or text/image blocks. */
9399
+ content;
9400
+ constructor(content, options = {}) {
9401
+ super(renderToolErrorMessage(content), { ...options, isRetryable: false });
9402
+ this.content = content;
9403
+ }
9404
+ };
9405
+ function renderToolErrorMessage(content) {
9406
+ if (typeof content === "string") return content;
9407
+ return content.map((block) => block.type === "text" ? block.text : `[${block.source.media_type} image]`).join("\n");
9408
+ }
9409
+
9242
9410
  // src/internal/runtime/tools/shell-tool.ts
9243
9411
  async function runShell(options) {
9244
9412
  if (options.sandbox === true && isObviouslyUnsafe(options.command)) {
@@ -9292,9 +9460,13 @@ async function runHandlerTool(kind, handler, call, signal, context) {
9292
9460
  return { stdout: "", stderr: `${kind} tool ${call.name} has no handler`, exitCode: 127 };
9293
9461
  }
9294
9462
  try {
9295
- const stdout = await handler(call.input, { signal, context });
9296
- return { stdout, stderr: "", exitCode: 0 };
9463
+ const out = await handler(call.input, { signal, context });
9464
+ if (typeof out !== "string") return { stdout: "", stderr: "", exitCode: 0, content: out };
9465
+ return { stdout: out, stderr: "", exitCode: 0 };
9297
9466
  } catch (cause) {
9467
+ if (cause instanceof ToolError) {
9468
+ return { stdout: "", stderr: cause.message, exitCode: 1, content: cause.content };
9469
+ }
9298
9470
  const message = cause instanceof Error ? cause.message : String(cause);
9299
9471
  return { stdout: "", stderr: message, exitCode: 1 };
9300
9472
  }
@@ -9413,6 +9585,11 @@ async function dispatchSingleCall(inputs, tools, call, events, parentSpan) {
9413
9585
  toolSpan?.end();
9414
9586
  return fileVeto;
9415
9587
  }
9588
+ emitRunEvent(inputs.runEventSink, {
9589
+ type: "tool_progress",
9590
+ toolName: workingCall.name,
9591
+ toolCallId: callId
9592
+ });
9416
9593
  const result = await runToolWithLifecycle(inputs, resolved, workingCall, callId);
9417
9594
  await inputs.pluginManager?.runPostToolCallHooks({
9418
9595
  name: workingCall.name,
@@ -9439,6 +9616,13 @@ function applyRepairAndExtractCall(tools, call) {
9439
9616
  function vetoFromForkWhitelist(inputs, call, callId, events) {
9440
9617
  const whitelistDecision = checkToolWhitelist(call.name);
9441
9618
  if (whitelistDecision.allowed) return void 0;
9619
+ emitRunEvent(inputs.runEventSink, {
9620
+ type: "permission_denied",
9621
+ toolName: call.name,
9622
+ toolCallId: callId,
9623
+ source: "fork_whitelist",
9624
+ message: whitelistDecision.reason ?? "tool not available in fork"
9625
+ });
9442
9626
  events.push(buildToolUseRunning(inputs, callId, call));
9443
9627
  events.push(
9444
9628
  buildToolUseCompleted(inputs, callId, call, {
@@ -9475,6 +9659,13 @@ async function vetoFromPluginPreHook(inputs, call, callId, events) {
9475
9659
  runId: inputs.runId
9476
9660
  });
9477
9661
  if (pluginVeto === void 0) return void 0;
9662
+ emitRunEvent(inputs.runEventSink, {
9663
+ type: "permission_denied",
9664
+ toolName: call.name,
9665
+ toolCallId: callId,
9666
+ source: "plugin",
9667
+ message: pluginVeto.message
9668
+ });
9478
9669
  events.push(
9479
9670
  buildToolUseCompleted(inputs, callId, call, {
9480
9671
  stdout: "",
@@ -9497,6 +9688,13 @@ async function vetoFromFileHookPreDecision(inputs, call, callId, events) {
9497
9688
  runId: inputs.runId
9498
9689
  });
9499
9690
  if (!preDecision.blocked) return void 0;
9691
+ emitRunEvent(inputs.runEventSink, {
9692
+ type: "permission_denied",
9693
+ toolName: call.name,
9694
+ toolCallId: callId,
9695
+ source: "file_hook",
9696
+ message: preDecision.reason ?? "blocked by hook"
9697
+ });
9500
9698
  events.push(
9501
9699
  buildToolUseCompleted(inputs, callId, call, {
9502
9700
  stdout: "",
@@ -9540,7 +9738,9 @@ async function runToolWithLifecycle(inputs, resolved, call, callId) {
9540
9738
  await safeEmitToolHook(inputs.onToolEnd, {
9541
9739
  toolName: call.name,
9542
9740
  args: call.input,
9543
- result: result.stdout,
9741
+ // SE7 — a block-returning handler has empty `stdout`; surface its structured
9742
+ // content to the hook instead of an empty string.
9743
+ result: result.content !== void 0 ? result.content : result.stdout,
9544
9744
  conversationId: inputs.agentId,
9545
9745
  callId,
9546
9746
  durationMs
@@ -9562,7 +9762,10 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
9562
9762
  output: {
9563
9763
  stdout: result.stdout,
9564
9764
  stderr: result.stderr,
9565
- exitCode: result.exitCode ?? 0
9765
+ exitCode: result.exitCode ?? 0,
9766
+ // SE7 — structured content (text + image blocks) when the handler
9767
+ // returned/threw blocks; absent for the string path.
9768
+ ...result.content !== void 0 ? { content: result.content } : {}
9566
9769
  },
9567
9770
  agentId: inputs.agentId,
9568
9771
  runId: inputs.runId
@@ -9575,7 +9778,9 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
9575
9778
  return {
9576
9779
  type: "tool_result",
9577
9780
  toolUseId: call.id,
9578
- content: renderToolResult(result),
9781
+ // SE7 — structured content blocks (handler-returned or ToolError-carried)
9782
+ // are authoritative; otherwise the legacy string rendering.
9783
+ content: result.content !== void 0 ? result.content : renderToolResult(result),
9579
9784
  ...result.exitCode !== 0 && result.exitCode !== void 0 ? { isError: true } : {}
9580
9785
  };
9581
9786
  }
@@ -9624,6 +9829,29 @@ function buildToolUseCompleted(inputs, callId, call, result) {
9624
9829
  };
9625
9830
  }
9626
9831
 
9832
+ // src/internal/llm/tool-result-content.ts
9833
+ init_errors();
9834
+ function toBlockToolResultContent(content) {
9835
+ return content;
9836
+ }
9837
+ function toStringToolResultContent(content, providerName) {
9838
+ if (typeof content === "string") return content;
9839
+ if (content.some((block) => block.type === "image")) {
9840
+ throw new ConfigurationError(
9841
+ `provider "${providerName}" does not support image content in tool results`
9842
+ );
9843
+ }
9844
+ return content.filter(
9845
+ (block) => block.type === "text"
9846
+ ).map((block) => block.text).join("\n");
9847
+ }
9848
+ function renderToolResultContentText(content, fn) {
9849
+ if (typeof content === "string") return fn(content);
9850
+ return content.map(
9851
+ (block) => block.type === "text" ? { type: "text", text: fn(block.text) } : block
9852
+ );
9853
+ }
9854
+
9627
9855
  // src/internal/agent-loop/tool-result-guard.ts
9628
9856
  var OPEN = "<untrusted-tool-output>";
9629
9857
  var CLOSE = "</untrusted-tool-output>";
@@ -9649,7 +9877,11 @@ ${CLOSE}`;
9649
9877
  function applyToolResultGuard(parts, opts) {
9650
9878
  if (opts.delimit !== true && opts.redactPii !== true) return parts;
9651
9879
  return parts.map(
9652
- (p) => p.type === "tool_result" ? { ...p, content: guardText(p.content, opts) } : p
9880
+ (p) => (
9881
+ // SE7 — guard the TEXT of a tool result (string or the text blocks of a
9882
+ // structured content); image blocks pass through untouched.
9883
+ p.type === "tool_result" ? { ...p, content: renderToolResultContentText(p.content, (t) => guardText(t, opts)) } : p
9884
+ )
9653
9885
  );
9654
9886
  }
9655
9887
 
@@ -10566,10 +10798,10 @@ function stripVertexPrefix(modelId) {
10566
10798
  }
10567
10799
 
10568
10800
  // src/internal/providers/builtin/index.ts
10569
- var registered2 = false;
10801
+ var registered3 = false;
10570
10802
  function registerBuiltins() {
10571
- if (registered2) return;
10572
- registered2 = true;
10803
+ if (registered3) return;
10804
+ registered3 = true;
10573
10805
  registerProvider(ANTHROPIC);
10574
10806
  registerProvider(OPENAI);
10575
10807
  registerProvider(OPENROUTER);
@@ -10797,7 +11029,9 @@ function toAnthropicWireMessage(message) {
10797
11029
  return {
10798
11030
  type: "tool_result",
10799
11031
  tool_use_id: part.toolUseId,
10800
- content: part.content,
11032
+ // SE7 — this wire is block-capable: strings pass through, structured
11033
+ // text/image blocks are forwarded natively.
11034
+ content: toBlockToolResultContent(part.content),
10801
11035
  ...part.isError === true ? { is_error: true } : {}
10802
11036
  };
10803
11037
  });
@@ -11589,7 +11823,8 @@ function toOllamaMessages(message) {
11589
11823
  if (part.type === "tool_result") {
11590
11824
  out.push({
11591
11825
  role: "tool",
11592
- content: part.content,
11826
+ // SE7 — string-only tool role: text blocks flatten; image fails fast.
11827
+ content: toStringToolResultContent(part.content, "ollama"),
11593
11828
  tool_name: part.toolUseId
11594
11829
  });
11595
11830
  }
@@ -12126,7 +12361,9 @@ function userOrToolMessages(message) {
12126
12361
  out.push({
12127
12362
  role: "tool",
12128
12363
  tool_call_id: part.toolUseId,
12129
- content: part.content
12364
+ // SE7 — this wire's tool role is string-only: text blocks flatten; an
12365
+ // image block fails fast (ConfigurationError).
12366
+ content: toStringToolResultContent(part.content, "openai")
12130
12367
  });
12131
12368
  }
12132
12369
  }
@@ -13055,18 +13292,20 @@ function levenshtein(a, b) {
13055
13292
  function createRealLocalRun(options) {
13056
13293
  const { userText, id, startTime } = prepareRunContext(options.message);
13057
13294
  const supported = /* @__PURE__ */ new Set(["stream", "wait", "cancel", "conversation"]);
13058
- const unusedFixtureScript = {
13295
+ const runScript = {
13059
13296
  events: [],
13060
13297
  finalStatus: "running",
13061
13298
  cancellable: false,
13062
- conversation: []
13299
+ conversation: [],
13300
+ // SE3 — carry the turn's provenance so buildResult forwards it onto RunResult.origin.
13301
+ ...options.sendOptions.origin !== void 0 ? { origin: options.sendOptions.origin } : {}
13063
13302
  };
13064
13303
  const handle = new RealLocalRun(
13065
13304
  {
13066
13305
  id,
13067
13306
  agentId: options.agentId,
13068
13307
  model: options.model,
13069
- script: unusedFixtureScript,
13308
+ script: runScript,
13070
13309
  supportedOps: supported,
13071
13310
  startTime
13072
13311
  },
@@ -13178,6 +13417,8 @@ function buildLoopInputs(options, runId, userText) {
13178
13417
  // M7 — forward SendOptions.context to the loop so every tool handler receives
13179
13418
  // it on `ctx.context` (shared run config set once, e.g. projectRoot).
13180
13419
  ...options.sendOptions.context !== void 0 ? { context: options.sendOptions.context } : {},
13420
+ // SE2 — forward the opt-in typed runtime-event sink to the loop.
13421
+ ...options.sendOptions.onRunEvent !== void 0 ? { runEventSink: options.sendOptions.onRunEvent } : {},
13181
13422
  // #58 / #57 — forward the per-tool timeout + tool-result guard so a consumer
13182
13423
  // can enable them via SendOptions (not only internal AgentLoopInputs).
13183
13424
  ...options.sendOptions.perToolTimeoutMs !== void 0 ? { perToolTimeoutMs: options.sendOptions.perToolTimeoutMs } : {},
@@ -15870,7 +16111,7 @@ function wrapRunWithPostReplyHook(args) {
15870
16111
  async function executeSendLocked(inputs, message, options) {
15871
16112
  if (inputs.disposed) throw new AgentDisposedError(inputs.agentId);
15872
16113
  await consumePending(inputs.agentId, inputs.invalidationPending, inputs.clearInvalidation, inputs.reload);
15873
- inputs.applyModelOverride(options.model);
16114
+ inputs.applyModelOverride(normalizeModel(options.model));
15874
16115
  const userText = typeof message === "string" ? message : message.text;
15875
16116
  if (inputs.options.onBeforeSend !== void 0) {
15876
16117
  await inputs.options.onBeforeSend({
@@ -16022,7 +16263,7 @@ var LocalAgent = class {
16022
16263
  constructor(options) {
16023
16264
  this.agentId = options.agentId ?? generateLocalAgentId();
16024
16265
  this._telemetry = createTelemetry(options.telemetry);
16025
- this.model = options.model;
16266
+ this.model = normalizeModel(options.model);
16026
16267
  this.options = options;
16027
16268
  this.workspaceCwd = resolveCwd(options.local?.cwd);
16028
16269
  this.conversationStorage = options.conversationStorage;
@@ -16089,6 +16330,11 @@ var LocalAgent = class {
16089
16330
  hooks() {
16090
16331
  return this.hooksExecutor;
16091
16332
  }
16333
+ // SE9 — integrated structured output; delegates to the shared helper.
16334
+ generate(message, options) {
16335
+ const { apiKey, local } = this.options;
16336
+ return agentGenerate(this, this.model, apiKey, local, message, options);
16337
+ }
16092
16338
  async send(message, options = {}) {
16093
16339
  if (options.tools !== void 0 && options.tools.length > 0) {
16094
16340
  validateToolCatalog(options.tools);
@@ -16328,6 +16574,7 @@ function includesSetting(options, source) {
16328
16574
 
16329
16575
  // src/agent-helpers.ts
16330
16576
  async function runCreateUnderSpan(options, span) {
16577
+ options = { ...options, model: normalizeModel(options.model) };
16331
16578
  validateAgentOptions(options);
16332
16579
  validateCloudToolParity(options);
16333
16580
  await guardAgainstIdCollision(options);
@@ -16401,7 +16648,10 @@ async function createLocalAgent(options) {
16401
16648
  const willFlowToProvider = !isFixtureApiKey(apiKey) && !shouldUseRealLocalRuntime(apiKey);
16402
16649
  const shape = validateApiKeyShape(apiKey, {
16403
16650
  strict: willFlowToProvider,
16404
- ...willFlowToProvider ? { provider: providerFromModelId(options.model?.id) } : {}
16651
+ // `options.model` is already normalized by `runCreateUnderSpan`; `normalizeModel`
16652
+ // here is the idempotent narrowing of the widened public type (`{id}` passes
16653
+ // through by reference) — cleaner than an `as` cast.
16654
+ ...willFlowToProvider ? { provider: providerFromModelId(normalizeModel(options.model)?.id) } : {}
16405
16655
  });
16406
16656
  if (shape.malformed) {
16407
16657
  throw new AuthenticationError(shape.message, { code: "malformed_api_key" });
@@ -16467,6 +16717,10 @@ async function rehydrateExistingAgent(agentId, existing, options) {
16467
16717
  ...existing.options,
16468
16718
  ...options,
16469
16719
  ...mergedLocal !== void 0 ? { local: mergedLocal } : {},
16720
+ // SE8 — normalize a caller-supplied bare-string model override at this resume
16721
+ // boundary (Agent.resume never flows through runCreateUnderSpan). Cloud resume
16722
+ // honors it; local resume forces the persisted model below.
16723
+ model: normalizeModel(options.model ?? existing.options.model),
16470
16724
  mcpServers: void 0,
16471
16725
  agentId
16472
16726
  };
@@ -16833,6 +17087,7 @@ async function getOrCreateUncached(agentId, options) {
16833
17087
  }
16834
17088
  setAgentFacade({
16835
17089
  create: (options) => Agent.create(options),
17090
+ delete: (agentId) => Agent.delete(agentId),
16836
17091
  prompt: (message, options) => Agent.prompt(message, options),
16837
17092
  get: (agentId) => Agent.get(agentId),
16838
17093
  resume: (agentId, options) => Agent.resume(agentId, options),