@theokit/sdk 2.20.0 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dist/a2a/index.cjs +592 -290
  3. package/dist/a2a/index.cjs.map +1 -1
  4. package/dist/a2a/index.js +593 -291
  5. package/dist/a2a/index.js.map +1 -1
  6. package/dist/a2a/types.d.cts +7 -0
  7. package/dist/a2a/types.d.ts +7 -0
  8. package/dist/agent-builder.d.ts +2 -1
  9. package/dist/agent-generate.d.ts +12 -0
  10. package/dist/client/index.cjs.map +1 -1
  11. package/dist/client/index.d.cts +5 -0
  12. package/dist/client/index.d.ts +5 -0
  13. package/dist/client/index.js.map +1 -1
  14. package/dist/client/theokit-client.d.cts +6 -0
  15. package/dist/client/theokit-client.d.ts +6 -0
  16. package/dist/client/types.d.cts +2 -0
  17. package/dist/client/types.d.ts +2 -0
  18. package/dist/{cron-D2yLOrk2.d.ts → cron-YrmsszEN.d.cts} +53 -3
  19. package/dist/{cron-B8fqui49.d.cts → cron-dpvtRoro.d.ts} +53 -3
  20. package/dist/cron.cjs +642 -387
  21. package/dist/cron.cjs.map +1 -1
  22. package/dist/cron.d.cts +3 -2
  23. package/dist/cron.d.ts +3 -2
  24. package/dist/cron.js +643 -388
  25. package/dist/cron.js.map +1 -1
  26. package/dist/{errors-5lj1EWgs.d.ts → errors-C4vZPqXf.d.ts} +2 -2
  27. package/dist/{errors-CE-lMBi2.d.cts → errors-DrcpYVfZ.d.cts} +2 -2
  28. package/dist/errors.d.cts +3 -2
  29. package/dist/eval.cjs +642 -387
  30. package/dist/eval.cjs.map +1 -1
  31. package/dist/eval.js +642 -387
  32. package/dist/eval.js.map +1 -1
  33. package/dist/generate-object.d.ts +4 -3
  34. package/dist/index.cjs +788 -390
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +259 -17
  37. package/dist/index.d.ts +259 -17
  38. package/dist/index.js +786 -392
  39. package/dist/index.js.map +1 -1
  40. package/dist/internal/agent-loop/loop-types.d.ts +2 -0
  41. package/dist/internal/agent-loop/tool-result-guard.d.ts +1 -0
  42. package/dist/internal/llm/tool-result-content.d.ts +40 -0
  43. package/dist/internal/llm/types.d.ts +6 -1
  44. package/dist/internal/persistence/conversation-storage-fs.d.cts +3 -1
  45. package/dist/internal/persistence/conversation-storage-fs.d.ts +3 -1
  46. package/dist/internal/persistence/conversation-storage-memory.d.cts +3 -1
  47. package/dist/internal/persistence/conversation-storage-memory.d.ts +3 -1
  48. package/dist/internal/persistence/session-meta.d.cts +13 -0
  49. package/dist/internal/persistence/session-meta.d.ts +13 -0
  50. package/dist/internal/runtime/model-selection.d.ts +14 -0
  51. package/dist/internal/structured-output-helpers.d.ts +1 -1
  52. package/dist/permission-engine.d.ts +32 -1
  53. package/dist/{run-BgfBWX-z.d.cts → run-BMo8yRwK.d.cts} +262 -19
  54. package/dist/{run-BgfBWX-z.d.ts → run-BMo8yRwK.d.ts} +262 -19
  55. package/dist/stream-object.d.ts +2 -1
  56. package/dist/tool-error.d.ts +30 -0
  57. package/dist/types/agent-prims.d.ts +10 -8
  58. package/dist/types/agent.d.ts +12 -1
  59. package/dist/types/content-blocks.d.ts +47 -0
  60. package/dist/types/conversation-storage.d.ts +38 -0
  61. package/dist/types/index.d.ts +1 -0
  62. package/dist/types/messages.d.ts +2 -9
  63. package/dist/types/run-events.d.ts +84 -0
  64. package/dist/types/run.d.ts +115 -1
  65. package/dist/types/session.d.ts +99 -0
  66. package/dist/types/workflow.d.ts +7 -0
  67. package/dist/workflow.cjs +7 -2
  68. package/dist/workflow.cjs.map +1 -1
  69. package/dist/workflow.d.cts +2 -0
  70. package/dist/workflow.d.ts +2 -0
  71. package/dist/workflow.js +7 -2
  72. package/dist/workflow.js.map +1 -1
  73. package/package.json +14 -34
  74. package/dist/internal/observability/context.d.cts +0 -23
  75. package/dist/internal/observability/context.d.ts +0 -23
  76. package/dist/internal/observability/index.cjs +0 -38
  77. package/dist/internal/observability/index.cjs.map +0 -1
  78. package/dist/internal/observability/index.d.cts +0 -8
  79. package/dist/internal/observability/index.d.ts +0 -8
  80. package/dist/internal/observability/index.js +0 -33
  81. package/dist/internal/observability/index.js.map +0 -1
  82. package/dist/internal/plugins/index.cjs +0 -363
  83. package/dist/internal/plugins/index.cjs.map +0 -1
  84. package/dist/internal/plugins/index.js +0 -357
  85. package/dist/internal/plugins/index.js.map +0 -1
  86. package/dist/internal/workflow/index.d.ts +0 -12
  87. package/dist/server/adapter/index.d.cts +0 -8
  88. package/dist/server/adapter/index.d.ts +0 -8
package/dist/a2a/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { existsSync, realpathSync, mkdirSync, lstatSync, readlinkSync, readFileSync, readdirSync } from 'fs';
2
2
  import { randomUUID, createHash, randomBytes } from 'crypto';
3
- import { join, dirname, resolve, sep, relative, isAbsolute } from 'path';
4
- import { stat, mkdir, readFile, rename, rm, readdir, open, unlink, appendFile, statfs, access } from 'fs/promises';
5
- import { homedir } from 'os';
6
3
  import { z, toJSONSchema } from 'zod';
7
4
  import { createRequire } from 'module';
5
+ import { join, dirname, resolve, sep, relative, isAbsolute } from 'path';
6
+ import { stat, mkdir, readFile, rename, rm, readdir, writeFile, open, unlink, appendFile, statfs, access } from 'fs/promises';
7
+ import { homedir } from 'os';
8
8
  import { spawn } from 'child_process';
9
9
  import { AsyncLocalStorage } from 'async_hooks';
10
10
  import { fileURLToPath } from 'url';
@@ -29,6 +29,7 @@ var init_agent_builder = __esm({
29
29
  constructor(deps) {
30
30
  this.deps = deps;
31
31
  }
32
+ /** SE8 — accepts a bare-string id shorthand (`"openai/gpt-4o-mini"`) or a {@link ModelSelection}. */
32
33
  model(m) {
33
34
  this.opts.model = m;
34
35
  return this;
@@ -895,6 +896,329 @@ var init_ids = __esm({
895
896
  }
896
897
  });
897
898
 
899
+ // src/internal/runtime/config/default-model.ts
900
+ var DEFAULT_AGENTIC_MODEL_ID;
901
+ var init_default_model = __esm({
902
+ "src/internal/runtime/config/default-model.ts"() {
903
+ DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
904
+ }
905
+ });
906
+ function toJsonSchema(schema, options = { unrepresentable: "any" }) {
907
+ return toJSONSchema(schema, options);
908
+ }
909
+ var init_to_json_schema = __esm({
910
+ "src/internal/zod/to-json-schema.ts"() {
911
+ }
912
+ });
913
+ function requireZod() {
914
+ if (cachedZ !== void 0) return cachedZ;
915
+ const r = createRequire(import.meta.url);
916
+ let mod;
917
+ try {
918
+ mod = r("zod");
919
+ } catch (cause) {
920
+ throw new ConfigurationError(
921
+ 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
922
+ { code: "zod_not_installed", cause }
923
+ );
924
+ }
925
+ cachedZ = mod.z ?? mod;
926
+ return cachedZ;
927
+ }
928
+ function makeOutputTool(jsonSchema, onCapture) {
929
+ return {
930
+ name: "output",
931
+ description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
932
+ inputSchema: jsonSchema,
933
+ handler: (input) => {
934
+ const out = onCapture(input);
935
+ if (typeof out === "string") return out;
936
+ if (out instanceof Promise) return out;
937
+ return "";
938
+ }
939
+ };
940
+ }
941
+ function buildTransientAgentOptions(params) {
942
+ return {
943
+ model: params.model,
944
+ local: params.local,
945
+ tools: [params.outputTool],
946
+ systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
947
+ ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
948
+ ...params.providers !== void 0 ? { providers: params.providers } : {}
949
+ };
950
+ }
951
+ function extractUsage(result) {
952
+ const u = result.usage;
953
+ if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
954
+ return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
955
+ }
956
+ function buildToolPrompt(prompt) {
957
+ return `${prompt}
958
+
959
+ Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
960
+ }
961
+ function setupStructuredOutput(schema, maxRetries) {
962
+ const z7 = requireZod();
963
+ const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
964
+ return {
965
+ z: z7,
966
+ jsonSchema,
967
+ maxRetries: maxRetries ?? 1,
968
+ initialUsage: { inputTokens: 0, outputTokens: 0 }
969
+ };
970
+ }
971
+ async function disposeAndDeleteTransient(agent, deletor) {
972
+ await agent.dispose();
973
+ try {
974
+ await deletor(agent.agentId);
975
+ } catch {
976
+ }
977
+ }
978
+ var cachedZ;
979
+ var init_structured_output_helpers = __esm({
980
+ "src/internal/structured-output-helpers.ts"() {
981
+ init_errors();
982
+ init_to_json_schema();
983
+ }
984
+ });
985
+
986
+ // src/generate-object.ts
987
+ var generate_object_exports = {};
988
+ __export(generate_object_exports, {
989
+ GenerateObjectError: () => GenerateObjectError,
990
+ generateObjectImpl: () => generateObjectImpl
991
+ });
992
+ function salvagePartial(schema, raw) {
993
+ if (typeof raw !== "object" || raw === null) return raw;
994
+ const shape = schema.shape;
995
+ if (shape === void 0 || typeof shape !== "object") return raw;
996
+ const rawObj = raw;
997
+ const out = {};
998
+ for (const [key, fieldSchema] of Object.entries(shape)) {
999
+ if (typeof fieldSchema?.safeParse !== "function") continue;
1000
+ const parsed = fieldSchema.safeParse(rawObj[key]);
1001
+ if (parsed.success) out[key] = parsed.data;
1002
+ }
1003
+ return out;
1004
+ }
1005
+ async function runReasoningPhase(options, deps) {
1006
+ const reasoningOptions = {
1007
+ model: options.model,
1008
+ local: options.local,
1009
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
1010
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
1011
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
1012
+ };
1013
+ const reasoningAgent = await deps.create(reasoningOptions);
1014
+ try {
1015
+ const run = await reasoningAgent.send(options.prompt);
1016
+ const result = await run.wait();
1017
+ const text = result.result;
1018
+ return typeof text === "string" ? text : options.prompt;
1019
+ } finally {
1020
+ await disposeAndDeleteTransient(reasoningAgent, deps.delete);
1021
+ }
1022
+ }
1023
+ async function generateObjectImpl(options, deps) {
1024
+ const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
1025
+ options.schema,
1026
+ options.maxRetries
1027
+ );
1028
+ let capturedRaw;
1029
+ let lastUsage = initialUsage;
1030
+ const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
1031
+ class CaptureSentinel extends Error {
1032
+ constructor(captured) {
1033
+ super("generate-object-capture");
1034
+ this.captured = captured;
1035
+ }
1036
+ captured;
1037
+ [sentinel] = true;
1038
+ }
1039
+ const outputTool = makeOutputTool(jsonSchema, (input) => {
1040
+ if (capturedRaw === void 0) {
1041
+ capturedRaw = input;
1042
+ }
1043
+ throw new CaptureSentinel(input);
1044
+ });
1045
+ const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
1046
+ const structuringModel = options.structuringModel ?? options.model;
1047
+ const structuringPrompt = reasoningText ?? options.prompt;
1048
+ const agentOptions = buildTransientAgentOptions({
1049
+ model: structuringModel,
1050
+ local: options.local,
1051
+ outputTool,
1052
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
1053
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
1054
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
1055
+ });
1056
+ const agent = await deps.create(agentOptions);
1057
+ try {
1058
+ const userMessage = buildToolPrompt(structuringPrompt);
1059
+ let lastParseError;
1060
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
1061
+ capturedRaw = void 0;
1062
+ const run = await agent.send(userMessage);
1063
+ const result = await run.wait();
1064
+ lastUsage = extractUsage(result);
1065
+ if (capturedRaw === void 0) {
1066
+ if (result.status === "error" && result.error !== void 0) {
1067
+ throw new GenerateObjectError(
1068
+ "no_tool_call",
1069
+ `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
1070
+ result.error
1071
+ );
1072
+ }
1073
+ if (attempt === maxRetries) {
1074
+ throw new GenerateObjectError(
1075
+ "no_tool_call",
1076
+ "The model returned text instead of calling the `output` tool."
1077
+ );
1078
+ }
1079
+ continue;
1080
+ }
1081
+ const parsed = options.schema.safeParse(capturedRaw);
1082
+ if (parsed.success) {
1083
+ return {
1084
+ object: parsed.data,
1085
+ raw: capturedRaw,
1086
+ usage: lastUsage,
1087
+ finishReason: "tool_use"
1088
+ };
1089
+ }
1090
+ lastParseError = parsed.error;
1091
+ }
1092
+ if (options.errorStrategy === "return-raw") {
1093
+ return {
1094
+ object: capturedRaw,
1095
+ raw: capturedRaw,
1096
+ usage: lastUsage,
1097
+ finishReason: "tool_use"
1098
+ };
1099
+ }
1100
+ if (options.errorStrategy === "return-partial") {
1101
+ return {
1102
+ object: salvagePartial(options.schema, capturedRaw),
1103
+ raw: capturedRaw,
1104
+ usage: lastUsage,
1105
+ finishReason: "tool_use"
1106
+ };
1107
+ }
1108
+ throw new GenerateObjectError(
1109
+ "parse_failed",
1110
+ "Schema parse failed after all retries.",
1111
+ lastParseError
1112
+ );
1113
+ } finally {
1114
+ await disposeAndDeleteTransient(agent, deps.delete);
1115
+ }
1116
+ }
1117
+ var GenerateObjectError;
1118
+ var init_generate_object = __esm({
1119
+ "src/generate-object.ts"() {
1120
+ init_structured_output_helpers();
1121
+ GenerateObjectError = class extends Error {
1122
+ name = "GenerateObjectError";
1123
+ code;
1124
+ cause;
1125
+ constructor(code, message, cause) {
1126
+ super(message);
1127
+ this.code = code;
1128
+ if (cause !== void 0) this.cause = cause;
1129
+ }
1130
+ };
1131
+ }
1132
+ });
1133
+
1134
+ // src/internal/runtime/registry/agent-factory-registry.ts
1135
+ var agent_factory_registry_exports = {};
1136
+ __export(agent_factory_registry_exports, {
1137
+ getAgentFacade: () => getAgentFacade,
1138
+ setAgentFacade: () => setAgentFacade
1139
+ });
1140
+ function setAgentFacade(facade) {
1141
+ registered = facade;
1142
+ }
1143
+ function getAgentFacade() {
1144
+ if (registered === void 0) {
1145
+ throw new Error(
1146
+ "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
1147
+ );
1148
+ }
1149
+ return registered;
1150
+ }
1151
+ var registered;
1152
+ var init_agent_factory_registry = __esm({
1153
+ "src/internal/runtime/registry/agent-factory-registry.ts"() {
1154
+ }
1155
+ });
1156
+
1157
+ // src/agent-generate.ts
1158
+ async function agentGenerate(agent, agentModel, agentApiKey, agentLocal, input, options) {
1159
+ const { output, maxRetries, errorStrategy, ...sendOptions } = options;
1160
+ const run = await agent.send(input, sendOptions);
1161
+ const result = await run.wait();
1162
+ const { generateObjectImpl: generateObjectImpl2, GenerateObjectError: GenerateObjectError2 } = await Promise.resolve().then(() => (init_generate_object(), generate_object_exports));
1163
+ const answer = structurableAnswerOrThrow(result, GenerateObjectError2);
1164
+ const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
1165
+ const facade = getAgentFacade2();
1166
+ const structured = await generateObjectImpl2(
1167
+ {
1168
+ prompt: answer,
1169
+ schema: output,
1170
+ // `agentModel` arrives already normalized (`this.model` passed through
1171
+ // `normalizeModel` in the LocalAgent/CloudAgent constructor), so the widened
1172
+ // `string | ModelSelection` field only ever receives a `ModelSelection` here.
1173
+ model: agentModel ?? { id: DEFAULT_AGENTIC_MODEL_ID },
1174
+ // `local` is required to keep the transient structuring agent local-only;
1175
+ // default to the process cwd when the agent had no explicit local config.
1176
+ local: agentLocal ?? { cwd: process.cwd() },
1177
+ ...agentApiKey !== void 0 ? { apiKey: agentApiKey } : {},
1178
+ ...maxRetries !== void 0 ? { maxRetries } : {},
1179
+ ...errorStrategy !== void 0 ? { errorStrategy } : {}
1180
+ },
1181
+ {
1182
+ create: (opts) => facade.create(opts),
1183
+ delete: (agentId) => facade.delete(agentId)
1184
+ }
1185
+ );
1186
+ return {
1187
+ object: structured.object,
1188
+ result,
1189
+ raw: structured.raw,
1190
+ usage: structured.usage
1191
+ };
1192
+ }
1193
+ function structurableAnswerOrThrow(result, GenerateObjectError2) {
1194
+ if (result.status === "error") {
1195
+ throw new GenerateObjectError2(
1196
+ "no_tool_call",
1197
+ `Agent run failed before an answer could be structured: ${result.error?.message ?? "unknown error"} [${result.error?.code ?? "?"}]`,
1198
+ result.error
1199
+ );
1200
+ }
1201
+ if (result.status === "cancelled") {
1202
+ throw new GenerateObjectError2(
1203
+ "no_tool_call",
1204
+ "Agent run was cancelled before an answer could be structured."
1205
+ );
1206
+ }
1207
+ const answer = result.result;
1208
+ if (answer === void 0 || answer.length === 0) {
1209
+ throw new GenerateObjectError2(
1210
+ "no_tool_call",
1211
+ "The tool loop finished with no text answer (tool-only completion) \u2014 there is nothing to structure."
1212
+ );
1213
+ }
1214
+ return answer;
1215
+ }
1216
+ var init_agent_generate = __esm({
1217
+ "src/agent-generate.ts"() {
1218
+ init_default_model();
1219
+ }
1220
+ });
1221
+
898
1222
  // src/internal/persistence/cwd-mutex.ts
899
1223
  function withCwdMutex(key, fn) {
900
1224
  const prev = tails.get(key) ?? Promise.resolve();
@@ -1064,11 +1388,21 @@ var init_path_guard = __esm({
1064
1388
  }
1065
1389
  });
1066
1390
 
1067
- // src/internal/runtime/config/default-model.ts
1068
- var DEFAULT_AGENTIC_MODEL_ID;
1069
- var init_default_model = __esm({
1070
- "src/internal/runtime/config/default-model.ts"() {
1071
- DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
1391
+ // src/internal/runtime/model-selection.ts
1392
+ function normalizeModel(m) {
1393
+ if (m === void 0 || typeof m !== "string") return m;
1394
+ const id = m.trim();
1395
+ if (id.length === 0) {
1396
+ throw new ConfigurationError(
1397
+ 'model must be a non-empty "provider/model" id string or a ModelSelection object',
1398
+ { code: "invalid_model_selection" }
1399
+ );
1400
+ }
1401
+ return { id };
1402
+ }
1403
+ var init_model_selection = __esm({
1404
+ "src/internal/runtime/model-selection.ts"() {
1405
+ init_errors();
1072
1406
  }
1073
1407
  });
1074
1408
  function detectNetworkFsName(typeMagic) {
@@ -1208,7 +1542,10 @@ var init_enabled_names = __esm({
1208
1542
  function stripSecretsFromOptions(options) {
1209
1543
  return assignDefined({
1210
1544
  name: options.name,
1211
- model: options.model !== void 0 ? { id: options.model.id } : void 0,
1545
+ model: (() => {
1546
+ const m = normalizeModel(options.model);
1547
+ return m !== void 0 ? { id: m.id } : void 0;
1548
+ })(),
1212
1549
  systemPrompt: typeof options.systemPrompt === "string" ? options.systemPrompt : void 0,
1213
1550
  local: serializeLocal(options.local),
1214
1551
  cloud: serializeCloud(options.cloud),
@@ -1379,6 +1716,7 @@ var init_agent_registry_store = __esm({
1379
1716
  init_cwd_mutex();
1380
1717
  init_schema_version();
1381
1718
  init_enabled_names();
1719
+ init_model_selection();
1382
1720
  SCHEMA_VERSION = 1;
1383
1721
  LEGACY_SCHEMA_VERSION_STRING = "1.0";
1384
1722
  REGISTRY_RELATIVE_PATH = join(".theokit", "agents", "registry.json");
@@ -1514,7 +1852,8 @@ function serializeCloudAgentConfig(options) {
1514
1852
  cloud: serializeCloud2(options.cloud)
1515
1853
  };
1516
1854
  if (typeof options.agentId === "string") payload.agentId = options.agentId;
1517
- if (options.model !== void 0) payload.model = { id: options.model.id };
1855
+ const cfgModel = normalizeModel(options.model);
1856
+ if (cfgModel !== void 0) payload.model = { id: cfgModel.id };
1518
1857
  if (typeof options.systemPrompt === "string") payload.systemPrompt = options.systemPrompt;
1519
1858
  const skills = serializeSkills(options.skills);
1520
1859
  if (skills !== void 0) payload.skills = skills;
@@ -1624,6 +1963,7 @@ function serializeMemory2(memory) {
1624
1963
  var init_cloud_config_serializer = __esm({
1625
1964
  "src/internal/runtime/cloud/cloud-config-serializer.ts"() {
1626
1965
  init_enabled_names();
1966
+ init_model_selection();
1627
1967
  }
1628
1968
  });
1629
1969
 
@@ -2334,6 +2674,7 @@ function applyScriptMetrics(base, script) {
2334
2674
  if (script.cost !== void 0) base.cost = script.cost;
2335
2675
  if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
2336
2676
  if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
2677
+ if (script.origin !== void 0) base.origin = script.origin;
2337
2678
  }
2338
2679
  var FixtureRunBase;
2339
2680
  var init_fixture_run_base = __esm({
@@ -2841,6 +3182,7 @@ function buildFixtureArtifacts() {
2841
3182
  var CloudAgent;
2842
3183
  var init_cloud_agent = __esm({
2843
3184
  "src/internal/runtime/cloud/cloud-agent.ts"() {
3185
+ init_agent_generate();
2844
3186
  init_errors();
2845
3187
  init_env();
2846
3188
  init_fixture_mode();
@@ -2848,6 +3190,7 @@ var init_cloud_agent = __esm({
2848
3190
  init_cwd_mutex();
2849
3191
  init_path_guard();
2850
3192
  init_default_model();
3193
+ init_model_selection();
2851
3194
  init_agent_registry();
2852
3195
  init_system_prompt();
2853
3196
  init_cloud_config_serializer();
@@ -2867,7 +3210,7 @@ var init_cloud_agent = __esm({
2867
3210
  disposed = false;
2868
3211
  constructor(options, providedAgentId) {
2869
3212
  this.agentId = providedAgentId ?? options.agentId ?? generateCloudAgentId();
2870
- this.model = options.model;
3213
+ this.model = normalizeModel(options.model);
2871
3214
  this.options = options;
2872
3215
  this.cloudPayload = serializeCloudAgentConfig({ ...options, agentId: this.agentId });
2873
3216
  const repoUrls = (options.cloud?.repos ?? []).map((repo) => repo.url);
@@ -2897,6 +3240,17 @@ var init_cloud_agent = __esm({
2897
3240
  const apiKey = resolveApiKey(this.options.apiKey);
2898
3241
  return isFixtureApiKey(apiKey) && getConfiguredBaseUrl() === void 0;
2899
3242
  }
3243
+ // SE9 — integrated structured output; delegates to the shared helper.
3244
+ generate(message, options) {
3245
+ return agentGenerate(
3246
+ this,
3247
+ this.model,
3248
+ this.options.apiKey,
3249
+ this.options.local,
3250
+ message,
3251
+ options
3252
+ );
3253
+ }
2900
3254
  async send(message, options = {}) {
2901
3255
  if (options.tools !== void 0 && options.tools.length > 0) {
2902
3256
  throw new ConfigurationError(
@@ -2927,7 +3281,7 @@ var init_cloud_agent = __esm({
2927
3281
  });
2928
3282
  }
2929
3283
  async sendLocked(message, options) {
2930
- const overrideModel = options.model;
3284
+ const overrideModel = normalizeModel(options.model);
2931
3285
  if (overrideModel !== void 0) {
2932
3286
  this.model = overrideModel;
2933
3287
  updateRegisteredAgent(this.agentId, { model: overrideModel });
@@ -4121,12 +4475,12 @@ function tryAutoRegisterAdapters(settings) {
4121
4475
  if (settings.autoDetect === false) return;
4122
4476
  const disabled = new Set(settings.disable ?? []);
4123
4477
  for (const adapter of ALL_ADAPTERS) {
4124
- if (registered.has(adapter.moduleName)) continue;
4478
+ if (registered2.has(adapter.moduleName)) continue;
4125
4479
  if (disabled.has(adapter.displayName.toLowerCase())) continue;
4126
4480
  if (!adapter.detect()) continue;
4127
4481
  try {
4128
4482
  adapter.register();
4129
- registered.add(adapter.moduleName);
4483
+ registered2.add(adapter.moduleName);
4130
4484
  process.stderr.write(`[theokit-sdk] telemetry: ${adapter.displayName} auto-instrumented.
4131
4485
  `);
4132
4486
  } catch (cause) {
@@ -4138,7 +4492,7 @@ function tryAutoRegisterAdapters(settings) {
4138
4492
  }
4139
4493
  }
4140
4494
  }
4141
- var ALL_ADAPTERS, registered;
4495
+ var ALL_ADAPTERS, registered2;
4142
4496
  var init_adapter_registry = __esm({
4143
4497
  "src/internal/telemetry/adapter-registry.ts"() {
4144
4498
  init_arize();
@@ -4157,7 +4511,7 @@ var init_adapter_registry = __esm({
4157
4511
  arizeAdapter,
4158
4512
  braintrustAdapter
4159
4513
  ];
4160
- registered = /* @__PURE__ */ new Set();
4514
+ registered2 = /* @__PURE__ */ new Set();
4161
4515
  }
4162
4516
  });
4163
4517
  function loadOtel() {
@@ -4748,6 +5102,30 @@ var init_pagination = __esm({
4748
5102
  "src/internal/persistence/pagination.ts"() {
4749
5103
  }
4750
5104
  });
5105
+
5106
+ // src/internal/persistence/session-meta.ts
5107
+ function applyMetaPatch(current, patch) {
5108
+ const next = {};
5109
+ if (current.title !== void 0) next.title = current.title;
5110
+ if (current.tag !== void 0) next.tag = current.tag;
5111
+ if (patch.title === null) delete next.title;
5112
+ else if (patch.title !== void 0) next.title = patch.title;
5113
+ if (patch.tag === null) delete next.tag;
5114
+ else if (patch.tag !== void 0) next.tag = patch.tag;
5115
+ return next;
5116
+ }
5117
+ function coerceSessionMeta(raw) {
5118
+ if (typeof raw !== "object" || raw === null) return void 0;
5119
+ const obj = raw;
5120
+ const meta = {};
5121
+ if (typeof obj.title === "string") meta.title = obj.title;
5122
+ if (typeof obj.tag === "string") meta.tag = obj.tag;
5123
+ return meta.title === void 0 && meta.tag === void 0 ? void 0 : meta;
5124
+ }
5125
+ var init_session_meta = __esm({
5126
+ "src/internal/persistence/session-meta.ts"() {
5127
+ }
5128
+ });
4751
5129
  function toStoredMessage(record) {
4752
5130
  return {
4753
5131
  role: record.role,
@@ -4763,7 +5141,9 @@ var init_conversation_storage_fs = __esm({
4763
5141
  "src/internal/persistence/conversation-storage-fs.ts"() {
4764
5142
  init_agent_session_store();
4765
5143
  init_security();
5144
+ init_file_lock();
4766
5145
  init_pagination();
5146
+ init_session_meta();
4767
5147
  FileSystemConversationStorage = class {
4768
5148
  #root;
4769
5149
  constructor(opts = {}) {
@@ -4813,6 +5193,37 @@ var init_conversation_storage_fs = __esm({
4813
5193
  async compact(conversationId, maxTurns) {
4814
5194
  await compactSessionFile(this.#root, conversationId, maxTurns);
4815
5195
  }
5196
+ // SE4 — session metadata persisted as a per-conversation sidecar
5197
+ // `<root>/.theokit/agents/<safeId>/session.json` (same sanitized perimeter as
5198
+ // the transcript). Kept separate from messages.jsonl so a title/tag write does
5199
+ // not rewrite the append-only log.
5200
+ #metaPath(conversationId) {
5201
+ const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
5202
+ return safePathJoin(this.#root, ".theokit", "agents", safe2, "session.json");
5203
+ }
5204
+ async getSessionMeta(conversationId) {
5205
+ try {
5206
+ const raw = await readFile(this.#metaPath(conversationId), "utf8");
5207
+ return coerceSessionMeta(JSON.parse(raw));
5208
+ } catch (cause) {
5209
+ if (cause.code === "ENOENT") return void 0;
5210
+ throw cause;
5211
+ }
5212
+ }
5213
+ async setSessionMeta(conversationId, patch) {
5214
+ const metaPath = this.#metaPath(conversationId);
5215
+ await mkdir(dirname(metaPath), { recursive: true });
5216
+ await withFileLock(metaPath, async () => {
5217
+ let current = {};
5218
+ try {
5219
+ current = coerceSessionMeta(JSON.parse(await readFile(metaPath, "utf8"))) ?? {};
5220
+ } catch (cause) {
5221
+ if (cause.code !== "ENOENT") throw cause;
5222
+ }
5223
+ const next = applyMetaPatch(current, patch);
5224
+ await writeFile(metaPath, redactSecrets(JSON.stringify(next)), "utf8");
5225
+ });
5226
+ }
4816
5227
  async dispose() {
4817
5228
  }
4818
5229
  };
@@ -6876,7 +7287,7 @@ function bootstrapSubmanagers(args) {
6876
7287
  const providerCount = (args.options.providers?.routes?.length ?? 0) + enabledPluginNames(args.options.plugins).length;
6877
7288
  if (providerCount > 0 || args.options.providers !== void 0) {
6878
7289
  out.providers = new ProvidersManagerImpl(
6879
- args.options.model,
7290
+ normalizeModel(args.options.model),
6880
7291
  args.options.providers,
6881
7292
  args.options.plugins
6882
7293
  );
@@ -6913,6 +7324,7 @@ var init_local_agent_bootstrap = __esm({
6913
7324
  init_enabled_names();
6914
7325
  init_providers_manager();
6915
7326
  init_context_manager();
7327
+ init_model_selection();
6916
7328
  init_plugins_manager();
6917
7329
  init_agent_registry();
6918
7330
  init_skills_manager();
@@ -6961,6 +7373,7 @@ function createLocalRun(options) {
6961
7373
  projectMcpServers: options.projectMcpServers,
6962
7374
  ...options.persistMemoryFact !== void 0 ? { persistMemoryFact: options.persistMemoryFact } : {}
6963
7375
  });
7376
+ if (options.sendOptions.origin !== void 0) script.origin = options.sendOptions.origin;
6964
7377
  const handle = new LocalRun({
6965
7378
  id,
6966
7379
  agentId: options.agentId,
@@ -7661,6 +8074,19 @@ var init_loop_llm_stream = __esm({
7661
8074
  init_message_builders();
7662
8075
  }
7663
8076
  });
8077
+
8078
+ // src/types/run-events.ts
8079
+ function emitRunEvent(sink, event) {
8080
+ if (sink === void 0) return;
8081
+ try {
8082
+ sink(event);
8083
+ } catch {
8084
+ }
8085
+ }
8086
+ var init_run_events = __esm({
8087
+ "src/types/run-events.ts"() {
8088
+ }
8089
+ });
7664
8090
  async function withToolWhitelist(whitelist, fn) {
7665
8091
  return toolWhitelistStore.run(whitelist, fn);
7666
8092
  }
@@ -7832,6 +8258,27 @@ var init_repair_middleware = __esm({
7832
8258
  }
7833
8259
  });
7834
8260
 
8261
+ // src/tool-error.ts
8262
+ function renderToolErrorMessage(content) {
8263
+ if (typeof content === "string") return content;
8264
+ return content.map((block) => block.type === "text" ? block.text : `[${block.source.media_type} image]`).join("\n");
8265
+ }
8266
+ var ToolError;
8267
+ var init_tool_error = __esm({
8268
+ "src/tool-error.ts"() {
8269
+ init_errors();
8270
+ ToolError = class extends TheokitAgentError {
8271
+ name = "ToolError";
8272
+ /** The error content surfaced to the model: a string, or text/image blocks. */
8273
+ content;
8274
+ constructor(content, options = {}) {
8275
+ super(renderToolErrorMessage(content), { ...options, isRetryable: false });
8276
+ this.content = content;
8277
+ }
8278
+ };
8279
+ }
8280
+ });
8281
+
7835
8282
  // src/internal/runtime/tools/shell-tool.ts
7836
8283
  async function runShell(options) {
7837
8284
  if (options.sandbox === true && isObviouslyUnsafe(options.command)) {
@@ -7890,9 +8337,13 @@ async function runHandlerTool(kind, handler, call, signal, context) {
7890
8337
  return { stdout: "", stderr: `${kind} tool ${call.name} has no handler`, exitCode: 127 };
7891
8338
  }
7892
8339
  try {
7893
- const stdout = await handler(call.input, { signal, context });
7894
- return { stdout, stderr: "", exitCode: 0 };
8340
+ const out = await handler(call.input, { signal, context });
8341
+ if (typeof out !== "string") return { stdout: "", stderr: "", exitCode: 0, content: out };
8342
+ return { stdout: out, stderr: "", exitCode: 0 };
7895
8343
  } catch (cause) {
8344
+ if (cause instanceof ToolError) {
8345
+ return { stdout: "", stderr: cause.message, exitCode: 1, content: cause.content };
8346
+ }
7896
8347
  const message = cause instanceof Error ? cause.message : String(cause);
7897
8348
  return { stdout: "", stderr: message, exitCode: 1 };
7898
8349
  }
@@ -7937,6 +8388,7 @@ ${result.stderr}`.trim();
7937
8388
  }
7938
8389
  var init_tool_executors = __esm({
7939
8390
  "src/internal/agent-loop/tool-executors.ts"() {
8391
+ init_tool_error();
7940
8392
  init_shell_tool();
7941
8393
  }
7942
8394
  });
@@ -8021,6 +8473,11 @@ async function dispatchSingleCall(inputs, tools, call, events, parentSpan) {
8021
8473
  toolSpan?.end();
8022
8474
  return fileVeto;
8023
8475
  }
8476
+ emitRunEvent(inputs.runEventSink, {
8477
+ type: "tool_progress",
8478
+ toolName: workingCall.name,
8479
+ toolCallId: callId
8480
+ });
8024
8481
  const result = await runToolWithLifecycle(inputs, resolved, workingCall, callId);
8025
8482
  await inputs.pluginManager?.runPostToolCallHooks({
8026
8483
  name: workingCall.name,
@@ -8047,6 +8504,13 @@ function applyRepairAndExtractCall(tools, call) {
8047
8504
  function vetoFromForkWhitelist(inputs, call, callId, events) {
8048
8505
  const whitelistDecision = checkToolWhitelist(call.name);
8049
8506
  if (whitelistDecision.allowed) return void 0;
8507
+ emitRunEvent(inputs.runEventSink, {
8508
+ type: "permission_denied",
8509
+ toolName: call.name,
8510
+ toolCallId: callId,
8511
+ source: "fork_whitelist",
8512
+ message: whitelistDecision.reason ?? "tool not available in fork"
8513
+ });
8050
8514
  events.push(buildToolUseRunning(inputs, callId, call));
8051
8515
  events.push(
8052
8516
  buildToolUseCompleted(inputs, callId, call, {
@@ -8083,6 +8547,13 @@ async function vetoFromPluginPreHook(inputs, call, callId, events) {
8083
8547
  runId: inputs.runId
8084
8548
  });
8085
8549
  if (pluginVeto === void 0) return void 0;
8550
+ emitRunEvent(inputs.runEventSink, {
8551
+ type: "permission_denied",
8552
+ toolName: call.name,
8553
+ toolCallId: callId,
8554
+ source: "plugin",
8555
+ message: pluginVeto.message
8556
+ });
8086
8557
  events.push(
8087
8558
  buildToolUseCompleted(inputs, callId, call, {
8088
8559
  stdout: "",
@@ -8105,6 +8576,13 @@ async function vetoFromFileHookPreDecision(inputs, call, callId, events) {
8105
8576
  runId: inputs.runId
8106
8577
  });
8107
8578
  if (!preDecision.blocked) return void 0;
8579
+ emitRunEvent(inputs.runEventSink, {
8580
+ type: "permission_denied",
8581
+ toolName: call.name,
8582
+ toolCallId: callId,
8583
+ source: "file_hook",
8584
+ message: preDecision.reason ?? "blocked by hook"
8585
+ });
8108
8586
  events.push(
8109
8587
  buildToolUseCompleted(inputs, callId, call, {
8110
8588
  stdout: "",
@@ -8148,7 +8626,9 @@ async function runToolWithLifecycle(inputs, resolved, call, callId) {
8148
8626
  await safeEmitToolHook(inputs.onToolEnd, {
8149
8627
  toolName: call.name,
8150
8628
  args: call.input,
8151
- result: result.stdout,
8629
+ // SE7 — a block-returning handler has empty `stdout`; surface its structured
8630
+ // content to the hook instead of an empty string.
8631
+ result: result.content !== void 0 ? result.content : result.stdout,
8152
8632
  conversationId: inputs.agentId,
8153
8633
  callId,
8154
8634
  durationMs
@@ -8170,7 +8650,10 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
8170
8650
  output: {
8171
8651
  stdout: result.stdout,
8172
8652
  stderr: result.stderr,
8173
- exitCode: result.exitCode ?? 0
8653
+ exitCode: result.exitCode ?? 0,
8654
+ // SE7 — structured content (text + image blocks) when the handler
8655
+ // returned/threw blocks; absent for the string path.
8656
+ ...result.content !== void 0 ? { content: result.content } : {}
8174
8657
  },
8175
8658
  agentId: inputs.agentId,
8176
8659
  runId: inputs.runId
@@ -8183,7 +8666,9 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
8183
8666
  return {
8184
8667
  type: "tool_result",
8185
8668
  toolUseId: call.id,
8186
- content: renderToolResult(result),
8669
+ // SE7 — structured content blocks (handler-returned or ToolError-carried)
8670
+ // are authoritative; otherwise the legacy string rendering.
8671
+ content: result.content !== void 0 ? result.content : renderToolResult(result),
8187
8672
  ...result.exitCode !== 0 && result.exitCode !== void 0 ? { isError: true } : {}
8188
8673
  };
8189
8674
  }
@@ -8233,6 +8718,7 @@ function buildToolUseCompleted(inputs, callId, call, result) {
8233
8718
  }
8234
8719
  var init_tool_dispatch = __esm({
8235
8720
  "src/internal/agent-loop/tool-dispatch.ts"() {
8721
+ init_run_events();
8236
8722
  init_ids();
8237
8723
  init_async_local_storage();
8238
8724
  init_map_with_concurrency();
@@ -8243,6 +8729,33 @@ var init_tool_dispatch = __esm({
8243
8729
  }
8244
8730
  });
8245
8731
 
8732
+ // src/internal/llm/tool-result-content.ts
8733
+ function toBlockToolResultContent(content) {
8734
+ return content;
8735
+ }
8736
+ function toStringToolResultContent(content, providerName) {
8737
+ if (typeof content === "string") return content;
8738
+ if (content.some((block) => block.type === "image")) {
8739
+ throw new ConfigurationError(
8740
+ `provider "${providerName}" does not support image content in tool results`
8741
+ );
8742
+ }
8743
+ return content.filter(
8744
+ (block) => block.type === "text"
8745
+ ).map((block) => block.text).join("\n");
8746
+ }
8747
+ function renderToolResultContentText(content, fn) {
8748
+ if (typeof content === "string") return fn(content);
8749
+ return content.map(
8750
+ (block) => block.type === "text" ? { type: "text", text: fn(block.text) } : block
8751
+ );
8752
+ }
8753
+ var init_tool_result_content = __esm({
8754
+ "src/internal/llm/tool-result-content.ts"() {
8755
+ init_errors();
8756
+ }
8757
+ });
8758
+
8246
8759
  // src/internal/agent-loop/tool-result-guard.ts
8247
8760
  function guardText(content, opts) {
8248
8761
  let out = content;
@@ -8260,12 +8773,17 @@ ${CLOSE}`;
8260
8773
  function applyToolResultGuard(parts, opts) {
8261
8774
  if (opts.delimit !== true && opts.redactPii !== true) return parts;
8262
8775
  return parts.map(
8263
- (p) => p.type === "tool_result" ? { ...p, content: guardText(p.content, opts) } : p
8776
+ (p) => (
8777
+ // SE7 — guard the TEXT of a tool result (string or the text blocks of a
8778
+ // structured content); image blocks pass through untouched.
8779
+ p.type === "tool_result" ? { ...p, content: renderToolResultContentText(p.content, (t) => guardText(t, opts)) } : p
8780
+ )
8264
8781
  );
8265
8782
  }
8266
8783
  var OPEN, CLOSE, PII_PATTERNS;
8267
8784
  var init_tool_result_guard = __esm({
8268
8785
  "src/internal/agent-loop/tool-result-guard.ts"() {
8786
+ init_tool_result_content();
8269
8787
  OPEN = "<untrusted-tool-output>";
8270
8788
  CLOSE = "</untrusted-tool-output>";
8271
8789
  PII_PATTERNS = [
@@ -9298,8 +9816,8 @@ var init_vertex = __esm({
9298
9816
 
9299
9817
  // src/internal/providers/builtin/index.ts
9300
9818
  function registerBuiltins() {
9301
- if (registered2) return;
9302
- registered2 = true;
9819
+ if (registered3) return;
9820
+ registered3 = true;
9303
9821
  registerProvider(ANTHROPIC);
9304
9822
  registerProvider(OPENAI);
9305
9823
  registerProvider(OPENROUTER);
@@ -9311,7 +9829,7 @@ function registerBuiltins() {
9311
9829
  registerProvider(VERTEX);
9312
9830
  registerCatalogProviders();
9313
9831
  }
9314
- var registered2;
9832
+ var registered3;
9315
9833
  var init_builtin = __esm({
9316
9834
  "src/internal/providers/builtin/index.ts"() {
9317
9835
  init_catalog_loader();
@@ -9325,7 +9843,7 @@ var init_builtin = __esm({
9325
9843
  init_openai();
9326
9844
  init_openrouter();
9327
9845
  init_vertex();
9328
- registered2 = false;
9846
+ registered3 = false;
9329
9847
  }
9330
9848
  });
9331
9849
 
@@ -9570,7 +10088,9 @@ function toAnthropicWireMessage(message) {
9570
10088
  return {
9571
10089
  type: "tool_result",
9572
10090
  tool_use_id: part.toolUseId,
9573
- content: part.content,
10091
+ // SE7 — this wire is block-capable: strings pass through, structured
10092
+ // text/image blocks are forwarded natively.
10093
+ content: toBlockToolResultContent(part.content),
9574
10094
  ...part.isError === true ? { is_error: true } : {}
9575
10095
  };
9576
10096
  });
@@ -9647,6 +10167,7 @@ function encodeAnthropicSystem(system) {
9647
10167
  var init_anthropic_shared = __esm({
9648
10168
  "src/internal/llm/anthropic-shared.ts"() {
9649
10169
  init_finish();
10170
+ init_tool_result_content();
9650
10171
  }
9651
10172
  });
9652
10173
 
@@ -10601,7 +11122,8 @@ function toOllamaMessages(message) {
10601
11122
  if (part.type === "tool_result") {
10602
11123
  out.push({
10603
11124
  role: "tool",
10604
- content: part.content,
11125
+ // SE7 — string-only tool role: text blocks flatten; image fails fast.
11126
+ content: toStringToolResultContent(part.content, "ollama"),
10605
11127
  tool_name: part.toolUseId
10606
11128
  });
10607
11129
  }
@@ -10642,6 +11164,7 @@ var init_ollama_native = __esm({
10642
11164
  "src/internal/llm/ollama-native.ts"() {
10643
11165
  init_ollama2();
10644
11166
  init_finish();
11167
+ init_tool_result_content();
10645
11168
  OllamaNativeClient = class {
10646
11169
  constructor(options) {
10647
11170
  this.options = options;
@@ -11006,7 +11529,9 @@ function userOrToolMessages(message) {
11006
11529
  out.push({
11007
11530
  role: "tool",
11008
11531
  tool_call_id: part.toolUseId,
11009
- content: part.content
11532
+ // SE7 — this wire's tool role is string-only: text blocks flatten; an
11533
+ // image block fails fast (ConfigurationError).
11534
+ content: toStringToolResultContent(part.content, "openai")
11010
11535
  });
11011
11536
  }
11012
11537
  }
@@ -11037,6 +11562,7 @@ var init_openai2 = __esm({
11037
11562
  init_finish();
11038
11563
  init_hermes_tool_extract();
11039
11564
  init_sse();
11565
+ init_tool_result_content();
11040
11566
  OpenAIClient = class {
11041
11567
  constructor(options) {
11042
11568
  this.options = options;
@@ -12276,18 +12802,20 @@ var init_personality_filter = __esm({
12276
12802
  function createRealLocalRun(options) {
12277
12803
  const { userText, id, startTime } = prepareRunContext(options.message);
12278
12804
  const supported = /* @__PURE__ */ new Set(["stream", "wait", "cancel", "conversation"]);
12279
- const unusedFixtureScript = {
12805
+ const runScript = {
12280
12806
  events: [],
12281
12807
  finalStatus: "running",
12282
12808
  cancellable: false,
12283
- conversation: []
12809
+ conversation: [],
12810
+ // SE3 — carry the turn's provenance so buildResult forwards it onto RunResult.origin.
12811
+ ...options.sendOptions.origin !== void 0 ? { origin: options.sendOptions.origin } : {}
12284
12812
  };
12285
12813
  const handle = new RealLocalRun(
12286
12814
  {
12287
12815
  id,
12288
12816
  agentId: options.agentId,
12289
12817
  model: options.model,
12290
- script: unusedFixtureScript,
12818
+ script: runScript,
12291
12819
  supportedOps: supported,
12292
12820
  startTime
12293
12821
  },
@@ -12398,6 +12926,8 @@ function buildLoopInputs(options, runId, userText) {
12398
12926
  // M7 — forward SendOptions.context to the loop so every tool handler receives
12399
12927
  // it on `ctx.context` (shared run config set once, e.g. projectRoot).
12400
12928
  ...options.sendOptions.context !== void 0 ? { context: options.sendOptions.context } : {},
12929
+ // SE2 — forward the opt-in typed runtime-event sink to the loop.
12930
+ ...options.sendOptions.onRunEvent !== void 0 ? { runEventSink: options.sendOptions.onRunEvent } : {},
12401
12931
  // #58 / #57 — forward the per-tool timeout + tool-result guard so a consumer
12402
12932
  // can enable them via SendOptions (not only internal AgentLoopInputs).
12403
12933
  ...options.sendOptions.perToolTimeoutMs !== void 0 ? { perToolTimeoutMs: options.sendOptions.perToolTimeoutMs } : {},
@@ -15471,29 +16001,6 @@ var init_judge_call = __esm({
15471
16001
  }
15472
16002
  });
15473
16003
 
15474
- // src/internal/runtime/registry/agent-factory-registry.ts
15475
- var agent_factory_registry_exports = {};
15476
- __export(agent_factory_registry_exports, {
15477
- getAgentFacade: () => getAgentFacade,
15478
- setAgentFacade: () => setAgentFacade
15479
- });
15480
- function setAgentFacade(facade) {
15481
- registered3 = facade;
15482
- }
15483
- function getAgentFacade() {
15484
- if (registered3 === void 0) {
15485
- throw new Error(
15486
- "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
15487
- );
15488
- }
15489
- return registered3;
15490
- }
15491
- var registered3;
15492
- var init_agent_factory_registry = __esm({
15493
- "src/internal/runtime/registry/agent-factory-registry.ts"() {
15494
- }
15495
- });
15496
-
15497
16004
  // src/internal/runtime/lifecycle/run-to-completion.ts
15498
16005
  var run_to_completion_exports = {};
15499
16006
  __export(run_to_completion_exports, {
@@ -15658,14 +16165,14 @@ async function forkAgentImpl(parent, options, deps) {
15658
16165
  return {
15659
16166
  result: result.result,
15660
16167
  toolCalls: [],
15661
- usage: extractUsage(result)
16168
+ usage: extractUsage2(result)
15662
16169
  };
15663
16170
  });
15664
16171
  } finally {
15665
16172
  await fork.dispose();
15666
16173
  }
15667
16174
  }
15668
- function extractUsage(result) {
16175
+ function extractUsage2(result) {
15669
16176
  if (typeof result !== "object" || result === null) {
15670
16177
  return { inputTokens: 0, outputTokens: 0 };
15671
16178
  }
@@ -15824,7 +16331,7 @@ var init_local_agent_memory_hooks = __esm({
15824
16331
  async function executeSendLocked(inputs, message, options) {
15825
16332
  if (inputs.disposed) throw new AgentDisposedError(inputs.agentId);
15826
16333
  await consumePending(inputs.agentId, inputs.invalidationPending, inputs.clearInvalidation, inputs.reload);
15827
- inputs.applyModelOverride(options.model);
16334
+ inputs.applyModelOverride(normalizeModel(options.model));
15828
16335
  const userText = typeof message === "string" ? message : message.text;
15829
16336
  if (inputs.options.onBeforeSend !== void 0) {
15830
16337
  await inputs.options.onBeforeSend({
@@ -15893,6 +16400,7 @@ var init_local_agent_send = __esm({
15893
16400
  init_abort_utils();
15894
16401
  init_memory_path_selector();
15895
16402
  init_memory_store();
16403
+ init_model_selection();
15896
16404
  init_agent_session();
15897
16405
  init_safe_call();
15898
16406
  init_local_agent_invalidate();
@@ -16472,6 +16980,7 @@ function includesSetting(options, source) {
16472
16980
  var LocalAgent;
16473
16981
  var init_local_agent = __esm({
16474
16982
  "src/internal/runtime/local-agent/local-agent.ts"() {
16983
+ init_agent_generate();
16475
16984
  init_errors();
16476
16985
  init_ids();
16477
16986
  init_cwd_mutex();
@@ -16482,6 +16991,7 @@ var init_local_agent = __esm({
16482
16991
  init_hooks_executor();
16483
16992
  init_post_run_lifecycle();
16484
16993
  init_memory_path_selector();
16994
+ init_model_selection();
16485
16995
  init_agent_registry();
16486
16996
  init_live_agent_registry();
16487
16997
  init_agent_session();
@@ -16558,7 +17068,7 @@ var init_local_agent = __esm({
16558
17068
  constructor(options) {
16559
17069
  this.agentId = options.agentId ?? generateLocalAgentId();
16560
17070
  this._telemetry = createTelemetry(options.telemetry);
16561
- this.model = options.model;
17071
+ this.model = normalizeModel(options.model);
16562
17072
  this.options = options;
16563
17073
  this.workspaceCwd = resolveCwd(options.local?.cwd);
16564
17074
  this.conversationStorage = options.conversationStorage;
@@ -16625,6 +17135,11 @@ var init_local_agent = __esm({
16625
17135
  hooks() {
16626
17136
  return this.hooksExecutor;
16627
17137
  }
17138
+ // SE9 — integrated structured output; delegates to the shared helper.
17139
+ generate(message, options) {
17140
+ const { apiKey, local } = this.options;
17141
+ return agentGenerate(this, this.model, apiKey, local, message, options);
17142
+ }
16628
17143
  async send(message, options = {}) {
16629
17144
  if (options.tools !== void 0 && options.tools.length > 0) {
16630
17145
  validateToolCatalog(options.tools);
@@ -16857,6 +17372,7 @@ var init_local_agent = __esm({
16857
17372
  }
16858
17373
  });
16859
17374
  async function runCreateUnderSpan(options, span) {
17375
+ options = { ...options, model: normalizeModel(options.model) };
16860
17376
  validateAgentOptions(options);
16861
17377
  validateCloudToolParity(options);
16862
17378
  await guardAgainstIdCollision(options);
@@ -16930,7 +17446,10 @@ async function createLocalAgent(options) {
16930
17446
  const willFlowToProvider = !isFixtureApiKey(apiKey) && !shouldUseRealLocalRuntime(apiKey);
16931
17447
  const shape = validateApiKeyShape(apiKey, {
16932
17448
  strict: willFlowToProvider,
16933
- ...willFlowToProvider ? { provider: providerFromModelId(options.model?.id) } : {}
17449
+ // `options.model` is already normalized by `runCreateUnderSpan`; `normalizeModel`
17450
+ // here is the idempotent narrowing of the widened public type (`{id}` passes
17451
+ // through by reference) — cleaner than an `as` cast.
17452
+ ...willFlowToProvider ? { provider: providerFromModelId(normalizeModel(options.model)?.id) } : {}
16934
17453
  });
16935
17454
  if (shape.malformed) {
16936
17455
  throw new AuthenticationError(shape.message, { code: "malformed_api_key" });
@@ -16996,6 +17515,10 @@ async function rehydrateExistingAgent(agentId, existing, options) {
16996
17515
  ...existing.options,
16997
17516
  ...options,
16998
17517
  ...mergedLocal !== void 0 ? { local: mergedLocal } : {},
17518
+ // SE8 — normalize a caller-supplied bare-string model override at this resume
17519
+ // boundary (Agent.resume never flows through runCreateUnderSpan). Cloud resume
17520
+ // honors it; local resume forces the persisted model below.
17521
+ model: normalizeModel(options.model ?? existing.options.model),
16999
17522
  mcpServers: void 0,
17000
17523
  agentId
17001
17524
  };
@@ -17078,237 +17601,11 @@ var init_agent_helpers = __esm({
17078
17601
  init_cloud_agent();
17079
17602
  init_cloud_tool_parity();
17080
17603
  init_local_agent();
17604
+ init_model_selection();
17081
17605
  init_agent_registry();
17082
17606
  init_validate_agent_options();
17083
17607
  }
17084
17608
  });
17085
- function toJsonSchema(schema, options = { unrepresentable: "any" }) {
17086
- return toJSONSchema(schema, options);
17087
- }
17088
- var init_to_json_schema = __esm({
17089
- "src/internal/zod/to-json-schema.ts"() {
17090
- }
17091
- });
17092
- function requireZod() {
17093
- if (cachedZ !== void 0) return cachedZ;
17094
- const r = createRequire(import.meta.url);
17095
- let mod;
17096
- try {
17097
- mod = r("zod");
17098
- } catch (cause) {
17099
- throw new ConfigurationError(
17100
- 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
17101
- { code: "zod_not_installed", cause }
17102
- );
17103
- }
17104
- cachedZ = mod.z ?? mod;
17105
- return cachedZ;
17106
- }
17107
- function makeOutputTool(jsonSchema, onCapture) {
17108
- return {
17109
- name: "output",
17110
- description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
17111
- inputSchema: jsonSchema,
17112
- handler: (input) => {
17113
- const out = onCapture(input);
17114
- if (typeof out === "string") return out;
17115
- if (out instanceof Promise) return out;
17116
- return "";
17117
- }
17118
- };
17119
- }
17120
- function buildTransientAgentOptions(params) {
17121
- return {
17122
- model: params.model,
17123
- local: params.local,
17124
- tools: [params.outputTool],
17125
- systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
17126
- ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
17127
- ...params.providers !== void 0 ? { providers: params.providers } : {}
17128
- };
17129
- }
17130
- function extractUsage2(result) {
17131
- const u = result.usage;
17132
- if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
17133
- return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
17134
- }
17135
- function buildToolPrompt(prompt) {
17136
- return `${prompt}
17137
-
17138
- Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
17139
- }
17140
- function setupStructuredOutput(schema, maxRetries) {
17141
- const z7 = requireZod();
17142
- const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
17143
- return {
17144
- z: z7,
17145
- jsonSchema,
17146
- maxRetries: maxRetries ?? 1,
17147
- initialUsage: { inputTokens: 0, outputTokens: 0 }
17148
- };
17149
- }
17150
- async function disposeAndDeleteTransient(agent, deletor) {
17151
- await agent.dispose();
17152
- try {
17153
- await deletor(agent.agentId);
17154
- } catch {
17155
- }
17156
- }
17157
- var cachedZ;
17158
- var init_structured_output_helpers = __esm({
17159
- "src/internal/structured-output-helpers.ts"() {
17160
- init_errors();
17161
- init_to_json_schema();
17162
- }
17163
- });
17164
-
17165
- // src/generate-object.ts
17166
- var generate_object_exports = {};
17167
- __export(generate_object_exports, {
17168
- GenerateObjectError: () => GenerateObjectError,
17169
- generateObjectImpl: () => generateObjectImpl
17170
- });
17171
- function salvagePartial(schema, raw) {
17172
- if (typeof raw !== "object" || raw === null) return raw;
17173
- const shape = schema.shape;
17174
- if (shape === void 0 || typeof shape !== "object") return raw;
17175
- const rawObj = raw;
17176
- const out = {};
17177
- for (const [key, fieldSchema] of Object.entries(shape)) {
17178
- if (typeof fieldSchema?.safeParse !== "function") continue;
17179
- const parsed = fieldSchema.safeParse(rawObj[key]);
17180
- if (parsed.success) out[key] = parsed.data;
17181
- }
17182
- return out;
17183
- }
17184
- async function runReasoningPhase(options, deps) {
17185
- const reasoningOptions = {
17186
- model: options.model,
17187
- local: options.local,
17188
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
17189
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
17190
- ...options.providers !== void 0 ? { providers: options.providers } : {}
17191
- };
17192
- const reasoningAgent = await deps.create(reasoningOptions);
17193
- try {
17194
- const run = await reasoningAgent.send(options.prompt);
17195
- const result = await run.wait();
17196
- const text = result.result;
17197
- return typeof text === "string" ? text : options.prompt;
17198
- } finally {
17199
- await disposeAndDeleteTransient(reasoningAgent, deps.delete);
17200
- }
17201
- }
17202
- async function generateObjectImpl(options, deps) {
17203
- const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
17204
- options.schema,
17205
- options.maxRetries
17206
- );
17207
- let capturedRaw;
17208
- let lastUsage = initialUsage;
17209
- const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
17210
- class CaptureSentinel extends Error {
17211
- constructor(captured) {
17212
- super("generate-object-capture");
17213
- this.captured = captured;
17214
- }
17215
- captured;
17216
- [sentinel] = true;
17217
- }
17218
- const outputTool = makeOutputTool(jsonSchema, (input) => {
17219
- if (capturedRaw === void 0) {
17220
- capturedRaw = input;
17221
- }
17222
- throw new CaptureSentinel(input);
17223
- });
17224
- const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
17225
- const structuringModel = options.structuringModel ?? options.model;
17226
- const structuringPrompt = reasoningText ?? options.prompt;
17227
- const agentOptions = buildTransientAgentOptions({
17228
- model: structuringModel,
17229
- local: options.local,
17230
- outputTool,
17231
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
17232
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
17233
- ...options.providers !== void 0 ? { providers: options.providers } : {}
17234
- });
17235
- const agent = await deps.create(agentOptions);
17236
- try {
17237
- const userMessage = buildToolPrompt(structuringPrompt);
17238
- let lastParseError;
17239
- for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
17240
- capturedRaw = void 0;
17241
- const run = await agent.send(userMessage);
17242
- const result = await run.wait();
17243
- lastUsage = extractUsage2(result);
17244
- if (capturedRaw === void 0) {
17245
- if (result.status === "error" && result.error !== void 0) {
17246
- throw new GenerateObjectError(
17247
- "no_tool_call",
17248
- `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
17249
- result.error
17250
- );
17251
- }
17252
- if (attempt === maxRetries) {
17253
- throw new GenerateObjectError(
17254
- "no_tool_call",
17255
- "The model returned text instead of calling the `output` tool."
17256
- );
17257
- }
17258
- continue;
17259
- }
17260
- const parsed = options.schema.safeParse(capturedRaw);
17261
- if (parsed.success) {
17262
- return {
17263
- object: parsed.data,
17264
- raw: capturedRaw,
17265
- usage: lastUsage,
17266
- finishReason: "tool_use"
17267
- };
17268
- }
17269
- lastParseError = parsed.error;
17270
- }
17271
- if (options.errorStrategy === "return-raw") {
17272
- return {
17273
- object: capturedRaw,
17274
- raw: capturedRaw,
17275
- usage: lastUsage,
17276
- finishReason: "tool_use"
17277
- };
17278
- }
17279
- if (options.errorStrategy === "return-partial") {
17280
- return {
17281
- object: salvagePartial(options.schema, capturedRaw),
17282
- raw: capturedRaw,
17283
- usage: lastUsage,
17284
- finishReason: "tool_use"
17285
- };
17286
- }
17287
- throw new GenerateObjectError(
17288
- "parse_failed",
17289
- "Schema parse failed after all retries.",
17290
- lastParseError
17291
- );
17292
- } finally {
17293
- await disposeAndDeleteTransient(agent, deps.delete);
17294
- }
17295
- }
17296
- var GenerateObjectError;
17297
- var init_generate_object = __esm({
17298
- "src/generate-object.ts"() {
17299
- init_structured_output_helpers();
17300
- GenerateObjectError = class extends Error {
17301
- name = "GenerateObjectError";
17302
- code;
17303
- cause;
17304
- constructor(code, message, cause) {
17305
- super(message);
17306
- this.code = code;
17307
- if (cause !== void 0) this.cause = cause;
17308
- }
17309
- };
17310
- }
17311
- });
17312
17609
 
17313
17610
  // src/stream-object.ts
17314
17611
  var stream_object_exports = {};
@@ -17364,7 +17661,7 @@ async function* streamObjectImpl(options, deps) {
17364
17661
  }
17365
17662
  }
17366
17663
  const result = await run.wait();
17367
- lastUsage = extractUsage2(result);
17664
+ lastUsage = extractUsage(result);
17368
17665
  if (capturedRaw === void 0) {
17369
17666
  if (result.status === "error" && result.error !== void 0) {
17370
17667
  throw new StreamObjectError(
@@ -18011,6 +18308,7 @@ var init_agent = __esm({
18011
18308
  };
18012
18309
  setAgentFacade({
18013
18310
  create: (options) => Agent.create(options),
18311
+ delete: (agentId) => Agent.delete(agentId),
18014
18312
  prompt: (message, options) => Agent.prompt(message, options),
18015
18313
  get: (agentId) => Agent.get(agentId),
18016
18314
  resume: (agentId, options) => Agent.resume(agentId, options),
@@ -18067,7 +18365,9 @@ var MessageBus = class {
18067
18365
  ...partial,
18068
18366
  from,
18069
18367
  to,
18070
- timestamp: Date.now()
18368
+ timestamp: Date.now(),
18369
+ // SE3 — provenance projection of the sender address (thin view over `from`).
18370
+ origin: { kind: "peer", from }
18071
18371
  };
18072
18372
  handler(message);
18073
18373
  }
@@ -18081,7 +18381,9 @@ var MessageBus = class {
18081
18381
  ...partial,
18082
18382
  from,
18083
18383
  to,
18084
- timestamp: Date.now()
18384
+ timestamp: Date.now(),
18385
+ // SE3 — provenance projection of the sender address (thin view over `from`).
18386
+ origin: { kind: "peer", from }
18085
18387
  };
18086
18388
  return Promise.race([
18087
18389
  Promise.resolve(handler(message)),