@electric-ax/agents 0.4.9 → 0.4.10
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.
- package/dist/entrypoint.js +98 -27
- package/dist/index.cjs +99 -26
- package/dist/index.d.cts +12 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +99 -28
- package/package.json +3 -3
package/dist/entrypoint.js
CHANGED
|
@@ -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";
|
|
@@ -774,6 +774,7 @@ function createSpawnWorkerTool(ctx, modelConfig) {
|
|
|
774
774
|
|
|
775
775
|
//#endregion
|
|
776
776
|
//#region src/model-catalog.ts
|
|
777
|
+
const MODEL_INPUTS_SCHEMA_DEF = `electricModelInputs`;
|
|
777
778
|
const REASONING_EFFORT_VALUES = [
|
|
778
779
|
`auto`,
|
|
779
780
|
`minimal`,
|
|
@@ -785,13 +786,15 @@ const DEFAULT_ANTHROPIC_MODEL = `claude-sonnet-4-6`;
|
|
|
785
786
|
const DEFAULT_OPENAI_MODEL = `gpt-4.1`;
|
|
786
787
|
const DEFAULT_CODEX_MODEL = `gpt-5.4`;
|
|
787
788
|
const DEFAULT_DEEPSEEK_MODEL = `deepseek-v4-flash`;
|
|
789
|
+
const DEFAULT_MOONSHOT_MODEL = `kimi-k2.6`;
|
|
788
790
|
function modelValue(provider, id) {
|
|
789
791
|
return `${provider}:${id}`;
|
|
790
792
|
}
|
|
791
|
-
function
|
|
793
|
+
function builtinModelProviderLabel(provider) {
|
|
792
794
|
if (provider === `anthropic`) return `Anthropic`;
|
|
793
795
|
if (provider === `openai-codex`) return `OpenAI Codex`;
|
|
794
796
|
if (provider === `deepseek`) return `DeepSeek`;
|
|
797
|
+
if (provider === MOONSHOT_PROVIDER) return `Kimi`;
|
|
795
798
|
return `OpenAI`;
|
|
796
799
|
}
|
|
797
800
|
function configuredProviders() {
|
|
@@ -803,7 +806,8 @@ function mockFallbackCatalog() {
|
|
|
803
806
|
id: DEFAULT_ANTHROPIC_MODEL,
|
|
804
807
|
label: `Anthropic ${DEFAULT_ANTHROPIC_MODEL}`,
|
|
805
808
|
value: modelValue(`anthropic`, DEFAULT_ANTHROPIC_MODEL),
|
|
806
|
-
reasoning: true
|
|
809
|
+
reasoning: true,
|
|
810
|
+
input: [`text`, `image`]
|
|
807
811
|
};
|
|
808
812
|
return {
|
|
809
813
|
choices: [fallback],
|
|
@@ -821,6 +825,9 @@ async function fetchAvailableModelIds(provider) {
|
|
|
821
825
|
}) : provider === `deepseek` ? await fetch(`https://api.deepseek.com/v1/models`, {
|
|
822
826
|
headers: { authorization: `Bearer ${process.env.DEEPSEEK_API_KEY ?? ``}` },
|
|
823
827
|
signal: AbortSignal.timeout(3e3)
|
|
828
|
+
}) : provider === MOONSHOT_PROVIDER ? await fetch(`${MOONSHOT_API_BASE_URL}/models`, {
|
|
829
|
+
headers: { authorization: `Bearer ${getMoonshotApiKey() ?? ``}` },
|
|
830
|
+
signal: AbortSignal.timeout(3e3)
|
|
824
831
|
}) : await fetch(`https://api.openai.com/v1/models`, {
|
|
825
832
|
headers: { authorization: `Bearer ${process.env.OPENAI_API_KEY ?? ``}` },
|
|
826
833
|
signal: AbortSignal.timeout(3e3)
|
|
@@ -834,24 +841,42 @@ async function fetchAvailableModelIds(provider) {
|
|
|
834
841
|
return null;
|
|
835
842
|
}
|
|
836
843
|
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
844
|
+
function knownModelsForProvider(provider) {
|
|
845
|
+
return provider === MOONSHOT_PROVIDER ? getMoonshotModels() : getModels(provider);
|
|
846
|
+
}
|
|
847
|
+
function choiceForKnownModel(provider, model) {
|
|
848
|
+
return {
|
|
840
849
|
provider,
|
|
841
850
|
id: model.id,
|
|
842
|
-
label: `${
|
|
851
|
+
label: `${builtinModelProviderLabel(provider)} ${model.name}`,
|
|
843
852
|
value: modelValue(provider, model.id),
|
|
844
|
-
reasoning: model.reasoning
|
|
845
|
-
|
|
853
|
+
reasoning: model.reasoning,
|
|
854
|
+
input: model.input
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
function listBuiltinModelChoices(providers) {
|
|
858
|
+
return providers.flatMap((provider) => knownModelsForProvider(provider).map((model) => choiceForKnownModel(provider, model)));
|
|
859
|
+
}
|
|
860
|
+
async function choicesForProvider(provider) {
|
|
861
|
+
const knownChoices = listBuiltinModelChoices([provider]);
|
|
862
|
+
if (provider === `openai-codex`) return knownChoices;
|
|
846
863
|
const availableIds = await fetchAvailableModelIds(provider);
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
864
|
+
return availableIds === null ? knownChoices : knownChoices.filter((choice) => availableIds.has(choice.id));
|
|
865
|
+
}
|
|
866
|
+
function enabledModelSet(values) {
|
|
867
|
+
if (!values) return null;
|
|
868
|
+
const enabled = new Set();
|
|
869
|
+
for (const value of values) {
|
|
870
|
+
const trimmed = value.trim();
|
|
871
|
+
if (trimmed) enabled.add(trimmed);
|
|
872
|
+
}
|
|
873
|
+
return enabled.size > 0 ? enabled : null;
|
|
874
|
+
}
|
|
875
|
+
function filterChoicesByEnabledModels(choices, values) {
|
|
876
|
+
const enabled = enabledModelSet(values);
|
|
877
|
+
if (!enabled) return choices;
|
|
878
|
+
const filtered = choices.filter((choice) => enabled.has(choice.value));
|
|
879
|
+
return filtered.length > 0 ? filtered : choices;
|
|
855
880
|
}
|
|
856
881
|
function withProviderPayloadDefaults(config, choice, reasoningEffort) {
|
|
857
882
|
if (choice.provider !== `openai` && choice.provider !== `openai-codex` || !choice.reasoning) return config;
|
|
@@ -879,9 +904,10 @@ function parseReasoningEffort(value) {
|
|
|
879
904
|
async function createBuiltinModelCatalog(options = {}) {
|
|
880
905
|
const providers = configuredProviders();
|
|
881
906
|
if (providers.length === 0 && options.allowMockFallback) return mockFallbackCatalog();
|
|
882
|
-
const
|
|
907
|
+
const providerChoices = (await Promise.all(providers.map((provider) => choicesForProvider(provider)))).flat();
|
|
908
|
+
const choices = filterChoicesByEnabledModels(providerChoices, options.enabledModelValues);
|
|
883
909
|
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];
|
|
910
|
+
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
911
|
return {
|
|
886
912
|
choices,
|
|
887
913
|
defaultChoice
|
|
@@ -897,13 +923,24 @@ function resolveBuiltinModelConfig(catalog, args) {
|
|
|
897
923
|
provider: choice.provider,
|
|
898
924
|
model: choice.id,
|
|
899
925
|
...reasoningEffort && { reasoningEffort },
|
|
900
|
-
...choice.provider === `openai-codex` && { getApiKey: () => readCodexAccessToken() }
|
|
926
|
+
...choice.provider === `openai-codex` && { getApiKey: () => readCodexAccessToken() },
|
|
927
|
+
...choice.provider === MOONSHOT_PROVIDER && { getApiKey: () => getMoonshotApiKey() }
|
|
901
928
|
};
|
|
902
929
|
return withProviderPayloadDefaults(config, choice, reasoningEffort);
|
|
903
930
|
}
|
|
904
931
|
function modelChoiceValues(catalog) {
|
|
905
932
|
return catalog.choices.map((choice) => choice.value);
|
|
906
933
|
}
|
|
934
|
+
function modelInputSchemaDefs(catalog) {
|
|
935
|
+
return { [MODEL_INPUTS_SCHEMA_DEF]: {
|
|
936
|
+
type: `object`,
|
|
937
|
+
properties: Object.fromEntries(catalog.choices.map((choice) => [choice.value, {
|
|
938
|
+
type: `array`,
|
|
939
|
+
items: { enum: [`text`, `image`] },
|
|
940
|
+
default: choice.input
|
|
941
|
+
}]))
|
|
942
|
+
} };
|
|
943
|
+
}
|
|
907
944
|
|
|
908
945
|
//#endregion
|
|
909
946
|
//#region src/agents/horton.ts
|
|
@@ -1122,10 +1159,40 @@ function payloadToTitleText(payload) {
|
|
|
1122
1159
|
}
|
|
1123
1160
|
return String(payload);
|
|
1124
1161
|
}
|
|
1162
|
+
function attachmentTitleText(attachment) {
|
|
1163
|
+
const mimeType = typeof attachment.mimeType === `string` ? attachment.mimeType : ``;
|
|
1164
|
+
const filename = typeof attachment.filename === `string` && attachment.filename.trim() ? attachment.filename.trim() : typeof attachment.id === `string` ? attachment.id : `attachment`;
|
|
1165
|
+
const kind = mimeType.startsWith(`image/`) ? `image` : `file`;
|
|
1166
|
+
return `Attached ${kind}: ${filename}`;
|
|
1167
|
+
}
|
|
1168
|
+
function attachmentsForInboxMessage(ctx, inboxKey) {
|
|
1169
|
+
const manifests = ctx.db.collections.manifests?.toArray;
|
|
1170
|
+
if (!Array.isArray(manifests)) return [];
|
|
1171
|
+
return manifests.filter((entry) => {
|
|
1172
|
+
if (!entry || typeof entry !== `object`) return false;
|
|
1173
|
+
const attachment = entry;
|
|
1174
|
+
if (attachment.kind !== `attachment`) return false;
|
|
1175
|
+
if (attachment.role !== `input`) return false;
|
|
1176
|
+
const subject = attachment.subject;
|
|
1177
|
+
return subject !== null && typeof subject === `object` && !Array.isArray(subject) && subject.type === `inbox` && subject.key === inboxKey;
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
function messageTitleText(ctx, message) {
|
|
1181
|
+
const pieces = [];
|
|
1182
|
+
const text = payloadToTitleText(message.payload).trim();
|
|
1183
|
+
if (text) pieces.push(text);
|
|
1184
|
+
const key = typeof message.key === `string` ? message.key : null;
|
|
1185
|
+
const attachments = key ? attachmentsForInboxMessage(ctx, key) : [];
|
|
1186
|
+
for (const attachment of attachments) {
|
|
1187
|
+
const attachmentText = attachmentTitleText(attachment);
|
|
1188
|
+
if (attachmentText) pieces.push(attachmentText);
|
|
1189
|
+
}
|
|
1190
|
+
return pieces.join(`\n`);
|
|
1191
|
+
}
|
|
1125
1192
|
async function extractFirstUserMessage(ctx) {
|
|
1126
1193
|
const firstMessage = ctx.db.collections.inbox.toArray.filter((message) => message.from !== `system`).sort((left, right) => messageSeq(left) - messageSeq(right))[0];
|
|
1127
1194
|
if (!firstMessage) return null;
|
|
1128
|
-
const text =
|
|
1195
|
+
const text = messageTitleText(ctx, firstMessage);
|
|
1129
1196
|
return text.length > 0 ? text : null;
|
|
1130
1197
|
}
|
|
1131
1198
|
function messageSeq(message) {
|
|
@@ -1165,7 +1232,7 @@ function createAssistantHandler(options) {
|
|
|
1165
1232
|
...mcp.tools()
|
|
1166
1233
|
];
|
|
1167
1234
|
const hasEventSourceTools = tools.some((tool) => getToolName(tool) === `list_event_sources`);
|
|
1168
|
-
const titlePromise =
|
|
1235
|
+
const titlePromise = !ctx.tags.title ? (async () => {
|
|
1169
1236
|
const firstUserMessage = await extractFirstUserMessage(ctx);
|
|
1170
1237
|
if (!firstUserMessage) return;
|
|
1171
1238
|
let title = null;
|
|
@@ -1286,7 +1353,7 @@ function registerHorton(registry, options) {
|
|
|
1286
1353
|
model: z.enum(modelChoiceValues(modelCatalog)).default(modelCatalog.defaultChoice.value),
|
|
1287
1354
|
reasoningEffort: z.enum(REASONING_EFFORT_VALUES).default(`auto`).describe(`Reasoning effort for compatible reasoning models. Auto uses a safe provider default.`),
|
|
1288
1355
|
workingDirectory: z.string().optional().describe(`Working directory for file operations. Defaults to the server's configured cwd.`)
|
|
1289
|
-
});
|
|
1356
|
+
}).meta({ $defs: modelInputSchemaDefs(modelCatalog) });
|
|
1290
1357
|
registry.define(`horton`, {
|
|
1291
1358
|
description: `Friendly capable assistant — chat, code, research, dispatch`,
|
|
1292
1359
|
creationSchema: hortonCreationSchema,
|
|
@@ -1506,10 +1573,13 @@ function createBuiltinElectricTools(custom) {
|
|
|
1506
1573
|
};
|
|
1507
1574
|
}
|
|
1508
1575
|
async function createBuiltinAgentHandler(options) {
|
|
1509
|
-
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1510
|
-
const modelCatalog = await createBuiltinModelCatalog({
|
|
1576
|
+
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, enabledModelValues, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1577
|
+
const modelCatalog = await createBuiltinModelCatalog({
|
|
1578
|
+
allowMockFallback: Boolean(streamFn),
|
|
1579
|
+
enabledModelValues
|
|
1580
|
+
});
|
|
1511
1581
|
if (!modelCatalog) {
|
|
1512
|
-
serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY or
|
|
1582
|
+
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
1583
|
return null;
|
|
1514
1584
|
}
|
|
1515
1585
|
const cwd = workingDirectory ?? process.cwd();
|
|
@@ -1703,9 +1773,10 @@ var BuiltinAgentsServer = class {
|
|
|
1703
1773
|
publicUrl,
|
|
1704
1774
|
runtimeName: `builtin-agents`,
|
|
1705
1775
|
baseSkillsDir: this.options.baseSkillsDir,
|
|
1776
|
+
enabledModelValues: this.options.enabledModelValues,
|
|
1706
1777
|
serverHeaders: pullWake.headers
|
|
1707
1778
|
});
|
|
1708
|
-
if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY or
|
|
1779
|
+
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
1780
|
await registerBuiltinAgentTypes(this.bootstrap);
|
|
1710
1781
|
const registeredRunner = pullWake.registerRunner ? await this.registerPullWakeRunner(pullWake) : null;
|
|
1711
1782
|
this.pullWakeRunner = createPullWakeRunner({
|
package/dist/index.cjs
CHANGED
|
@@ -798,6 +798,7 @@ function createSpawnWorkerTool(ctx, modelConfig) {
|
|
|
798
798
|
|
|
799
799
|
//#endregion
|
|
800
800
|
//#region src/model-catalog.ts
|
|
801
|
+
const MODEL_INPUTS_SCHEMA_DEF = `electricModelInputs`;
|
|
801
802
|
const REASONING_EFFORT_VALUES = [
|
|
802
803
|
`auto`,
|
|
803
804
|
`minimal`,
|
|
@@ -809,13 +810,15 @@ const DEFAULT_ANTHROPIC_MODEL = `claude-sonnet-4-6`;
|
|
|
809
810
|
const DEFAULT_OPENAI_MODEL = `gpt-4.1`;
|
|
810
811
|
const DEFAULT_CODEX_MODEL = `gpt-5.4`;
|
|
811
812
|
const DEFAULT_DEEPSEEK_MODEL = `deepseek-v4-flash`;
|
|
813
|
+
const DEFAULT_MOONSHOT_MODEL = `kimi-k2.6`;
|
|
812
814
|
function modelValue(provider, id) {
|
|
813
815
|
return `${provider}:${id}`;
|
|
814
816
|
}
|
|
815
|
-
function
|
|
817
|
+
function builtinModelProviderLabel(provider) {
|
|
816
818
|
if (provider === `anthropic`) return `Anthropic`;
|
|
817
819
|
if (provider === `openai-codex`) return `OpenAI Codex`;
|
|
818
820
|
if (provider === `deepseek`) return `DeepSeek`;
|
|
821
|
+
if (provider === __electric_ax_agents_runtime.MOONSHOT_PROVIDER) return `Kimi`;
|
|
819
822
|
return `OpenAI`;
|
|
820
823
|
}
|
|
821
824
|
function configuredProviders() {
|
|
@@ -827,7 +830,8 @@ function mockFallbackCatalog() {
|
|
|
827
830
|
id: DEFAULT_ANTHROPIC_MODEL,
|
|
828
831
|
label: `Anthropic ${DEFAULT_ANTHROPIC_MODEL}`,
|
|
829
832
|
value: modelValue(`anthropic`, DEFAULT_ANTHROPIC_MODEL),
|
|
830
|
-
reasoning: true
|
|
833
|
+
reasoning: true,
|
|
834
|
+
input: [`text`, `image`]
|
|
831
835
|
};
|
|
832
836
|
return {
|
|
833
837
|
choices: [fallback],
|
|
@@ -845,6 +849,9 @@ async function fetchAvailableModelIds(provider) {
|
|
|
845
849
|
}) : provider === `deepseek` ? await fetch(`https://api.deepseek.com/v1/models`, {
|
|
846
850
|
headers: { authorization: `Bearer ${process.env.DEEPSEEK_API_KEY ?? ``}` },
|
|
847
851
|
signal: AbortSignal.timeout(3e3)
|
|
852
|
+
}) : provider === __electric_ax_agents_runtime.MOONSHOT_PROVIDER ? await fetch(`${__electric_ax_agents_runtime.MOONSHOT_API_BASE_URL}/models`, {
|
|
853
|
+
headers: { authorization: `Bearer ${(0, __electric_ax_agents_runtime.getMoonshotApiKey)() ?? ``}` },
|
|
854
|
+
signal: AbortSignal.timeout(3e3)
|
|
848
855
|
}) : await fetch(`https://api.openai.com/v1/models`, {
|
|
849
856
|
headers: { authorization: `Bearer ${process.env.OPENAI_API_KEY ?? ``}` },
|
|
850
857
|
signal: AbortSignal.timeout(3e3)
|
|
@@ -858,24 +865,42 @@ async function fetchAvailableModelIds(provider) {
|
|
|
858
865
|
return null;
|
|
859
866
|
}
|
|
860
867
|
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
868
|
+
function knownModelsForProvider(provider) {
|
|
869
|
+
return provider === __electric_ax_agents_runtime.MOONSHOT_PROVIDER ? (0, __electric_ax_agents_runtime.getMoonshotModels)() : (0, __mariozechner_pi_ai.getModels)(provider);
|
|
870
|
+
}
|
|
871
|
+
function choiceForKnownModel(provider, model) {
|
|
872
|
+
return {
|
|
864
873
|
provider,
|
|
865
874
|
id: model.id,
|
|
866
|
-
label: `${
|
|
875
|
+
label: `${builtinModelProviderLabel(provider)} ${model.name}`,
|
|
867
876
|
value: modelValue(provider, model.id),
|
|
868
|
-
reasoning: model.reasoning
|
|
869
|
-
|
|
877
|
+
reasoning: model.reasoning,
|
|
878
|
+
input: model.input
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
function listBuiltinModelChoices(providers) {
|
|
882
|
+
return providers.flatMap((provider) => knownModelsForProvider(provider).map((model) => choiceForKnownModel(provider, model)));
|
|
883
|
+
}
|
|
884
|
+
async function choicesForProvider(provider) {
|
|
885
|
+
const knownChoices = listBuiltinModelChoices([provider]);
|
|
886
|
+
if (provider === `openai-codex`) return knownChoices;
|
|
870
887
|
const availableIds = await fetchAvailableModelIds(provider);
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
888
|
+
return availableIds === null ? knownChoices : knownChoices.filter((choice) => availableIds.has(choice.id));
|
|
889
|
+
}
|
|
890
|
+
function enabledModelSet(values) {
|
|
891
|
+
if (!values) return null;
|
|
892
|
+
const enabled = new Set();
|
|
893
|
+
for (const value of values) {
|
|
894
|
+
const trimmed = value.trim();
|
|
895
|
+
if (trimmed) enabled.add(trimmed);
|
|
896
|
+
}
|
|
897
|
+
return enabled.size > 0 ? enabled : null;
|
|
898
|
+
}
|
|
899
|
+
function filterChoicesByEnabledModels(choices, values) {
|
|
900
|
+
const enabled = enabledModelSet(values);
|
|
901
|
+
if (!enabled) return choices;
|
|
902
|
+
const filtered = choices.filter((choice) => enabled.has(choice.value));
|
|
903
|
+
return filtered.length > 0 ? filtered : choices;
|
|
879
904
|
}
|
|
880
905
|
function withProviderPayloadDefaults(config, choice, reasoningEffort) {
|
|
881
906
|
if (choice.provider !== `openai` && choice.provider !== `openai-codex` || !choice.reasoning) return config;
|
|
@@ -903,9 +928,10 @@ function parseReasoningEffort(value) {
|
|
|
903
928
|
async function createBuiltinModelCatalog(options = {}) {
|
|
904
929
|
const providers = configuredProviders();
|
|
905
930
|
if (providers.length === 0 && options.allowMockFallback) return mockFallbackCatalog();
|
|
906
|
-
const
|
|
931
|
+
const providerChoices = (await Promise.all(providers.map((provider) => choicesForProvider(provider)))).flat();
|
|
932
|
+
const choices = filterChoicesByEnabledModels(providerChoices, options.enabledModelValues);
|
|
907
933
|
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];
|
|
934
|
+
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
935
|
return {
|
|
910
936
|
choices,
|
|
911
937
|
defaultChoice
|
|
@@ -921,13 +947,24 @@ function resolveBuiltinModelConfig(catalog, args) {
|
|
|
921
947
|
provider: choice.provider,
|
|
922
948
|
model: choice.id,
|
|
923
949
|
...reasoningEffort && { reasoningEffort },
|
|
924
|
-
...choice.provider === `openai-codex` && { getApiKey: () => (0, __electric_ax_agents_runtime.readCodexAccessToken)() }
|
|
950
|
+
...choice.provider === `openai-codex` && { getApiKey: () => (0, __electric_ax_agents_runtime.readCodexAccessToken)() },
|
|
951
|
+
...choice.provider === __electric_ax_agents_runtime.MOONSHOT_PROVIDER && { getApiKey: () => (0, __electric_ax_agents_runtime.getMoonshotApiKey)() }
|
|
925
952
|
};
|
|
926
953
|
return withProviderPayloadDefaults(config, choice, reasoningEffort);
|
|
927
954
|
}
|
|
928
955
|
function modelChoiceValues(catalog) {
|
|
929
956
|
return catalog.choices.map((choice) => choice.value);
|
|
930
957
|
}
|
|
958
|
+
function modelInputSchemaDefs(catalog) {
|
|
959
|
+
return { [MODEL_INPUTS_SCHEMA_DEF]: {
|
|
960
|
+
type: `object`,
|
|
961
|
+
properties: Object.fromEntries(catalog.choices.map((choice) => [choice.value, {
|
|
962
|
+
type: `array`,
|
|
963
|
+
items: { enum: [`text`, `image`] },
|
|
964
|
+
default: choice.input
|
|
965
|
+
}]))
|
|
966
|
+
} };
|
|
967
|
+
}
|
|
931
968
|
|
|
932
969
|
//#endregion
|
|
933
970
|
//#region src/agents/horton.ts
|
|
@@ -1147,10 +1184,40 @@ function payloadToTitleText(payload) {
|
|
|
1147
1184
|
}
|
|
1148
1185
|
return String(payload);
|
|
1149
1186
|
}
|
|
1187
|
+
function attachmentTitleText(attachment) {
|
|
1188
|
+
const mimeType = typeof attachment.mimeType === `string` ? attachment.mimeType : ``;
|
|
1189
|
+
const filename = typeof attachment.filename === `string` && attachment.filename.trim() ? attachment.filename.trim() : typeof attachment.id === `string` ? attachment.id : `attachment`;
|
|
1190
|
+
const kind = mimeType.startsWith(`image/`) ? `image` : `file`;
|
|
1191
|
+
return `Attached ${kind}: ${filename}`;
|
|
1192
|
+
}
|
|
1193
|
+
function attachmentsForInboxMessage(ctx, inboxKey) {
|
|
1194
|
+
const manifests = ctx.db.collections.manifests?.toArray;
|
|
1195
|
+
if (!Array.isArray(manifests)) return [];
|
|
1196
|
+
return manifests.filter((entry) => {
|
|
1197
|
+
if (!entry || typeof entry !== `object`) return false;
|
|
1198
|
+
const attachment = entry;
|
|
1199
|
+
if (attachment.kind !== `attachment`) return false;
|
|
1200
|
+
if (attachment.role !== `input`) return false;
|
|
1201
|
+
const subject = attachment.subject;
|
|
1202
|
+
return subject !== null && typeof subject === `object` && !Array.isArray(subject) && subject.type === `inbox` && subject.key === inboxKey;
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
function messageTitleText(ctx, message) {
|
|
1206
|
+
const pieces = [];
|
|
1207
|
+
const text = payloadToTitleText(message.payload).trim();
|
|
1208
|
+
if (text) pieces.push(text);
|
|
1209
|
+
const key = typeof message.key === `string` ? message.key : null;
|
|
1210
|
+
const attachments = key ? attachmentsForInboxMessage(ctx, key) : [];
|
|
1211
|
+
for (const attachment of attachments) {
|
|
1212
|
+
const attachmentText = attachmentTitleText(attachment);
|
|
1213
|
+
if (attachmentText) pieces.push(attachmentText);
|
|
1214
|
+
}
|
|
1215
|
+
return pieces.join(`\n`);
|
|
1216
|
+
}
|
|
1150
1217
|
async function extractFirstUserMessage(ctx) {
|
|
1151
1218
|
const firstMessage = ctx.db.collections.inbox.toArray.filter((message) => message.from !== `system`).sort((left, right) => messageSeq(left) - messageSeq(right))[0];
|
|
1152
1219
|
if (!firstMessage) return null;
|
|
1153
|
-
const text =
|
|
1220
|
+
const text = messageTitleText(ctx, firstMessage);
|
|
1154
1221
|
return text.length > 0 ? text : null;
|
|
1155
1222
|
}
|
|
1156
1223
|
function messageSeq(message) {
|
|
@@ -1190,7 +1257,7 @@ function createAssistantHandler(options) {
|
|
|
1190
1257
|
...__electric_ax_agents_mcp.mcp.tools()
|
|
1191
1258
|
];
|
|
1192
1259
|
const hasEventSourceTools = tools.some((tool) => getToolName(tool) === `list_event_sources`);
|
|
1193
|
-
const titlePromise =
|
|
1260
|
+
const titlePromise = !ctx.tags.title ? (async () => {
|
|
1194
1261
|
const firstUserMessage = await extractFirstUserMessage(ctx);
|
|
1195
1262
|
if (!firstUserMessage) return;
|
|
1196
1263
|
let title = null;
|
|
@@ -1311,7 +1378,7 @@ function registerHorton(registry, options) {
|
|
|
1311
1378
|
model: zod.z.enum(modelChoiceValues(modelCatalog)).default(modelCatalog.defaultChoice.value),
|
|
1312
1379
|
reasoningEffort: zod.z.enum(REASONING_EFFORT_VALUES).default(`auto`).describe(`Reasoning effort for compatible reasoning models. Auto uses a safe provider default.`),
|
|
1313
1380
|
workingDirectory: zod.z.string().optional().describe(`Working directory for file operations. Defaults to the server's configured cwd.`)
|
|
1314
|
-
});
|
|
1381
|
+
}).meta({ $defs: modelInputSchemaDefs(modelCatalog) });
|
|
1315
1382
|
registry.define(`horton`, {
|
|
1316
1383
|
description: `Friendly capable assistant — chat, code, research, dispatch`,
|
|
1317
1384
|
creationSchema: hortonCreationSchema,
|
|
@@ -1532,10 +1599,13 @@ function createBuiltinElectricTools(custom) {
|
|
|
1532
1599
|
};
|
|
1533
1600
|
}
|
|
1534
1601
|
async function createBuiltinAgentHandler(options) {
|
|
1535
|
-
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1536
|
-
const modelCatalog = await createBuiltinModelCatalog({
|
|
1602
|
+
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, enabledModelValues, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1603
|
+
const modelCatalog = await createBuiltinModelCatalog({
|
|
1604
|
+
allowMockFallback: Boolean(streamFn),
|
|
1605
|
+
enabledModelValues
|
|
1606
|
+
});
|
|
1537
1607
|
if (!modelCatalog) {
|
|
1538
|
-
serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY or
|
|
1608
|
+
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
1609
|
return null;
|
|
1540
1610
|
}
|
|
1541
1611
|
const cwd = workingDirectory ?? process.cwd();
|
|
@@ -1739,9 +1809,10 @@ var BuiltinAgentsServer = class {
|
|
|
1739
1809
|
publicUrl,
|
|
1740
1810
|
runtimeName: `builtin-agents`,
|
|
1741
1811
|
baseSkillsDir: this.options.baseSkillsDir,
|
|
1812
|
+
enabledModelValues: this.options.enabledModelValues,
|
|
1742
1813
|
serverHeaders: pullWake.headers
|
|
1743
1814
|
});
|
|
1744
|
-
if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY or
|
|
1815
|
+
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
1816
|
await registerBuiltinAgentTypes(this.bootstrap);
|
|
1746
1817
|
const registeredRunner = pullWake.registerRunner ? await this.registerPullWakeRunner(pullWake) : null;
|
|
1747
1818
|
this.pullWakeRunner = (0, __electric_ax_agents_runtime.createPullWakeRunner)({
|
|
@@ -1913,6 +1984,7 @@ Object.defineProperty(exports, 'braveSearchTool', {
|
|
|
1913
1984
|
}
|
|
1914
1985
|
});
|
|
1915
1986
|
exports.buildHortonSystemPrompt = buildHortonSystemPrompt
|
|
1987
|
+
exports.builtinModelProviderLabel = builtinModelProviderLabel
|
|
1916
1988
|
exports.createAgentHandler = createAgentHandler
|
|
1917
1989
|
exports.createBuiltinAgentHandler = createBuiltinAgentHandler
|
|
1918
1990
|
exports.createBuiltinElectricTools = createBuiltinElectricTools
|
|
@@ -1920,6 +1992,7 @@ exports.createHortonDocsSupport = createHortonDocsSupport
|
|
|
1920
1992
|
exports.createHortonTools = createHortonTools
|
|
1921
1993
|
exports.createSpawnWorkerTool = createSpawnWorkerTool
|
|
1922
1994
|
exports.generateTitle = generateTitle
|
|
1995
|
+
exports.listBuiltinModelChoices = listBuiltinModelChoices
|
|
1923
1996
|
exports.registerAgentTypes = registerAgentTypes
|
|
1924
1997
|
exports.registerBuiltinAgentTypes = registerBuiltinAgentTypes
|
|
1925
1998
|
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";
|
|
@@ -774,6 +774,7 @@ function createSpawnWorkerTool(ctx, modelConfig) {
|
|
|
774
774
|
|
|
775
775
|
//#endregion
|
|
776
776
|
//#region src/model-catalog.ts
|
|
777
|
+
const MODEL_INPUTS_SCHEMA_DEF = `electricModelInputs`;
|
|
777
778
|
const REASONING_EFFORT_VALUES = [
|
|
778
779
|
`auto`,
|
|
779
780
|
`minimal`,
|
|
@@ -785,13 +786,15 @@ const DEFAULT_ANTHROPIC_MODEL = `claude-sonnet-4-6`;
|
|
|
785
786
|
const DEFAULT_OPENAI_MODEL = `gpt-4.1`;
|
|
786
787
|
const DEFAULT_CODEX_MODEL = `gpt-5.4`;
|
|
787
788
|
const DEFAULT_DEEPSEEK_MODEL = `deepseek-v4-flash`;
|
|
789
|
+
const DEFAULT_MOONSHOT_MODEL = `kimi-k2.6`;
|
|
788
790
|
function modelValue(provider, id) {
|
|
789
791
|
return `${provider}:${id}`;
|
|
790
792
|
}
|
|
791
|
-
function
|
|
793
|
+
function builtinModelProviderLabel(provider) {
|
|
792
794
|
if (provider === `anthropic`) return `Anthropic`;
|
|
793
795
|
if (provider === `openai-codex`) return `OpenAI Codex`;
|
|
794
796
|
if (provider === `deepseek`) return `DeepSeek`;
|
|
797
|
+
if (provider === MOONSHOT_PROVIDER) return `Kimi`;
|
|
795
798
|
return `OpenAI`;
|
|
796
799
|
}
|
|
797
800
|
function configuredProviders() {
|
|
@@ -803,7 +806,8 @@ function mockFallbackCatalog() {
|
|
|
803
806
|
id: DEFAULT_ANTHROPIC_MODEL,
|
|
804
807
|
label: `Anthropic ${DEFAULT_ANTHROPIC_MODEL}`,
|
|
805
808
|
value: modelValue(`anthropic`, DEFAULT_ANTHROPIC_MODEL),
|
|
806
|
-
reasoning: true
|
|
809
|
+
reasoning: true,
|
|
810
|
+
input: [`text`, `image`]
|
|
807
811
|
};
|
|
808
812
|
return {
|
|
809
813
|
choices: [fallback],
|
|
@@ -821,6 +825,9 @@ async function fetchAvailableModelIds(provider) {
|
|
|
821
825
|
}) : provider === `deepseek` ? await fetch(`https://api.deepseek.com/v1/models`, {
|
|
822
826
|
headers: { authorization: `Bearer ${process.env.DEEPSEEK_API_KEY ?? ``}` },
|
|
823
827
|
signal: AbortSignal.timeout(3e3)
|
|
828
|
+
}) : provider === MOONSHOT_PROVIDER ? await fetch(`${MOONSHOT_API_BASE_URL}/models`, {
|
|
829
|
+
headers: { authorization: `Bearer ${getMoonshotApiKey() ?? ``}` },
|
|
830
|
+
signal: AbortSignal.timeout(3e3)
|
|
824
831
|
}) : await fetch(`https://api.openai.com/v1/models`, {
|
|
825
832
|
headers: { authorization: `Bearer ${process.env.OPENAI_API_KEY ?? ``}` },
|
|
826
833
|
signal: AbortSignal.timeout(3e3)
|
|
@@ -834,24 +841,42 @@ async function fetchAvailableModelIds(provider) {
|
|
|
834
841
|
return null;
|
|
835
842
|
}
|
|
836
843
|
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
844
|
+
function knownModelsForProvider(provider) {
|
|
845
|
+
return provider === MOONSHOT_PROVIDER ? getMoonshotModels() : getModels(provider);
|
|
846
|
+
}
|
|
847
|
+
function choiceForKnownModel(provider, model) {
|
|
848
|
+
return {
|
|
840
849
|
provider,
|
|
841
850
|
id: model.id,
|
|
842
|
-
label: `${
|
|
851
|
+
label: `${builtinModelProviderLabel(provider)} ${model.name}`,
|
|
843
852
|
value: modelValue(provider, model.id),
|
|
844
|
-
reasoning: model.reasoning
|
|
845
|
-
|
|
853
|
+
reasoning: model.reasoning,
|
|
854
|
+
input: model.input
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
function listBuiltinModelChoices(providers) {
|
|
858
|
+
return providers.flatMap((provider) => knownModelsForProvider(provider).map((model) => choiceForKnownModel(provider, model)));
|
|
859
|
+
}
|
|
860
|
+
async function choicesForProvider(provider) {
|
|
861
|
+
const knownChoices = listBuiltinModelChoices([provider]);
|
|
862
|
+
if (provider === `openai-codex`) return knownChoices;
|
|
846
863
|
const availableIds = await fetchAvailableModelIds(provider);
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
864
|
+
return availableIds === null ? knownChoices : knownChoices.filter((choice) => availableIds.has(choice.id));
|
|
865
|
+
}
|
|
866
|
+
function enabledModelSet(values) {
|
|
867
|
+
if (!values) return null;
|
|
868
|
+
const enabled = new Set();
|
|
869
|
+
for (const value of values) {
|
|
870
|
+
const trimmed = value.trim();
|
|
871
|
+
if (trimmed) enabled.add(trimmed);
|
|
872
|
+
}
|
|
873
|
+
return enabled.size > 0 ? enabled : null;
|
|
874
|
+
}
|
|
875
|
+
function filterChoicesByEnabledModels(choices, values) {
|
|
876
|
+
const enabled = enabledModelSet(values);
|
|
877
|
+
if (!enabled) return choices;
|
|
878
|
+
const filtered = choices.filter((choice) => enabled.has(choice.value));
|
|
879
|
+
return filtered.length > 0 ? filtered : choices;
|
|
855
880
|
}
|
|
856
881
|
function withProviderPayloadDefaults(config, choice, reasoningEffort) {
|
|
857
882
|
if (choice.provider !== `openai` && choice.provider !== `openai-codex` || !choice.reasoning) return config;
|
|
@@ -879,9 +904,10 @@ function parseReasoningEffort(value) {
|
|
|
879
904
|
async function createBuiltinModelCatalog(options = {}) {
|
|
880
905
|
const providers = configuredProviders();
|
|
881
906
|
if (providers.length === 0 && options.allowMockFallback) return mockFallbackCatalog();
|
|
882
|
-
const
|
|
907
|
+
const providerChoices = (await Promise.all(providers.map((provider) => choicesForProvider(provider)))).flat();
|
|
908
|
+
const choices = filterChoicesByEnabledModels(providerChoices, options.enabledModelValues);
|
|
883
909
|
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];
|
|
910
|
+
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
911
|
return {
|
|
886
912
|
choices,
|
|
887
913
|
defaultChoice
|
|
@@ -897,13 +923,24 @@ function resolveBuiltinModelConfig(catalog, args) {
|
|
|
897
923
|
provider: choice.provider,
|
|
898
924
|
model: choice.id,
|
|
899
925
|
...reasoningEffort && { reasoningEffort },
|
|
900
|
-
...choice.provider === `openai-codex` && { getApiKey: () => readCodexAccessToken() }
|
|
926
|
+
...choice.provider === `openai-codex` && { getApiKey: () => readCodexAccessToken() },
|
|
927
|
+
...choice.provider === MOONSHOT_PROVIDER && { getApiKey: () => getMoonshotApiKey() }
|
|
901
928
|
};
|
|
902
929
|
return withProviderPayloadDefaults(config, choice, reasoningEffort);
|
|
903
930
|
}
|
|
904
931
|
function modelChoiceValues(catalog) {
|
|
905
932
|
return catalog.choices.map((choice) => choice.value);
|
|
906
933
|
}
|
|
934
|
+
function modelInputSchemaDefs(catalog) {
|
|
935
|
+
return { [MODEL_INPUTS_SCHEMA_DEF]: {
|
|
936
|
+
type: `object`,
|
|
937
|
+
properties: Object.fromEntries(catalog.choices.map((choice) => [choice.value, {
|
|
938
|
+
type: `array`,
|
|
939
|
+
items: { enum: [`text`, `image`] },
|
|
940
|
+
default: choice.input
|
|
941
|
+
}]))
|
|
942
|
+
} };
|
|
943
|
+
}
|
|
907
944
|
|
|
908
945
|
//#endregion
|
|
909
946
|
//#region src/agents/horton.ts
|
|
@@ -1123,10 +1160,40 @@ function payloadToTitleText(payload) {
|
|
|
1123
1160
|
}
|
|
1124
1161
|
return String(payload);
|
|
1125
1162
|
}
|
|
1163
|
+
function attachmentTitleText(attachment) {
|
|
1164
|
+
const mimeType = typeof attachment.mimeType === `string` ? attachment.mimeType : ``;
|
|
1165
|
+
const filename = typeof attachment.filename === `string` && attachment.filename.trim() ? attachment.filename.trim() : typeof attachment.id === `string` ? attachment.id : `attachment`;
|
|
1166
|
+
const kind = mimeType.startsWith(`image/`) ? `image` : `file`;
|
|
1167
|
+
return `Attached ${kind}: ${filename}`;
|
|
1168
|
+
}
|
|
1169
|
+
function attachmentsForInboxMessage(ctx, inboxKey) {
|
|
1170
|
+
const manifests = ctx.db.collections.manifests?.toArray;
|
|
1171
|
+
if (!Array.isArray(manifests)) return [];
|
|
1172
|
+
return manifests.filter((entry) => {
|
|
1173
|
+
if (!entry || typeof entry !== `object`) return false;
|
|
1174
|
+
const attachment = entry;
|
|
1175
|
+
if (attachment.kind !== `attachment`) return false;
|
|
1176
|
+
if (attachment.role !== `input`) return false;
|
|
1177
|
+
const subject = attachment.subject;
|
|
1178
|
+
return subject !== null && typeof subject === `object` && !Array.isArray(subject) && subject.type === `inbox` && subject.key === inboxKey;
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
function messageTitleText(ctx, message) {
|
|
1182
|
+
const pieces = [];
|
|
1183
|
+
const text = payloadToTitleText(message.payload).trim();
|
|
1184
|
+
if (text) pieces.push(text);
|
|
1185
|
+
const key = typeof message.key === `string` ? message.key : null;
|
|
1186
|
+
const attachments = key ? attachmentsForInboxMessage(ctx, key) : [];
|
|
1187
|
+
for (const attachment of attachments) {
|
|
1188
|
+
const attachmentText = attachmentTitleText(attachment);
|
|
1189
|
+
if (attachmentText) pieces.push(attachmentText);
|
|
1190
|
+
}
|
|
1191
|
+
return pieces.join(`\n`);
|
|
1192
|
+
}
|
|
1126
1193
|
async function extractFirstUserMessage(ctx) {
|
|
1127
1194
|
const firstMessage = ctx.db.collections.inbox.toArray.filter((message) => message.from !== `system`).sort((left, right) => messageSeq(left) - messageSeq(right))[0];
|
|
1128
1195
|
if (!firstMessage) return null;
|
|
1129
|
-
const text =
|
|
1196
|
+
const text = messageTitleText(ctx, firstMessage);
|
|
1130
1197
|
return text.length > 0 ? text : null;
|
|
1131
1198
|
}
|
|
1132
1199
|
function messageSeq(message) {
|
|
@@ -1166,7 +1233,7 @@ function createAssistantHandler(options) {
|
|
|
1166
1233
|
...mcp.tools()
|
|
1167
1234
|
];
|
|
1168
1235
|
const hasEventSourceTools = tools.some((tool) => getToolName(tool) === `list_event_sources`);
|
|
1169
|
-
const titlePromise =
|
|
1236
|
+
const titlePromise = !ctx.tags.title ? (async () => {
|
|
1170
1237
|
const firstUserMessage = await extractFirstUserMessage(ctx);
|
|
1171
1238
|
if (!firstUserMessage) return;
|
|
1172
1239
|
let title = null;
|
|
@@ -1287,7 +1354,7 @@ function registerHorton(registry, options) {
|
|
|
1287
1354
|
model: z.enum(modelChoiceValues(modelCatalog)).default(modelCatalog.defaultChoice.value),
|
|
1288
1355
|
reasoningEffort: z.enum(REASONING_EFFORT_VALUES).default(`auto`).describe(`Reasoning effort for compatible reasoning models. Auto uses a safe provider default.`),
|
|
1289
1356
|
workingDirectory: z.string().optional().describe(`Working directory for file operations. Defaults to the server's configured cwd.`)
|
|
1290
|
-
});
|
|
1357
|
+
}).meta({ $defs: modelInputSchemaDefs(modelCatalog) });
|
|
1291
1358
|
registry.define(`horton`, {
|
|
1292
1359
|
description: `Friendly capable assistant — chat, code, research, dispatch`,
|
|
1293
1360
|
creationSchema: hortonCreationSchema,
|
|
@@ -1508,10 +1575,13 @@ function createBuiltinElectricTools(custom) {
|
|
|
1508
1575
|
};
|
|
1509
1576
|
}
|
|
1510
1577
|
async function createBuiltinAgentHandler(options) {
|
|
1511
|
-
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1512
|
-
const modelCatalog = await createBuiltinModelCatalog({
|
|
1578
|
+
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, enabledModelValues, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1579
|
+
const modelCatalog = await createBuiltinModelCatalog({
|
|
1580
|
+
allowMockFallback: Boolean(streamFn),
|
|
1581
|
+
enabledModelValues
|
|
1582
|
+
});
|
|
1513
1583
|
if (!modelCatalog) {
|
|
1514
|
-
serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY or
|
|
1584
|
+
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
1585
|
return null;
|
|
1516
1586
|
}
|
|
1517
1587
|
const cwd = workingDirectory ?? process.cwd();
|
|
@@ -1715,9 +1785,10 @@ var BuiltinAgentsServer = class {
|
|
|
1715
1785
|
publicUrl,
|
|
1716
1786
|
runtimeName: `builtin-agents`,
|
|
1717
1787
|
baseSkillsDir: this.options.baseSkillsDir,
|
|
1788
|
+
enabledModelValues: this.options.enabledModelValues,
|
|
1718
1789
|
serverHeaders: pullWake.headers
|
|
1719
1790
|
});
|
|
1720
|
-
if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY or
|
|
1791
|
+
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
1792
|
await registerBuiltinAgentTypes(this.bootstrap);
|
|
1722
1793
|
const registeredRunner = pullWake.registerRunner ? await this.registerPullWakeRunner(pullWake) : null;
|
|
1723
1794
|
this.pullWakeRunner = createPullWakeRunner({
|
|
@@ -1878,4 +1949,4 @@ async function runBuiltinAgentsEntrypoint({ env = process.env, cwd = process.cwd
|
|
|
1878
1949
|
}
|
|
1879
1950
|
|
|
1880
1951
|
//#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 };
|
|
1952
|
+
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.
|
|
3
|
+
"version": "0.4.10",
|
|
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": "
|
|
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.
|
|
52
|
+
"@electric-ax/agents-runtime": "0.3.6"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/better-sqlite3": "^7.6.13",
|