@theokit/sdk 2.19.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 (89) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/dist/a2a/index.cjs +611 -274
  3. package/dist/a2a/index.cjs.map +1 -1
  4. package/dist/a2a/index.js +612 -275
  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/create-skill.d.ts +29 -0
  19. package/dist/{cron-D_wK1S-0.d.cts → cron-YrmsszEN.d.cts} +110 -3
  20. package/dist/{cron-Cep07kTz.d.ts → cron-dpvtRoro.d.ts} +110 -3
  21. package/dist/cron.cjs +660 -370
  22. package/dist/cron.cjs.map +1 -1
  23. package/dist/cron.d.cts +3 -2
  24. package/dist/cron.d.ts +3 -2
  25. package/dist/cron.js +661 -371
  26. package/dist/cron.js.map +1 -1
  27. package/dist/{errors-5lj1EWgs.d.ts → errors-C4vZPqXf.d.ts} +2 -2
  28. package/dist/{errors-CE-lMBi2.d.cts → errors-DrcpYVfZ.d.cts} +2 -2
  29. package/dist/errors.d.cts +3 -2
  30. package/dist/eval.cjs +660 -370
  31. package/dist/eval.cjs.map +1 -1
  32. package/dist/eval.js +660 -370
  33. package/dist/eval.js.map +1 -1
  34. package/dist/generate-object.d.ts +19 -4
  35. package/dist/index.cjs +867 -371
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +274 -16
  38. package/dist/index.d.ts +274 -16
  39. package/dist/index.js +863 -373
  40. package/dist/index.js.map +1 -1
  41. package/dist/internal/agent-loop/loop-types.d.ts +2 -0
  42. package/dist/internal/agent-loop/tool-result-guard.d.ts +1 -0
  43. package/dist/internal/llm/tool-result-content.d.ts +40 -0
  44. package/dist/internal/llm/types.d.ts +6 -1
  45. package/dist/internal/persistence/conversation-storage-fs.d.cts +3 -1
  46. package/dist/internal/persistence/conversation-storage-fs.d.ts +3 -1
  47. package/dist/internal/persistence/conversation-storage-memory.d.cts +3 -1
  48. package/dist/internal/persistence/conversation-storage-memory.d.ts +3 -1
  49. package/dist/internal/persistence/session-meta.d.cts +13 -0
  50. package/dist/internal/persistence/session-meta.d.ts +13 -0
  51. package/dist/internal/runtime/model-selection.d.ts +14 -0
  52. package/dist/internal/structured-output-helpers.d.ts +1 -1
  53. package/dist/permission-engine.d.ts +32 -1
  54. package/dist/{run-BgfBWX-z.d.cts → run-BMo8yRwK.d.cts} +262 -19
  55. package/dist/{run-BgfBWX-z.d.ts → run-BMo8yRwK.d.ts} +262 -19
  56. package/dist/stream-object.d.ts +2 -1
  57. package/dist/tool-error.d.ts +30 -0
  58. package/dist/types/agent-prims.d.ts +10 -8
  59. package/dist/types/agent.d.ts +22 -1
  60. package/dist/types/content-blocks.d.ts +47 -0
  61. package/dist/types/conversation-storage.d.ts +38 -0
  62. package/dist/types/index.d.ts +1 -0
  63. package/dist/types/messages.d.ts +2 -9
  64. package/dist/types/run-events.d.ts +84 -0
  65. package/dist/types/run.d.ts +115 -1
  66. package/dist/types/session.d.ts +99 -0
  67. package/dist/types/workflow.d.ts +7 -0
  68. package/dist/workflow.cjs +7 -2
  69. package/dist/workflow.cjs.map +1 -1
  70. package/dist/workflow.d.cts +2 -0
  71. package/dist/workflow.d.ts +2 -0
  72. package/dist/workflow.js +7 -2
  73. package/dist/workflow.js.map +1 -1
  74. package/package.json +3 -23
  75. package/dist/internal/observability/context.d.cts +0 -23
  76. package/dist/internal/observability/context.d.ts +0 -23
  77. package/dist/internal/observability/index.cjs +0 -38
  78. package/dist/internal/observability/index.cjs.map +0 -1
  79. package/dist/internal/observability/index.d.cts +0 -8
  80. package/dist/internal/observability/index.d.ts +0 -8
  81. package/dist/internal/observability/index.js +0 -33
  82. package/dist/internal/observability/index.js.map +0 -1
  83. package/dist/internal/plugins/index.cjs +0 -363
  84. package/dist/internal/plugins/index.cjs.map +0 -1
  85. package/dist/internal/plugins/index.js +0 -357
  86. package/dist/internal/plugins/index.js.map +0 -1
  87. package/dist/internal/workflow/index.d.ts +0 -12
  88. package/dist/server/adapter/index.d.cts +0 -8
  89. package/dist/server/adapter/index.d.ts +0 -8
package/dist/cron.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
+ var zod = require('zod');
4
+ var module$1 = require('module');
3
5
  var crypto = require('crypto');
4
6
  var promises = require('fs/promises');
5
7
  var path = require('path');
6
- var zod = require('zod');
7
8
  var fs = require('fs');
8
9
  var async_hooks = require('async_hooks');
9
- var module$1 = require('module');
10
10
  var os = require('os');
11
11
  var child_process = require('child_process');
12
12
  var url = require('url');
@@ -513,6 +513,256 @@ var init_errors = __esm({
513
513
  };
514
514
  }
515
515
  });
516
+ function toJsonSchema(schema, options = { unrepresentable: "any" }) {
517
+ return zod.toJSONSchema(schema, options);
518
+ }
519
+ var init_to_json_schema = __esm({
520
+ "src/internal/zod/to-json-schema.ts"() {
521
+ }
522
+ });
523
+ function requireZod() {
524
+ if (cachedZ !== void 0) return cachedZ;
525
+ const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cron.cjs', document.baseURI).href)));
526
+ let mod;
527
+ try {
528
+ mod = r("zod");
529
+ } catch (cause) {
530
+ throw new ConfigurationError(
531
+ 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
532
+ { code: "zod_not_installed", cause }
533
+ );
534
+ }
535
+ cachedZ = mod.z ?? mod;
536
+ return cachedZ;
537
+ }
538
+ function makeOutputTool(jsonSchema, onCapture) {
539
+ return {
540
+ name: "output",
541
+ description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
542
+ inputSchema: jsonSchema,
543
+ handler: (input) => {
544
+ const out = onCapture(input);
545
+ if (typeof out === "string") return out;
546
+ if (out instanceof Promise) return out;
547
+ return "";
548
+ }
549
+ };
550
+ }
551
+ function buildTransientAgentOptions(params) {
552
+ return {
553
+ model: params.model,
554
+ local: params.local,
555
+ tools: [params.outputTool],
556
+ systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
557
+ ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
558
+ ...params.providers !== void 0 ? { providers: params.providers } : {}
559
+ };
560
+ }
561
+ function extractUsage(result) {
562
+ const u = result.usage;
563
+ if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
564
+ return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
565
+ }
566
+ function buildToolPrompt(prompt) {
567
+ return `${prompt}
568
+
569
+ Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
570
+ }
571
+ function setupStructuredOutput(schema, maxRetries) {
572
+ const z6 = requireZod();
573
+ const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
574
+ return {
575
+ z: z6,
576
+ jsonSchema,
577
+ maxRetries: maxRetries ?? 1,
578
+ initialUsage: { inputTokens: 0, outputTokens: 0 }
579
+ };
580
+ }
581
+ async function disposeAndDeleteTransient(agent, deletor) {
582
+ await agent.dispose();
583
+ try {
584
+ await deletor(agent.agentId);
585
+ } catch {
586
+ }
587
+ }
588
+ var cachedZ;
589
+ var init_structured_output_helpers = __esm({
590
+ "src/internal/structured-output-helpers.ts"() {
591
+ init_errors();
592
+ init_to_json_schema();
593
+ }
594
+ });
595
+
596
+ // src/generate-object.ts
597
+ var generate_object_exports = {};
598
+ __export(generate_object_exports, {
599
+ GenerateObjectError: () => GenerateObjectError,
600
+ generateObjectImpl: () => generateObjectImpl
601
+ });
602
+ function salvagePartial(schema, raw) {
603
+ if (typeof raw !== "object" || raw === null) return raw;
604
+ const shape = schema.shape;
605
+ if (shape === void 0 || typeof shape !== "object") return raw;
606
+ const rawObj = raw;
607
+ const out = {};
608
+ for (const [key, fieldSchema] of Object.entries(shape)) {
609
+ if (typeof fieldSchema?.safeParse !== "function") continue;
610
+ const parsed = fieldSchema.safeParse(rawObj[key]);
611
+ if (parsed.success) out[key] = parsed.data;
612
+ }
613
+ return out;
614
+ }
615
+ async function runReasoningPhase(options, deps) {
616
+ const reasoningOptions = {
617
+ model: options.model,
618
+ local: options.local,
619
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
620
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
621
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
622
+ };
623
+ const reasoningAgent = await deps.create(reasoningOptions);
624
+ try {
625
+ const run = await reasoningAgent.send(options.prompt);
626
+ const result = await run.wait();
627
+ const text = result.result;
628
+ return typeof text === "string" ? text : options.prompt;
629
+ } finally {
630
+ await disposeAndDeleteTransient(reasoningAgent, deps.delete);
631
+ }
632
+ }
633
+ async function generateObjectImpl(options, deps) {
634
+ const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
635
+ options.schema,
636
+ options.maxRetries
637
+ );
638
+ let capturedRaw;
639
+ let lastUsage = initialUsage;
640
+ const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
641
+ class CaptureSentinel extends Error {
642
+ constructor(captured) {
643
+ super("generate-object-capture");
644
+ this.captured = captured;
645
+ }
646
+ captured;
647
+ [sentinel] = true;
648
+ }
649
+ const outputTool = makeOutputTool(jsonSchema, (input) => {
650
+ if (capturedRaw === void 0) {
651
+ capturedRaw = input;
652
+ }
653
+ throw new CaptureSentinel(input);
654
+ });
655
+ const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
656
+ const structuringModel = options.structuringModel ?? options.model;
657
+ const structuringPrompt = reasoningText ?? options.prompt;
658
+ const agentOptions = buildTransientAgentOptions({
659
+ model: structuringModel,
660
+ local: options.local,
661
+ outputTool,
662
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
663
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
664
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
665
+ });
666
+ const agent = await deps.create(agentOptions);
667
+ try {
668
+ const userMessage = buildToolPrompt(structuringPrompt);
669
+ let lastParseError;
670
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
671
+ capturedRaw = void 0;
672
+ const run = await agent.send(userMessage);
673
+ const result = await run.wait();
674
+ lastUsage = extractUsage(result);
675
+ if (capturedRaw === void 0) {
676
+ if (result.status === "error" && result.error !== void 0) {
677
+ throw new GenerateObjectError(
678
+ "no_tool_call",
679
+ `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
680
+ result.error
681
+ );
682
+ }
683
+ if (attempt === maxRetries) {
684
+ throw new GenerateObjectError(
685
+ "no_tool_call",
686
+ "The model returned text instead of calling the `output` tool."
687
+ );
688
+ }
689
+ continue;
690
+ }
691
+ const parsed = options.schema.safeParse(capturedRaw);
692
+ if (parsed.success) {
693
+ return {
694
+ object: parsed.data,
695
+ raw: capturedRaw,
696
+ usage: lastUsage,
697
+ finishReason: "tool_use"
698
+ };
699
+ }
700
+ lastParseError = parsed.error;
701
+ }
702
+ if (options.errorStrategy === "return-raw") {
703
+ return {
704
+ object: capturedRaw,
705
+ raw: capturedRaw,
706
+ usage: lastUsage,
707
+ finishReason: "tool_use"
708
+ };
709
+ }
710
+ if (options.errorStrategy === "return-partial") {
711
+ return {
712
+ object: salvagePartial(options.schema, capturedRaw),
713
+ raw: capturedRaw,
714
+ usage: lastUsage,
715
+ finishReason: "tool_use"
716
+ };
717
+ }
718
+ throw new GenerateObjectError(
719
+ "parse_failed",
720
+ "Schema parse failed after all retries.",
721
+ lastParseError
722
+ );
723
+ } finally {
724
+ await disposeAndDeleteTransient(agent, deps.delete);
725
+ }
726
+ }
727
+ var GenerateObjectError;
728
+ var init_generate_object = __esm({
729
+ "src/generate-object.ts"() {
730
+ init_structured_output_helpers();
731
+ GenerateObjectError = class extends Error {
732
+ name = "GenerateObjectError";
733
+ code;
734
+ cause;
735
+ constructor(code, message, cause) {
736
+ super(message);
737
+ this.code = code;
738
+ if (cause !== void 0) this.cause = cause;
739
+ }
740
+ };
741
+ }
742
+ });
743
+
744
+ // src/internal/runtime/registry/agent-factory-registry.ts
745
+ var agent_factory_registry_exports = {};
746
+ __export(agent_factory_registry_exports, {
747
+ getAgentFacade: () => getAgentFacade,
748
+ setAgentFacade: () => setAgentFacade
749
+ });
750
+ function setAgentFacade(facade) {
751
+ registered = facade;
752
+ }
753
+ function getAgentFacade() {
754
+ if (registered === void 0) {
755
+ throw new Error(
756
+ "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
757
+ );
758
+ }
759
+ return registered;
760
+ }
761
+ var registered;
762
+ var init_agent_factory_registry = __esm({
763
+ "src/internal/runtime/registry/agent-factory-registry.ts"() {
764
+ }
765
+ });
516
766
 
517
767
  // src/internal/persistence/cwd-mutex.ts
518
768
  function withCwdMutex(key, fn) {
@@ -1430,29 +1680,6 @@ var init_judge_call = __esm({
1430
1680
  }
1431
1681
  });
1432
1682
 
1433
- // src/internal/runtime/registry/agent-factory-registry.ts
1434
- var agent_factory_registry_exports = {};
1435
- __export(agent_factory_registry_exports, {
1436
- getAgentFacade: () => getAgentFacade,
1437
- setAgentFacade: () => setAgentFacade
1438
- });
1439
- function setAgentFacade(facade) {
1440
- registered3 = facade;
1441
- }
1442
- function getAgentFacade() {
1443
- if (registered3 === void 0) {
1444
- throw new Error(
1445
- "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
1446
- );
1447
- }
1448
- return registered3;
1449
- }
1450
- var registered3;
1451
- var init_agent_factory_registry = __esm({
1452
- "src/internal/runtime/registry/agent-factory-registry.ts"() {
1453
- }
1454
- });
1455
-
1456
1683
  // src/internal/runtime/lifecycle/run-to-completion.ts
1457
1684
  var run_to_completion_exports = {};
1458
1685
  __export(run_to_completion_exports, {
@@ -1617,14 +1844,14 @@ async function forkAgentImpl(parent, options, deps) {
1617
1844
  return {
1618
1845
  result: result.result,
1619
1846
  toolCalls: [],
1620
- usage: extractUsage(result)
1847
+ usage: extractUsage2(result)
1621
1848
  };
1622
1849
  });
1623
1850
  } finally {
1624
1851
  await fork.dispose();
1625
1852
  }
1626
1853
  }
1627
- function extractUsage(result) {
1854
+ function extractUsage2(result) {
1628
1855
  if (typeof result !== "object" || result === null) {
1629
1856
  return { inputTokens: 0, outputTokens: 0 };
1630
1857
  }
@@ -2058,323 +2285,117 @@ async function runWorkAndFinalize(taskId, aborter, work) {
2058
2285
  void checkCancelRequestedAt(taskId, aborter);
2059
2286
  emitToSubscribers(taskId, {
2060
2287
  type: "progress",
2061
- taskId,
2062
- at: Date.now(),
2063
- payload
2064
- });
2065
- }
2066
- };
2067
- try {
2068
- const result = await reentryAls.run(true, () => Promise.resolve().then(() => work(ctx)));
2069
- if (aborter.signal.aborted) {
2070
- const cancelledAt = Date.now();
2071
- await transition(taskId, "cancelled", { cancelledAt });
2072
- emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2073
- return;
2074
- }
2075
- const finishedAt = Date.now();
2076
- await transition(taskId, "finished", { finishedAt, result });
2077
- emitToSubscribers(taskId, { type: "finished", taskId, finishedAt, result });
2078
- } catch (err) {
2079
- if (aborter.signal.aborted) {
2080
- const cancelledAt = Date.now();
2081
- await transition(taskId, "cancelled", { cancelledAt });
2082
- emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2083
- return;
2084
- }
2085
- const erroredAt = Date.now();
2086
- const error = err instanceof Error ? { code: err.code ?? "work_threw", message: err.message } : { code: "work_threw", message: String(err) };
2087
- await transition(taskId, "error", { erroredAt, error });
2088
- emitToSubscribers(taskId, { type: "errored", taskId, erroredAt, error });
2089
- }
2090
- }
2091
- async function checkCancelRequestedAt(taskId, aborter) {
2092
- if (aborter.signal.aborted) return;
2093
- try {
2094
- const handle = await state.store.get(taskId);
2095
- if (handle?.cancelRequested === true) {
2096
- aborter.abort("cancelRequested");
2097
- }
2098
- } catch {
2099
- }
2100
- }
2101
- async function acquireSlot() {
2102
- if (reentryAls.getStore() === true) {
2103
- return () => {
2104
- };
2105
- }
2106
- return state.semaphore.acquire();
2107
- }
2108
- async function submit(internal) {
2109
- state.firstSubmitSeen = true;
2110
- startEvictTimerIfNeeded();
2111
- const resolvedId = resolveIdInternal(internal);
2112
- const submitSpan = startTaskSubmitSpan({ taskId: resolvedId, kind: internal.kind });
2113
- const existing = await state.store.get(resolvedId);
2114
- if (existing !== void 0) {
2115
- submitSpan.end();
2116
- return existing;
2117
- }
2118
- const queuedHandle = await buildAndInsertQueued(internal, resolvedId);
2119
- submitSpan.end();
2120
- if (internal.signal?.aborted === true) {
2121
- return shortCircuitAborted(queuedHandle, internal.signal);
2122
- }
2123
- const aborter = new AbortController();
2124
- state.aborters.set(resolvedId, aborter);
2125
- if (internal.signal !== void 0) {
2126
- internal.signal.addEventListener("abort", () => aborter.abort(internal.signal?.reason), {
2127
- once: true
2128
- });
2129
- }
2130
- const release = await acquireSlot();
2131
- void (async () => {
2132
- try {
2133
- await checkCancelRequestedAt(resolvedId, aborter);
2134
- if (aborter.signal.aborted) {
2135
- const cancelledAt = Date.now();
2136
- await transition(resolvedId, "cancelled", { cancelledAt });
2137
- emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
2138
- return;
2139
- }
2140
- const startedAt = Date.now();
2141
- await transition(resolvedId, "running", { startedAt });
2142
- emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
2143
- await runWorkAndFinalize(resolvedId, aborter, internal.work);
2144
- } finally {
2145
- release();
2146
- state.aborters.delete(resolvedId);
2147
- }
2148
- })();
2149
- return queuedHandle;
2150
- }
2151
- async function get(id) {
2152
- if (!isValidTaskId(id, true)) return void 0;
2153
- return state.store.get(id);
2154
- }
2155
- var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state;
2156
- var init_registry = __esm({
2157
- "src/internal/task/registry.ts"() {
2158
- init_errors();
2159
- init_task();
2160
- init_async_semaphore();
2161
- init_ring_buffer();
2162
- init_store();
2163
- init_subscribe();
2164
- init_telemetry();
2165
- DEFAULT_CONCURRENCY = 8;
2166
- DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
2167
- EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
2168
- RING_CAP = 64;
2169
- reentryAls = new async_hooks.AsyncLocalStorage();
2170
- state = buildState({});
2171
- }
2172
- });
2173
- function toJsonSchema(schema, options = { unrepresentable: "any" }) {
2174
- return zod.toJSONSchema(schema, options);
2175
- }
2176
- var init_to_json_schema = __esm({
2177
- "src/internal/zod/to-json-schema.ts"() {
2178
- }
2179
- });
2180
- function requireZod() {
2181
- if (cachedZ !== void 0) return cachedZ;
2182
- const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cron.cjs', document.baseURI).href)));
2183
- let mod;
2184
- try {
2185
- mod = r("zod");
2186
- } catch (cause) {
2187
- throw new ConfigurationError(
2188
- 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
2189
- { code: "zod_not_installed", cause }
2190
- );
2191
- }
2192
- cachedZ = mod.z ?? mod;
2193
- return cachedZ;
2194
- }
2195
- function makeOutputTool(jsonSchema, onCapture) {
2196
- return {
2197
- name: "output",
2198
- description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
2199
- inputSchema: jsonSchema,
2200
- handler: (input) => {
2201
- const out = onCapture(input);
2202
- if (typeof out === "string") return out;
2203
- if (out instanceof Promise) return out;
2204
- return "";
2205
- }
2206
- };
2207
- }
2208
- function buildTransientAgentOptions(params) {
2209
- return {
2210
- model: params.model,
2211
- local: params.local,
2212
- tools: [params.outputTool],
2213
- systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
2214
- ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
2215
- ...params.providers !== void 0 ? { providers: params.providers } : {}
2216
- };
2217
- }
2218
- function extractUsage2(result) {
2219
- const u = result.usage;
2220
- if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
2221
- return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
2222
- }
2223
- function buildToolPrompt(prompt) {
2224
- return `${prompt}
2225
-
2226
- Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
2227
- }
2228
- function setupStructuredOutput(schema, maxRetries) {
2229
- const z6 = requireZod();
2230
- const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
2231
- return {
2232
- z: z6,
2233
- jsonSchema,
2234
- maxRetries: maxRetries ?? 1,
2235
- initialUsage: { inputTokens: 0, outputTokens: 0 }
2236
- };
2237
- }
2238
- async function disposeAndDeleteTransient(agent, deletor) {
2239
- await agent.dispose();
2240
- try {
2241
- await deletor(agent.agentId);
2242
- } catch {
2243
- }
2244
- }
2245
- var cachedZ;
2246
- var init_structured_output_helpers = __esm({
2247
- "src/internal/structured-output-helpers.ts"() {
2248
- init_errors();
2249
- init_to_json_schema();
2250
- }
2251
- });
2252
-
2253
- // src/generate-object.ts
2254
- var generate_object_exports = {};
2255
- __export(generate_object_exports, {
2256
- GenerateObjectError: () => GenerateObjectError,
2257
- generateObjectImpl: () => generateObjectImpl
2258
- });
2259
- function salvagePartial(schema, raw) {
2260
- if (typeof raw !== "object" || raw === null) return raw;
2261
- const shape = schema.shape;
2262
- if (shape === void 0 || typeof shape !== "object") return raw;
2263
- const rawObj = raw;
2264
- const out = {};
2265
- for (const [key, fieldSchema] of Object.entries(shape)) {
2266
- if (typeof fieldSchema?.safeParse !== "function") continue;
2267
- const parsed = fieldSchema.safeParse(rawObj[key]);
2268
- if (parsed.success) out[key] = parsed.data;
2269
- }
2270
- return out;
2271
- }
2272
- async function generateObjectImpl(options, deps) {
2273
- const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
2274
- options.schema,
2275
- options.maxRetries
2276
- );
2277
- let capturedRaw;
2278
- let lastUsage = initialUsage;
2279
- const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
2280
- class CaptureSentinel extends Error {
2281
- constructor(captured) {
2282
- super("generate-object-capture");
2283
- this.captured = captured;
2284
- }
2285
- captured;
2286
- [sentinel] = true;
2287
- }
2288
- const outputTool = makeOutputTool(jsonSchema, (input) => {
2289
- if (capturedRaw === void 0) {
2290
- capturedRaw = input;
2291
- }
2292
- throw new CaptureSentinel(input);
2293
- });
2294
- const agentOptions = buildTransientAgentOptions({
2295
- model: options.model,
2296
- local: options.local,
2297
- outputTool,
2298
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
2299
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
2300
- ...options.providers !== void 0 ? { providers: options.providers } : {}
2301
- });
2302
- const agent = await deps.create(agentOptions);
2288
+ taskId,
2289
+ at: Date.now(),
2290
+ payload
2291
+ });
2292
+ }
2293
+ };
2303
2294
  try {
2304
- const userMessage = buildToolPrompt(options.prompt);
2305
- let lastParseError;
2306
- for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
2307
- capturedRaw = void 0;
2308
- const run = await agent.send(userMessage);
2309
- const result = await run.wait();
2310
- lastUsage = extractUsage2(result);
2311
- if (capturedRaw === void 0) {
2312
- if (result.status === "error" && result.error !== void 0) {
2313
- throw new GenerateObjectError(
2314
- "no_tool_call",
2315
- `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
2316
- result.error
2317
- );
2318
- }
2319
- if (attempt === maxRetries) {
2320
- throw new GenerateObjectError(
2321
- "no_tool_call",
2322
- "The model returned text instead of calling the `output` tool."
2323
- );
2324
- }
2325
- continue;
2326
- }
2327
- const parsed = options.schema.safeParse(capturedRaw);
2328
- if (parsed.success) {
2329
- return {
2330
- object: parsed.data,
2331
- raw: capturedRaw,
2332
- usage: lastUsage,
2333
- finishReason: "tool_use"
2334
- };
2335
- }
2336
- lastParseError = parsed.error;
2295
+ const result = await reentryAls.run(true, () => Promise.resolve().then(() => work(ctx)));
2296
+ if (aborter.signal.aborted) {
2297
+ const cancelledAt = Date.now();
2298
+ await transition(taskId, "cancelled", { cancelledAt });
2299
+ emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2300
+ return;
2337
2301
  }
2338
- if (options.errorStrategy === "return-raw") {
2339
- return {
2340
- object: capturedRaw,
2341
- raw: capturedRaw,
2342
- usage: lastUsage,
2343
- finishReason: "tool_use"
2344
- };
2302
+ const finishedAt = Date.now();
2303
+ await transition(taskId, "finished", { finishedAt, result });
2304
+ emitToSubscribers(taskId, { type: "finished", taskId, finishedAt, result });
2305
+ } catch (err) {
2306
+ if (aborter.signal.aborted) {
2307
+ const cancelledAt = Date.now();
2308
+ await transition(taskId, "cancelled", { cancelledAt });
2309
+ emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2310
+ return;
2345
2311
  }
2346
- if (options.errorStrategy === "return-partial") {
2347
- return {
2348
- object: salvagePartial(options.schema, capturedRaw),
2349
- raw: capturedRaw,
2350
- usage: lastUsage,
2351
- finishReason: "tool_use"
2352
- };
2312
+ const erroredAt = Date.now();
2313
+ const error = err instanceof Error ? { code: err.code ?? "work_threw", message: err.message } : { code: "work_threw", message: String(err) };
2314
+ await transition(taskId, "error", { erroredAt, error });
2315
+ emitToSubscribers(taskId, { type: "errored", taskId, erroredAt, error });
2316
+ }
2317
+ }
2318
+ async function checkCancelRequestedAt(taskId, aborter) {
2319
+ if (aborter.signal.aborted) return;
2320
+ try {
2321
+ const handle = await state.store.get(taskId);
2322
+ if (handle?.cancelRequested === true) {
2323
+ aborter.abort("cancelRequested");
2353
2324
  }
2354
- throw new GenerateObjectError(
2355
- "parse_failed",
2356
- "Schema parse failed after all retries.",
2357
- lastParseError
2358
- );
2359
- } finally {
2360
- await disposeAndDeleteTransient(agent, deps.delete);
2325
+ } catch {
2361
2326
  }
2362
2327
  }
2363
- var GenerateObjectError;
2364
- var init_generate_object = __esm({
2365
- "src/generate-object.ts"() {
2366
- init_structured_output_helpers();
2367
- GenerateObjectError = class extends Error {
2368
- name = "GenerateObjectError";
2369
- code;
2370
- cause;
2371
- constructor(code, message, cause) {
2372
- super(message);
2373
- this.code = code;
2374
- if (cause !== void 0) this.cause = cause;
2375
- }
2328
+ async function acquireSlot() {
2329
+ if (reentryAls.getStore() === true) {
2330
+ return () => {
2376
2331
  };
2377
2332
  }
2333
+ return state.semaphore.acquire();
2334
+ }
2335
+ async function submit(internal) {
2336
+ state.firstSubmitSeen = true;
2337
+ startEvictTimerIfNeeded();
2338
+ const resolvedId = resolveIdInternal(internal);
2339
+ const submitSpan = startTaskSubmitSpan({ taskId: resolvedId, kind: internal.kind });
2340
+ const existing = await state.store.get(resolvedId);
2341
+ if (existing !== void 0) {
2342
+ submitSpan.end();
2343
+ return existing;
2344
+ }
2345
+ const queuedHandle = await buildAndInsertQueued(internal, resolvedId);
2346
+ submitSpan.end();
2347
+ if (internal.signal?.aborted === true) {
2348
+ return shortCircuitAborted(queuedHandle, internal.signal);
2349
+ }
2350
+ const aborter = new AbortController();
2351
+ state.aborters.set(resolvedId, aborter);
2352
+ if (internal.signal !== void 0) {
2353
+ internal.signal.addEventListener("abort", () => aborter.abort(internal.signal?.reason), {
2354
+ once: true
2355
+ });
2356
+ }
2357
+ const release = await acquireSlot();
2358
+ void (async () => {
2359
+ try {
2360
+ await checkCancelRequestedAt(resolvedId, aborter);
2361
+ if (aborter.signal.aborted) {
2362
+ const cancelledAt = Date.now();
2363
+ await transition(resolvedId, "cancelled", { cancelledAt });
2364
+ emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
2365
+ return;
2366
+ }
2367
+ const startedAt = Date.now();
2368
+ await transition(resolvedId, "running", { startedAt });
2369
+ emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
2370
+ await runWorkAndFinalize(resolvedId, aborter, internal.work);
2371
+ } finally {
2372
+ release();
2373
+ state.aborters.delete(resolvedId);
2374
+ }
2375
+ })();
2376
+ return queuedHandle;
2377
+ }
2378
+ async function get(id) {
2379
+ if (!isValidTaskId(id, true)) return void 0;
2380
+ return state.store.get(id);
2381
+ }
2382
+ var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state;
2383
+ var init_registry = __esm({
2384
+ "src/internal/task/registry.ts"() {
2385
+ init_errors();
2386
+ init_task();
2387
+ init_async_semaphore();
2388
+ init_ring_buffer();
2389
+ init_store();
2390
+ init_subscribe();
2391
+ init_telemetry();
2392
+ DEFAULT_CONCURRENCY = 8;
2393
+ DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
2394
+ EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
2395
+ RING_CAP = 64;
2396
+ reentryAls = new async_hooks.AsyncLocalStorage();
2397
+ state = buildState({});
2398
+ }
2378
2399
  });
2379
2400
 
2380
2401
  // src/stream-object.ts
@@ -2431,7 +2452,7 @@ async function* streamObjectImpl(options, deps) {
2431
2452
  }
2432
2453
  }
2433
2454
  const result = await run.wait();
2434
- lastUsage = extractUsage2(result);
2455
+ lastUsage = extractUsage(result);
2435
2456
  if (capturedRaw === void 0) {
2436
2457
  if (result.status === "error" && result.error !== void 0) {
2437
2458
  throw new StreamObjectError(
@@ -2777,6 +2798,7 @@ var AgentBuilder = class {
2777
2798
  constructor(deps) {
2778
2799
  this.deps = deps;
2779
2800
  }
2801
+ /** SE8 — accepts a bare-string id shorthand (`"openai/gpt-4o-mini"`) or a {@link ModelSelection}. */
2780
2802
  model(m) {
2781
2803
  this.opts.model = m;
2782
2804
  return this;
@@ -3130,6 +3152,69 @@ function isLocalAgentId(id) {
3130
3152
  return id.startsWith(PREFIX.localAgent);
3131
3153
  }
3132
3154
 
3155
+ // src/internal/runtime/config/default-model.ts
3156
+ var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
3157
+
3158
+ // src/agent-generate.ts
3159
+ async function agentGenerate(agent, agentModel, agentApiKey, agentLocal, input, options) {
3160
+ const { output, maxRetries, errorStrategy, ...sendOptions } = options;
3161
+ const run = await agent.send(input, sendOptions);
3162
+ const result = await run.wait();
3163
+ const { generateObjectImpl: generateObjectImpl2, GenerateObjectError: GenerateObjectError2 } = await Promise.resolve().then(() => (init_generate_object(), generate_object_exports));
3164
+ const answer = structurableAnswerOrThrow(result, GenerateObjectError2);
3165
+ const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
3166
+ const facade = getAgentFacade2();
3167
+ const structured = await generateObjectImpl2(
3168
+ {
3169
+ prompt: answer,
3170
+ schema: output,
3171
+ // `agentModel` arrives already normalized (`this.model` passed through
3172
+ // `normalizeModel` in the LocalAgent/CloudAgent constructor), so the widened
3173
+ // `string | ModelSelection` field only ever receives a `ModelSelection` here.
3174
+ model: agentModel ?? { id: DEFAULT_AGENTIC_MODEL_ID },
3175
+ // `local` is required to keep the transient structuring agent local-only;
3176
+ // default to the process cwd when the agent had no explicit local config.
3177
+ local: agentLocal ?? { cwd: process.cwd() },
3178
+ ...agentApiKey !== void 0 ? { apiKey: agentApiKey } : {},
3179
+ ...maxRetries !== void 0 ? { maxRetries } : {},
3180
+ ...errorStrategy !== void 0 ? { errorStrategy } : {}
3181
+ },
3182
+ {
3183
+ create: (opts) => facade.create(opts),
3184
+ delete: (agentId) => facade.delete(agentId)
3185
+ }
3186
+ );
3187
+ return {
3188
+ object: structured.object,
3189
+ result,
3190
+ raw: structured.raw,
3191
+ usage: structured.usage
3192
+ };
3193
+ }
3194
+ function structurableAnswerOrThrow(result, GenerateObjectError2) {
3195
+ if (result.status === "error") {
3196
+ throw new GenerateObjectError2(
3197
+ "no_tool_call",
3198
+ `Agent run failed before an answer could be structured: ${result.error?.message ?? "unknown error"} [${result.error?.code ?? "?"}]`,
3199
+ result.error
3200
+ );
3201
+ }
3202
+ if (result.status === "cancelled") {
3203
+ throw new GenerateObjectError2(
3204
+ "no_tool_call",
3205
+ "Agent run was cancelled before an answer could be structured."
3206
+ );
3207
+ }
3208
+ const answer = result.result;
3209
+ if (answer === void 0 || answer.length === 0) {
3210
+ throw new GenerateObjectError2(
3211
+ "no_tool_call",
3212
+ "The tool loop finished with no text answer (tool-only completion) \u2014 there is nothing to structure."
3213
+ );
3214
+ }
3215
+ return answer;
3216
+ }
3217
+
3133
3218
  // src/internal/runtime/cloud/cloud-agent.ts
3134
3219
  init_errors();
3135
3220
  init_cwd_mutex();
@@ -3280,8 +3365,19 @@ function safeFilenameForId(id, options) {
3280
3365
  return `h-${crypto.createHash("sha256").update(id).digest("hex").slice(0, 16)}`;
3281
3366
  }
3282
3367
 
3283
- // src/internal/runtime/config/default-model.ts
3284
- var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
3368
+ // src/internal/runtime/model-selection.ts
3369
+ init_errors();
3370
+ function normalizeModel(m) {
3371
+ if (m === void 0 || typeof m !== "string") return m;
3372
+ const id = m.trim();
3373
+ if (id.length === 0) {
3374
+ throw new ConfigurationError(
3375
+ 'model must be a non-empty "provider/model" id string or a ModelSelection object',
3376
+ { code: "invalid_model_selection" }
3377
+ );
3378
+ }
3379
+ return { id };
3380
+ }
3285
3381
 
3286
3382
  // src/internal/runtime/registry/agent-registry-store.ts
3287
3383
  init_cwd_mutex();
@@ -3358,7 +3454,10 @@ var REGISTRY_RELATIVE_PATH = path.join(".theokit", "agents", "registry.json");
3358
3454
  function stripSecretsFromOptions(options) {
3359
3455
  return assignDefined({
3360
3456
  name: options.name,
3361
- model: options.model !== void 0 ? { id: options.model.id } : void 0,
3457
+ model: (() => {
3458
+ const m = normalizeModel(options.model);
3459
+ return m !== void 0 ? { id: m.id } : void 0;
3460
+ })(),
3362
3461
  systemPrompt: typeof options.systemPrompt === "string" ? options.systemPrompt : void 0,
3363
3462
  local: serializeLocal(options.local),
3364
3463
  cloud: serializeCloud(options.cloud),
@@ -3643,7 +3742,8 @@ function serializeCloudAgentConfig(options) {
3643
3742
  cloud: serializeCloud2(options.cloud)
3644
3743
  };
3645
3744
  if (typeof options.agentId === "string") payload.agentId = options.agentId;
3646
- if (options.model !== void 0) payload.model = { id: options.model.id };
3745
+ const cfgModel = normalizeModel(options.model);
3746
+ if (cfgModel !== void 0) payload.model = { id: cfgModel.id };
3647
3747
  if (typeof options.systemPrompt === "string") payload.systemPrompt = options.systemPrompt;
3648
3748
  const skills = serializeSkills(options.skills);
3649
3749
  if (skills !== void 0) payload.skills = skills;
@@ -4539,6 +4639,7 @@ function applyScriptMetrics(base, script) {
4539
4639
  if (script.cost !== void 0) base.cost = script.cost;
4540
4640
  if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
4541
4641
  if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
4642
+ if (script.origin !== void 0) base.origin = script.origin;
4542
4643
  }
4543
4644
 
4544
4645
  // src/internal/runtime/cloud/cloud-run.ts
@@ -4894,7 +4995,7 @@ var CloudAgent = class {
4894
4995
  disposed = false;
4895
4996
  constructor(options, providedAgentId) {
4896
4997
  this.agentId = providedAgentId ?? options.agentId ?? generateCloudAgentId();
4897
- this.model = options.model;
4998
+ this.model = normalizeModel(options.model);
4898
4999
  this.options = options;
4899
5000
  this.cloudPayload = serializeCloudAgentConfig({ ...options, agentId: this.agentId });
4900
5001
  const repoUrls = (options.cloud?.repos ?? []).map((repo) => repo.url);
@@ -4924,6 +5025,17 @@ var CloudAgent = class {
4924
5025
  const apiKey = resolveApiKey(this.options.apiKey);
4925
5026
  return isFixtureApiKey(apiKey) && getConfiguredBaseUrl() === void 0;
4926
5027
  }
5028
+ // SE9 — integrated structured output; delegates to the shared helper.
5029
+ generate(message, options) {
5030
+ return agentGenerate(
5031
+ this,
5032
+ this.model,
5033
+ this.options.apiKey,
5034
+ this.options.local,
5035
+ message,
5036
+ options
5037
+ );
5038
+ }
4927
5039
  async send(message, options = {}) {
4928
5040
  if (options.tools !== void 0 && options.tools.length > 0) {
4929
5041
  throw new ConfigurationError(
@@ -4954,7 +5066,7 @@ var CloudAgent = class {
4954
5066
  });
4955
5067
  }
4956
5068
  async sendLocked(message, options) {
4957
- const overrideModel = options.model;
5069
+ const overrideModel = normalizeModel(options.model);
4958
5070
  if (overrideModel !== void 0) {
4959
5071
  this.model = overrideModel;
4960
5072
  updateRegisteredAgent(this.agentId, { model: overrideModel });
@@ -5868,18 +5980,18 @@ var ALL_ADAPTERS = [
5868
5980
  arizeAdapter,
5869
5981
  braintrustAdapter
5870
5982
  ];
5871
- var registered = /* @__PURE__ */ new Set();
5983
+ var registered2 = /* @__PURE__ */ new Set();
5872
5984
  function tryAutoRegisterAdapters(settings) {
5873
5985
  if (settings?.enabled !== true) return;
5874
5986
  if (settings.autoDetect === false) return;
5875
5987
  const disabled = new Set(settings.disable ?? []);
5876
5988
  for (const adapter of ALL_ADAPTERS) {
5877
- if (registered.has(adapter.moduleName)) continue;
5989
+ if (registered2.has(adapter.moduleName)) continue;
5878
5990
  if (disabled.has(adapter.displayName.toLowerCase())) continue;
5879
5991
  if (!adapter.detect()) continue;
5880
5992
  try {
5881
5993
  adapter.register();
5882
- registered.add(adapter.moduleName);
5994
+ registered2.add(adapter.moduleName);
5883
5995
  process.stderr.write(`[theokit-sdk] telemetry: ${adapter.displayName} auto-instrumented.
5884
5996
  `);
5885
5997
  } catch (cause) {
@@ -6442,6 +6554,26 @@ function paginate(items, opts) {
6442
6554
  return items.slice(start, end);
6443
6555
  }
6444
6556
 
6557
+ // src/internal/persistence/session-meta.ts
6558
+ function applyMetaPatch(current, patch) {
6559
+ const next = {};
6560
+ if (current.title !== void 0) next.title = current.title;
6561
+ if (current.tag !== void 0) next.tag = current.tag;
6562
+ if (patch.title === null) delete next.title;
6563
+ else if (patch.title !== void 0) next.title = patch.title;
6564
+ if (patch.tag === null) delete next.tag;
6565
+ else if (patch.tag !== void 0) next.tag = patch.tag;
6566
+ return next;
6567
+ }
6568
+ function coerceSessionMeta(raw) {
6569
+ if (typeof raw !== "object" || raw === null) return void 0;
6570
+ const obj = raw;
6571
+ const meta = {};
6572
+ if (typeof obj.title === "string") meta.title = obj.title;
6573
+ if (typeof obj.tag === "string") meta.tag = obj.tag;
6574
+ return meta.title === void 0 && meta.tag === void 0 ? void 0 : meta;
6575
+ }
6576
+
6445
6577
  // src/internal/persistence/conversation-storage-fs.ts
6446
6578
  var FileSystemConversationStorage = class {
6447
6579
  #root;
@@ -6492,6 +6624,37 @@ var FileSystemConversationStorage = class {
6492
6624
  async compact(conversationId, maxTurns) {
6493
6625
  await compactSessionFile(this.#root, conversationId, maxTurns);
6494
6626
  }
6627
+ // SE4 — session metadata persisted as a per-conversation sidecar
6628
+ // `<root>/.theokit/agents/<safeId>/session.json` (same sanitized perimeter as
6629
+ // the transcript). Kept separate from messages.jsonl so a title/tag write does
6630
+ // not rewrite the append-only log.
6631
+ #metaPath(conversationId) {
6632
+ const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
6633
+ return safePathJoin(this.#root, ".theokit", "agents", safe2, "session.json");
6634
+ }
6635
+ async getSessionMeta(conversationId) {
6636
+ try {
6637
+ const raw = await promises.readFile(this.#metaPath(conversationId), "utf8");
6638
+ return coerceSessionMeta(JSON.parse(raw));
6639
+ } catch (cause) {
6640
+ if (cause.code === "ENOENT") return void 0;
6641
+ throw cause;
6642
+ }
6643
+ }
6644
+ async setSessionMeta(conversationId, patch) {
6645
+ const metaPath = this.#metaPath(conversationId);
6646
+ await promises.mkdir(path.dirname(metaPath), { recursive: true });
6647
+ await withFileLock(metaPath, async () => {
6648
+ let current = {};
6649
+ try {
6650
+ current = coerceSessionMeta(JSON.parse(await promises.readFile(metaPath, "utf8"))) ?? {};
6651
+ } catch (cause) {
6652
+ if (cause.code !== "ENOENT") throw cause;
6653
+ }
6654
+ const next = applyMetaPatch(current, patch);
6655
+ await promises.writeFile(metaPath, redactSecrets(JSON.stringify(next)), "utf8");
6656
+ });
6657
+ }
6495
6658
  async dispose() {
6496
6659
  }
6497
6660
  };
@@ -8341,23 +8504,27 @@ function tryParseSkill(raw, fallbackName, source, options) {
8341
8504
 
8342
8505
  // src/internal/runtime/skills/skills-manager.ts
8343
8506
  var SkillsManager = class {
8344
- constructor(cwd, _enabled, settingSourcesIncludeProject) {
8507
+ constructor(cwd, _enabled, settingSourcesIncludeProject, skillsDir, inline) {
8345
8508
  this.cwd = cwd;
8346
8509
  this.settingSourcesIncludeProject = settingSourcesIncludeProject;
8510
+ this.skillsDir = skillsDir;
8511
+ this.inline = inline;
8347
8512
  }
8348
8513
  cwd;
8349
8514
  settingSourcesIncludeProject;
8515
+ skillsDir;
8516
+ inline;
8350
8517
  skills = [];
8351
8518
  async initialize() {
8352
8519
  if (!this.settingSourcesIncludeProject) {
8353
- this.skills = [];
8520
+ this.skills = this.mergeInline([]);
8354
8521
  return;
8355
8522
  }
8356
8523
  await this.refresh();
8357
8524
  }
8358
8525
  async refresh() {
8359
- const skillsRoot = path.join(this.cwd, ".theokit", "skills");
8360
- this.skills = await discoverSkills(skillsRoot, {
8526
+ const skillsRoot = this.skillsDir ?? path.join(this.cwd, ".theokit", "skills");
8527
+ const discovered = await discoverSkills(skillsRoot, {
8361
8528
  onInvalidSkill: (info) => {
8362
8529
  process.stderr.write(
8363
8530
  `[theokit-sdk] skill ${info.name} skipped (${info.code}): ${info.message}
@@ -8365,6 +8532,13 @@ var SkillsManager = class {
8365
8532
  );
8366
8533
  }
8367
8534
  });
8535
+ this.skills = this.mergeInline(discovered);
8536
+ }
8537
+ /** M22 — merge inline skills over discovered ones; inline wins on a name conflict. */
8538
+ mergeInline(discovered) {
8539
+ if (this.inline === void 0 || this.inline.length === 0) return discovered;
8540
+ const inlineNames = new Set(this.inline.map((s) => s.name));
8541
+ return [...discovered.filter((s) => !inlineNames.has(s.name)), ...this.inline];
8368
8542
  }
8369
8543
  list() {
8370
8544
  return Promise.resolve(this.skills);
@@ -8400,7 +8574,7 @@ function bootstrapSubmanagers(args) {
8400
8574
  const providerCount = (args.options.providers?.routes?.length ?? 0) + enabledPluginNames(args.options.plugins).length;
8401
8575
  if (providerCount > 0 || args.options.providers !== void 0) {
8402
8576
  out.providers = new ProvidersManagerImpl(
8403
- args.options.model,
8577
+ normalizeModel(args.options.model),
8404
8578
  args.options.providers,
8405
8579
  args.options.plugins
8406
8580
  );
@@ -8409,7 +8583,10 @@ function bootstrapSubmanagers(args) {
8409
8583
  out.skillsManager = new SkillsManager(
8410
8584
  args.workspaceCwd,
8411
8585
  args.options.skills?.enabled,
8412
- args.settingSourcesIncludeProject
8586
+ args.settingSourcesIncludeProject,
8587
+ // M22 — custom skills directory + inline (code-defined) skills.
8588
+ args.options.skills?.skillsDir,
8589
+ args.options.skills?.inline
8413
8590
  );
8414
8591
  const localSkills = out.skillsManager;
8415
8592
  out.skills = { list: () => localSkills.list() };
@@ -8467,6 +8644,7 @@ function createLocalRun(options) {
8467
8644
  projectMcpServers: options.projectMcpServers,
8468
8645
  ...options.persistMemoryFact !== void 0 ? { persistMemoryFact: options.persistMemoryFact } : {}
8469
8646
  });
8647
+ if (options.sendOptions.origin !== void 0) script.origin = options.sendOptions.origin;
8470
8648
  const handle = new LocalRun({
8471
8649
  id,
8472
8650
  agentId: options.agentId,
@@ -9115,6 +9293,15 @@ async function emitReasoningDeltaCallback(inputs, text) {
9115
9293
  );
9116
9294
  }
9117
9295
 
9296
+ // src/types/run-events.ts
9297
+ function emitRunEvent(sink, event) {
9298
+ if (sink === void 0) return;
9299
+ try {
9300
+ sink(event);
9301
+ } catch {
9302
+ }
9303
+ }
9304
+
9118
9305
  // src/internal/agent-loop/tool-dispatch.ts
9119
9306
  init_async_local_storage();
9120
9307
 
@@ -9212,6 +9399,22 @@ function coerceArgsToSchema(args, schema) {
9212
9399
  return { value: out, changed };
9213
9400
  }
9214
9401
 
9402
+ // src/tool-error.ts
9403
+ init_errors();
9404
+ var ToolError = class extends TheokitAgentError {
9405
+ name = "ToolError";
9406
+ /** The error content surfaced to the model: a string, or text/image blocks. */
9407
+ content;
9408
+ constructor(content, options = {}) {
9409
+ super(renderToolErrorMessage(content), { ...options, isRetryable: false });
9410
+ this.content = content;
9411
+ }
9412
+ };
9413
+ function renderToolErrorMessage(content) {
9414
+ if (typeof content === "string") return content;
9415
+ return content.map((block) => block.type === "text" ? block.text : `[${block.source.media_type} image]`).join("\n");
9416
+ }
9417
+
9215
9418
  // src/internal/runtime/tools/shell-tool.ts
9216
9419
  async function runShell(options) {
9217
9420
  if (options.sandbox === true && isObviouslyUnsafe(options.command)) {
@@ -9265,9 +9468,13 @@ async function runHandlerTool(kind, handler, call, signal, context) {
9265
9468
  return { stdout: "", stderr: `${kind} tool ${call.name} has no handler`, exitCode: 127 };
9266
9469
  }
9267
9470
  try {
9268
- const stdout = await handler(call.input, { signal, context });
9269
- return { stdout, stderr: "", exitCode: 0 };
9471
+ const out = await handler(call.input, { signal, context });
9472
+ if (typeof out !== "string") return { stdout: "", stderr: "", exitCode: 0, content: out };
9473
+ return { stdout: out, stderr: "", exitCode: 0 };
9270
9474
  } catch (cause) {
9475
+ if (cause instanceof ToolError) {
9476
+ return { stdout: "", stderr: cause.message, exitCode: 1, content: cause.content };
9477
+ }
9271
9478
  const message = cause instanceof Error ? cause.message : String(cause);
9272
9479
  return { stdout: "", stderr: message, exitCode: 1 };
9273
9480
  }
@@ -9386,6 +9593,11 @@ async function dispatchSingleCall(inputs, tools, call, events, parentSpan) {
9386
9593
  toolSpan?.end();
9387
9594
  return fileVeto;
9388
9595
  }
9596
+ emitRunEvent(inputs.runEventSink, {
9597
+ type: "tool_progress",
9598
+ toolName: workingCall.name,
9599
+ toolCallId: callId
9600
+ });
9389
9601
  const result = await runToolWithLifecycle(inputs, resolved, workingCall, callId);
9390
9602
  await inputs.pluginManager?.runPostToolCallHooks({
9391
9603
  name: workingCall.name,
@@ -9412,6 +9624,13 @@ function applyRepairAndExtractCall(tools, call) {
9412
9624
  function vetoFromForkWhitelist(inputs, call, callId, events) {
9413
9625
  const whitelistDecision = checkToolWhitelist(call.name);
9414
9626
  if (whitelistDecision.allowed) return void 0;
9627
+ emitRunEvent(inputs.runEventSink, {
9628
+ type: "permission_denied",
9629
+ toolName: call.name,
9630
+ toolCallId: callId,
9631
+ source: "fork_whitelist",
9632
+ message: whitelistDecision.reason ?? "tool not available in fork"
9633
+ });
9415
9634
  events.push(buildToolUseRunning(inputs, callId, call));
9416
9635
  events.push(
9417
9636
  buildToolUseCompleted(inputs, callId, call, {
@@ -9448,6 +9667,13 @@ async function vetoFromPluginPreHook(inputs, call, callId, events) {
9448
9667
  runId: inputs.runId
9449
9668
  });
9450
9669
  if (pluginVeto === void 0) return void 0;
9670
+ emitRunEvent(inputs.runEventSink, {
9671
+ type: "permission_denied",
9672
+ toolName: call.name,
9673
+ toolCallId: callId,
9674
+ source: "plugin",
9675
+ message: pluginVeto.message
9676
+ });
9451
9677
  events.push(
9452
9678
  buildToolUseCompleted(inputs, callId, call, {
9453
9679
  stdout: "",
@@ -9470,6 +9696,13 @@ async function vetoFromFileHookPreDecision(inputs, call, callId, events) {
9470
9696
  runId: inputs.runId
9471
9697
  });
9472
9698
  if (!preDecision.blocked) return void 0;
9699
+ emitRunEvent(inputs.runEventSink, {
9700
+ type: "permission_denied",
9701
+ toolName: call.name,
9702
+ toolCallId: callId,
9703
+ source: "file_hook",
9704
+ message: preDecision.reason ?? "blocked by hook"
9705
+ });
9473
9706
  events.push(
9474
9707
  buildToolUseCompleted(inputs, callId, call, {
9475
9708
  stdout: "",
@@ -9513,7 +9746,9 @@ async function runToolWithLifecycle(inputs, resolved, call, callId) {
9513
9746
  await safeEmitToolHook(inputs.onToolEnd, {
9514
9747
  toolName: call.name,
9515
9748
  args: call.input,
9516
- result: result.stdout,
9749
+ // SE7 — a block-returning handler has empty `stdout`; surface its structured
9750
+ // content to the hook instead of an empty string.
9751
+ result: result.content !== void 0 ? result.content : result.stdout,
9517
9752
  conversationId: inputs.agentId,
9518
9753
  callId,
9519
9754
  durationMs
@@ -9535,7 +9770,10 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
9535
9770
  output: {
9536
9771
  stdout: result.stdout,
9537
9772
  stderr: result.stderr,
9538
- exitCode: result.exitCode ?? 0
9773
+ exitCode: result.exitCode ?? 0,
9774
+ // SE7 — structured content (text + image blocks) when the handler
9775
+ // returned/threw blocks; absent for the string path.
9776
+ ...result.content !== void 0 ? { content: result.content } : {}
9539
9777
  },
9540
9778
  agentId: inputs.agentId,
9541
9779
  runId: inputs.runId
@@ -9548,7 +9786,9 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
9548
9786
  return {
9549
9787
  type: "tool_result",
9550
9788
  toolUseId: call.id,
9551
- content: renderToolResult(result),
9789
+ // SE7 — structured content blocks (handler-returned or ToolError-carried)
9790
+ // are authoritative; otherwise the legacy string rendering.
9791
+ content: result.content !== void 0 ? result.content : renderToolResult(result),
9552
9792
  ...result.exitCode !== 0 && result.exitCode !== void 0 ? { isError: true } : {}
9553
9793
  };
9554
9794
  }
@@ -9597,6 +9837,29 @@ function buildToolUseCompleted(inputs, callId, call, result) {
9597
9837
  };
9598
9838
  }
9599
9839
 
9840
+ // src/internal/llm/tool-result-content.ts
9841
+ init_errors();
9842
+ function toBlockToolResultContent(content) {
9843
+ return content;
9844
+ }
9845
+ function toStringToolResultContent(content, providerName) {
9846
+ if (typeof content === "string") return content;
9847
+ if (content.some((block) => block.type === "image")) {
9848
+ throw new ConfigurationError(
9849
+ `provider "${providerName}" does not support image content in tool results`
9850
+ );
9851
+ }
9852
+ return content.filter(
9853
+ (block) => block.type === "text"
9854
+ ).map((block) => block.text).join("\n");
9855
+ }
9856
+ function renderToolResultContentText(content, fn) {
9857
+ if (typeof content === "string") return fn(content);
9858
+ return content.map(
9859
+ (block) => block.type === "text" ? { type: "text", text: fn(block.text) } : block
9860
+ );
9861
+ }
9862
+
9600
9863
  // src/internal/agent-loop/tool-result-guard.ts
9601
9864
  var OPEN = "<untrusted-tool-output>";
9602
9865
  var CLOSE = "</untrusted-tool-output>";
@@ -9622,7 +9885,11 @@ ${CLOSE}`;
9622
9885
  function applyToolResultGuard(parts, opts) {
9623
9886
  if (opts.delimit !== true && opts.redactPii !== true) return parts;
9624
9887
  return parts.map(
9625
- (p) => p.type === "tool_result" ? { ...p, content: guardText(p.content, opts) } : p
9888
+ (p) => (
9889
+ // SE7 — guard the TEXT of a tool result (string or the text blocks of a
9890
+ // structured content); image blocks pass through untouched.
9891
+ p.type === "tool_result" ? { ...p, content: renderToolResultContentText(p.content, (t) => guardText(t, opts)) } : p
9892
+ )
9626
9893
  );
9627
9894
  }
9628
9895
 
@@ -10539,10 +10806,10 @@ function stripVertexPrefix(modelId) {
10539
10806
  }
10540
10807
 
10541
10808
  // src/internal/providers/builtin/index.ts
10542
- var registered2 = false;
10809
+ var registered3 = false;
10543
10810
  function registerBuiltins() {
10544
- if (registered2) return;
10545
- registered2 = true;
10811
+ if (registered3) return;
10812
+ registered3 = true;
10546
10813
  registerProvider(ANTHROPIC);
10547
10814
  registerProvider(OPENAI);
10548
10815
  registerProvider(OPENROUTER);
@@ -10770,7 +11037,9 @@ function toAnthropicWireMessage(message) {
10770
11037
  return {
10771
11038
  type: "tool_result",
10772
11039
  tool_use_id: part.toolUseId,
10773
- content: part.content,
11040
+ // SE7 — this wire is block-capable: strings pass through, structured
11041
+ // text/image blocks are forwarded natively.
11042
+ content: toBlockToolResultContent(part.content),
10774
11043
  ...part.isError === true ? { is_error: true } : {}
10775
11044
  };
10776
11045
  });
@@ -11562,7 +11831,8 @@ function toOllamaMessages(message) {
11562
11831
  if (part.type === "tool_result") {
11563
11832
  out.push({
11564
11833
  role: "tool",
11565
- content: part.content,
11834
+ // SE7 — string-only tool role: text blocks flatten; image fails fast.
11835
+ content: toStringToolResultContent(part.content, "ollama"),
11566
11836
  tool_name: part.toolUseId
11567
11837
  });
11568
11838
  }
@@ -12099,7 +12369,9 @@ function userOrToolMessages(message) {
12099
12369
  out.push({
12100
12370
  role: "tool",
12101
12371
  tool_call_id: part.toolUseId,
12102
- content: part.content
12372
+ // SE7 — this wire's tool role is string-only: text blocks flatten; an
12373
+ // image block fails fast (ConfigurationError).
12374
+ content: toStringToolResultContent(part.content, "openai")
12103
12375
  });
12104
12376
  }
12105
12377
  }
@@ -13028,18 +13300,20 @@ function levenshtein(a, b) {
13028
13300
  function createRealLocalRun(options) {
13029
13301
  const { userText, id, startTime } = prepareRunContext(options.message);
13030
13302
  const supported = /* @__PURE__ */ new Set(["stream", "wait", "cancel", "conversation"]);
13031
- const unusedFixtureScript = {
13303
+ const runScript = {
13032
13304
  events: [],
13033
13305
  finalStatus: "running",
13034
13306
  cancellable: false,
13035
- conversation: []
13307
+ conversation: [],
13308
+ // SE3 — carry the turn's provenance so buildResult forwards it onto RunResult.origin.
13309
+ ...options.sendOptions.origin !== void 0 ? { origin: options.sendOptions.origin } : {}
13036
13310
  };
13037
13311
  const handle = new RealLocalRun(
13038
13312
  {
13039
13313
  id,
13040
13314
  agentId: options.agentId,
13041
13315
  model: options.model,
13042
- script: unusedFixtureScript,
13316
+ script: runScript,
13043
13317
  supportedOps: supported,
13044
13318
  startTime
13045
13319
  },
@@ -13151,6 +13425,8 @@ function buildLoopInputs(options, runId, userText) {
13151
13425
  // M7 — forward SendOptions.context to the loop so every tool handler receives
13152
13426
  // it on `ctx.context` (shared run config set once, e.g. projectRoot).
13153
13427
  ...options.sendOptions.context !== void 0 ? { context: options.sendOptions.context } : {},
13428
+ // SE2 — forward the opt-in typed runtime-event sink to the loop.
13429
+ ...options.sendOptions.onRunEvent !== void 0 ? { runEventSink: options.sendOptions.onRunEvent } : {},
13154
13430
  // #58 / #57 — forward the per-tool timeout + tool-result guard so a consumer
13155
13431
  // can enable them via SendOptions (not only internal AgentLoopInputs).
13156
13432
  ...options.sendOptions.perToolTimeoutMs !== void 0 ? { perToolTimeoutMs: options.sendOptions.perToolTimeoutMs } : {},
@@ -15843,7 +16119,7 @@ function wrapRunWithPostReplyHook(args) {
15843
16119
  async function executeSendLocked(inputs, message, options) {
15844
16120
  if (inputs.disposed) throw new AgentDisposedError(inputs.agentId);
15845
16121
  await consumePending(inputs.agentId, inputs.invalidationPending, inputs.clearInvalidation, inputs.reload);
15846
- inputs.applyModelOverride(options.model);
16122
+ inputs.applyModelOverride(normalizeModel(options.model));
15847
16123
  const userText = typeof message === "string" ? message : message.text;
15848
16124
  if (inputs.options.onBeforeSend !== void 0) {
15849
16125
  await inputs.options.onBeforeSend({
@@ -15995,7 +16271,7 @@ var LocalAgent = class {
15995
16271
  constructor(options) {
15996
16272
  this.agentId = options.agentId ?? generateLocalAgentId();
15997
16273
  this._telemetry = createTelemetry(options.telemetry);
15998
- this.model = options.model;
16274
+ this.model = normalizeModel(options.model);
15999
16275
  this.options = options;
16000
16276
  this.workspaceCwd = resolveCwd(options.local?.cwd);
16001
16277
  this.conversationStorage = options.conversationStorage;
@@ -16062,6 +16338,11 @@ var LocalAgent = class {
16062
16338
  hooks() {
16063
16339
  return this.hooksExecutor;
16064
16340
  }
16341
+ // SE9 — integrated structured output; delegates to the shared helper.
16342
+ generate(message, options) {
16343
+ const { apiKey, local } = this.options;
16344
+ return agentGenerate(this, this.model, apiKey, local, message, options);
16345
+ }
16065
16346
  async send(message, options = {}) {
16066
16347
  if (options.tools !== void 0 && options.tools.length > 0) {
16067
16348
  validateToolCatalog(options.tools);
@@ -16301,6 +16582,7 @@ function includesSetting(options, source) {
16301
16582
 
16302
16583
  // src/agent-helpers.ts
16303
16584
  async function runCreateUnderSpan(options, span) {
16585
+ options = { ...options, model: normalizeModel(options.model) };
16304
16586
  validateAgentOptions(options);
16305
16587
  validateCloudToolParity(options);
16306
16588
  await guardAgainstIdCollision(options);
@@ -16374,7 +16656,10 @@ async function createLocalAgent(options) {
16374
16656
  const willFlowToProvider = !isFixtureApiKey(apiKey) && !shouldUseRealLocalRuntime(apiKey);
16375
16657
  const shape = validateApiKeyShape(apiKey, {
16376
16658
  strict: willFlowToProvider,
16377
- ...willFlowToProvider ? { provider: providerFromModelId(options.model?.id) } : {}
16659
+ // `options.model` is already normalized by `runCreateUnderSpan`; `normalizeModel`
16660
+ // here is the idempotent narrowing of the widened public type (`{id}` passes
16661
+ // through by reference) — cleaner than an `as` cast.
16662
+ ...willFlowToProvider ? { provider: providerFromModelId(normalizeModel(options.model)?.id) } : {}
16378
16663
  });
16379
16664
  if (shape.malformed) {
16380
16665
  throw new AuthenticationError(shape.message, { code: "malformed_api_key" });
@@ -16440,6 +16725,10 @@ async function rehydrateExistingAgent(agentId, existing, options) {
16440
16725
  ...existing.options,
16441
16726
  ...options,
16442
16727
  ...mergedLocal !== void 0 ? { local: mergedLocal } : {},
16728
+ // SE8 — normalize a caller-supplied bare-string model override at this resume
16729
+ // boundary (Agent.resume never flows through runCreateUnderSpan). Cloud resume
16730
+ // honors it; local resume forces the persisted model below.
16731
+ model: normalizeModel(options.model ?? existing.options.model),
16443
16732
  mcpServers: void 0,
16444
16733
  agentId
16445
16734
  };
@@ -16806,6 +17095,7 @@ async function getOrCreateUncached(agentId, options) {
16806
17095
  }
16807
17096
  setAgentFacade({
16808
17097
  create: (options) => Agent.create(options),
17098
+ delete: (agentId) => Agent.delete(agentId),
16809
17099
  prompt: (message, options) => Agent.prompt(message, options),
16810
17100
  get: (agentId) => Agent.get(agentId),
16811
17101
  resume: (agentId, options) => Agent.resume(agentId, options),