@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/eval.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
+ var zod = require('zod');
4
+ var module$1 = require('module');
3
5
  var crypto = require('crypto');
4
6
  var promises = require('fs/promises');
5
7
  var path = require('path');
6
- var zod = require('zod');
7
8
  var fs = require('fs');
8
9
  var async_hooks = require('async_hooks');
9
- var module$1 = require('module');
10
10
  var os = require('os');
11
11
  var child_process = require('child_process');
12
12
  var url = require('url');
@@ -512,6 +512,256 @@ var init_errors = __esm({
512
512
  };
513
513
  }
514
514
  });
515
+ function toJsonSchema(schema, options = { unrepresentable: "any" }) {
516
+ return zod.toJSONSchema(schema, options);
517
+ }
518
+ var init_to_json_schema = __esm({
519
+ "src/internal/zod/to-json-schema.ts"() {
520
+ }
521
+ });
522
+ function requireZod() {
523
+ if (cachedZ !== void 0) return cachedZ;
524
+ const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('eval.cjs', document.baseURI).href)));
525
+ let mod;
526
+ try {
527
+ mod = r("zod");
528
+ } catch (cause) {
529
+ throw new ConfigurationError(
530
+ 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
531
+ { code: "zod_not_installed", cause }
532
+ );
533
+ }
534
+ cachedZ = mod.z ?? mod;
535
+ return cachedZ;
536
+ }
537
+ function makeOutputTool(jsonSchema, onCapture) {
538
+ return {
539
+ name: "output",
540
+ description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
541
+ inputSchema: jsonSchema,
542
+ handler: (input) => {
543
+ const out = onCapture(input);
544
+ if (typeof out === "string") return out;
545
+ if (out instanceof Promise) return out;
546
+ return "";
547
+ }
548
+ };
549
+ }
550
+ function buildTransientAgentOptions(params) {
551
+ return {
552
+ model: params.model,
553
+ local: params.local,
554
+ tools: [params.outputTool],
555
+ systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
556
+ ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
557
+ ...params.providers !== void 0 ? { providers: params.providers } : {}
558
+ };
559
+ }
560
+ function extractUsage(result) {
561
+ const u = result.usage;
562
+ if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
563
+ return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
564
+ }
565
+ function buildToolPrompt(prompt) {
566
+ return `${prompt}
567
+
568
+ Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
569
+ }
570
+ function setupStructuredOutput(schema, maxRetries) {
571
+ const z7 = requireZod();
572
+ const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
573
+ return {
574
+ z: z7,
575
+ jsonSchema,
576
+ maxRetries: maxRetries ?? 1,
577
+ initialUsage: { inputTokens: 0, outputTokens: 0 }
578
+ };
579
+ }
580
+ async function disposeAndDeleteTransient(agent, deletor) {
581
+ await agent.dispose();
582
+ try {
583
+ await deletor(agent.agentId);
584
+ } catch {
585
+ }
586
+ }
587
+ var cachedZ;
588
+ var init_structured_output_helpers = __esm({
589
+ "src/internal/structured-output-helpers.ts"() {
590
+ init_errors();
591
+ init_to_json_schema();
592
+ }
593
+ });
594
+
595
+ // src/generate-object.ts
596
+ var generate_object_exports = {};
597
+ __export(generate_object_exports, {
598
+ GenerateObjectError: () => GenerateObjectError,
599
+ generateObjectImpl: () => generateObjectImpl
600
+ });
601
+ function salvagePartial(schema, raw) {
602
+ if (typeof raw !== "object" || raw === null) return raw;
603
+ const shape = schema.shape;
604
+ if (shape === void 0 || typeof shape !== "object") return raw;
605
+ const rawObj = raw;
606
+ const out = {};
607
+ for (const [key, fieldSchema] of Object.entries(shape)) {
608
+ if (typeof fieldSchema?.safeParse !== "function") continue;
609
+ const parsed = fieldSchema.safeParse(rawObj[key]);
610
+ if (parsed.success) out[key] = parsed.data;
611
+ }
612
+ return out;
613
+ }
614
+ async function runReasoningPhase(options, deps) {
615
+ const reasoningOptions = {
616
+ model: options.model,
617
+ local: options.local,
618
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
619
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
620
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
621
+ };
622
+ const reasoningAgent = await deps.create(reasoningOptions);
623
+ try {
624
+ const run = await reasoningAgent.send(options.prompt);
625
+ const result = await run.wait();
626
+ const text = result.result;
627
+ return typeof text === "string" ? text : options.prompt;
628
+ } finally {
629
+ await disposeAndDeleteTransient(reasoningAgent, deps.delete);
630
+ }
631
+ }
632
+ async function generateObjectImpl(options, deps) {
633
+ const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
634
+ options.schema,
635
+ options.maxRetries
636
+ );
637
+ let capturedRaw;
638
+ let lastUsage = initialUsage;
639
+ const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
640
+ class CaptureSentinel extends Error {
641
+ constructor(captured) {
642
+ super("generate-object-capture");
643
+ this.captured = captured;
644
+ }
645
+ captured;
646
+ [sentinel] = true;
647
+ }
648
+ const outputTool = makeOutputTool(jsonSchema, (input) => {
649
+ if (capturedRaw === void 0) {
650
+ capturedRaw = input;
651
+ }
652
+ throw new CaptureSentinel(input);
653
+ });
654
+ const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
655
+ const structuringModel = options.structuringModel ?? options.model;
656
+ const structuringPrompt = reasoningText ?? options.prompt;
657
+ const agentOptions = buildTransientAgentOptions({
658
+ model: structuringModel,
659
+ local: options.local,
660
+ outputTool,
661
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
662
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
663
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
664
+ });
665
+ const agent = await deps.create(agentOptions);
666
+ try {
667
+ const userMessage = buildToolPrompt(structuringPrompt);
668
+ let lastParseError;
669
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
670
+ capturedRaw = void 0;
671
+ const run = await agent.send(userMessage);
672
+ const result = await run.wait();
673
+ lastUsage = extractUsage(result);
674
+ if (capturedRaw === void 0) {
675
+ if (result.status === "error" && result.error !== void 0) {
676
+ throw new GenerateObjectError(
677
+ "no_tool_call",
678
+ `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
679
+ result.error
680
+ );
681
+ }
682
+ if (attempt === maxRetries) {
683
+ throw new GenerateObjectError(
684
+ "no_tool_call",
685
+ "The model returned text instead of calling the `output` tool."
686
+ );
687
+ }
688
+ continue;
689
+ }
690
+ const parsed = options.schema.safeParse(capturedRaw);
691
+ if (parsed.success) {
692
+ return {
693
+ object: parsed.data,
694
+ raw: capturedRaw,
695
+ usage: lastUsage,
696
+ finishReason: "tool_use"
697
+ };
698
+ }
699
+ lastParseError = parsed.error;
700
+ }
701
+ if (options.errorStrategy === "return-raw") {
702
+ return {
703
+ object: capturedRaw,
704
+ raw: capturedRaw,
705
+ usage: lastUsage,
706
+ finishReason: "tool_use"
707
+ };
708
+ }
709
+ if (options.errorStrategy === "return-partial") {
710
+ return {
711
+ object: salvagePartial(options.schema, capturedRaw),
712
+ raw: capturedRaw,
713
+ usage: lastUsage,
714
+ finishReason: "tool_use"
715
+ };
716
+ }
717
+ throw new GenerateObjectError(
718
+ "parse_failed",
719
+ "Schema parse failed after all retries.",
720
+ lastParseError
721
+ );
722
+ } finally {
723
+ await disposeAndDeleteTransient(agent, deps.delete);
724
+ }
725
+ }
726
+ var GenerateObjectError;
727
+ var init_generate_object = __esm({
728
+ "src/generate-object.ts"() {
729
+ init_structured_output_helpers();
730
+ GenerateObjectError = class extends Error {
731
+ name = "GenerateObjectError";
732
+ code;
733
+ cause;
734
+ constructor(code, message, cause) {
735
+ super(message);
736
+ this.code = code;
737
+ if (cause !== void 0) this.cause = cause;
738
+ }
739
+ };
740
+ }
741
+ });
742
+
743
+ // src/internal/runtime/registry/agent-factory-registry.ts
744
+ var agent_factory_registry_exports = {};
745
+ __export(agent_factory_registry_exports, {
746
+ getAgentFacade: () => getAgentFacade,
747
+ setAgentFacade: () => setAgentFacade
748
+ });
749
+ function setAgentFacade(facade) {
750
+ registered = facade;
751
+ }
752
+ function getAgentFacade() {
753
+ if (registered === void 0) {
754
+ throw new Error(
755
+ "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
756
+ );
757
+ }
758
+ return registered;
759
+ }
760
+ var registered;
761
+ var init_agent_factory_registry = __esm({
762
+ "src/internal/runtime/registry/agent-factory-registry.ts"() {
763
+ }
764
+ });
515
765
 
516
766
  // src/internal/persistence/cwd-mutex.ts
517
767
  function withCwdMutex(key, fn) {
@@ -1429,29 +1679,6 @@ var init_judge_call = __esm({
1429
1679
  }
1430
1680
  });
1431
1681
 
1432
- // src/internal/runtime/registry/agent-factory-registry.ts
1433
- var agent_factory_registry_exports = {};
1434
- __export(agent_factory_registry_exports, {
1435
- getAgentFacade: () => getAgentFacade,
1436
- setAgentFacade: () => setAgentFacade
1437
- });
1438
- function setAgentFacade(facade) {
1439
- registered3 = facade;
1440
- }
1441
- function getAgentFacade() {
1442
- if (registered3 === void 0) {
1443
- throw new Error(
1444
- "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
1445
- );
1446
- }
1447
- return registered3;
1448
- }
1449
- var registered3;
1450
- var init_agent_factory_registry = __esm({
1451
- "src/internal/runtime/registry/agent-factory-registry.ts"() {
1452
- }
1453
- });
1454
-
1455
1682
  // src/internal/runtime/lifecycle/run-to-completion.ts
1456
1683
  var run_to_completion_exports = {};
1457
1684
  __export(run_to_completion_exports, {
@@ -1616,14 +1843,14 @@ async function forkAgentImpl(parent, options, deps) {
1616
1843
  return {
1617
1844
  result: result.result,
1618
1845
  toolCalls: [],
1619
- usage: extractUsage(result)
1846
+ usage: extractUsage2(result)
1620
1847
  };
1621
1848
  });
1622
1849
  } finally {
1623
1850
  await fork.dispose();
1624
1851
  }
1625
1852
  }
1626
- function extractUsage(result) {
1853
+ function extractUsage2(result) {
1627
1854
  if (typeof result !== "object" || result === null) {
1628
1855
  return { inputTokens: 0, outputTokens: 0 };
1629
1856
  }
@@ -2057,323 +2284,117 @@ async function runWorkAndFinalize(taskId, aborter, work) {
2057
2284
  void checkCancelRequestedAt(taskId, aborter);
2058
2285
  emitToSubscribers(taskId, {
2059
2286
  type: "progress",
2060
- taskId,
2061
- at: Date.now(),
2062
- payload
2063
- });
2064
- }
2065
- };
2066
- try {
2067
- const result = await reentryAls.run(true, () => Promise.resolve().then(() => work(ctx)));
2068
- if (aborter.signal.aborted) {
2069
- const cancelledAt = Date.now();
2070
- await transition(taskId, "cancelled", { cancelledAt });
2071
- emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2072
- return;
2073
- }
2074
- const finishedAt = Date.now();
2075
- await transition(taskId, "finished", { finishedAt, result });
2076
- emitToSubscribers(taskId, { type: "finished", taskId, finishedAt, result });
2077
- } catch (err) {
2078
- if (aborter.signal.aborted) {
2079
- const cancelledAt = Date.now();
2080
- await transition(taskId, "cancelled", { cancelledAt });
2081
- emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2082
- return;
2083
- }
2084
- const erroredAt = Date.now();
2085
- const error = err instanceof Error ? { code: err.code ?? "work_threw", message: err.message } : { code: "work_threw", message: String(err) };
2086
- await transition(taskId, "error", { erroredAt, error });
2087
- emitToSubscribers(taskId, { type: "errored", taskId, erroredAt, error });
2088
- }
2089
- }
2090
- async function checkCancelRequestedAt(taskId, aborter) {
2091
- if (aborter.signal.aborted) return;
2092
- try {
2093
- const handle = await state.store.get(taskId);
2094
- if (handle?.cancelRequested === true) {
2095
- aborter.abort("cancelRequested");
2096
- }
2097
- } catch {
2098
- }
2099
- }
2100
- async function acquireSlot() {
2101
- if (reentryAls.getStore() === true) {
2102
- return () => {
2103
- };
2104
- }
2105
- return state.semaphore.acquire();
2106
- }
2107
- async function submit(internal) {
2108
- state.firstSubmitSeen = true;
2109
- startEvictTimerIfNeeded();
2110
- const resolvedId = resolveIdInternal(internal);
2111
- const submitSpan = startTaskSubmitSpan({ taskId: resolvedId, kind: internal.kind });
2112
- const existing = await state.store.get(resolvedId);
2113
- if (existing !== void 0) {
2114
- submitSpan.end();
2115
- return existing;
2116
- }
2117
- const queuedHandle = await buildAndInsertQueued(internal, resolvedId);
2118
- submitSpan.end();
2119
- if (internal.signal?.aborted === true) {
2120
- return shortCircuitAborted(queuedHandle, internal.signal);
2121
- }
2122
- const aborter = new AbortController();
2123
- state.aborters.set(resolvedId, aborter);
2124
- if (internal.signal !== void 0) {
2125
- internal.signal.addEventListener("abort", () => aborter.abort(internal.signal?.reason), {
2126
- once: true
2127
- });
2128
- }
2129
- const release = await acquireSlot();
2130
- void (async () => {
2131
- try {
2132
- await checkCancelRequestedAt(resolvedId, aborter);
2133
- if (aborter.signal.aborted) {
2134
- const cancelledAt = Date.now();
2135
- await transition(resolvedId, "cancelled", { cancelledAt });
2136
- emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
2137
- return;
2138
- }
2139
- const startedAt = Date.now();
2140
- await transition(resolvedId, "running", { startedAt });
2141
- emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
2142
- await runWorkAndFinalize(resolvedId, aborter, internal.work);
2143
- } finally {
2144
- release();
2145
- state.aborters.delete(resolvedId);
2146
- }
2147
- })();
2148
- return queuedHandle;
2149
- }
2150
- async function get(id) {
2151
- if (!isValidTaskId(id, true)) return void 0;
2152
- return state.store.get(id);
2153
- }
2154
- var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state;
2155
- var init_registry = __esm({
2156
- "src/internal/task/registry.ts"() {
2157
- init_errors();
2158
- init_task();
2159
- init_async_semaphore();
2160
- init_ring_buffer();
2161
- init_store();
2162
- init_subscribe();
2163
- init_telemetry();
2164
- DEFAULT_CONCURRENCY = 8;
2165
- DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
2166
- EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
2167
- RING_CAP = 64;
2168
- reentryAls = new async_hooks.AsyncLocalStorage();
2169
- state = buildState({});
2170
- }
2171
- });
2172
- function toJsonSchema(schema, options = { unrepresentable: "any" }) {
2173
- return zod.toJSONSchema(schema, options);
2174
- }
2175
- var init_to_json_schema = __esm({
2176
- "src/internal/zod/to-json-schema.ts"() {
2177
- }
2178
- });
2179
- function requireZod() {
2180
- if (cachedZ !== void 0) return cachedZ;
2181
- const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('eval.cjs', document.baseURI).href)));
2182
- let mod;
2183
- try {
2184
- mod = r("zod");
2185
- } catch (cause) {
2186
- throw new ConfigurationError(
2187
- 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
2188
- { code: "zod_not_installed", cause }
2189
- );
2190
- }
2191
- cachedZ = mod.z ?? mod;
2192
- return cachedZ;
2193
- }
2194
- function makeOutputTool(jsonSchema, onCapture) {
2195
- return {
2196
- name: "output",
2197
- description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
2198
- inputSchema: jsonSchema,
2199
- handler: (input) => {
2200
- const out = onCapture(input);
2201
- if (typeof out === "string") return out;
2202
- if (out instanceof Promise) return out;
2203
- return "";
2204
- }
2205
- };
2206
- }
2207
- function buildTransientAgentOptions(params) {
2208
- return {
2209
- model: params.model,
2210
- local: params.local,
2211
- tools: [params.outputTool],
2212
- systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
2213
- ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
2214
- ...params.providers !== void 0 ? { providers: params.providers } : {}
2215
- };
2216
- }
2217
- function extractUsage2(result) {
2218
- const u = result.usage;
2219
- if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
2220
- return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
2221
- }
2222
- function buildToolPrompt(prompt) {
2223
- return `${prompt}
2224
-
2225
- Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
2226
- }
2227
- function setupStructuredOutput(schema, maxRetries) {
2228
- const z7 = requireZod();
2229
- const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
2230
- return {
2231
- z: z7,
2232
- jsonSchema,
2233
- maxRetries: maxRetries ?? 1,
2234
- initialUsage: { inputTokens: 0, outputTokens: 0 }
2235
- };
2236
- }
2237
- async function disposeAndDeleteTransient(agent, deletor) {
2238
- await agent.dispose();
2239
- try {
2240
- await deletor(agent.agentId);
2241
- } catch {
2242
- }
2243
- }
2244
- var cachedZ;
2245
- var init_structured_output_helpers = __esm({
2246
- "src/internal/structured-output-helpers.ts"() {
2247
- init_errors();
2248
- init_to_json_schema();
2249
- }
2250
- });
2251
-
2252
- // src/generate-object.ts
2253
- var generate_object_exports = {};
2254
- __export(generate_object_exports, {
2255
- GenerateObjectError: () => GenerateObjectError,
2256
- generateObjectImpl: () => generateObjectImpl
2257
- });
2258
- function salvagePartial(schema, raw) {
2259
- if (typeof raw !== "object" || raw === null) return raw;
2260
- const shape = schema.shape;
2261
- if (shape === void 0 || typeof shape !== "object") return raw;
2262
- const rawObj = raw;
2263
- const out = {};
2264
- for (const [key, fieldSchema] of Object.entries(shape)) {
2265
- if (typeof fieldSchema?.safeParse !== "function") continue;
2266
- const parsed = fieldSchema.safeParse(rawObj[key]);
2267
- if (parsed.success) out[key] = parsed.data;
2268
- }
2269
- return out;
2270
- }
2271
- async function generateObjectImpl(options, deps) {
2272
- const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
2273
- options.schema,
2274
- options.maxRetries
2275
- );
2276
- let capturedRaw;
2277
- let lastUsage = initialUsage;
2278
- const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
2279
- class CaptureSentinel extends Error {
2280
- constructor(captured) {
2281
- super("generate-object-capture");
2282
- this.captured = captured;
2283
- }
2284
- captured;
2285
- [sentinel] = true;
2286
- }
2287
- const outputTool = makeOutputTool(jsonSchema, (input) => {
2288
- if (capturedRaw === void 0) {
2289
- capturedRaw = input;
2290
- }
2291
- throw new CaptureSentinel(input);
2292
- });
2293
- const agentOptions = buildTransientAgentOptions({
2294
- model: options.model,
2295
- local: options.local,
2296
- outputTool,
2297
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
2298
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
2299
- ...options.providers !== void 0 ? { providers: options.providers } : {}
2300
- });
2301
- const agent = await deps.create(agentOptions);
2287
+ taskId,
2288
+ at: Date.now(),
2289
+ payload
2290
+ });
2291
+ }
2292
+ };
2302
2293
  try {
2303
- const userMessage = buildToolPrompt(options.prompt);
2304
- let lastParseError;
2305
- for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
2306
- capturedRaw = void 0;
2307
- const run = await agent.send(userMessage);
2308
- const result = await run.wait();
2309
- lastUsage = extractUsage2(result);
2310
- if (capturedRaw === void 0) {
2311
- if (result.status === "error" && result.error !== void 0) {
2312
- throw new GenerateObjectError(
2313
- "no_tool_call",
2314
- `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
2315
- result.error
2316
- );
2317
- }
2318
- if (attempt === maxRetries) {
2319
- throw new GenerateObjectError(
2320
- "no_tool_call",
2321
- "The model returned text instead of calling the `output` tool."
2322
- );
2323
- }
2324
- continue;
2325
- }
2326
- const parsed = options.schema.safeParse(capturedRaw);
2327
- if (parsed.success) {
2328
- return {
2329
- object: parsed.data,
2330
- raw: capturedRaw,
2331
- usage: lastUsage,
2332
- finishReason: "tool_use"
2333
- };
2334
- }
2335
- lastParseError = parsed.error;
2294
+ const result = await reentryAls.run(true, () => Promise.resolve().then(() => work(ctx)));
2295
+ if (aborter.signal.aborted) {
2296
+ const cancelledAt = Date.now();
2297
+ await transition(taskId, "cancelled", { cancelledAt });
2298
+ emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2299
+ return;
2336
2300
  }
2337
- if (options.errorStrategy === "return-raw") {
2338
- return {
2339
- object: capturedRaw,
2340
- raw: capturedRaw,
2341
- usage: lastUsage,
2342
- finishReason: "tool_use"
2343
- };
2301
+ const finishedAt = Date.now();
2302
+ await transition(taskId, "finished", { finishedAt, result });
2303
+ emitToSubscribers(taskId, { type: "finished", taskId, finishedAt, result });
2304
+ } catch (err) {
2305
+ if (aborter.signal.aborted) {
2306
+ const cancelledAt = Date.now();
2307
+ await transition(taskId, "cancelled", { cancelledAt });
2308
+ emitToSubscribers(taskId, { type: "cancelled", taskId, cancelledAt });
2309
+ return;
2344
2310
  }
2345
- if (options.errorStrategy === "return-partial") {
2346
- return {
2347
- object: salvagePartial(options.schema, capturedRaw),
2348
- raw: capturedRaw,
2349
- usage: lastUsage,
2350
- finishReason: "tool_use"
2351
- };
2311
+ const erroredAt = Date.now();
2312
+ const error = err instanceof Error ? { code: err.code ?? "work_threw", message: err.message } : { code: "work_threw", message: String(err) };
2313
+ await transition(taskId, "error", { erroredAt, error });
2314
+ emitToSubscribers(taskId, { type: "errored", taskId, erroredAt, error });
2315
+ }
2316
+ }
2317
+ async function checkCancelRequestedAt(taskId, aborter) {
2318
+ if (aborter.signal.aborted) return;
2319
+ try {
2320
+ const handle = await state.store.get(taskId);
2321
+ if (handle?.cancelRequested === true) {
2322
+ aborter.abort("cancelRequested");
2352
2323
  }
2353
- throw new GenerateObjectError(
2354
- "parse_failed",
2355
- "Schema parse failed after all retries.",
2356
- lastParseError
2357
- );
2358
- } finally {
2359
- await disposeAndDeleteTransient(agent, deps.delete);
2324
+ } catch {
2360
2325
  }
2361
2326
  }
2362
- var GenerateObjectError;
2363
- var init_generate_object = __esm({
2364
- "src/generate-object.ts"() {
2365
- init_structured_output_helpers();
2366
- GenerateObjectError = class extends Error {
2367
- name = "GenerateObjectError";
2368
- code;
2369
- cause;
2370
- constructor(code, message, cause) {
2371
- super(message);
2372
- this.code = code;
2373
- if (cause !== void 0) this.cause = cause;
2374
- }
2327
+ async function acquireSlot() {
2328
+ if (reentryAls.getStore() === true) {
2329
+ return () => {
2375
2330
  };
2376
2331
  }
2332
+ return state.semaphore.acquire();
2333
+ }
2334
+ async function submit(internal) {
2335
+ state.firstSubmitSeen = true;
2336
+ startEvictTimerIfNeeded();
2337
+ const resolvedId = resolveIdInternal(internal);
2338
+ const submitSpan = startTaskSubmitSpan({ taskId: resolvedId, kind: internal.kind });
2339
+ const existing = await state.store.get(resolvedId);
2340
+ if (existing !== void 0) {
2341
+ submitSpan.end();
2342
+ return existing;
2343
+ }
2344
+ const queuedHandle = await buildAndInsertQueued(internal, resolvedId);
2345
+ submitSpan.end();
2346
+ if (internal.signal?.aborted === true) {
2347
+ return shortCircuitAborted(queuedHandle, internal.signal);
2348
+ }
2349
+ const aborter = new AbortController();
2350
+ state.aborters.set(resolvedId, aborter);
2351
+ if (internal.signal !== void 0) {
2352
+ internal.signal.addEventListener("abort", () => aborter.abort(internal.signal?.reason), {
2353
+ once: true
2354
+ });
2355
+ }
2356
+ const release = await acquireSlot();
2357
+ void (async () => {
2358
+ try {
2359
+ await checkCancelRequestedAt(resolvedId, aborter);
2360
+ if (aborter.signal.aborted) {
2361
+ const cancelledAt = Date.now();
2362
+ await transition(resolvedId, "cancelled", { cancelledAt });
2363
+ emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
2364
+ return;
2365
+ }
2366
+ const startedAt = Date.now();
2367
+ await transition(resolvedId, "running", { startedAt });
2368
+ emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
2369
+ await runWorkAndFinalize(resolvedId, aborter, internal.work);
2370
+ } finally {
2371
+ release();
2372
+ state.aborters.delete(resolvedId);
2373
+ }
2374
+ })();
2375
+ return queuedHandle;
2376
+ }
2377
+ async function get(id) {
2378
+ if (!isValidTaskId(id, true)) return void 0;
2379
+ return state.store.get(id);
2380
+ }
2381
+ var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state;
2382
+ var init_registry = __esm({
2383
+ "src/internal/task/registry.ts"() {
2384
+ init_errors();
2385
+ init_task();
2386
+ init_async_semaphore();
2387
+ init_ring_buffer();
2388
+ init_store();
2389
+ init_subscribe();
2390
+ init_telemetry();
2391
+ DEFAULT_CONCURRENCY = 8;
2392
+ DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
2393
+ EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
2394
+ RING_CAP = 64;
2395
+ reentryAls = new async_hooks.AsyncLocalStorage();
2396
+ state = buildState({});
2397
+ }
2377
2398
  });
2378
2399
 
2379
2400
  // src/stream-object.ts
@@ -2430,7 +2451,7 @@ async function* streamObjectImpl(options, deps) {
2430
2451
  }
2431
2452
  }
2432
2453
  const result = await run.wait();
2433
- lastUsage = extractUsage2(result);
2454
+ lastUsage = extractUsage(result);
2434
2455
  if (capturedRaw === void 0) {
2435
2456
  if (result.status === "error" && result.error !== void 0) {
2436
2457
  throw new StreamObjectError(
@@ -2776,6 +2797,7 @@ var AgentBuilder = class {
2776
2797
  constructor(deps) {
2777
2798
  this.deps = deps;
2778
2799
  }
2800
+ /** SE8 — accepts a bare-string id shorthand (`"openai/gpt-4o-mini"`) or a {@link ModelSelection}. */
2779
2801
  model(m) {
2780
2802
  this.opts.model = m;
2781
2803
  return this;
@@ -3125,6 +3147,69 @@ function isLocalAgentId(id) {
3125
3147
  return id.startsWith(PREFIX.localAgent);
3126
3148
  }
3127
3149
 
3150
+ // src/internal/runtime/config/default-model.ts
3151
+ var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
3152
+
3153
+ // src/agent-generate.ts
3154
+ async function agentGenerate(agent, agentModel, agentApiKey, agentLocal, input, options) {
3155
+ const { output, maxRetries, errorStrategy, ...sendOptions } = options;
3156
+ const run = await agent.send(input, sendOptions);
3157
+ const result = await run.wait();
3158
+ const { generateObjectImpl: generateObjectImpl2, GenerateObjectError: GenerateObjectError2 } = await Promise.resolve().then(() => (init_generate_object(), generate_object_exports));
3159
+ const answer = structurableAnswerOrThrow(result, GenerateObjectError2);
3160
+ const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
3161
+ const facade = getAgentFacade2();
3162
+ const structured = await generateObjectImpl2(
3163
+ {
3164
+ prompt: answer,
3165
+ schema: output,
3166
+ // `agentModel` arrives already normalized (`this.model` passed through
3167
+ // `normalizeModel` in the LocalAgent/CloudAgent constructor), so the widened
3168
+ // `string | ModelSelection` field only ever receives a `ModelSelection` here.
3169
+ model: agentModel ?? { id: DEFAULT_AGENTIC_MODEL_ID },
3170
+ // `local` is required to keep the transient structuring agent local-only;
3171
+ // default to the process cwd when the agent had no explicit local config.
3172
+ local: agentLocal ?? { cwd: process.cwd() },
3173
+ ...agentApiKey !== void 0 ? { apiKey: agentApiKey } : {},
3174
+ ...maxRetries !== void 0 ? { maxRetries } : {},
3175
+ ...errorStrategy !== void 0 ? { errorStrategy } : {}
3176
+ },
3177
+ {
3178
+ create: (opts) => facade.create(opts),
3179
+ delete: (agentId) => facade.delete(agentId)
3180
+ }
3181
+ );
3182
+ return {
3183
+ object: structured.object,
3184
+ result,
3185
+ raw: structured.raw,
3186
+ usage: structured.usage
3187
+ };
3188
+ }
3189
+ function structurableAnswerOrThrow(result, GenerateObjectError2) {
3190
+ if (result.status === "error") {
3191
+ throw new GenerateObjectError2(
3192
+ "no_tool_call",
3193
+ `Agent run failed before an answer could be structured: ${result.error?.message ?? "unknown error"} [${result.error?.code ?? "?"}]`,
3194
+ result.error
3195
+ );
3196
+ }
3197
+ if (result.status === "cancelled") {
3198
+ throw new GenerateObjectError2(
3199
+ "no_tool_call",
3200
+ "Agent run was cancelled before an answer could be structured."
3201
+ );
3202
+ }
3203
+ const answer = result.result;
3204
+ if (answer === void 0 || answer.length === 0) {
3205
+ throw new GenerateObjectError2(
3206
+ "no_tool_call",
3207
+ "The tool loop finished with no text answer (tool-only completion) \u2014 there is nothing to structure."
3208
+ );
3209
+ }
3210
+ return answer;
3211
+ }
3212
+
3128
3213
  // src/internal/runtime/cloud/cloud-agent.ts
3129
3214
  init_errors();
3130
3215
  init_cwd_mutex();
@@ -3275,8 +3360,19 @@ function safeFilenameForId(id, options) {
3275
3360
  return `h-${crypto.createHash("sha256").update(id).digest("hex").slice(0, 16)}`;
3276
3361
  }
3277
3362
 
3278
- // src/internal/runtime/config/default-model.ts
3279
- var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
3363
+ // src/internal/runtime/model-selection.ts
3364
+ init_errors();
3365
+ function normalizeModel(m) {
3366
+ if (m === void 0 || typeof m !== "string") return m;
3367
+ const id = m.trim();
3368
+ if (id.length === 0) {
3369
+ throw new ConfigurationError(
3370
+ 'model must be a non-empty "provider/model" id string or a ModelSelection object',
3371
+ { code: "invalid_model_selection" }
3372
+ );
3373
+ }
3374
+ return { id };
3375
+ }
3280
3376
 
3281
3377
  // src/internal/runtime/registry/agent-registry-store.ts
3282
3378
  init_cwd_mutex();
@@ -3353,7 +3449,10 @@ var REGISTRY_RELATIVE_PATH = path.join(".theokit", "agents", "registry.json");
3353
3449
  function stripSecretsFromOptions(options) {
3354
3450
  return assignDefined({
3355
3451
  name: options.name,
3356
- model: options.model !== void 0 ? { id: options.model.id } : void 0,
3452
+ model: (() => {
3453
+ const m = normalizeModel(options.model);
3454
+ return m !== void 0 ? { id: m.id } : void 0;
3455
+ })(),
3357
3456
  systemPrompt: typeof options.systemPrompt === "string" ? options.systemPrompt : void 0,
3358
3457
  local: serializeLocal(options.local),
3359
3458
  cloud: serializeCloud(options.cloud),
@@ -3638,7 +3737,8 @@ function serializeCloudAgentConfig(options) {
3638
3737
  cloud: serializeCloud2(options.cloud)
3639
3738
  };
3640
3739
  if (typeof options.agentId === "string") payload.agentId = options.agentId;
3641
- if (options.model !== void 0) payload.model = { id: options.model.id };
3740
+ const cfgModel = normalizeModel(options.model);
3741
+ if (cfgModel !== void 0) payload.model = { id: cfgModel.id };
3642
3742
  if (typeof options.systemPrompt === "string") payload.systemPrompt = options.systemPrompt;
3643
3743
  const skills = serializeSkills(options.skills);
3644
3744
  if (skills !== void 0) payload.skills = skills;
@@ -4534,6 +4634,7 @@ function applyScriptMetrics(base, script) {
4534
4634
  if (script.cost !== void 0) base.cost = script.cost;
4535
4635
  if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
4536
4636
  if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
4637
+ if (script.origin !== void 0) base.origin = script.origin;
4537
4638
  }
4538
4639
 
4539
4640
  // src/internal/runtime/cloud/cloud-run.ts
@@ -4889,7 +4990,7 @@ var CloudAgent = class {
4889
4990
  disposed = false;
4890
4991
  constructor(options, providedAgentId) {
4891
4992
  this.agentId = providedAgentId ?? options.agentId ?? generateCloudAgentId();
4892
- this.model = options.model;
4993
+ this.model = normalizeModel(options.model);
4893
4994
  this.options = options;
4894
4995
  this.cloudPayload = serializeCloudAgentConfig({ ...options, agentId: this.agentId });
4895
4996
  const repoUrls = (options.cloud?.repos ?? []).map((repo) => repo.url);
@@ -4919,6 +5020,17 @@ var CloudAgent = class {
4919
5020
  const apiKey = resolveApiKey(this.options.apiKey);
4920
5021
  return isFixtureApiKey(apiKey) && getConfiguredBaseUrl() === void 0;
4921
5022
  }
5023
+ // SE9 — integrated structured output; delegates to the shared helper.
5024
+ generate(message, options) {
5025
+ return agentGenerate(
5026
+ this,
5027
+ this.model,
5028
+ this.options.apiKey,
5029
+ this.options.local,
5030
+ message,
5031
+ options
5032
+ );
5033
+ }
4922
5034
  async send(message, options = {}) {
4923
5035
  if (options.tools !== void 0 && options.tools.length > 0) {
4924
5036
  throw new ConfigurationError(
@@ -4949,7 +5061,7 @@ var CloudAgent = class {
4949
5061
  });
4950
5062
  }
4951
5063
  async sendLocked(message, options) {
4952
- const overrideModel = options.model;
5064
+ const overrideModel = normalizeModel(options.model);
4953
5065
  if (overrideModel !== void 0) {
4954
5066
  this.model = overrideModel;
4955
5067
  updateRegisteredAgent(this.agentId, { model: overrideModel });
@@ -5863,18 +5975,18 @@ var ALL_ADAPTERS = [
5863
5975
  arizeAdapter,
5864
5976
  braintrustAdapter
5865
5977
  ];
5866
- var registered = /* @__PURE__ */ new Set();
5978
+ var registered2 = /* @__PURE__ */ new Set();
5867
5979
  function tryAutoRegisterAdapters(settings) {
5868
5980
  if (settings?.enabled !== true) return;
5869
5981
  if (settings.autoDetect === false) return;
5870
5982
  const disabled = new Set(settings.disable ?? []);
5871
5983
  for (const adapter of ALL_ADAPTERS) {
5872
- if (registered.has(adapter.moduleName)) continue;
5984
+ if (registered2.has(adapter.moduleName)) continue;
5873
5985
  if (disabled.has(adapter.displayName.toLowerCase())) continue;
5874
5986
  if (!adapter.detect()) continue;
5875
5987
  try {
5876
5988
  adapter.register();
5877
- registered.add(adapter.moduleName);
5989
+ registered2.add(adapter.moduleName);
5878
5990
  process.stderr.write(`[theokit-sdk] telemetry: ${adapter.displayName} auto-instrumented.
5879
5991
  `);
5880
5992
  } catch (cause) {
@@ -6437,6 +6549,26 @@ function paginate(items, opts) {
6437
6549
  return items.slice(start, end);
6438
6550
  }
6439
6551
 
6552
+ // src/internal/persistence/session-meta.ts
6553
+ function applyMetaPatch(current, patch) {
6554
+ const next = {};
6555
+ if (current.title !== void 0) next.title = current.title;
6556
+ if (current.tag !== void 0) next.tag = current.tag;
6557
+ if (patch.title === null) delete next.title;
6558
+ else if (patch.title !== void 0) next.title = patch.title;
6559
+ if (patch.tag === null) delete next.tag;
6560
+ else if (patch.tag !== void 0) next.tag = patch.tag;
6561
+ return next;
6562
+ }
6563
+ function coerceSessionMeta(raw) {
6564
+ if (typeof raw !== "object" || raw === null) return void 0;
6565
+ const obj = raw;
6566
+ const meta = {};
6567
+ if (typeof obj.title === "string") meta.title = obj.title;
6568
+ if (typeof obj.tag === "string") meta.tag = obj.tag;
6569
+ return meta.title === void 0 && meta.tag === void 0 ? void 0 : meta;
6570
+ }
6571
+
6440
6572
  // src/internal/persistence/conversation-storage-fs.ts
6441
6573
  var FileSystemConversationStorage = class {
6442
6574
  #root;
@@ -6487,6 +6619,37 @@ var FileSystemConversationStorage = class {
6487
6619
  async compact(conversationId, maxTurns) {
6488
6620
  await compactSessionFile(this.#root, conversationId, maxTurns);
6489
6621
  }
6622
+ // SE4 — session metadata persisted as a per-conversation sidecar
6623
+ // `<root>/.theokit/agents/<safeId>/session.json` (same sanitized perimeter as
6624
+ // the transcript). Kept separate from messages.jsonl so a title/tag write does
6625
+ // not rewrite the append-only log.
6626
+ #metaPath(conversationId) {
6627
+ const safe3 = sanitizeIdentifier(conversationId, { maxLen: 128 });
6628
+ return safePathJoin(this.#root, ".theokit", "agents", safe3, "session.json");
6629
+ }
6630
+ async getSessionMeta(conversationId) {
6631
+ try {
6632
+ const raw = await promises.readFile(this.#metaPath(conversationId), "utf8");
6633
+ return coerceSessionMeta(JSON.parse(raw));
6634
+ } catch (cause) {
6635
+ if (cause.code === "ENOENT") return void 0;
6636
+ throw cause;
6637
+ }
6638
+ }
6639
+ async setSessionMeta(conversationId, patch) {
6640
+ const metaPath = this.#metaPath(conversationId);
6641
+ await promises.mkdir(path.dirname(metaPath), { recursive: true });
6642
+ await withFileLock(metaPath, async () => {
6643
+ let current = {};
6644
+ try {
6645
+ current = coerceSessionMeta(JSON.parse(await promises.readFile(metaPath, "utf8"))) ?? {};
6646
+ } catch (cause) {
6647
+ if (cause.code !== "ENOENT") throw cause;
6648
+ }
6649
+ const next = applyMetaPatch(current, patch);
6650
+ await promises.writeFile(metaPath, redactSecrets(JSON.stringify(next)), "utf8");
6651
+ });
6652
+ }
6490
6653
  async dispose() {
6491
6654
  }
6492
6655
  };
@@ -8336,23 +8499,27 @@ function tryParseSkill(raw, fallbackName, source, options) {
8336
8499
 
8337
8500
  // src/internal/runtime/skills/skills-manager.ts
8338
8501
  var SkillsManager = class {
8339
- constructor(cwd, _enabled, settingSourcesIncludeProject) {
8502
+ constructor(cwd, _enabled, settingSourcesIncludeProject, skillsDir, inline) {
8340
8503
  this.cwd = cwd;
8341
8504
  this.settingSourcesIncludeProject = settingSourcesIncludeProject;
8505
+ this.skillsDir = skillsDir;
8506
+ this.inline = inline;
8342
8507
  }
8343
8508
  cwd;
8344
8509
  settingSourcesIncludeProject;
8510
+ skillsDir;
8511
+ inline;
8345
8512
  skills = [];
8346
8513
  async initialize() {
8347
8514
  if (!this.settingSourcesIncludeProject) {
8348
- this.skills = [];
8515
+ this.skills = this.mergeInline([]);
8349
8516
  return;
8350
8517
  }
8351
8518
  await this.refresh();
8352
8519
  }
8353
8520
  async refresh() {
8354
- const skillsRoot = path.join(this.cwd, ".theokit", "skills");
8355
- this.skills = await discoverSkills(skillsRoot, {
8521
+ const skillsRoot = this.skillsDir ?? path.join(this.cwd, ".theokit", "skills");
8522
+ const discovered = await discoverSkills(skillsRoot, {
8356
8523
  onInvalidSkill: (info) => {
8357
8524
  process.stderr.write(
8358
8525
  `[theokit-sdk] skill ${info.name} skipped (${info.code}): ${info.message}
@@ -8360,6 +8527,13 @@ var SkillsManager = class {
8360
8527
  );
8361
8528
  }
8362
8529
  });
8530
+ this.skills = this.mergeInline(discovered);
8531
+ }
8532
+ /** M22 — merge inline skills over discovered ones; inline wins on a name conflict. */
8533
+ mergeInline(discovered) {
8534
+ if (this.inline === void 0 || this.inline.length === 0) return discovered;
8535
+ const inlineNames = new Set(this.inline.map((s) => s.name));
8536
+ return [...discovered.filter((s) => !inlineNames.has(s.name)), ...this.inline];
8363
8537
  }
8364
8538
  list() {
8365
8539
  return Promise.resolve(this.skills);
@@ -8395,7 +8569,7 @@ function bootstrapSubmanagers(args) {
8395
8569
  const providerCount = (args.options.providers?.routes?.length ?? 0) + enabledPluginNames(args.options.plugins).length;
8396
8570
  if (providerCount > 0 || args.options.providers !== void 0) {
8397
8571
  out.providers = new ProvidersManagerImpl(
8398
- args.options.model,
8572
+ normalizeModel(args.options.model),
8399
8573
  args.options.providers,
8400
8574
  args.options.plugins
8401
8575
  );
@@ -8404,7 +8578,10 @@ function bootstrapSubmanagers(args) {
8404
8578
  out.skillsManager = new SkillsManager(
8405
8579
  args.workspaceCwd,
8406
8580
  args.options.skills?.enabled,
8407
- args.settingSourcesIncludeProject
8581
+ args.settingSourcesIncludeProject,
8582
+ // M22 — custom skills directory + inline (code-defined) skills.
8583
+ args.options.skills?.skillsDir,
8584
+ args.options.skills?.inline
8408
8585
  );
8409
8586
  const localSkills = out.skillsManager;
8410
8587
  out.skills = { list: () => localSkills.list() };
@@ -8462,6 +8639,7 @@ function createLocalRun(options) {
8462
8639
  projectMcpServers: options.projectMcpServers,
8463
8640
  ...options.persistMemoryFact !== void 0 ? { persistMemoryFact: options.persistMemoryFact } : {}
8464
8641
  });
8642
+ if (options.sendOptions.origin !== void 0) script.origin = options.sendOptions.origin;
8465
8643
  const handle = new LocalRun({
8466
8644
  id,
8467
8645
  agentId: options.agentId,
@@ -9110,6 +9288,15 @@ async function emitReasoningDeltaCallback(inputs, text) {
9110
9288
  );
9111
9289
  }
9112
9290
 
9291
+ // src/types/run-events.ts
9292
+ function emitRunEvent(sink, event) {
9293
+ if (sink === void 0) return;
9294
+ try {
9295
+ sink(event);
9296
+ } catch {
9297
+ }
9298
+ }
9299
+
9113
9300
  // src/internal/agent-loop/tool-dispatch.ts
9114
9301
  init_async_local_storage();
9115
9302
 
@@ -9207,6 +9394,22 @@ function coerceArgsToSchema(args, schema) {
9207
9394
  return { value: out, changed };
9208
9395
  }
9209
9396
 
9397
+ // src/tool-error.ts
9398
+ init_errors();
9399
+ var ToolError = class extends TheokitAgentError {
9400
+ name = "ToolError";
9401
+ /** The error content surfaced to the model: a string, or text/image blocks. */
9402
+ content;
9403
+ constructor(content, options = {}) {
9404
+ super(renderToolErrorMessage(content), { ...options, isRetryable: false });
9405
+ this.content = content;
9406
+ }
9407
+ };
9408
+ function renderToolErrorMessage(content) {
9409
+ if (typeof content === "string") return content;
9410
+ return content.map((block) => block.type === "text" ? block.text : `[${block.source.media_type} image]`).join("\n");
9411
+ }
9412
+
9210
9413
  // src/internal/runtime/tools/shell-tool.ts
9211
9414
  async function runShell(options) {
9212
9415
  if (options.sandbox === true && isObviouslyUnsafe(options.command)) {
@@ -9260,9 +9463,13 @@ async function runHandlerTool(kind, handler, call, signal, context) {
9260
9463
  return { stdout: "", stderr: `${kind} tool ${call.name} has no handler`, exitCode: 127 };
9261
9464
  }
9262
9465
  try {
9263
- const stdout = await handler(call.input, { signal, context });
9264
- return { stdout, stderr: "", exitCode: 0 };
9466
+ const out = await handler(call.input, { signal, context });
9467
+ if (typeof out !== "string") return { stdout: "", stderr: "", exitCode: 0, content: out };
9468
+ return { stdout: out, stderr: "", exitCode: 0 };
9265
9469
  } catch (cause) {
9470
+ if (cause instanceof ToolError) {
9471
+ return { stdout: "", stderr: cause.message, exitCode: 1, content: cause.content };
9472
+ }
9266
9473
  const message = cause instanceof Error ? cause.message : String(cause);
9267
9474
  return { stdout: "", stderr: message, exitCode: 1 };
9268
9475
  }
@@ -9381,6 +9588,11 @@ async function dispatchSingleCall(inputs, tools, call, events, parentSpan) {
9381
9588
  toolSpan?.end();
9382
9589
  return fileVeto;
9383
9590
  }
9591
+ emitRunEvent(inputs.runEventSink, {
9592
+ type: "tool_progress",
9593
+ toolName: workingCall.name,
9594
+ toolCallId: callId
9595
+ });
9384
9596
  const result = await runToolWithLifecycle(inputs, resolved, workingCall, callId);
9385
9597
  await inputs.pluginManager?.runPostToolCallHooks({
9386
9598
  name: workingCall.name,
@@ -9407,6 +9619,13 @@ function applyRepairAndExtractCall(tools, call) {
9407
9619
  function vetoFromForkWhitelist(inputs, call, callId, events) {
9408
9620
  const whitelistDecision = checkToolWhitelist(call.name);
9409
9621
  if (whitelistDecision.allowed) return void 0;
9622
+ emitRunEvent(inputs.runEventSink, {
9623
+ type: "permission_denied",
9624
+ toolName: call.name,
9625
+ toolCallId: callId,
9626
+ source: "fork_whitelist",
9627
+ message: whitelistDecision.reason ?? "tool not available in fork"
9628
+ });
9410
9629
  events.push(buildToolUseRunning(inputs, callId, call));
9411
9630
  events.push(
9412
9631
  buildToolUseCompleted(inputs, callId, call, {
@@ -9443,6 +9662,13 @@ async function vetoFromPluginPreHook(inputs, call, callId, events) {
9443
9662
  runId: inputs.runId
9444
9663
  });
9445
9664
  if (pluginVeto === void 0) return void 0;
9665
+ emitRunEvent(inputs.runEventSink, {
9666
+ type: "permission_denied",
9667
+ toolName: call.name,
9668
+ toolCallId: callId,
9669
+ source: "plugin",
9670
+ message: pluginVeto.message
9671
+ });
9446
9672
  events.push(
9447
9673
  buildToolUseCompleted(inputs, callId, call, {
9448
9674
  stdout: "",
@@ -9465,6 +9691,13 @@ async function vetoFromFileHookPreDecision(inputs, call, callId, events) {
9465
9691
  runId: inputs.runId
9466
9692
  });
9467
9693
  if (!preDecision.blocked) return void 0;
9694
+ emitRunEvent(inputs.runEventSink, {
9695
+ type: "permission_denied",
9696
+ toolName: call.name,
9697
+ toolCallId: callId,
9698
+ source: "file_hook",
9699
+ message: preDecision.reason ?? "blocked by hook"
9700
+ });
9468
9701
  events.push(
9469
9702
  buildToolUseCompleted(inputs, callId, call, {
9470
9703
  stdout: "",
@@ -9508,7 +9741,9 @@ async function runToolWithLifecycle(inputs, resolved, call, callId) {
9508
9741
  await safeEmitToolHook(inputs.onToolEnd, {
9509
9742
  toolName: call.name,
9510
9743
  args: call.input,
9511
- result: result.stdout,
9744
+ // SE7 — a block-returning handler has empty `stdout`; surface its structured
9745
+ // content to the hook instead of an empty string.
9746
+ result: result.content !== void 0 ? result.content : result.stdout,
9512
9747
  conversationId: inputs.agentId,
9513
9748
  callId,
9514
9749
  durationMs
@@ -9530,7 +9765,10 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
9530
9765
  output: {
9531
9766
  stdout: result.stdout,
9532
9767
  stderr: result.stderr,
9533
- exitCode: result.exitCode ?? 0
9768
+ exitCode: result.exitCode ?? 0,
9769
+ // SE7 — structured content (text + image blocks) when the handler
9770
+ // returned/threw blocks; absent for the string path.
9771
+ ...result.content !== void 0 ? { content: result.content } : {}
9534
9772
  },
9535
9773
  agentId: inputs.agentId,
9536
9774
  runId: inputs.runId
@@ -9543,7 +9781,9 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
9543
9781
  return {
9544
9782
  type: "tool_result",
9545
9783
  toolUseId: call.id,
9546
- content: renderToolResult(result),
9784
+ // SE7 — structured content blocks (handler-returned or ToolError-carried)
9785
+ // are authoritative; otherwise the legacy string rendering.
9786
+ content: result.content !== void 0 ? result.content : renderToolResult(result),
9547
9787
  ...result.exitCode !== 0 && result.exitCode !== void 0 ? { isError: true } : {}
9548
9788
  };
9549
9789
  }
@@ -9592,6 +9832,29 @@ function buildToolUseCompleted(inputs, callId, call, result) {
9592
9832
  };
9593
9833
  }
9594
9834
 
9835
+ // src/internal/llm/tool-result-content.ts
9836
+ init_errors();
9837
+ function toBlockToolResultContent(content) {
9838
+ return content;
9839
+ }
9840
+ function toStringToolResultContent(content, providerName) {
9841
+ if (typeof content === "string") return content;
9842
+ if (content.some((block) => block.type === "image")) {
9843
+ throw new ConfigurationError(
9844
+ `provider "${providerName}" does not support image content in tool results`
9845
+ );
9846
+ }
9847
+ return content.filter(
9848
+ (block) => block.type === "text"
9849
+ ).map((block) => block.text).join("\n");
9850
+ }
9851
+ function renderToolResultContentText(content, fn) {
9852
+ if (typeof content === "string") return fn(content);
9853
+ return content.map(
9854
+ (block) => block.type === "text" ? { type: "text", text: fn(block.text) } : block
9855
+ );
9856
+ }
9857
+
9595
9858
  // src/internal/agent-loop/tool-result-guard.ts
9596
9859
  var OPEN = "<untrusted-tool-output>";
9597
9860
  var CLOSE = "</untrusted-tool-output>";
@@ -9617,7 +9880,11 @@ ${CLOSE}`;
9617
9880
  function applyToolResultGuard(parts, opts) {
9618
9881
  if (opts.delimit !== true && opts.redactPii !== true) return parts;
9619
9882
  return parts.map(
9620
- (p) => p.type === "tool_result" ? { ...p, content: guardText(p.content, opts) } : p
9883
+ (p) => (
9884
+ // SE7 — guard the TEXT of a tool result (string or the text blocks of a
9885
+ // structured content); image blocks pass through untouched.
9886
+ p.type === "tool_result" ? { ...p, content: renderToolResultContentText(p.content, (t) => guardText(t, opts)) } : p
9887
+ )
9621
9888
  );
9622
9889
  }
9623
9890
 
@@ -10534,10 +10801,10 @@ function stripVertexPrefix(modelId) {
10534
10801
  }
10535
10802
 
10536
10803
  // src/internal/providers/builtin/index.ts
10537
- var registered2 = false;
10804
+ var registered3 = false;
10538
10805
  function registerBuiltins() {
10539
- if (registered2) return;
10540
- registered2 = true;
10806
+ if (registered3) return;
10807
+ registered3 = true;
10541
10808
  registerProvider(ANTHROPIC);
10542
10809
  registerProvider(OPENAI);
10543
10810
  registerProvider(OPENROUTER);
@@ -10765,7 +11032,9 @@ function toAnthropicWireMessage(message) {
10765
11032
  return {
10766
11033
  type: "tool_result",
10767
11034
  tool_use_id: part.toolUseId,
10768
- content: part.content,
11035
+ // SE7 — this wire is block-capable: strings pass through, structured
11036
+ // text/image blocks are forwarded natively.
11037
+ content: toBlockToolResultContent(part.content),
10769
11038
  ...part.isError === true ? { is_error: true } : {}
10770
11039
  };
10771
11040
  });
@@ -11557,7 +11826,8 @@ function toOllamaMessages(message) {
11557
11826
  if (part.type === "tool_result") {
11558
11827
  out.push({
11559
11828
  role: "tool",
11560
- content: part.content,
11829
+ // SE7 — string-only tool role: text blocks flatten; image fails fast.
11830
+ content: toStringToolResultContent(part.content, "ollama"),
11561
11831
  tool_name: part.toolUseId
11562
11832
  });
11563
11833
  }
@@ -12094,7 +12364,9 @@ function userOrToolMessages(message) {
12094
12364
  out.push({
12095
12365
  role: "tool",
12096
12366
  tool_call_id: part.toolUseId,
12097
- content: part.content
12367
+ // SE7 — this wire's tool role is string-only: text blocks flatten; an
12368
+ // image block fails fast (ConfigurationError).
12369
+ content: toStringToolResultContent(part.content, "openai")
12098
12370
  });
12099
12371
  }
12100
12372
  }
@@ -13023,18 +13295,20 @@ function levenshtein(a, b) {
13023
13295
  function createRealLocalRun(options) {
13024
13296
  const { userText, id, startTime } = prepareRunContext(options.message);
13025
13297
  const supported = /* @__PURE__ */ new Set(["stream", "wait", "cancel", "conversation"]);
13026
- const unusedFixtureScript = {
13298
+ const runScript = {
13027
13299
  events: [],
13028
13300
  finalStatus: "running",
13029
13301
  cancellable: false,
13030
- conversation: []
13302
+ conversation: [],
13303
+ // SE3 — carry the turn's provenance so buildResult forwards it onto RunResult.origin.
13304
+ ...options.sendOptions.origin !== void 0 ? { origin: options.sendOptions.origin } : {}
13031
13305
  };
13032
13306
  const handle = new RealLocalRun(
13033
13307
  {
13034
13308
  id,
13035
13309
  agentId: options.agentId,
13036
13310
  model: options.model,
13037
- script: unusedFixtureScript,
13311
+ script: runScript,
13038
13312
  supportedOps: supported,
13039
13313
  startTime
13040
13314
  },
@@ -13146,6 +13420,8 @@ function buildLoopInputs(options, runId, userText) {
13146
13420
  // M7 — forward SendOptions.context to the loop so every tool handler receives
13147
13421
  // it on `ctx.context` (shared run config set once, e.g. projectRoot).
13148
13422
  ...options.sendOptions.context !== void 0 ? { context: options.sendOptions.context } : {},
13423
+ // SE2 — forward the opt-in typed runtime-event sink to the loop.
13424
+ ...options.sendOptions.onRunEvent !== void 0 ? { runEventSink: options.sendOptions.onRunEvent } : {},
13149
13425
  // #58 / #57 — forward the per-tool timeout + tool-result guard so a consumer
13150
13426
  // can enable them via SendOptions (not only internal AgentLoopInputs).
13151
13427
  ...options.sendOptions.perToolTimeoutMs !== void 0 ? { perToolTimeoutMs: options.sendOptions.perToolTimeoutMs } : {},
@@ -15838,7 +16114,7 @@ function wrapRunWithPostReplyHook(args) {
15838
16114
  async function executeSendLocked(inputs, message, options) {
15839
16115
  if (inputs.disposed) throw new AgentDisposedError(inputs.agentId);
15840
16116
  await consumePending(inputs.agentId, inputs.invalidationPending, inputs.clearInvalidation, inputs.reload);
15841
- inputs.applyModelOverride(options.model);
16117
+ inputs.applyModelOverride(normalizeModel(options.model));
15842
16118
  const userText = typeof message === "string" ? message : message.text;
15843
16119
  if (inputs.options.onBeforeSend !== void 0) {
15844
16120
  await inputs.options.onBeforeSend({
@@ -15990,7 +16266,7 @@ var LocalAgent = class {
15990
16266
  constructor(options) {
15991
16267
  this.agentId = options.agentId ?? generateLocalAgentId();
15992
16268
  this._telemetry = createTelemetry(options.telemetry);
15993
- this.model = options.model;
16269
+ this.model = normalizeModel(options.model);
15994
16270
  this.options = options;
15995
16271
  this.workspaceCwd = resolveCwd(options.local?.cwd);
15996
16272
  this.conversationStorage = options.conversationStorage;
@@ -16057,6 +16333,11 @@ var LocalAgent = class {
16057
16333
  hooks() {
16058
16334
  return this.hooksExecutor;
16059
16335
  }
16336
+ // SE9 — integrated structured output; delegates to the shared helper.
16337
+ generate(message, options) {
16338
+ const { apiKey, local } = this.options;
16339
+ return agentGenerate(this, this.model, apiKey, local, message, options);
16340
+ }
16060
16341
  async send(message, options = {}) {
16061
16342
  if (options.tools !== void 0 && options.tools.length > 0) {
16062
16343
  validateToolCatalog(options.tools);
@@ -16296,6 +16577,7 @@ function includesSetting(options, source) {
16296
16577
 
16297
16578
  // src/agent-helpers.ts
16298
16579
  async function runCreateUnderSpan(options, span) {
16580
+ options = { ...options, model: normalizeModel(options.model) };
16299
16581
  validateAgentOptions(options);
16300
16582
  validateCloudToolParity(options);
16301
16583
  await guardAgainstIdCollision(options);
@@ -16369,7 +16651,10 @@ async function createLocalAgent(options) {
16369
16651
  const willFlowToProvider = !isFixtureApiKey(apiKey) && !shouldUseRealLocalRuntime(apiKey);
16370
16652
  const shape = validateApiKeyShape(apiKey, {
16371
16653
  strict: willFlowToProvider,
16372
- ...willFlowToProvider ? { provider: providerFromModelId(options.model?.id) } : {}
16654
+ // `options.model` is already normalized by `runCreateUnderSpan`; `normalizeModel`
16655
+ // here is the idempotent narrowing of the widened public type (`{id}` passes
16656
+ // through by reference) — cleaner than an `as` cast.
16657
+ ...willFlowToProvider ? { provider: providerFromModelId(normalizeModel(options.model)?.id) } : {}
16373
16658
  });
16374
16659
  if (shape.malformed) {
16375
16660
  throw new AuthenticationError(shape.message, { code: "malformed_api_key" });
@@ -16435,6 +16720,10 @@ async function rehydrateExistingAgent(agentId, existing, options) {
16435
16720
  ...existing.options,
16436
16721
  ...options,
16437
16722
  ...mergedLocal !== void 0 ? { local: mergedLocal } : {},
16723
+ // SE8 — normalize a caller-supplied bare-string model override at this resume
16724
+ // boundary (Agent.resume never flows through runCreateUnderSpan). Cloud resume
16725
+ // honors it; local resume forces the persisted model below.
16726
+ model: normalizeModel(options.model ?? existing.options.model),
16438
16727
  mcpServers: void 0,
16439
16728
  agentId
16440
16729
  };
@@ -16801,6 +17090,7 @@ async function getOrCreateUncached(agentId, options) {
16801
17090
  }
16802
17091
  setAgentFacade({
16803
17092
  create: (options) => Agent.create(options),
17093
+ delete: (agentId) => Agent.delete(agentId),
16804
17094
  prompt: (message, options) => Agent.prompt(message, options),
16805
17095
  get: (agentId) => Agent.get(agentId),
16806
17096
  resume: (agentId, options) => Agent.resume(agentId, options),