@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.
- package/dist/entrypoint.js +142 -52
- package/dist/index.cjs +143 -51
- package/dist/index.d.cts +12 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +143 -53
- 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";
|
|
@@ -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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
73
|
+
getLogger().debug(msg);
|
|
55
74
|
},
|
|
56
75
|
info(...args) {
|
|
57
76
|
const { msg } = formatArgs(args);
|
|
58
|
-
|
|
77
|
+
getLogger().info(msg);
|
|
59
78
|
},
|
|
60
79
|
warn(...args) {
|
|
61
80
|
const { err, msg } = formatArgs(args);
|
|
62
|
-
if (err)
|
|
63
|
-
else
|
|
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)
|
|
68
|
-
else
|
|
86
|
+
if (err) getLogger().error({ err }, msg);
|
|
87
|
+
else getLogger().error(msg);
|
|
69
88
|
},
|
|
70
89
|
event(obj, msg) {
|
|
71
|
-
|
|
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
|
|
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
|
-
|
|
838
|
-
|
|
839
|
-
|
|
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: `${
|
|
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
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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({
|
|
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
|
|
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
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
97
|
+
getLogger().debug(msg);
|
|
79
98
|
},
|
|
80
99
|
info(...args) {
|
|
81
100
|
const { msg } = formatArgs(args);
|
|
82
|
-
|
|
101
|
+
getLogger().info(msg);
|
|
83
102
|
},
|
|
84
103
|
warn(...args) {
|
|
85
104
|
const { err, msg } = formatArgs(args);
|
|
86
|
-
if (err)
|
|
87
|
-
else
|
|
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)
|
|
92
|
-
else
|
|
110
|
+
if (err) getLogger().error({ err }, msg);
|
|
111
|
+
else getLogger().error(msg);
|
|
93
112
|
},
|
|
94
113
|
event(obj, msg) {
|
|
95
|
-
|
|
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
|
|
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
|
-
|
|
862
|
-
|
|
863
|
-
|
|
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: `${
|
|
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
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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({
|
|
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
|
|
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
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
73
|
+
getLogger().debug(msg);
|
|
55
74
|
},
|
|
56
75
|
info(...args) {
|
|
57
76
|
const { msg } = formatArgs(args);
|
|
58
|
-
|
|
77
|
+
getLogger().info(msg);
|
|
59
78
|
},
|
|
60
79
|
warn(...args) {
|
|
61
80
|
const { err, msg } = formatArgs(args);
|
|
62
|
-
if (err)
|
|
63
|
-
else
|
|
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)
|
|
68
|
-
else
|
|
86
|
+
if (err) getLogger().error({ err }, msg);
|
|
87
|
+
else getLogger().error(msg);
|
|
69
88
|
},
|
|
70
89
|
event(obj, msg) {
|
|
71
|
-
|
|
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
|
|
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
|
-
|
|
838
|
-
|
|
839
|
-
|
|
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: `${
|
|
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
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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({
|
|
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
|
|
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
|
|
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.
|
|
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": "
|
|
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.7"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/better-sqlite3": "^7.6.13",
|