@electric-ax/agents 0.4.9 → 0.4.11

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.
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import fs from "node:fs";
4
4
  import pino from "pino";
5
5
  import { fileURLToPath } from "node:url";
6
- import { appendPathToUrl, completeWithLowCostModel, createEntityRegistry, createPullWakeRunner, createRuntimeHandler, createSkillTools, createSkillsRegistry, db, detectAvailableProviders, readCodexAccessToken, registerToolProvider, unregisterToolProvider } from "@electric-ax/agents-runtime";
6
+ import { MOONSHOT_API_BASE_URL, MOONSHOT_PROVIDER, appendPathToUrl, completeWithLowCostModel, createEntityRegistry, createPullWakeRunner, createRuntimeHandler, createSkillTools, createSkillsRegistry, db, detectAvailableProviders, getMoonshotApiKey, getMoonshotModels, readCodexAccessToken, registerToolProvider, unregisterToolProvider } from "@electric-ax/agents-runtime";
7
7
  import { braveSearchTool, createBashTool, createEditTool, createEventSourceTools, createFetchUrlTool, createReadFileTool, createSendTool, createWriteTool, fetchUrlTool } from "@electric-ax/agents-runtime/tools";
8
8
  import { z } from "zod";
9
9
  import { createHash } from "node:crypto";
@@ -16,28 +16,47 @@ import { getModels } from "@mariozechner/pi-ai";
16
16
  import { bridgeMcpTool, buildPromptTools, buildResourceTools, createRegistry, keychainPersistence, loadConfig, mcp, watchConfig } from "@electric-ax/agents-mcp";
17
17
 
18
18
  //#region src/log.ts
19
- const LOG_DIR = process.env.ELECTRIC_AGENTS_LOG_DIR ?? path.resolve(process.cwd(), `logs`);
20
- fs.mkdirSync(LOG_DIR, { recursive: true });
21
- const LOG_FILE = path.join(LOG_DIR, `builtin-agents-${Date.now()}.jsonl`);
22
19
  const LOG_LEVEL = process.env.ELECTRIC_AGENTS_LOG_LEVEL ?? `info`;
23
20
  const IS_ELECTRON_MAIN = Boolean(process.versions.electron);
21
+ const USE_FILE_LOGS = process.env.ELECTRIC_AGENTS_LOG_FILE !== `false`;
24
22
  const USE_PRETTY_LOGS = LOG_LEVEL !== `silent` && !process.env.VITEST && !IS_ELECTRON_MAIN;
25
- const streams = [{ stream: pino.destination({
26
- dest: LOG_FILE,
27
- sync: IS_ELECTRON_MAIN
28
- }) }];
29
- if (USE_PRETTY_LOGS) streams.push({ stream: pino.transport({
30
- target: `pino-pretty`,
31
- options: {
32
- colorize: true,
33
- ignore: `pid,hostname,name`,
34
- translateTime: `SYS:HH:MM:ss`
23
+ let _logger;
24
+ function getLogger() {
25
+ if (_logger) return _logger;
26
+ const streams = [];
27
+ try {
28
+ if (USE_FILE_LOGS) {
29
+ const logDir = process.env.ELECTRIC_AGENTS_LOG_DIR ?? path.resolve(process.cwd(), `logs`);
30
+ fs.mkdirSync(logDir, { recursive: true });
31
+ const logFile = path.join(logDir, `builtin-agents-${Date.now()}.jsonl`);
32
+ streams.push({ stream: pino.destination({
33
+ dest: logFile,
34
+ sync: IS_ELECTRON_MAIN
35
+ }) });
36
+ }
37
+ } catch (err) {
38
+ process.stderr.write(`[agents] Failed to initialize file logging: ${err instanceof Error ? err.message : err}\n`);
35
39
  }
36
- }) });
37
- const logger = pino({
38
- base: void 0,
39
- level: LOG_LEVEL
40
- }, pino.multistream(streams));
40
+ try {
41
+ if (USE_PRETTY_LOGS) streams.push({ stream: pino.transport({
42
+ target: `pino-pretty`,
43
+ options: {
44
+ colorize: true,
45
+ ignore: `pid,hostname,name`,
46
+ translateTime: `SYS:HH:MM:ss`
47
+ }
48
+ }) });
49
+ } catch {}
50
+ _logger = streams.length > 0 ? pino({
51
+ base: void 0,
52
+ level: LOG_LEVEL
53
+ }, pino.multistream(streams)) : pino({
54
+ base: void 0,
55
+ enabled: false,
56
+ level: LOG_LEVEL
57
+ });
58
+ return _logger;
59
+ }
41
60
  function formatArgs(args) {
42
61
  const errors = [];
43
62
  const parts = [];
@@ -51,24 +70,24 @@ function formatArgs(args) {
51
70
  const serverLog = {
52
71
  debug(...args) {
53
72
  const { msg } = formatArgs(args);
54
- logger.debug(msg);
73
+ getLogger().debug(msg);
55
74
  },
56
75
  info(...args) {
57
76
  const { msg } = formatArgs(args);
58
- logger.info(msg);
77
+ getLogger().info(msg);
59
78
  },
60
79
  warn(...args) {
61
80
  const { err, msg } = formatArgs(args);
62
- if (err) logger.warn({ err }, msg);
63
- else logger.warn(msg);
81
+ if (err) getLogger().warn({ err }, msg);
82
+ else getLogger().warn(msg);
64
83
  },
65
84
  error(...args) {
66
85
  const { err, msg } = formatArgs(args);
67
- if (err) logger.error({ err }, msg);
68
- else logger.error(msg);
86
+ if (err) getLogger().error({ err }, msg);
87
+ else getLogger().error(msg);
69
88
  },
70
89
  event(obj, msg) {
71
- logger.info(obj, msg);
90
+ getLogger().info(obj, msg);
72
91
  }
73
92
  };
74
93
 
@@ -774,6 +793,7 @@ function createSpawnWorkerTool(ctx, modelConfig) {
774
793
 
775
794
  //#endregion
776
795
  //#region src/model-catalog.ts
796
+ const MODEL_INPUTS_SCHEMA_DEF = `electricModelInputs`;
777
797
  const REASONING_EFFORT_VALUES = [
778
798
  `auto`,
779
799
  `minimal`,
@@ -785,13 +805,15 @@ const DEFAULT_ANTHROPIC_MODEL = `claude-sonnet-4-6`;
785
805
  const DEFAULT_OPENAI_MODEL = `gpt-4.1`;
786
806
  const DEFAULT_CODEX_MODEL = `gpt-5.4`;
787
807
  const DEFAULT_DEEPSEEK_MODEL = `deepseek-v4-flash`;
808
+ const DEFAULT_MOONSHOT_MODEL = `kimi-k2.6`;
788
809
  function modelValue(provider, id) {
789
810
  return `${provider}:${id}`;
790
811
  }
791
- function providerLabel(provider) {
812
+ function builtinModelProviderLabel(provider) {
792
813
  if (provider === `anthropic`) return `Anthropic`;
793
814
  if (provider === `openai-codex`) return `OpenAI Codex`;
794
815
  if (provider === `deepseek`) return `DeepSeek`;
816
+ if (provider === MOONSHOT_PROVIDER) return `Kimi`;
795
817
  return `OpenAI`;
796
818
  }
797
819
  function configuredProviders() {
@@ -803,7 +825,8 @@ function mockFallbackCatalog() {
803
825
  id: DEFAULT_ANTHROPIC_MODEL,
804
826
  label: `Anthropic ${DEFAULT_ANTHROPIC_MODEL}`,
805
827
  value: modelValue(`anthropic`, DEFAULT_ANTHROPIC_MODEL),
806
- reasoning: true
828
+ reasoning: true,
829
+ input: [`text`, `image`]
807
830
  };
808
831
  return {
809
832
  choices: [fallback],
@@ -821,6 +844,9 @@ async function fetchAvailableModelIds(provider) {
821
844
  }) : provider === `deepseek` ? await fetch(`https://api.deepseek.com/v1/models`, {
822
845
  headers: { authorization: `Bearer ${process.env.DEEPSEEK_API_KEY ?? ``}` },
823
846
  signal: AbortSignal.timeout(3e3)
847
+ }) : provider === MOONSHOT_PROVIDER ? await fetch(`${MOONSHOT_API_BASE_URL}/models`, {
848
+ headers: { authorization: `Bearer ${getMoonshotApiKey() ?? ``}` },
849
+ signal: AbortSignal.timeout(3e3)
824
850
  }) : await fetch(`https://api.openai.com/v1/models`, {
825
851
  headers: { authorization: `Bearer ${process.env.OPENAI_API_KEY ?? ``}` },
826
852
  signal: AbortSignal.timeout(3e3)
@@ -834,24 +860,42 @@ async function fetchAvailableModelIds(provider) {
834
860
  return null;
835
861
  }
836
862
  }
837
- async function choicesForProvider(provider) {
838
- const knownModels = getModels(provider);
839
- if (provider === `openai-codex`) return knownModels.map((model) => ({
863
+ function knownModelsForProvider(provider) {
864
+ return provider === MOONSHOT_PROVIDER ? getMoonshotModels() : getModels(provider);
865
+ }
866
+ function choiceForKnownModel(provider, model) {
867
+ return {
840
868
  provider,
841
869
  id: model.id,
842
- label: `${providerLabel(provider)} ${model.name}`,
870
+ label: `${builtinModelProviderLabel(provider)} ${model.name}`,
843
871
  value: modelValue(provider, model.id),
844
- reasoning: model.reasoning
845
- }));
872
+ reasoning: model.reasoning,
873
+ input: model.input
874
+ };
875
+ }
876
+ function listBuiltinModelChoices(providers) {
877
+ return providers.flatMap((provider) => knownModelsForProvider(provider).map((model) => choiceForKnownModel(provider, model)));
878
+ }
879
+ async function choicesForProvider(provider) {
880
+ const knownChoices = listBuiltinModelChoices([provider]);
881
+ if (provider === `openai-codex`) return knownChoices;
846
882
  const availableIds = await fetchAvailableModelIds(provider);
847
- const models = availableIds === null ? knownModels : knownModels.filter((model) => availableIds.has(model.id));
848
- return models.map((model) => ({
849
- provider,
850
- id: model.id,
851
- label: `${providerLabel(provider)} ${model.name}`,
852
- value: modelValue(provider, model.id),
853
- reasoning: model.reasoning
854
- }));
883
+ return availableIds === null ? knownChoices : knownChoices.filter((choice) => availableIds.has(choice.id));
884
+ }
885
+ function enabledModelSet(values) {
886
+ if (!values) return null;
887
+ const enabled = new Set();
888
+ for (const value of values) {
889
+ const trimmed = value.trim();
890
+ if (trimmed) enabled.add(trimmed);
891
+ }
892
+ return enabled.size > 0 ? enabled : null;
893
+ }
894
+ function filterChoicesByEnabledModels(choices, values) {
895
+ const enabled = enabledModelSet(values);
896
+ if (!enabled) return choices;
897
+ const filtered = choices.filter((choice) => enabled.has(choice.value));
898
+ return filtered.length > 0 ? filtered : choices;
855
899
  }
856
900
  function withProviderPayloadDefaults(config, choice, reasoningEffort) {
857
901
  if (choice.provider !== `openai` && choice.provider !== `openai-codex` || !choice.reasoning) return config;
@@ -879,9 +923,10 @@ function parseReasoningEffort(value) {
879
923
  async function createBuiltinModelCatalog(options = {}) {
880
924
  const providers = configuredProviders();
881
925
  if (providers.length === 0 && options.allowMockFallback) return mockFallbackCatalog();
882
- const choices = (await Promise.all(providers.map((provider) => choicesForProvider(provider)))).flat();
926
+ const providerChoices = (await Promise.all(providers.map((provider) => choicesForProvider(provider)))).flat();
927
+ const choices = filterChoicesByEnabledModels(providerChoices, options.enabledModelValues);
883
928
  if (choices.length === 0) return options.allowMockFallback ? mockFallbackCatalog() : null;
884
- const defaultChoice = choices.find((choice) => choice.provider === `anthropic` && choice.id === DEFAULT_ANTHROPIC_MODEL) ?? choices.find((choice) => choice.provider === `openai` && choice.id === DEFAULT_OPENAI_MODEL) ?? choices.find((choice) => choice.provider === `openai-codex` && choice.id === DEFAULT_CODEX_MODEL) ?? choices.find((choice) => choice.provider === `deepseek` && choice.id === DEFAULT_DEEPSEEK_MODEL) ?? choices[0];
929
+ const defaultChoice = choices.find((choice) => choice.provider === `anthropic` && choice.id === DEFAULT_ANTHROPIC_MODEL) ?? choices.find((choice) => choice.provider === `openai` && choice.id === DEFAULT_OPENAI_MODEL) ?? choices.find((choice) => choice.provider === `openai-codex` && choice.id === DEFAULT_CODEX_MODEL) ?? choices.find((choice) => choice.provider === `deepseek` && choice.id === DEFAULT_DEEPSEEK_MODEL) ?? choices.find((choice) => choice.provider === MOONSHOT_PROVIDER && choice.id === DEFAULT_MOONSHOT_MODEL) ?? choices[0];
885
930
  return {
886
931
  choices,
887
932
  defaultChoice
@@ -897,13 +942,24 @@ function resolveBuiltinModelConfig(catalog, args) {
897
942
  provider: choice.provider,
898
943
  model: choice.id,
899
944
  ...reasoningEffort && { reasoningEffort },
900
- ...choice.provider === `openai-codex` && { getApiKey: () => readCodexAccessToken() }
945
+ ...choice.provider === `openai-codex` && { getApiKey: () => readCodexAccessToken() },
946
+ ...choice.provider === MOONSHOT_PROVIDER && { getApiKey: () => getMoonshotApiKey() }
901
947
  };
902
948
  return withProviderPayloadDefaults(config, choice, reasoningEffort);
903
949
  }
904
950
  function modelChoiceValues(catalog) {
905
951
  return catalog.choices.map((choice) => choice.value);
906
952
  }
953
+ function modelInputSchemaDefs(catalog) {
954
+ return { [MODEL_INPUTS_SCHEMA_DEF]: {
955
+ type: `object`,
956
+ properties: Object.fromEntries(catalog.choices.map((choice) => [choice.value, {
957
+ type: `array`,
958
+ items: { enum: [`text`, `image`] },
959
+ default: choice.input
960
+ }]))
961
+ } };
962
+ }
907
963
 
908
964
  //#endregion
909
965
  //#region src/agents/horton.ts
@@ -1122,10 +1178,40 @@ function payloadToTitleText(payload) {
1122
1178
  }
1123
1179
  return String(payload);
1124
1180
  }
1181
+ function attachmentTitleText(attachment) {
1182
+ const mimeType = typeof attachment.mimeType === `string` ? attachment.mimeType : ``;
1183
+ const filename = typeof attachment.filename === `string` && attachment.filename.trim() ? attachment.filename.trim() : typeof attachment.id === `string` ? attachment.id : `attachment`;
1184
+ const kind = mimeType.startsWith(`image/`) ? `image` : `file`;
1185
+ return `Attached ${kind}: ${filename}`;
1186
+ }
1187
+ function attachmentsForInboxMessage(ctx, inboxKey) {
1188
+ const manifests = ctx.db.collections.manifests?.toArray;
1189
+ if (!Array.isArray(manifests)) return [];
1190
+ return manifests.filter((entry) => {
1191
+ if (!entry || typeof entry !== `object`) return false;
1192
+ const attachment = entry;
1193
+ if (attachment.kind !== `attachment`) return false;
1194
+ if (attachment.role !== `input`) return false;
1195
+ const subject = attachment.subject;
1196
+ return subject !== null && typeof subject === `object` && !Array.isArray(subject) && subject.type === `inbox` && subject.key === inboxKey;
1197
+ });
1198
+ }
1199
+ function messageTitleText(ctx, message) {
1200
+ const pieces = [];
1201
+ const text = payloadToTitleText(message.payload).trim();
1202
+ if (text) pieces.push(text);
1203
+ const key = typeof message.key === `string` ? message.key : null;
1204
+ const attachments = key ? attachmentsForInboxMessage(ctx, key) : [];
1205
+ for (const attachment of attachments) {
1206
+ const attachmentText = attachmentTitleText(attachment);
1207
+ if (attachmentText) pieces.push(attachmentText);
1208
+ }
1209
+ return pieces.join(`\n`);
1210
+ }
1125
1211
  async function extractFirstUserMessage(ctx) {
1126
1212
  const firstMessage = ctx.db.collections.inbox.toArray.filter((message) => message.from !== `system`).sort((left, right) => messageSeq(left) - messageSeq(right))[0];
1127
1213
  if (!firstMessage) return null;
1128
- const text = payloadToTitleText(firstMessage.payload);
1214
+ const text = messageTitleText(ctx, firstMessage);
1129
1215
  return text.length > 0 ? text : null;
1130
1216
  }
1131
1217
  function messageSeq(message) {
@@ -1165,7 +1251,7 @@ function createAssistantHandler(options) {
1165
1251
  ...mcp.tools()
1166
1252
  ];
1167
1253
  const hasEventSourceTools = tools.some((tool) => getToolName(tool) === `list_event_sources`);
1168
- const titlePromise = ctx.firstWake && !ctx.tags.title ? (async () => {
1254
+ const titlePromise = !ctx.tags.title ? (async () => {
1169
1255
  const firstUserMessage = await extractFirstUserMessage(ctx);
1170
1256
  if (!firstUserMessage) return;
1171
1257
  let title = null;
@@ -1286,7 +1372,7 @@ function registerHorton(registry, options) {
1286
1372
  model: z.enum(modelChoiceValues(modelCatalog)).default(modelCatalog.defaultChoice.value),
1287
1373
  reasoningEffort: z.enum(REASONING_EFFORT_VALUES).default(`auto`).describe(`Reasoning effort for compatible reasoning models. Auto uses a safe provider default.`),
1288
1374
  workingDirectory: z.string().optional().describe(`Working directory for file operations. Defaults to the server's configured cwd.`)
1289
- });
1375
+ }).meta({ $defs: modelInputSchemaDefs(modelCatalog) });
1290
1376
  registry.define(`horton`, {
1291
1377
  description: `Friendly capable assistant — chat, code, research, dispatch`,
1292
1378
  creationSchema: hortonCreationSchema,
@@ -1506,10 +1592,13 @@ function createBuiltinElectricTools(custom) {
1506
1592
  };
1507
1593
  }
1508
1594
  async function createBuiltinAgentHandler(options) {
1509
- const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
1510
- const modelCatalog = await createBuiltinModelCatalog({ allowMockFallback: Boolean(streamFn) });
1595
+ const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, enabledModelValues, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
1596
+ const modelCatalog = await createBuiltinModelCatalog({
1597
+ allowMockFallback: Boolean(streamFn),
1598
+ enabledModelValues
1599
+ });
1511
1600
  if (!modelCatalog) {
1512
- serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY or OPENAI_API_KEY`);
1601
+ serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY, OPENAI_API_KEY, DEEPSEEK_API_KEY, or MOONSHOT_API_KEY`);
1513
1602
  return null;
1514
1603
  }
1515
1604
  const cwd = workingDirectory ?? process.cwd();
@@ -1703,9 +1792,10 @@ var BuiltinAgentsServer = class {
1703
1792
  publicUrl,
1704
1793
  runtimeName: `builtin-agents`,
1705
1794
  baseSkillsDir: this.options.baseSkillsDir,
1795
+ enabledModelValues: this.options.enabledModelValues,
1706
1796
  serverHeaders: pullWake.headers
1707
1797
  });
1708
- if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY or OPENAI_API_KEY must be set before starting builtin agents`);
1798
+ if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY, OPENAI_API_KEY, DEEPSEEK_API_KEY, or MOONSHOT_API_KEY must be set before starting builtin agents`);
1709
1799
  await registerBuiltinAgentTypes(this.bootstrap);
1710
1800
  const registeredRunner = pullWake.registerRunner ? await this.registerPullWakeRunner(pullWake) : null;
1711
1801
  this.pullWakeRunner = createPullWakeRunner({
package/dist/index.cjs CHANGED
@@ -40,28 +40,47 @@ const __mariozechner_pi_ai = __toESM(require("@mariozechner/pi-ai"));
40
40
  const __electric_ax_agents_mcp = __toESM(require("@electric-ax/agents-mcp"));
41
41
 
42
42
  //#region src/log.ts
43
- const LOG_DIR = process.env.ELECTRIC_AGENTS_LOG_DIR ?? node_path.default.resolve(process.cwd(), `logs`);
44
- node_fs.default.mkdirSync(LOG_DIR, { recursive: true });
45
- const LOG_FILE = node_path.default.join(LOG_DIR, `builtin-agents-${Date.now()}.jsonl`);
46
43
  const LOG_LEVEL = process.env.ELECTRIC_AGENTS_LOG_LEVEL ?? `info`;
47
44
  const IS_ELECTRON_MAIN = Boolean(process.versions.electron);
45
+ const USE_FILE_LOGS = process.env.ELECTRIC_AGENTS_LOG_FILE !== `false`;
48
46
  const USE_PRETTY_LOGS = LOG_LEVEL !== `silent` && !process.env.VITEST && !IS_ELECTRON_MAIN;
49
- const streams = [{ stream: pino.default.destination({
50
- dest: LOG_FILE,
51
- sync: IS_ELECTRON_MAIN
52
- }) }];
53
- if (USE_PRETTY_LOGS) streams.push({ stream: pino.default.transport({
54
- target: `pino-pretty`,
55
- options: {
56
- colorize: true,
57
- ignore: `pid,hostname,name`,
58
- translateTime: `SYS:HH:MM:ss`
47
+ let _logger;
48
+ function getLogger() {
49
+ if (_logger) return _logger;
50
+ const streams = [];
51
+ try {
52
+ if (USE_FILE_LOGS) {
53
+ const logDir = process.env.ELECTRIC_AGENTS_LOG_DIR ?? node_path.default.resolve(process.cwd(), `logs`);
54
+ node_fs.default.mkdirSync(logDir, { recursive: true });
55
+ const logFile = node_path.default.join(logDir, `builtin-agents-${Date.now()}.jsonl`);
56
+ streams.push({ stream: pino.default.destination({
57
+ dest: logFile,
58
+ sync: IS_ELECTRON_MAIN
59
+ }) });
60
+ }
61
+ } catch (err) {
62
+ process.stderr.write(`[agents] Failed to initialize file logging: ${err instanceof Error ? err.message : err}\n`);
59
63
  }
60
- }) });
61
- const logger = (0, pino.default)({
62
- base: void 0,
63
- level: LOG_LEVEL
64
- }, pino.default.multistream(streams));
64
+ try {
65
+ if (USE_PRETTY_LOGS) streams.push({ stream: pino.default.transport({
66
+ target: `pino-pretty`,
67
+ options: {
68
+ colorize: true,
69
+ ignore: `pid,hostname,name`,
70
+ translateTime: `SYS:HH:MM:ss`
71
+ }
72
+ }) });
73
+ } catch {}
74
+ _logger = streams.length > 0 ? (0, pino.default)({
75
+ base: void 0,
76
+ level: LOG_LEVEL
77
+ }, pino.default.multistream(streams)) : (0, pino.default)({
78
+ base: void 0,
79
+ enabled: false,
80
+ level: LOG_LEVEL
81
+ });
82
+ return _logger;
83
+ }
65
84
  function formatArgs(args) {
66
85
  const errors = [];
67
86
  const parts = [];
@@ -75,24 +94,24 @@ function formatArgs(args) {
75
94
  const serverLog = {
76
95
  debug(...args) {
77
96
  const { msg } = formatArgs(args);
78
- logger.debug(msg);
97
+ getLogger().debug(msg);
79
98
  },
80
99
  info(...args) {
81
100
  const { msg } = formatArgs(args);
82
- logger.info(msg);
101
+ getLogger().info(msg);
83
102
  },
84
103
  warn(...args) {
85
104
  const { err, msg } = formatArgs(args);
86
- if (err) logger.warn({ err }, msg);
87
- else logger.warn(msg);
105
+ if (err) getLogger().warn({ err }, msg);
106
+ else getLogger().warn(msg);
88
107
  },
89
108
  error(...args) {
90
109
  const { err, msg } = formatArgs(args);
91
- if (err) logger.error({ err }, msg);
92
- else logger.error(msg);
110
+ if (err) getLogger().error({ err }, msg);
111
+ else getLogger().error(msg);
93
112
  },
94
113
  event(obj, msg) {
95
- logger.info(obj, msg);
114
+ getLogger().info(obj, msg);
96
115
  }
97
116
  };
98
117
 
@@ -798,6 +817,7 @@ function createSpawnWorkerTool(ctx, modelConfig) {
798
817
 
799
818
  //#endregion
800
819
  //#region src/model-catalog.ts
820
+ const MODEL_INPUTS_SCHEMA_DEF = `electricModelInputs`;
801
821
  const REASONING_EFFORT_VALUES = [
802
822
  `auto`,
803
823
  `minimal`,
@@ -809,13 +829,15 @@ const DEFAULT_ANTHROPIC_MODEL = `claude-sonnet-4-6`;
809
829
  const DEFAULT_OPENAI_MODEL = `gpt-4.1`;
810
830
  const DEFAULT_CODEX_MODEL = `gpt-5.4`;
811
831
  const DEFAULT_DEEPSEEK_MODEL = `deepseek-v4-flash`;
832
+ const DEFAULT_MOONSHOT_MODEL = `kimi-k2.6`;
812
833
  function modelValue(provider, id) {
813
834
  return `${provider}:${id}`;
814
835
  }
815
- function providerLabel(provider) {
836
+ function builtinModelProviderLabel(provider) {
816
837
  if (provider === `anthropic`) return `Anthropic`;
817
838
  if (provider === `openai-codex`) return `OpenAI Codex`;
818
839
  if (provider === `deepseek`) return `DeepSeek`;
840
+ if (provider === __electric_ax_agents_runtime.MOONSHOT_PROVIDER) return `Kimi`;
819
841
  return `OpenAI`;
820
842
  }
821
843
  function configuredProviders() {
@@ -827,7 +849,8 @@ function mockFallbackCatalog() {
827
849
  id: DEFAULT_ANTHROPIC_MODEL,
828
850
  label: `Anthropic ${DEFAULT_ANTHROPIC_MODEL}`,
829
851
  value: modelValue(`anthropic`, DEFAULT_ANTHROPIC_MODEL),
830
- reasoning: true
852
+ reasoning: true,
853
+ input: [`text`, `image`]
831
854
  };
832
855
  return {
833
856
  choices: [fallback],
@@ -845,6 +868,9 @@ async function fetchAvailableModelIds(provider) {
845
868
  }) : provider === `deepseek` ? await fetch(`https://api.deepseek.com/v1/models`, {
846
869
  headers: { authorization: `Bearer ${process.env.DEEPSEEK_API_KEY ?? ``}` },
847
870
  signal: AbortSignal.timeout(3e3)
871
+ }) : provider === __electric_ax_agents_runtime.MOONSHOT_PROVIDER ? await fetch(`${__electric_ax_agents_runtime.MOONSHOT_API_BASE_URL}/models`, {
872
+ headers: { authorization: `Bearer ${(0, __electric_ax_agents_runtime.getMoonshotApiKey)() ?? ``}` },
873
+ signal: AbortSignal.timeout(3e3)
848
874
  }) : await fetch(`https://api.openai.com/v1/models`, {
849
875
  headers: { authorization: `Bearer ${process.env.OPENAI_API_KEY ?? ``}` },
850
876
  signal: AbortSignal.timeout(3e3)
@@ -858,24 +884,42 @@ async function fetchAvailableModelIds(provider) {
858
884
  return null;
859
885
  }
860
886
  }
861
- async function choicesForProvider(provider) {
862
- const knownModels = (0, __mariozechner_pi_ai.getModels)(provider);
863
- if (provider === `openai-codex`) return knownModels.map((model) => ({
887
+ function knownModelsForProvider(provider) {
888
+ return provider === __electric_ax_agents_runtime.MOONSHOT_PROVIDER ? (0, __electric_ax_agents_runtime.getMoonshotModels)() : (0, __mariozechner_pi_ai.getModels)(provider);
889
+ }
890
+ function choiceForKnownModel(provider, model) {
891
+ return {
864
892
  provider,
865
893
  id: model.id,
866
- label: `${providerLabel(provider)} ${model.name}`,
894
+ label: `${builtinModelProviderLabel(provider)} ${model.name}`,
867
895
  value: modelValue(provider, model.id),
868
- reasoning: model.reasoning
869
- }));
896
+ reasoning: model.reasoning,
897
+ input: model.input
898
+ };
899
+ }
900
+ function listBuiltinModelChoices(providers) {
901
+ return providers.flatMap((provider) => knownModelsForProvider(provider).map((model) => choiceForKnownModel(provider, model)));
902
+ }
903
+ async function choicesForProvider(provider) {
904
+ const knownChoices = listBuiltinModelChoices([provider]);
905
+ if (provider === `openai-codex`) return knownChoices;
870
906
  const availableIds = await fetchAvailableModelIds(provider);
871
- const models = availableIds === null ? knownModels : knownModels.filter((model) => availableIds.has(model.id));
872
- return models.map((model) => ({
873
- provider,
874
- id: model.id,
875
- label: `${providerLabel(provider)} ${model.name}`,
876
- value: modelValue(provider, model.id),
877
- reasoning: model.reasoning
878
- }));
907
+ return availableIds === null ? knownChoices : knownChoices.filter((choice) => availableIds.has(choice.id));
908
+ }
909
+ function enabledModelSet(values) {
910
+ if (!values) return null;
911
+ const enabled = new Set();
912
+ for (const value of values) {
913
+ const trimmed = value.trim();
914
+ if (trimmed) enabled.add(trimmed);
915
+ }
916
+ return enabled.size > 0 ? enabled : null;
917
+ }
918
+ function filterChoicesByEnabledModels(choices, values) {
919
+ const enabled = enabledModelSet(values);
920
+ if (!enabled) return choices;
921
+ const filtered = choices.filter((choice) => enabled.has(choice.value));
922
+ return filtered.length > 0 ? filtered : choices;
879
923
  }
880
924
  function withProviderPayloadDefaults(config, choice, reasoningEffort) {
881
925
  if (choice.provider !== `openai` && choice.provider !== `openai-codex` || !choice.reasoning) return config;
@@ -903,9 +947,10 @@ function parseReasoningEffort(value) {
903
947
  async function createBuiltinModelCatalog(options = {}) {
904
948
  const providers = configuredProviders();
905
949
  if (providers.length === 0 && options.allowMockFallback) return mockFallbackCatalog();
906
- const choices = (await Promise.all(providers.map((provider) => choicesForProvider(provider)))).flat();
950
+ const providerChoices = (await Promise.all(providers.map((provider) => choicesForProvider(provider)))).flat();
951
+ const choices = filterChoicesByEnabledModels(providerChoices, options.enabledModelValues);
907
952
  if (choices.length === 0) return options.allowMockFallback ? mockFallbackCatalog() : null;
908
- const defaultChoice = choices.find((choice) => choice.provider === `anthropic` && choice.id === DEFAULT_ANTHROPIC_MODEL) ?? choices.find((choice) => choice.provider === `openai` && choice.id === DEFAULT_OPENAI_MODEL) ?? choices.find((choice) => choice.provider === `openai-codex` && choice.id === DEFAULT_CODEX_MODEL) ?? choices.find((choice) => choice.provider === `deepseek` && choice.id === DEFAULT_DEEPSEEK_MODEL) ?? choices[0];
953
+ const defaultChoice = choices.find((choice) => choice.provider === `anthropic` && choice.id === DEFAULT_ANTHROPIC_MODEL) ?? choices.find((choice) => choice.provider === `openai` && choice.id === DEFAULT_OPENAI_MODEL) ?? choices.find((choice) => choice.provider === `openai-codex` && choice.id === DEFAULT_CODEX_MODEL) ?? choices.find((choice) => choice.provider === `deepseek` && choice.id === DEFAULT_DEEPSEEK_MODEL) ?? choices.find((choice) => choice.provider === __electric_ax_agents_runtime.MOONSHOT_PROVIDER && choice.id === DEFAULT_MOONSHOT_MODEL) ?? choices[0];
909
954
  return {
910
955
  choices,
911
956
  defaultChoice
@@ -921,13 +966,24 @@ function resolveBuiltinModelConfig(catalog, args) {
921
966
  provider: choice.provider,
922
967
  model: choice.id,
923
968
  ...reasoningEffort && { reasoningEffort },
924
- ...choice.provider === `openai-codex` && { getApiKey: () => (0, __electric_ax_agents_runtime.readCodexAccessToken)() }
969
+ ...choice.provider === `openai-codex` && { getApiKey: () => (0, __electric_ax_agents_runtime.readCodexAccessToken)() },
970
+ ...choice.provider === __electric_ax_agents_runtime.MOONSHOT_PROVIDER && { getApiKey: () => (0, __electric_ax_agents_runtime.getMoonshotApiKey)() }
925
971
  };
926
972
  return withProviderPayloadDefaults(config, choice, reasoningEffort);
927
973
  }
928
974
  function modelChoiceValues(catalog) {
929
975
  return catalog.choices.map((choice) => choice.value);
930
976
  }
977
+ function modelInputSchemaDefs(catalog) {
978
+ return { [MODEL_INPUTS_SCHEMA_DEF]: {
979
+ type: `object`,
980
+ properties: Object.fromEntries(catalog.choices.map((choice) => [choice.value, {
981
+ type: `array`,
982
+ items: { enum: [`text`, `image`] },
983
+ default: choice.input
984
+ }]))
985
+ } };
986
+ }
931
987
 
932
988
  //#endregion
933
989
  //#region src/agents/horton.ts
@@ -1147,10 +1203,40 @@ function payloadToTitleText(payload) {
1147
1203
  }
1148
1204
  return String(payload);
1149
1205
  }
1206
+ function attachmentTitleText(attachment) {
1207
+ const mimeType = typeof attachment.mimeType === `string` ? attachment.mimeType : ``;
1208
+ const filename = typeof attachment.filename === `string` && attachment.filename.trim() ? attachment.filename.trim() : typeof attachment.id === `string` ? attachment.id : `attachment`;
1209
+ const kind = mimeType.startsWith(`image/`) ? `image` : `file`;
1210
+ return `Attached ${kind}: ${filename}`;
1211
+ }
1212
+ function attachmentsForInboxMessage(ctx, inboxKey) {
1213
+ const manifests = ctx.db.collections.manifests?.toArray;
1214
+ if (!Array.isArray(manifests)) return [];
1215
+ return manifests.filter((entry) => {
1216
+ if (!entry || typeof entry !== `object`) return false;
1217
+ const attachment = entry;
1218
+ if (attachment.kind !== `attachment`) return false;
1219
+ if (attachment.role !== `input`) return false;
1220
+ const subject = attachment.subject;
1221
+ return subject !== null && typeof subject === `object` && !Array.isArray(subject) && subject.type === `inbox` && subject.key === inboxKey;
1222
+ });
1223
+ }
1224
+ function messageTitleText(ctx, message) {
1225
+ const pieces = [];
1226
+ const text = payloadToTitleText(message.payload).trim();
1227
+ if (text) pieces.push(text);
1228
+ const key = typeof message.key === `string` ? message.key : null;
1229
+ const attachments = key ? attachmentsForInboxMessage(ctx, key) : [];
1230
+ for (const attachment of attachments) {
1231
+ const attachmentText = attachmentTitleText(attachment);
1232
+ if (attachmentText) pieces.push(attachmentText);
1233
+ }
1234
+ return pieces.join(`\n`);
1235
+ }
1150
1236
  async function extractFirstUserMessage(ctx) {
1151
1237
  const firstMessage = ctx.db.collections.inbox.toArray.filter((message) => message.from !== `system`).sort((left, right) => messageSeq(left) - messageSeq(right))[0];
1152
1238
  if (!firstMessage) return null;
1153
- const text = payloadToTitleText(firstMessage.payload);
1239
+ const text = messageTitleText(ctx, firstMessage);
1154
1240
  return text.length > 0 ? text : null;
1155
1241
  }
1156
1242
  function messageSeq(message) {
@@ -1190,7 +1276,7 @@ function createAssistantHandler(options) {
1190
1276
  ...__electric_ax_agents_mcp.mcp.tools()
1191
1277
  ];
1192
1278
  const hasEventSourceTools = tools.some((tool) => getToolName(tool) === `list_event_sources`);
1193
- const titlePromise = ctx.firstWake && !ctx.tags.title ? (async () => {
1279
+ const titlePromise = !ctx.tags.title ? (async () => {
1194
1280
  const firstUserMessage = await extractFirstUserMessage(ctx);
1195
1281
  if (!firstUserMessage) return;
1196
1282
  let title = null;
@@ -1311,7 +1397,7 @@ function registerHorton(registry, options) {
1311
1397
  model: zod.z.enum(modelChoiceValues(modelCatalog)).default(modelCatalog.defaultChoice.value),
1312
1398
  reasoningEffort: zod.z.enum(REASONING_EFFORT_VALUES).default(`auto`).describe(`Reasoning effort for compatible reasoning models. Auto uses a safe provider default.`),
1313
1399
  workingDirectory: zod.z.string().optional().describe(`Working directory for file operations. Defaults to the server's configured cwd.`)
1314
- });
1400
+ }).meta({ $defs: modelInputSchemaDefs(modelCatalog) });
1315
1401
  registry.define(`horton`, {
1316
1402
  description: `Friendly capable assistant — chat, code, research, dispatch`,
1317
1403
  creationSchema: hortonCreationSchema,
@@ -1532,10 +1618,13 @@ function createBuiltinElectricTools(custom) {
1532
1618
  };
1533
1619
  }
1534
1620
  async function createBuiltinAgentHandler(options) {
1535
- const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
1536
- const modelCatalog = await createBuiltinModelCatalog({ allowMockFallback: Boolean(streamFn) });
1621
+ const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, enabledModelValues, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
1622
+ const modelCatalog = await createBuiltinModelCatalog({
1623
+ allowMockFallback: Boolean(streamFn),
1624
+ enabledModelValues
1625
+ });
1537
1626
  if (!modelCatalog) {
1538
- serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY or OPENAI_API_KEY`);
1627
+ serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY, OPENAI_API_KEY, DEEPSEEK_API_KEY, or MOONSHOT_API_KEY`);
1539
1628
  return null;
1540
1629
  }
1541
1630
  const cwd = workingDirectory ?? process.cwd();
@@ -1739,9 +1828,10 @@ var BuiltinAgentsServer = class {
1739
1828
  publicUrl,
1740
1829
  runtimeName: `builtin-agents`,
1741
1830
  baseSkillsDir: this.options.baseSkillsDir,
1831
+ enabledModelValues: this.options.enabledModelValues,
1742
1832
  serverHeaders: pullWake.headers
1743
1833
  });
1744
- if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY or OPENAI_API_KEY must be set before starting builtin agents`);
1834
+ if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY, OPENAI_API_KEY, DEEPSEEK_API_KEY, or MOONSHOT_API_KEY must be set before starting builtin agents`);
1745
1835
  await registerBuiltinAgentTypes(this.bootstrap);
1746
1836
  const registeredRunner = pullWake.registerRunner ? await this.registerPullWakeRunner(pullWake) : null;
1747
1837
  this.pullWakeRunner = (0, __electric_ax_agents_runtime.createPullWakeRunner)({
@@ -1913,6 +2003,7 @@ Object.defineProperty(exports, 'braveSearchTool', {
1913
2003
  }
1914
2004
  });
1915
2005
  exports.buildHortonSystemPrompt = buildHortonSystemPrompt
2006
+ exports.builtinModelProviderLabel = builtinModelProviderLabel
1916
2007
  exports.createAgentHandler = createAgentHandler
1917
2008
  exports.createBuiltinAgentHandler = createBuiltinAgentHandler
1918
2009
  exports.createBuiltinElectricTools = createBuiltinElectricTools
@@ -1920,6 +2011,7 @@ exports.createHortonDocsSupport = createHortonDocsSupport
1920
2011
  exports.createHortonTools = createHortonTools
1921
2012
  exports.createSpawnWorkerTool = createSpawnWorkerTool
1922
2013
  exports.generateTitle = generateTitle
2014
+ exports.listBuiltinModelChoices = listBuiltinModelChoices
1923
2015
  exports.registerAgentTypes = registerAgentTypes
1924
2016
  exports.registerBuiltinAgentTypes = registerBuiltinAgentTypes
1925
2017
  exports.registerHorton = registerHorton
package/dist/index.d.cts CHANGED
@@ -20,6 +20,7 @@ interface BuiltinAgentHandlerOptions {
20
20
  serveEndpoint?: string;
21
21
  workingDirectory?: string;
22
22
  streamFn?: StreamFn;
23
+ enabledModelValues?: ReadonlyArray<string> | null;
23
24
  publicUrl?: string;
24
25
  runtimeName?: string;
25
26
  /** Override for the built-in skills directory; required when embedders bundle this package. */
@@ -79,6 +80,8 @@ interface BuiltinAgentsServerOptions {
79
80
  loadProjectMcpConfig?: boolean;
80
81
  /** Override for the built-in skills directory; required when embedders bundle this package. */
81
82
  baseSkillsDir?: string;
83
+ /** Restrict the model values exposed by built-in agent creation schemas. */
84
+ enabledModelValues?: ReadonlyArray<string> | null;
82
85
  createElectricTools?: NonNullable<ProcessWakeConfig[`createElectricTools`]>;
83
86
  }
84
87
  declare class BuiltinAgentsServer {
@@ -125,23 +128,31 @@ declare function runBuiltinAgentsEntrypoint({
125
128
  //#endregion
126
129
  //#region src/model-catalog.d.ts
127
130
  type BuiltinModelProvider = AvailableProvider;
131
+ type BuiltinModelInput = `text` | `image`;
128
132
  interface BuiltinModelChoice {
129
133
  provider: BuiltinModelProvider;
130
134
  id: string;
131
135
  label: string;
132
136
  value: string;
133
137
  reasoning: boolean;
138
+ input: Array<BuiltinModelInput>;
134
139
  }
135
140
  interface BuiltinModelCatalog {
136
141
  choices: Array<BuiltinModelChoice>;
137
142
  defaultChoice: BuiltinModelChoice;
138
143
  }
144
+ interface BuiltinModelCatalogOptions {
145
+ allowMockFallback?: boolean;
146
+ enabledModelValues?: ReadonlyArray<string> | null;
147
+ }
139
148
  declare const REASONING_EFFORT_VALUES: readonly ["auto", "minimal", "low", "medium", "high"];
140
149
  type BuiltinReasoningEffort = (typeof REASONING_EFFORT_VALUES)[number];
141
150
  type ExplicitReasoningEffort = Exclude<BuiltinReasoningEffort, `auto`>;
142
151
  type BuiltinAgentModelConfig = Pick<AgentConfig, `model` | `provider` | `onPayload` | `getApiKey`> & {
143
152
  reasoningEffort?: ExplicitReasoningEffort;
144
153
  };
154
+ declare function builtinModelProviderLabel(provider: BuiltinModelProvider): string;
155
+ declare function listBuiltinModelChoices(providers: ReadonlyArray<BuiltinModelProvider>): Array<BuiltinModelChoice>;
145
156
  declare function resolveBuiltinModelConfig(catalog: BuiltinModelCatalog, args: Readonly<Record<string, unknown>>): BuiltinAgentModelConfig;
146
157
 
147
158
  //#endregion
@@ -203,4 +214,4 @@ declare function createHortonDocsSupport(workingDirectory: string, opts?: {
203
214
  }): HortonDocsSupport | null;
204
215
 
205
216
  //#endregion
206
- export { AgentHandlerResult, BuiltinAgentHandlerOptions, BuiltinAgentsEntrypointOptions, BuiltinAgentsEntrypointServer, BuiltinAgentsServer, BuiltinAgentsServerOptions, BuiltinElectricToolsFactory, DEFAULT_BUILTIN_AGENT_HANDLER_PATH, HORTON_MODEL, McpConfig, McpListedEntry, McpRegistry, McpServerConfig, RegistrySnapshot, RegistrySubscriber, RunBuiltinAgentsEntrypointOptions, WORKER_TOOL_NAMES, WorkerToolName, braveSearchTool, buildHortonSystemPrompt, createAgentHandler, createBuiltinAgentHandler, createBuiltinElectricTools, createHortonDocsSupport, createHortonTools, createSpawnWorkerTool, generateTitle, registerAgentTypes, registerBuiltinAgentTypes, registerHorton, registerWorker, resolveBuiltinAgentsEntrypointOptions, runBuiltinAgentsEntrypoint };
217
+ export { AgentHandlerResult, BuiltinAgentHandlerOptions, BuiltinAgentsEntrypointOptions, BuiltinAgentsEntrypointServer, BuiltinAgentsServer, BuiltinAgentsServerOptions, BuiltinElectricToolsFactory, BuiltinModelCatalogOptions, BuiltinModelChoice, BuiltinModelProvider, DEFAULT_BUILTIN_AGENT_HANDLER_PATH, HORTON_MODEL, McpConfig, McpListedEntry, McpRegistry, McpServerConfig, RegistrySnapshot, RegistrySubscriber, RunBuiltinAgentsEntrypointOptions, WORKER_TOOL_NAMES, WorkerToolName, braveSearchTool, buildHortonSystemPrompt, builtinModelProviderLabel, createAgentHandler, createBuiltinAgentHandler, createBuiltinElectricTools, createHortonDocsSupport, createHortonTools, createSpawnWorkerTool, generateTitle, listBuiltinModelChoices, registerAgentTypes, registerBuiltinAgentTypes, registerHorton, registerWorker, resolveBuiltinAgentsEntrypointOptions, runBuiltinAgentsEntrypoint };
package/dist/index.d.ts CHANGED
@@ -20,6 +20,7 @@ interface BuiltinAgentHandlerOptions {
20
20
  serveEndpoint?: string;
21
21
  workingDirectory?: string;
22
22
  streamFn?: StreamFn;
23
+ enabledModelValues?: ReadonlyArray<string> | null;
23
24
  publicUrl?: string;
24
25
  runtimeName?: string;
25
26
  /** Override for the built-in skills directory; required when embedders bundle this package. */
@@ -79,6 +80,8 @@ interface BuiltinAgentsServerOptions {
79
80
  loadProjectMcpConfig?: boolean;
80
81
  /** Override for the built-in skills directory; required when embedders bundle this package. */
81
82
  baseSkillsDir?: string;
83
+ /** Restrict the model values exposed by built-in agent creation schemas. */
84
+ enabledModelValues?: ReadonlyArray<string> | null;
82
85
  createElectricTools?: NonNullable<ProcessWakeConfig[`createElectricTools`]>;
83
86
  }
84
87
  declare class BuiltinAgentsServer {
@@ -125,23 +128,31 @@ declare function runBuiltinAgentsEntrypoint({
125
128
  //#endregion
126
129
  //#region src/model-catalog.d.ts
127
130
  type BuiltinModelProvider = AvailableProvider;
131
+ type BuiltinModelInput = `text` | `image`;
128
132
  interface BuiltinModelChoice {
129
133
  provider: BuiltinModelProvider;
130
134
  id: string;
131
135
  label: string;
132
136
  value: string;
133
137
  reasoning: boolean;
138
+ input: Array<BuiltinModelInput>;
134
139
  }
135
140
  interface BuiltinModelCatalog {
136
141
  choices: Array<BuiltinModelChoice>;
137
142
  defaultChoice: BuiltinModelChoice;
138
143
  }
144
+ interface BuiltinModelCatalogOptions {
145
+ allowMockFallback?: boolean;
146
+ enabledModelValues?: ReadonlyArray<string> | null;
147
+ }
139
148
  declare const REASONING_EFFORT_VALUES: readonly ["auto", "minimal", "low", "medium", "high"];
140
149
  type BuiltinReasoningEffort = (typeof REASONING_EFFORT_VALUES)[number];
141
150
  type ExplicitReasoningEffort = Exclude<BuiltinReasoningEffort, `auto`>;
142
151
  type BuiltinAgentModelConfig = Pick<AgentConfig, `model` | `provider` | `onPayload` | `getApiKey`> & {
143
152
  reasoningEffort?: ExplicitReasoningEffort;
144
153
  };
154
+ declare function builtinModelProviderLabel(provider: BuiltinModelProvider): string;
155
+ declare function listBuiltinModelChoices(providers: ReadonlyArray<BuiltinModelProvider>): Array<BuiltinModelChoice>;
145
156
  declare function resolveBuiltinModelConfig(catalog: BuiltinModelCatalog, args: Readonly<Record<string, unknown>>): BuiltinAgentModelConfig;
146
157
 
147
158
  //#endregion
@@ -203,4 +214,4 @@ declare function createHortonDocsSupport(workingDirectory: string, opts?: {
203
214
  }): HortonDocsSupport | null;
204
215
 
205
216
  //#endregion
206
- export { AgentHandlerResult, BuiltinAgentHandlerOptions, BuiltinAgentsEntrypointOptions, BuiltinAgentsEntrypointServer, BuiltinAgentsServer, BuiltinAgentsServerOptions, BuiltinElectricToolsFactory, DEFAULT_BUILTIN_AGENT_HANDLER_PATH, HORTON_MODEL, McpConfig, McpListedEntry, McpRegistry, McpServerConfig, RegistrySnapshot, RegistrySubscriber, RunBuiltinAgentsEntrypointOptions, WORKER_TOOL_NAMES, WorkerToolName, braveSearchTool, buildHortonSystemPrompt, createAgentHandler, createBuiltinAgentHandler, createBuiltinElectricTools, createHortonDocsSupport, createHortonTools, createSpawnWorkerTool, generateTitle, registerAgentTypes, registerBuiltinAgentTypes, registerHorton, registerWorker, resolveBuiltinAgentsEntrypointOptions, runBuiltinAgentsEntrypoint };
217
+ export { AgentHandlerResult, BuiltinAgentHandlerOptions, BuiltinAgentsEntrypointOptions, BuiltinAgentsEntrypointServer, BuiltinAgentsServer, BuiltinAgentsServerOptions, BuiltinElectricToolsFactory, BuiltinModelCatalogOptions, BuiltinModelChoice, BuiltinModelProvider, DEFAULT_BUILTIN_AGENT_HANDLER_PATH, HORTON_MODEL, McpConfig, McpListedEntry, McpRegistry, McpServerConfig, RegistrySnapshot, RegistrySubscriber, RunBuiltinAgentsEntrypointOptions, WORKER_TOOL_NAMES, WorkerToolName, braveSearchTool, buildHortonSystemPrompt, builtinModelProviderLabel, createAgentHandler, createBuiltinAgentHandler, createBuiltinElectricTools, createHortonDocsSupport, createHortonTools, createSpawnWorkerTool, generateTitle, listBuiltinModelChoices, registerAgentTypes, registerBuiltinAgentTypes, registerHorton, registerWorker, resolveBuiltinAgentsEntrypointOptions, runBuiltinAgentsEntrypoint };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { mergeElectricPrincipalHeader } from "./server-headers-KD5yHFYT.js";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- import { appendPathToUrl, completeWithLowCostModel, createEntityRegistry, createPullWakeRunner, createRuntimeHandler, createSkillTools, createSkillsRegistry, db, detectAvailableProviders, readCodexAccessToken, registerToolProvider, unregisterToolProvider } from "@electric-ax/agents-runtime";
4
+ import { MOONSHOT_API_BASE_URL, MOONSHOT_PROVIDER, appendPathToUrl, completeWithLowCostModel, createEntityRegistry, createPullWakeRunner, createRuntimeHandler, createSkillTools, createSkillsRegistry, db, detectAvailableProviders, getMoonshotApiKey, getMoonshotModels, readCodexAccessToken, registerToolProvider, unregisterToolProvider } from "@electric-ax/agents-runtime";
5
5
  import { braveSearchTool, braveSearchTool as braveSearchTool$1, createBashTool, createEditTool, createEventSourceTools, createFetchUrlTool, createReadFileTool, createSendTool, createWriteTool, fetchUrlTool } from "@electric-ax/agents-runtime/tools";
6
6
  import fs from "node:fs";
7
7
  import pino from "pino";
@@ -16,28 +16,47 @@ import { getModels } from "@mariozechner/pi-ai";
16
16
  import { bridgeMcpTool, buildPromptTools, buildResourceTools, createRegistry, keychainPersistence, loadConfig, mcp, watchConfig } from "@electric-ax/agents-mcp";
17
17
 
18
18
  //#region src/log.ts
19
- const LOG_DIR = process.env.ELECTRIC_AGENTS_LOG_DIR ?? path.resolve(process.cwd(), `logs`);
20
- fs.mkdirSync(LOG_DIR, { recursive: true });
21
- const LOG_FILE = path.join(LOG_DIR, `builtin-agents-${Date.now()}.jsonl`);
22
19
  const LOG_LEVEL = process.env.ELECTRIC_AGENTS_LOG_LEVEL ?? `info`;
23
20
  const IS_ELECTRON_MAIN = Boolean(process.versions.electron);
21
+ const USE_FILE_LOGS = process.env.ELECTRIC_AGENTS_LOG_FILE !== `false`;
24
22
  const USE_PRETTY_LOGS = LOG_LEVEL !== `silent` && !process.env.VITEST && !IS_ELECTRON_MAIN;
25
- const streams = [{ stream: pino.destination({
26
- dest: LOG_FILE,
27
- sync: IS_ELECTRON_MAIN
28
- }) }];
29
- if (USE_PRETTY_LOGS) streams.push({ stream: pino.transport({
30
- target: `pino-pretty`,
31
- options: {
32
- colorize: true,
33
- ignore: `pid,hostname,name`,
34
- translateTime: `SYS:HH:MM:ss`
23
+ let _logger;
24
+ function getLogger() {
25
+ if (_logger) return _logger;
26
+ const streams = [];
27
+ try {
28
+ if (USE_FILE_LOGS) {
29
+ const logDir = process.env.ELECTRIC_AGENTS_LOG_DIR ?? path.resolve(process.cwd(), `logs`);
30
+ fs.mkdirSync(logDir, { recursive: true });
31
+ const logFile = path.join(logDir, `builtin-agents-${Date.now()}.jsonl`);
32
+ streams.push({ stream: pino.destination({
33
+ dest: logFile,
34
+ sync: IS_ELECTRON_MAIN
35
+ }) });
36
+ }
37
+ } catch (err) {
38
+ process.stderr.write(`[agents] Failed to initialize file logging: ${err instanceof Error ? err.message : err}\n`);
35
39
  }
36
- }) });
37
- const logger = pino({
38
- base: void 0,
39
- level: LOG_LEVEL
40
- }, pino.multistream(streams));
40
+ try {
41
+ if (USE_PRETTY_LOGS) streams.push({ stream: pino.transport({
42
+ target: `pino-pretty`,
43
+ options: {
44
+ colorize: true,
45
+ ignore: `pid,hostname,name`,
46
+ translateTime: `SYS:HH:MM:ss`
47
+ }
48
+ }) });
49
+ } catch {}
50
+ _logger = streams.length > 0 ? pino({
51
+ base: void 0,
52
+ level: LOG_LEVEL
53
+ }, pino.multistream(streams)) : pino({
54
+ base: void 0,
55
+ enabled: false,
56
+ level: LOG_LEVEL
57
+ });
58
+ return _logger;
59
+ }
41
60
  function formatArgs(args) {
42
61
  const errors = [];
43
62
  const parts = [];
@@ -51,24 +70,24 @@ function formatArgs(args) {
51
70
  const serverLog = {
52
71
  debug(...args) {
53
72
  const { msg } = formatArgs(args);
54
- logger.debug(msg);
73
+ getLogger().debug(msg);
55
74
  },
56
75
  info(...args) {
57
76
  const { msg } = formatArgs(args);
58
- logger.info(msg);
77
+ getLogger().info(msg);
59
78
  },
60
79
  warn(...args) {
61
80
  const { err, msg } = formatArgs(args);
62
- if (err) logger.warn({ err }, msg);
63
- else logger.warn(msg);
81
+ if (err) getLogger().warn({ err }, msg);
82
+ else getLogger().warn(msg);
64
83
  },
65
84
  error(...args) {
66
85
  const { err, msg } = formatArgs(args);
67
- if (err) logger.error({ err }, msg);
68
- else logger.error(msg);
86
+ if (err) getLogger().error({ err }, msg);
87
+ else getLogger().error(msg);
69
88
  },
70
89
  event(obj, msg) {
71
- logger.info(obj, msg);
90
+ getLogger().info(obj, msg);
72
91
  }
73
92
  };
74
93
 
@@ -774,6 +793,7 @@ function createSpawnWorkerTool(ctx, modelConfig) {
774
793
 
775
794
  //#endregion
776
795
  //#region src/model-catalog.ts
796
+ const MODEL_INPUTS_SCHEMA_DEF = `electricModelInputs`;
777
797
  const REASONING_EFFORT_VALUES = [
778
798
  `auto`,
779
799
  `minimal`,
@@ -785,13 +805,15 @@ const DEFAULT_ANTHROPIC_MODEL = `claude-sonnet-4-6`;
785
805
  const DEFAULT_OPENAI_MODEL = `gpt-4.1`;
786
806
  const DEFAULT_CODEX_MODEL = `gpt-5.4`;
787
807
  const DEFAULT_DEEPSEEK_MODEL = `deepseek-v4-flash`;
808
+ const DEFAULT_MOONSHOT_MODEL = `kimi-k2.6`;
788
809
  function modelValue(provider, id) {
789
810
  return `${provider}:${id}`;
790
811
  }
791
- function providerLabel(provider) {
812
+ function builtinModelProviderLabel(provider) {
792
813
  if (provider === `anthropic`) return `Anthropic`;
793
814
  if (provider === `openai-codex`) return `OpenAI Codex`;
794
815
  if (provider === `deepseek`) return `DeepSeek`;
816
+ if (provider === MOONSHOT_PROVIDER) return `Kimi`;
795
817
  return `OpenAI`;
796
818
  }
797
819
  function configuredProviders() {
@@ -803,7 +825,8 @@ function mockFallbackCatalog() {
803
825
  id: DEFAULT_ANTHROPIC_MODEL,
804
826
  label: `Anthropic ${DEFAULT_ANTHROPIC_MODEL}`,
805
827
  value: modelValue(`anthropic`, DEFAULT_ANTHROPIC_MODEL),
806
- reasoning: true
828
+ reasoning: true,
829
+ input: [`text`, `image`]
807
830
  };
808
831
  return {
809
832
  choices: [fallback],
@@ -821,6 +844,9 @@ async function fetchAvailableModelIds(provider) {
821
844
  }) : provider === `deepseek` ? await fetch(`https://api.deepseek.com/v1/models`, {
822
845
  headers: { authorization: `Bearer ${process.env.DEEPSEEK_API_KEY ?? ``}` },
823
846
  signal: AbortSignal.timeout(3e3)
847
+ }) : provider === MOONSHOT_PROVIDER ? await fetch(`${MOONSHOT_API_BASE_URL}/models`, {
848
+ headers: { authorization: `Bearer ${getMoonshotApiKey() ?? ``}` },
849
+ signal: AbortSignal.timeout(3e3)
824
850
  }) : await fetch(`https://api.openai.com/v1/models`, {
825
851
  headers: { authorization: `Bearer ${process.env.OPENAI_API_KEY ?? ``}` },
826
852
  signal: AbortSignal.timeout(3e3)
@@ -834,24 +860,42 @@ async function fetchAvailableModelIds(provider) {
834
860
  return null;
835
861
  }
836
862
  }
837
- async function choicesForProvider(provider) {
838
- const knownModels = getModels(provider);
839
- if (provider === `openai-codex`) return knownModels.map((model) => ({
863
+ function knownModelsForProvider(provider) {
864
+ return provider === MOONSHOT_PROVIDER ? getMoonshotModels() : getModels(provider);
865
+ }
866
+ function choiceForKnownModel(provider, model) {
867
+ return {
840
868
  provider,
841
869
  id: model.id,
842
- label: `${providerLabel(provider)} ${model.name}`,
870
+ label: `${builtinModelProviderLabel(provider)} ${model.name}`,
843
871
  value: modelValue(provider, model.id),
844
- reasoning: model.reasoning
845
- }));
872
+ reasoning: model.reasoning,
873
+ input: model.input
874
+ };
875
+ }
876
+ function listBuiltinModelChoices(providers) {
877
+ return providers.flatMap((provider) => knownModelsForProvider(provider).map((model) => choiceForKnownModel(provider, model)));
878
+ }
879
+ async function choicesForProvider(provider) {
880
+ const knownChoices = listBuiltinModelChoices([provider]);
881
+ if (provider === `openai-codex`) return knownChoices;
846
882
  const availableIds = await fetchAvailableModelIds(provider);
847
- const models = availableIds === null ? knownModels : knownModels.filter((model) => availableIds.has(model.id));
848
- return models.map((model) => ({
849
- provider,
850
- id: model.id,
851
- label: `${providerLabel(provider)} ${model.name}`,
852
- value: modelValue(provider, model.id),
853
- reasoning: model.reasoning
854
- }));
883
+ return availableIds === null ? knownChoices : knownChoices.filter((choice) => availableIds.has(choice.id));
884
+ }
885
+ function enabledModelSet(values) {
886
+ if (!values) return null;
887
+ const enabled = new Set();
888
+ for (const value of values) {
889
+ const trimmed = value.trim();
890
+ if (trimmed) enabled.add(trimmed);
891
+ }
892
+ return enabled.size > 0 ? enabled : null;
893
+ }
894
+ function filterChoicesByEnabledModels(choices, values) {
895
+ const enabled = enabledModelSet(values);
896
+ if (!enabled) return choices;
897
+ const filtered = choices.filter((choice) => enabled.has(choice.value));
898
+ return filtered.length > 0 ? filtered : choices;
855
899
  }
856
900
  function withProviderPayloadDefaults(config, choice, reasoningEffort) {
857
901
  if (choice.provider !== `openai` && choice.provider !== `openai-codex` || !choice.reasoning) return config;
@@ -879,9 +923,10 @@ function parseReasoningEffort(value) {
879
923
  async function createBuiltinModelCatalog(options = {}) {
880
924
  const providers = configuredProviders();
881
925
  if (providers.length === 0 && options.allowMockFallback) return mockFallbackCatalog();
882
- const choices = (await Promise.all(providers.map((provider) => choicesForProvider(provider)))).flat();
926
+ const providerChoices = (await Promise.all(providers.map((provider) => choicesForProvider(provider)))).flat();
927
+ const choices = filterChoicesByEnabledModels(providerChoices, options.enabledModelValues);
883
928
  if (choices.length === 0) return options.allowMockFallback ? mockFallbackCatalog() : null;
884
- const defaultChoice = choices.find((choice) => choice.provider === `anthropic` && choice.id === DEFAULT_ANTHROPIC_MODEL) ?? choices.find((choice) => choice.provider === `openai` && choice.id === DEFAULT_OPENAI_MODEL) ?? choices.find((choice) => choice.provider === `openai-codex` && choice.id === DEFAULT_CODEX_MODEL) ?? choices.find((choice) => choice.provider === `deepseek` && choice.id === DEFAULT_DEEPSEEK_MODEL) ?? choices[0];
929
+ const defaultChoice = choices.find((choice) => choice.provider === `anthropic` && choice.id === DEFAULT_ANTHROPIC_MODEL) ?? choices.find((choice) => choice.provider === `openai` && choice.id === DEFAULT_OPENAI_MODEL) ?? choices.find((choice) => choice.provider === `openai-codex` && choice.id === DEFAULT_CODEX_MODEL) ?? choices.find((choice) => choice.provider === `deepseek` && choice.id === DEFAULT_DEEPSEEK_MODEL) ?? choices.find((choice) => choice.provider === MOONSHOT_PROVIDER && choice.id === DEFAULT_MOONSHOT_MODEL) ?? choices[0];
885
930
  return {
886
931
  choices,
887
932
  defaultChoice
@@ -897,13 +942,24 @@ function resolveBuiltinModelConfig(catalog, args) {
897
942
  provider: choice.provider,
898
943
  model: choice.id,
899
944
  ...reasoningEffort && { reasoningEffort },
900
- ...choice.provider === `openai-codex` && { getApiKey: () => readCodexAccessToken() }
945
+ ...choice.provider === `openai-codex` && { getApiKey: () => readCodexAccessToken() },
946
+ ...choice.provider === MOONSHOT_PROVIDER && { getApiKey: () => getMoonshotApiKey() }
901
947
  };
902
948
  return withProviderPayloadDefaults(config, choice, reasoningEffort);
903
949
  }
904
950
  function modelChoiceValues(catalog) {
905
951
  return catalog.choices.map((choice) => choice.value);
906
952
  }
953
+ function modelInputSchemaDefs(catalog) {
954
+ return { [MODEL_INPUTS_SCHEMA_DEF]: {
955
+ type: `object`,
956
+ properties: Object.fromEntries(catalog.choices.map((choice) => [choice.value, {
957
+ type: `array`,
958
+ items: { enum: [`text`, `image`] },
959
+ default: choice.input
960
+ }]))
961
+ } };
962
+ }
907
963
 
908
964
  //#endregion
909
965
  //#region src/agents/horton.ts
@@ -1123,10 +1179,40 @@ function payloadToTitleText(payload) {
1123
1179
  }
1124
1180
  return String(payload);
1125
1181
  }
1182
+ function attachmentTitleText(attachment) {
1183
+ const mimeType = typeof attachment.mimeType === `string` ? attachment.mimeType : ``;
1184
+ const filename = typeof attachment.filename === `string` && attachment.filename.trim() ? attachment.filename.trim() : typeof attachment.id === `string` ? attachment.id : `attachment`;
1185
+ const kind = mimeType.startsWith(`image/`) ? `image` : `file`;
1186
+ return `Attached ${kind}: ${filename}`;
1187
+ }
1188
+ function attachmentsForInboxMessage(ctx, inboxKey) {
1189
+ const manifests = ctx.db.collections.manifests?.toArray;
1190
+ if (!Array.isArray(manifests)) return [];
1191
+ return manifests.filter((entry) => {
1192
+ if (!entry || typeof entry !== `object`) return false;
1193
+ const attachment = entry;
1194
+ if (attachment.kind !== `attachment`) return false;
1195
+ if (attachment.role !== `input`) return false;
1196
+ const subject = attachment.subject;
1197
+ return subject !== null && typeof subject === `object` && !Array.isArray(subject) && subject.type === `inbox` && subject.key === inboxKey;
1198
+ });
1199
+ }
1200
+ function messageTitleText(ctx, message) {
1201
+ const pieces = [];
1202
+ const text = payloadToTitleText(message.payload).trim();
1203
+ if (text) pieces.push(text);
1204
+ const key = typeof message.key === `string` ? message.key : null;
1205
+ const attachments = key ? attachmentsForInboxMessage(ctx, key) : [];
1206
+ for (const attachment of attachments) {
1207
+ const attachmentText = attachmentTitleText(attachment);
1208
+ if (attachmentText) pieces.push(attachmentText);
1209
+ }
1210
+ return pieces.join(`\n`);
1211
+ }
1126
1212
  async function extractFirstUserMessage(ctx) {
1127
1213
  const firstMessage = ctx.db.collections.inbox.toArray.filter((message) => message.from !== `system`).sort((left, right) => messageSeq(left) - messageSeq(right))[0];
1128
1214
  if (!firstMessage) return null;
1129
- const text = payloadToTitleText(firstMessage.payload);
1215
+ const text = messageTitleText(ctx, firstMessage);
1130
1216
  return text.length > 0 ? text : null;
1131
1217
  }
1132
1218
  function messageSeq(message) {
@@ -1166,7 +1252,7 @@ function createAssistantHandler(options) {
1166
1252
  ...mcp.tools()
1167
1253
  ];
1168
1254
  const hasEventSourceTools = tools.some((tool) => getToolName(tool) === `list_event_sources`);
1169
- const titlePromise = ctx.firstWake && !ctx.tags.title ? (async () => {
1255
+ const titlePromise = !ctx.tags.title ? (async () => {
1170
1256
  const firstUserMessage = await extractFirstUserMessage(ctx);
1171
1257
  if (!firstUserMessage) return;
1172
1258
  let title = null;
@@ -1287,7 +1373,7 @@ function registerHorton(registry, options) {
1287
1373
  model: z.enum(modelChoiceValues(modelCatalog)).default(modelCatalog.defaultChoice.value),
1288
1374
  reasoningEffort: z.enum(REASONING_EFFORT_VALUES).default(`auto`).describe(`Reasoning effort for compatible reasoning models. Auto uses a safe provider default.`),
1289
1375
  workingDirectory: z.string().optional().describe(`Working directory for file operations. Defaults to the server's configured cwd.`)
1290
- });
1376
+ }).meta({ $defs: modelInputSchemaDefs(modelCatalog) });
1291
1377
  registry.define(`horton`, {
1292
1378
  description: `Friendly capable assistant — chat, code, research, dispatch`,
1293
1379
  creationSchema: hortonCreationSchema,
@@ -1508,10 +1594,13 @@ function createBuiltinElectricTools(custom) {
1508
1594
  };
1509
1595
  }
1510
1596
  async function createBuiltinAgentHandler(options) {
1511
- const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
1512
- const modelCatalog = await createBuiltinModelCatalog({ allowMockFallback: Boolean(streamFn) });
1597
+ const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, enabledModelValues, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
1598
+ const modelCatalog = await createBuiltinModelCatalog({
1599
+ allowMockFallback: Boolean(streamFn),
1600
+ enabledModelValues
1601
+ });
1513
1602
  if (!modelCatalog) {
1514
- serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY or OPENAI_API_KEY`);
1603
+ serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY, OPENAI_API_KEY, DEEPSEEK_API_KEY, or MOONSHOT_API_KEY`);
1515
1604
  return null;
1516
1605
  }
1517
1606
  const cwd = workingDirectory ?? process.cwd();
@@ -1715,9 +1804,10 @@ var BuiltinAgentsServer = class {
1715
1804
  publicUrl,
1716
1805
  runtimeName: `builtin-agents`,
1717
1806
  baseSkillsDir: this.options.baseSkillsDir,
1807
+ enabledModelValues: this.options.enabledModelValues,
1718
1808
  serverHeaders: pullWake.headers
1719
1809
  });
1720
- if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY or OPENAI_API_KEY must be set before starting builtin agents`);
1810
+ if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY, OPENAI_API_KEY, DEEPSEEK_API_KEY, or MOONSHOT_API_KEY must be set before starting builtin agents`);
1721
1811
  await registerBuiltinAgentTypes(this.bootstrap);
1722
1812
  const registeredRunner = pullWake.registerRunner ? await this.registerPullWakeRunner(pullWake) : null;
1723
1813
  this.pullWakeRunner = createPullWakeRunner({
@@ -1878,4 +1968,4 @@ async function runBuiltinAgentsEntrypoint({ env = process.env, cwd = process.cwd
1878
1968
  }
1879
1969
 
1880
1970
  //#endregion
1881
- export { BuiltinAgentsServer, DEFAULT_BUILTIN_AGENT_HANDLER_PATH, HORTON_MODEL, WORKER_TOOL_NAMES, braveSearchTool, buildHortonSystemPrompt, createAgentHandler, createBuiltinAgentHandler, createBuiltinElectricTools, createHortonDocsSupport, createHortonTools, createSpawnWorkerTool, generateTitle, registerAgentTypes, registerBuiltinAgentTypes, registerHorton, registerWorker, resolveBuiltinAgentsEntrypointOptions, runBuiltinAgentsEntrypoint };
1971
+ export { BuiltinAgentsServer, DEFAULT_BUILTIN_AGENT_HANDLER_PATH, HORTON_MODEL, WORKER_TOOL_NAMES, braveSearchTool, buildHortonSystemPrompt, builtinModelProviderLabel, createAgentHandler, createBuiltinAgentHandler, createBuiltinElectricTools, createHortonDocsSupport, createHortonTools, createSpawnWorkerTool, generateTitle, listBuiltinModelChoices, registerAgentTypes, registerBuiltinAgentTypes, registerHorton, registerWorker, resolveBuiltinAgentsEntrypointOptions, runBuiltinAgentsEntrypoint };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electric-ax/agents",
3
- "version": "0.4.9",
3
+ "version": "0.4.11",
4
4
  "description": "Built-in Electric Agents runtimes such as Horton and worker",
5
5
  "repository": {
6
6
  "type": "git",
@@ -38,7 +38,7 @@
38
38
  "./package.json": "./package.json"
39
39
  },
40
40
  "dependencies": {
41
- "@durable-streams/state": "https://pkg.pr.new/durable-streams/durable-streams/@durable-streams/state@5d5c217",
41
+ "@durable-streams/state": "^0.2.9",
42
42
  "@mariozechner/pi-agent-core": "^0.70.2",
43
43
  "@mariozechner/pi-ai": "^0.70.2",
44
44
  "@sinclair/typebox": "^0.34.48",
@@ -49,7 +49,7 @@
49
49
  "sqlite-vec": "^0.1.9",
50
50
  "zod": "^4.3.6",
51
51
  "@electric-ax/agents-mcp": "0.2.2",
52
- "@electric-ax/agents-runtime": "0.3.5"
52
+ "@electric-ax/agents-runtime": "0.3.7"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/better-sqlite3": "^7.6.13",