@electric-ax/agents 0.4.18 → 0.4.19
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 +88 -14
- package/dist/index.cjs +87 -13
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +88 -14
- package/package.json +3 -3
package/dist/entrypoint.js
CHANGED
|
@@ -5,7 +5,7 @@ import fs from "node:fs";
|
|
|
5
5
|
import pino from "pino";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { GOAL_SLASH_COMMAND, MOONSHOT_API_BASE_URL, MOONSHOT_PROVIDER, appendPathToUrl, buildSkillSlashCommands, commentsCollection, completeWithLowCostModel, createContextSkillLoader, createEntityRegistry, createPullWakeRunner, createRuntimeHandler, createSkillsRegistry, db, detectAvailableProviders, dispatchGoalCommand, formatTokenCount, getMoonshotApiKey, getMoonshotModel, getMoonshotModels, isGoalCommandText, parseGoalCommand, pgSync, readCodexAccessToken, registerToolProvider, unregisterToolProvider } from "@electric-ax/agents-runtime";
|
|
8
|
-
import { braveSearchTool, createBashTool, createEditTool,
|
|
8
|
+
import { braveSearchTool, createBashTool, createEditTool, createFetchUrlTool, createMarkGoalCompleteTool, createReadFileTool, createScheduleTools, createSendTool, createWebhookSourceTools, createWriteTool } from "@electric-ax/agents-runtime/tools";
|
|
9
9
|
import { chooseDefaultSandbox, isE2BAvailable, lazySandbox, remoteSandbox } from "@electric-ax/agents-runtime/sandbox";
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
import { createHash } from "node:crypto";
|
|
@@ -814,7 +814,7 @@ function createSpawnWorkerTool(ctx, modelConfig) {
|
|
|
814
814
|
|
|
815
815
|
//#endregion
|
|
816
816
|
//#region src/tools/observe-pg-sync.ts
|
|
817
|
-
function asToolResult(value) {
|
|
817
|
+
function asToolResult$1(value) {
|
|
818
818
|
return {
|
|
819
819
|
content: [{
|
|
820
820
|
type: `text`,
|
|
@@ -832,9 +832,9 @@ function createObservePgSyncTool(ctx) {
|
|
|
832
832
|
return {
|
|
833
833
|
name: `observe_pg_sync`,
|
|
834
834
|
label: `Observe Postgres Sync`,
|
|
835
|
-
description: `Observe an Electric Postgres shape stream and wake this agent when matching row changes arrive.`,
|
|
835
|
+
description: `Observe an Electric Postgres shape stream and wake this agent when matching row changes arrive. Requires the HTTP(S) URL of an Electric shape endpoint — ask the user for it if you don't know it. Registration validates the endpoint up front and fails with Electric's error if the shape can't be fetched.`,
|
|
836
836
|
parameters: Type.Object({
|
|
837
|
-
url: Type.
|
|
837
|
+
url: Type.String({ description: `HTTP(S) URL of the Electric shape endpoint, e.g. http://localhost:3000/v1/shape. Not a postgres:// connection string. Never guess this — ask the user if it hasn't been provided.` }),
|
|
838
838
|
table: Type.String({
|
|
839
839
|
minLength: 1,
|
|
840
840
|
pattern: `\\S`,
|
|
@@ -851,6 +851,7 @@ function createObservePgSyncTool(ctx) {
|
|
|
851
851
|
}),
|
|
852
852
|
execute: async (_toolCallId, params) => {
|
|
853
853
|
const args = params;
|
|
854
|
+
if (typeof args.url !== `string` || args.url.trim().length === 0) throw new Error(`url is required`);
|
|
854
855
|
if (typeof args.table !== `string` || args.table.trim().length === 0) throw new Error(`table is required`);
|
|
855
856
|
const source = pgSync({
|
|
856
857
|
url: args.url,
|
|
@@ -865,16 +866,79 @@ function createObservePgSyncTool(ctx) {
|
|
|
865
866
|
...args.wake?.ops ? { ops: args.wake.ops } : {},
|
|
866
867
|
...args.wake?.debounceMs !== void 0 ? { debounceMs: args.wake.debounceMs } : {}
|
|
867
868
|
};
|
|
868
|
-
await ctx.observe(source, { wake });
|
|
869
|
-
return
|
|
870
|
-
|
|
871
|
-
|
|
869
|
+
const handle = await ctx.observe(source, { wake });
|
|
870
|
+
if (!handle.streamUrl) throw new Error(`pg-sync observation did not return a stream URL for ${handle.sourceRef}`);
|
|
871
|
+
return asToolResult$1({
|
|
872
|
+
sourceRef: handle.sourceRef,
|
|
873
|
+
streamUrl: handle.streamUrl,
|
|
872
874
|
wake
|
|
873
875
|
});
|
|
874
876
|
}
|
|
875
877
|
};
|
|
876
878
|
}
|
|
877
879
|
|
|
880
|
+
//#endregion
|
|
881
|
+
//#region src/tools/unobserve-pg-sync.ts
|
|
882
|
+
function asToolResult(value) {
|
|
883
|
+
return {
|
|
884
|
+
content: [{
|
|
885
|
+
type: `text`,
|
|
886
|
+
text: typeof value === `string` ? value : JSON.stringify(value, null, 2)
|
|
887
|
+
}],
|
|
888
|
+
details: {}
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
function isRecord$1(value) {
|
|
892
|
+
return typeof value === `object` && value !== null && !Array.isArray(value);
|
|
893
|
+
}
|
|
894
|
+
function listPgSyncObservations(ctx) {
|
|
895
|
+
const manifests = ctx.db.collections.manifests?.toArray;
|
|
896
|
+
if (!Array.isArray(manifests)) return [];
|
|
897
|
+
const observations = [];
|
|
898
|
+
for (const entry of manifests) {
|
|
899
|
+
if (!isRecord$1(entry) || entry.kind !== `source` || entry.sourceType !== `pgSync` || typeof entry.sourceRef !== `string`) continue;
|
|
900
|
+
const config = isRecord$1(entry.config) ? entry.config : {};
|
|
901
|
+
observations.push({
|
|
902
|
+
sourceRef: entry.sourceRef,
|
|
903
|
+
...typeof config.table === `string` ? { table: config.table } : {},
|
|
904
|
+
...typeof config.url === `string` ? { url: config.url } : {},
|
|
905
|
+
...typeof entry.streamUrl === `string` ? { streamUrl: entry.streamUrl } : {}
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
return observations.sort((left, right) => left.sourceRef.localeCompare(right.sourceRef));
|
|
909
|
+
}
|
|
910
|
+
function createUnobservePgSyncTool(ctx) {
|
|
911
|
+
return {
|
|
912
|
+
name: `unobserve_pg_sync`,
|
|
913
|
+
label: `Stop Observing Postgres Sync`,
|
|
914
|
+
description: `Stop being woken by a Postgres shape stream you previously observed with observe_pg_sync. Identify the observation by its sourceRef (preferred) or table. Call with no arguments to list your active pg-sync observations. This only removes your own subscription; any other agents observing the same shape keep their stream.`,
|
|
915
|
+
parameters: Type.Object({
|
|
916
|
+
sourceRef: Type.Optional(Type.String({ description: `The sourceRef returned by observe_pg_sync. Preferred — unambiguous.` })),
|
|
917
|
+
table: Type.Optional(Type.String({ description: `The observed table name. Used only when sourceRef is not given; fails if more than one observation matches.` }))
|
|
918
|
+
}),
|
|
919
|
+
execute: async (_toolCallId, params) => {
|
|
920
|
+
const args = params;
|
|
921
|
+
const observations = listPgSyncObservations(ctx);
|
|
922
|
+
if (!args.sourceRef && !args.table) return asToolResult(observations.length > 0 ? { observations } : `You have no active pg-sync observations.`);
|
|
923
|
+
let sourceRef = args.sourceRef;
|
|
924
|
+
if (!sourceRef) {
|
|
925
|
+
const matches = observations.filter((o) => o.table === args.table);
|
|
926
|
+
if (matches.length === 0) return asToolResult(`No active pg-sync observation found for table "${args.table}".`);
|
|
927
|
+
if (matches.length > 1) return asToolResult({
|
|
928
|
+
error: `Multiple pg-sync observations match table "${args.table}"; pass a sourceRef instead.`,
|
|
929
|
+
matches
|
|
930
|
+
});
|
|
931
|
+
sourceRef = matches[0].sourceRef;
|
|
932
|
+
} else if (!observations.some((o) => o.sourceRef === sourceRef)) return asToolResult(`No active pg-sync observation found for sourceRef "${sourceRef}".`);
|
|
933
|
+
await ctx.unobserve(sourceRef);
|
|
934
|
+
return asToolResult({
|
|
935
|
+
unobserved: true,
|
|
936
|
+
sourceRef
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
|
|
878
942
|
//#endregion
|
|
879
943
|
//#region src/tools/fork.ts
|
|
880
944
|
function createForkTool(ctx) {
|
|
@@ -1306,7 +1370,7 @@ async function generateTitle(userMessage, llmCall, onFallback) {
|
|
|
1306
1370
|
}
|
|
1307
1371
|
function buildHortonSystemPrompt(workingDirectory, opts = {}) {
|
|
1308
1372
|
const docsTools = opts.hasDocsSupport ? `\n- search_electric_agents_docs: hybrid search over the built-in Electric Agents docs index` : ``;
|
|
1309
|
-
const
|
|
1373
|
+
const webhookSourceTools = opts.hasWebhookSourceTools ? `\n- list_webhook_sources: list external webhook feeds you can subscribe to, including available buckets and parameters\n- subscribe_webhook_source: subscribe yourself to one of those feeds or buckets so matching future webhooks wake you\n- list_webhook_source_subscriptions: list your active webhook source subscriptions\n- unsubscribe_webhook_source: remove one of your webhook source subscriptions by id` : ``;
|
|
1310
1374
|
const titleTool = `\n- set_title: set or rename this chat session's UI title`;
|
|
1311
1375
|
const scheduleTools = opts.hasScheduleTools ? `\n- upsert_cron_schedule: create or update a recurring cron wake for yourself. Always include payload with the concrete instruction/message you should receive when the cron fires.\n- delete_schedule: delete one of your cron or future-send schedules by stable id\n- list_schedules: list your manifest-backed cron and future-send schedules` : ``;
|
|
1312
1376
|
const skillsTools = opts.hasSkills ? `\n- use_skill: load a skill (knowledge, instructions, or a tutorial) into your context to help with the user's request\n- remove_skill: unload a skill from context when you're done with it` : ``;
|
|
@@ -1363,9 +1427,10 @@ When a user opens with a greeting ("hi", "hello", "hey", etc.) or a broad statem
|
|
|
1363
1427
|
- fetch_url: fetch and convert a URL to markdown
|
|
1364
1428
|
- spawn_worker: dispatch a subagent for an isolated task
|
|
1365
1429
|
- fork: spawn a child session that inherits this conversation's history up to the latest completed response. Same parent-ownership model as spawn_worker — when the fork's next run finishes, you'll wake with its response.
|
|
1366
|
-
- observe_pg_sync: observe an Electric Postgres sync stream and wake on matching changes
|
|
1430
|
+
- observe_pg_sync: observe an Electric Postgres sync stream and wake on matching changes (see "Observing Postgres tables")
|
|
1431
|
+
- unobserve_pg_sync: stop being woken by a pg-sync stream you previously observed (see "Observing Postgres tables")
|
|
1367
1432
|
- send: send a message to an Electric Agent/entity. To schedule future work for yourself, call send with self: true and afterMs.
|
|
1368
|
-
${
|
|
1433
|
+
${webhookSourceTools}${titleTool}${scheduleTools}${docsTools}${skillsTools}
|
|
1369
1434
|
|
|
1370
1435
|
# Working with files
|
|
1371
1436
|
- Prefer edit over write when modifying existing files.
|
|
@@ -1373,6 +1438,14 @@ ${eventSourceTools}${titleTool}${scheduleTools}${docsTools}${skillsTools}
|
|
|
1373
1438
|
- Use absolute paths or paths relative to the current working directory.
|
|
1374
1439
|
${modelGuidance}${docsGuidance}${skillsGuidance}${onboardingGuidance}${docsUrlGuidance}
|
|
1375
1440
|
|
|
1441
|
+
# Observing Postgres tables
|
|
1442
|
+
observe_pg_sync subscribes you to row changes in a Postgres table via an Electric shape stream:
|
|
1443
|
+
- The \`url\` parameter is the HTTP(S) URL of an Electric shape endpoint (e.g. \`http://localhost:3000/v1/shape\`). It is NOT a \`postgres://\` connection string and there is no default — if the user hasn't given you the endpoint URL, ask for it. Never guess or invent one.
|
|
1444
|
+
- Registration validates the endpoint by fetching the shape log first. If it fails, the error includes Electric's response or the failure reason — use it to correct the table name, where clause, or URL, or relay it to the user.
|
|
1445
|
+
- Use \`where\` and \`columns\` to narrow the shape so you only wake on changes you care about; use \`wake.ops\` to filter by operation and \`wake.debounceMs\` to batch bursts.
|
|
1446
|
+
- The observation persists across wakes — register it once, don't re-register on every wake.
|
|
1447
|
+
- To stop, call unobserve_pg_sync with the sourceRef from observe_pg_sync (or the table name). Call it with no arguments to list your active observations. This only ends your own subscription.
|
|
1448
|
+
|
|
1376
1449
|
# Risky actions
|
|
1377
1450
|
Pause and confirm with the user before:
|
|
1378
1451
|
- Destructive operations (deleting files, rm -rf, dropping data, force-pushing)
|
|
@@ -1444,6 +1517,7 @@ function createHortonTools(sandbox, ctx, readSet, opts = {}) {
|
|
|
1444
1517
|
createSpawnWorkerTool(ctx, opts.modelConfig),
|
|
1445
1518
|
createForkTool(ctx),
|
|
1446
1519
|
createObservePgSyncTool(ctx),
|
|
1520
|
+
createUnobservePgSyncTool(ctx),
|
|
1447
1521
|
createSetTitleTool(ctx),
|
|
1448
1522
|
createSendTool(ctx.send, { selfEntityUrl: ctx.entityUrl }),
|
|
1449
1523
|
...ctx.getGoal()?.status === `active` ? [createMarkGoalCompleteTool(ctx)] : [],
|
|
@@ -1584,7 +1658,7 @@ function createAssistantHandler(options) {
|
|
|
1584
1658
|
...loadedSkills.tools,
|
|
1585
1659
|
...mcp.tools()
|
|
1586
1660
|
];
|
|
1587
|
-
const
|
|
1661
|
+
const hasWebhookSourceTools = tools.some((tool) => getToolName(tool) === `list_webhook_sources`);
|
|
1588
1662
|
const hasScheduleTools = tools.some((tool) => getToolName(tool) === `upsert_cron_schedule`);
|
|
1589
1663
|
const titlePromise = !ctx.tags.title ? (async () => {
|
|
1590
1664
|
const firstUserMessage = await extractFirstUserMessage(ctx);
|
|
@@ -1686,7 +1760,7 @@ function createAssistantHandler(options) {
|
|
|
1686
1760
|
docsUrl,
|
|
1687
1761
|
modelProvider: modelConfig.provider,
|
|
1688
1762
|
modelId: String(modelConfig.model),
|
|
1689
|
-
|
|
1763
|
+
hasWebhookSourceTools,
|
|
1690
1764
|
hasScheduleTools,
|
|
1691
1765
|
...activeGoalPromptInfo && { activeGoal: activeGoalPromptInfo }
|
|
1692
1766
|
}),
|
|
@@ -1974,7 +2048,7 @@ function dedupeToolsByName(tools) {
|
|
|
1974
2048
|
}
|
|
1975
2049
|
function createBuiltinElectricTools(custom) {
|
|
1976
2050
|
return async (context) => {
|
|
1977
|
-
const builtinTools = [...
|
|
2051
|
+
const builtinTools = [...createWebhookSourceTools(context), ...createScheduleTools({
|
|
1978
2052
|
...context,
|
|
1979
2053
|
db: context.db
|
|
1980
2054
|
})];
|
package/dist/index.cjs
CHANGED
|
@@ -820,7 +820,7 @@ function createSpawnWorkerTool(ctx, modelConfig) {
|
|
|
820
820
|
|
|
821
821
|
//#endregion
|
|
822
822
|
//#region src/tools/observe-pg-sync.ts
|
|
823
|
-
function asToolResult(value) {
|
|
823
|
+
function asToolResult$1(value) {
|
|
824
824
|
return {
|
|
825
825
|
content: [{
|
|
826
826
|
type: `text`,
|
|
@@ -838,9 +838,9 @@ function createObservePgSyncTool(ctx) {
|
|
|
838
838
|
return {
|
|
839
839
|
name: `observe_pg_sync`,
|
|
840
840
|
label: `Observe Postgres Sync`,
|
|
841
|
-
description: `Observe an Electric Postgres shape stream and wake this agent when matching row changes arrive.`,
|
|
841
|
+
description: `Observe an Electric Postgres shape stream and wake this agent when matching row changes arrive. Requires the HTTP(S) URL of an Electric shape endpoint — ask the user for it if you don't know it. Registration validates the endpoint up front and fails with Electric's error if the shape can't be fetched.`,
|
|
842
842
|
parameters: __sinclair_typebox.Type.Object({
|
|
843
|
-
url: __sinclair_typebox.Type.
|
|
843
|
+
url: __sinclair_typebox.Type.String({ description: `HTTP(S) URL of the Electric shape endpoint, e.g. http://localhost:3000/v1/shape. Not a postgres:// connection string. Never guess this — ask the user if it hasn't been provided.` }),
|
|
844
844
|
table: __sinclair_typebox.Type.String({
|
|
845
845
|
minLength: 1,
|
|
846
846
|
pattern: `\\S`,
|
|
@@ -857,6 +857,7 @@ function createObservePgSyncTool(ctx) {
|
|
|
857
857
|
}),
|
|
858
858
|
execute: async (_toolCallId, params) => {
|
|
859
859
|
const args = params;
|
|
860
|
+
if (typeof args.url !== `string` || args.url.trim().length === 0) throw new Error(`url is required`);
|
|
860
861
|
if (typeof args.table !== `string` || args.table.trim().length === 0) throw new Error(`table is required`);
|
|
861
862
|
const source = (0, __electric_ax_agents_runtime.pgSync)({
|
|
862
863
|
url: args.url,
|
|
@@ -871,16 +872,79 @@ function createObservePgSyncTool(ctx) {
|
|
|
871
872
|
...args.wake?.ops ? { ops: args.wake.ops } : {},
|
|
872
873
|
...args.wake?.debounceMs !== void 0 ? { debounceMs: args.wake.debounceMs } : {}
|
|
873
874
|
};
|
|
874
|
-
await ctx.observe(source, { wake });
|
|
875
|
-
return
|
|
876
|
-
|
|
877
|
-
|
|
875
|
+
const handle = await ctx.observe(source, { wake });
|
|
876
|
+
if (!handle.streamUrl) throw new Error(`pg-sync observation did not return a stream URL for ${handle.sourceRef}`);
|
|
877
|
+
return asToolResult$1({
|
|
878
|
+
sourceRef: handle.sourceRef,
|
|
879
|
+
streamUrl: handle.streamUrl,
|
|
878
880
|
wake
|
|
879
881
|
});
|
|
880
882
|
}
|
|
881
883
|
};
|
|
882
884
|
}
|
|
883
885
|
|
|
886
|
+
//#endregion
|
|
887
|
+
//#region src/tools/unobserve-pg-sync.ts
|
|
888
|
+
function asToolResult(value) {
|
|
889
|
+
return {
|
|
890
|
+
content: [{
|
|
891
|
+
type: `text`,
|
|
892
|
+
text: typeof value === `string` ? value : JSON.stringify(value, null, 2)
|
|
893
|
+
}],
|
|
894
|
+
details: {}
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
function isRecord$1(value) {
|
|
898
|
+
return typeof value === `object` && value !== null && !Array.isArray(value);
|
|
899
|
+
}
|
|
900
|
+
function listPgSyncObservations(ctx) {
|
|
901
|
+
const manifests = ctx.db.collections.manifests?.toArray;
|
|
902
|
+
if (!Array.isArray(manifests)) return [];
|
|
903
|
+
const observations = [];
|
|
904
|
+
for (const entry of manifests) {
|
|
905
|
+
if (!isRecord$1(entry) || entry.kind !== `source` || entry.sourceType !== `pgSync` || typeof entry.sourceRef !== `string`) continue;
|
|
906
|
+
const config = isRecord$1(entry.config) ? entry.config : {};
|
|
907
|
+
observations.push({
|
|
908
|
+
sourceRef: entry.sourceRef,
|
|
909
|
+
...typeof config.table === `string` ? { table: config.table } : {},
|
|
910
|
+
...typeof config.url === `string` ? { url: config.url } : {},
|
|
911
|
+
...typeof entry.streamUrl === `string` ? { streamUrl: entry.streamUrl } : {}
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
return observations.sort((left, right) => left.sourceRef.localeCompare(right.sourceRef));
|
|
915
|
+
}
|
|
916
|
+
function createUnobservePgSyncTool(ctx) {
|
|
917
|
+
return {
|
|
918
|
+
name: `unobserve_pg_sync`,
|
|
919
|
+
label: `Stop Observing Postgres Sync`,
|
|
920
|
+
description: `Stop being woken by a Postgres shape stream you previously observed with observe_pg_sync. Identify the observation by its sourceRef (preferred) or table. Call with no arguments to list your active pg-sync observations. This only removes your own subscription; any other agents observing the same shape keep their stream.`,
|
|
921
|
+
parameters: __sinclair_typebox.Type.Object({
|
|
922
|
+
sourceRef: __sinclair_typebox.Type.Optional(__sinclair_typebox.Type.String({ description: `The sourceRef returned by observe_pg_sync. Preferred — unambiguous.` })),
|
|
923
|
+
table: __sinclair_typebox.Type.Optional(__sinclair_typebox.Type.String({ description: `The observed table name. Used only when sourceRef is not given; fails if more than one observation matches.` }))
|
|
924
|
+
}),
|
|
925
|
+
execute: async (_toolCallId, params) => {
|
|
926
|
+
const args = params;
|
|
927
|
+
const observations = listPgSyncObservations(ctx);
|
|
928
|
+
if (!args.sourceRef && !args.table) return asToolResult(observations.length > 0 ? { observations } : `You have no active pg-sync observations.`);
|
|
929
|
+
let sourceRef = args.sourceRef;
|
|
930
|
+
if (!sourceRef) {
|
|
931
|
+
const matches = observations.filter((o) => o.table === args.table);
|
|
932
|
+
if (matches.length === 0) return asToolResult(`No active pg-sync observation found for table "${args.table}".`);
|
|
933
|
+
if (matches.length > 1) return asToolResult({
|
|
934
|
+
error: `Multiple pg-sync observations match table "${args.table}"; pass a sourceRef instead.`,
|
|
935
|
+
matches
|
|
936
|
+
});
|
|
937
|
+
sourceRef = matches[0].sourceRef;
|
|
938
|
+
} else if (!observations.some((o) => o.sourceRef === sourceRef)) return asToolResult(`No active pg-sync observation found for sourceRef "${sourceRef}".`);
|
|
939
|
+
await ctx.unobserve(sourceRef);
|
|
940
|
+
return asToolResult({
|
|
941
|
+
unobserved: true,
|
|
942
|
+
sourceRef
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
|
|
884
948
|
//#endregion
|
|
885
949
|
//#region src/tools/fork.ts
|
|
886
950
|
function createForkTool(ctx) {
|
|
@@ -1313,7 +1377,7 @@ async function generateTitle(userMessage, llmCall, onFallback) {
|
|
|
1313
1377
|
}
|
|
1314
1378
|
function buildHortonSystemPrompt(workingDirectory, opts = {}) {
|
|
1315
1379
|
const docsTools = opts.hasDocsSupport ? `\n- search_electric_agents_docs: hybrid search over the built-in Electric Agents docs index` : ``;
|
|
1316
|
-
const
|
|
1380
|
+
const webhookSourceTools = opts.hasWebhookSourceTools ? `\n- list_webhook_sources: list external webhook feeds you can subscribe to, including available buckets and parameters\n- subscribe_webhook_source: subscribe yourself to one of those feeds or buckets so matching future webhooks wake you\n- list_webhook_source_subscriptions: list your active webhook source subscriptions\n- unsubscribe_webhook_source: remove one of your webhook source subscriptions by id` : ``;
|
|
1317
1381
|
const titleTool = `\n- set_title: set or rename this chat session's UI title`;
|
|
1318
1382
|
const scheduleTools = opts.hasScheduleTools ? `\n- upsert_cron_schedule: create or update a recurring cron wake for yourself. Always include payload with the concrete instruction/message you should receive when the cron fires.\n- delete_schedule: delete one of your cron or future-send schedules by stable id\n- list_schedules: list your manifest-backed cron and future-send schedules` : ``;
|
|
1319
1383
|
const skillsTools = opts.hasSkills ? `\n- use_skill: load a skill (knowledge, instructions, or a tutorial) into your context to help with the user's request\n- remove_skill: unload a skill from context when you're done with it` : ``;
|
|
@@ -1370,9 +1434,10 @@ When a user opens with a greeting ("hi", "hello", "hey", etc.) or a broad statem
|
|
|
1370
1434
|
- fetch_url: fetch and convert a URL to markdown
|
|
1371
1435
|
- spawn_worker: dispatch a subagent for an isolated task
|
|
1372
1436
|
- fork: spawn a child session that inherits this conversation's history up to the latest completed response. Same parent-ownership model as spawn_worker — when the fork's next run finishes, you'll wake with its response.
|
|
1373
|
-
- observe_pg_sync: observe an Electric Postgres sync stream and wake on matching changes
|
|
1437
|
+
- observe_pg_sync: observe an Electric Postgres sync stream and wake on matching changes (see "Observing Postgres tables")
|
|
1438
|
+
- unobserve_pg_sync: stop being woken by a pg-sync stream you previously observed (see "Observing Postgres tables")
|
|
1374
1439
|
- send: send a message to an Electric Agent/entity. To schedule future work for yourself, call send with self: true and afterMs.
|
|
1375
|
-
${
|
|
1440
|
+
${webhookSourceTools}${titleTool}${scheduleTools}${docsTools}${skillsTools}
|
|
1376
1441
|
|
|
1377
1442
|
# Working with files
|
|
1378
1443
|
- Prefer edit over write when modifying existing files.
|
|
@@ -1380,6 +1445,14 @@ ${eventSourceTools}${titleTool}${scheduleTools}${docsTools}${skillsTools}
|
|
|
1380
1445
|
- Use absolute paths or paths relative to the current working directory.
|
|
1381
1446
|
${modelGuidance}${docsGuidance}${skillsGuidance}${onboardingGuidance}${docsUrlGuidance}
|
|
1382
1447
|
|
|
1448
|
+
# Observing Postgres tables
|
|
1449
|
+
observe_pg_sync subscribes you to row changes in a Postgres table via an Electric shape stream:
|
|
1450
|
+
- The \`url\` parameter is the HTTP(S) URL of an Electric shape endpoint (e.g. \`http://localhost:3000/v1/shape\`). It is NOT a \`postgres://\` connection string and there is no default — if the user hasn't given you the endpoint URL, ask for it. Never guess or invent one.
|
|
1451
|
+
- Registration validates the endpoint by fetching the shape log first. If it fails, the error includes Electric's response or the failure reason — use it to correct the table name, where clause, or URL, or relay it to the user.
|
|
1452
|
+
- Use \`where\` and \`columns\` to narrow the shape so you only wake on changes you care about; use \`wake.ops\` to filter by operation and \`wake.debounceMs\` to batch bursts.
|
|
1453
|
+
- The observation persists across wakes — register it once, don't re-register on every wake.
|
|
1454
|
+
- To stop, call unobserve_pg_sync with the sourceRef from observe_pg_sync (or the table name). Call it with no arguments to list your active observations. This only ends your own subscription.
|
|
1455
|
+
|
|
1383
1456
|
# Risky actions
|
|
1384
1457
|
Pause and confirm with the user before:
|
|
1385
1458
|
- Destructive operations (deleting files, rm -rf, dropping data, force-pushing)
|
|
@@ -1451,6 +1524,7 @@ function createHortonTools(sandbox, ctx, readSet, opts = {}) {
|
|
|
1451
1524
|
createSpawnWorkerTool(ctx, opts.modelConfig),
|
|
1452
1525
|
createForkTool(ctx),
|
|
1453
1526
|
createObservePgSyncTool(ctx),
|
|
1527
|
+
createUnobservePgSyncTool(ctx),
|
|
1454
1528
|
createSetTitleTool(ctx),
|
|
1455
1529
|
(0, __electric_ax_agents_runtime_tools.createSendTool)(ctx.send, { selfEntityUrl: ctx.entityUrl }),
|
|
1456
1530
|
...ctx.getGoal()?.status === `active` ? [(0, __electric_ax_agents_runtime_tools.createMarkGoalCompleteTool)(ctx)] : [],
|
|
@@ -1591,7 +1665,7 @@ function createAssistantHandler(options) {
|
|
|
1591
1665
|
...loadedSkills.tools,
|
|
1592
1666
|
...__electric_ax_agents_mcp.mcp.tools()
|
|
1593
1667
|
];
|
|
1594
|
-
const
|
|
1668
|
+
const hasWebhookSourceTools = tools.some((tool) => getToolName(tool) === `list_webhook_sources`);
|
|
1595
1669
|
const hasScheduleTools = tools.some((tool) => getToolName(tool) === `upsert_cron_schedule`);
|
|
1596
1670
|
const titlePromise = !ctx.tags.title ? (async () => {
|
|
1597
1671
|
const firstUserMessage = await extractFirstUserMessage(ctx);
|
|
@@ -1693,7 +1767,7 @@ function createAssistantHandler(options) {
|
|
|
1693
1767
|
docsUrl,
|
|
1694
1768
|
modelProvider: modelConfig.provider,
|
|
1695
1769
|
modelId: String(modelConfig.model),
|
|
1696
|
-
|
|
1770
|
+
hasWebhookSourceTools,
|
|
1697
1771
|
hasScheduleTools,
|
|
1698
1772
|
...activeGoalPromptInfo && { activeGoal: activeGoalPromptInfo }
|
|
1699
1773
|
}),
|
|
@@ -1982,7 +2056,7 @@ function dedupeToolsByName(tools) {
|
|
|
1982
2056
|
}
|
|
1983
2057
|
function createBuiltinElectricTools(custom) {
|
|
1984
2058
|
return async (context) => {
|
|
1985
|
-
const builtinTools = [...(0, __electric_ax_agents_runtime_tools.
|
|
2059
|
+
const builtinTools = [...(0, __electric_ax_agents_runtime_tools.createWebhookSourceTools)(context), ...(0, __electric_ax_agents_runtime_tools.createScheduleTools)({
|
|
1986
2060
|
...context,
|
|
1987
2061
|
db: context.db
|
|
1988
2062
|
})];
|
package/dist/index.d.cts
CHANGED
|
@@ -239,7 +239,7 @@ interface ActiveGoalPromptInfo {
|
|
|
239
239
|
}
|
|
240
240
|
declare function buildHortonSystemPrompt(workingDirectory: string, opts?: {
|
|
241
241
|
hasDocsSupport?: boolean;
|
|
242
|
-
|
|
242
|
+
hasWebhookSourceTools?: boolean;
|
|
243
243
|
hasScheduleTools?: boolean;
|
|
244
244
|
hasSkills?: boolean;
|
|
245
245
|
docsUrl?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -239,7 +239,7 @@ interface ActiveGoalPromptInfo {
|
|
|
239
239
|
}
|
|
240
240
|
declare function buildHortonSystemPrompt(workingDirectory: string, opts?: {
|
|
241
241
|
hasDocsSupport?: boolean;
|
|
242
|
-
|
|
242
|
+
hasWebhookSourceTools?: boolean;
|
|
243
243
|
hasScheduleTools?: boolean;
|
|
244
244
|
hasSkills?: boolean;
|
|
245
245
|
docsUrl?: string;
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { mergeElectricPrincipalHeader } from "./server-headers-KD5yHFYT.js";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { GOAL_SLASH_COMMAND, MOONSHOT_API_BASE_URL, MOONSHOT_PROVIDER, appendPathToUrl, buildSkillSlashCommands, commentsCollection, completeWithLowCostModel, createContextSkillLoader, createEntityRegistry, createPullWakeRunner, createRuntimeHandler, createSkillsRegistry, db, detectAvailableProviders, dispatchGoalCommand, formatTokenCount, getMoonshotApiKey, getMoonshotModel, getMoonshotModels, isGoalCommandText, parseGoalCommand, pgSync, readCodexAccessToken, registerToolProvider, unregisterToolProvider } from "@electric-ax/agents-runtime";
|
|
5
|
-
import { braveSearchTool, braveSearchTool as braveSearchTool$1, createBashTool, createEditTool,
|
|
5
|
+
import { braveSearchTool, braveSearchTool as braveSearchTool$1, createBashTool, createEditTool, createFetchUrlTool, createMarkGoalCompleteTool, createReadFileTool, createScheduleTools, createSendTool, createWebhookSourceTools, createWriteTool } from "@electric-ax/agents-runtime/tools";
|
|
6
6
|
import { chooseDefaultSandbox, isE2BAvailable, lazySandbox, remoteSandbox } from "@electric-ax/agents-runtime/sandbox";
|
|
7
7
|
import fsSync from "node:fs";
|
|
8
8
|
import pino from "pino";
|
|
@@ -796,7 +796,7 @@ function createSpawnWorkerTool(ctx, modelConfig) {
|
|
|
796
796
|
|
|
797
797
|
//#endregion
|
|
798
798
|
//#region src/tools/observe-pg-sync.ts
|
|
799
|
-
function asToolResult(value) {
|
|
799
|
+
function asToolResult$1(value) {
|
|
800
800
|
return {
|
|
801
801
|
content: [{
|
|
802
802
|
type: `text`,
|
|
@@ -814,9 +814,9 @@ function createObservePgSyncTool(ctx) {
|
|
|
814
814
|
return {
|
|
815
815
|
name: `observe_pg_sync`,
|
|
816
816
|
label: `Observe Postgres Sync`,
|
|
817
|
-
description: `Observe an Electric Postgres shape stream and wake this agent when matching row changes arrive.`,
|
|
817
|
+
description: `Observe an Electric Postgres shape stream and wake this agent when matching row changes arrive. Requires the HTTP(S) URL of an Electric shape endpoint — ask the user for it if you don't know it. Registration validates the endpoint up front and fails with Electric's error if the shape can't be fetched.`,
|
|
818
818
|
parameters: Type.Object({
|
|
819
|
-
url: Type.
|
|
819
|
+
url: Type.String({ description: `HTTP(S) URL of the Electric shape endpoint, e.g. http://localhost:3000/v1/shape. Not a postgres:// connection string. Never guess this — ask the user if it hasn't been provided.` }),
|
|
820
820
|
table: Type.String({
|
|
821
821
|
minLength: 1,
|
|
822
822
|
pattern: `\\S`,
|
|
@@ -833,6 +833,7 @@ function createObservePgSyncTool(ctx) {
|
|
|
833
833
|
}),
|
|
834
834
|
execute: async (_toolCallId, params) => {
|
|
835
835
|
const args = params;
|
|
836
|
+
if (typeof args.url !== `string` || args.url.trim().length === 0) throw new Error(`url is required`);
|
|
836
837
|
if (typeof args.table !== `string` || args.table.trim().length === 0) throw new Error(`table is required`);
|
|
837
838
|
const source = pgSync({
|
|
838
839
|
url: args.url,
|
|
@@ -847,16 +848,79 @@ function createObservePgSyncTool(ctx) {
|
|
|
847
848
|
...args.wake?.ops ? { ops: args.wake.ops } : {},
|
|
848
849
|
...args.wake?.debounceMs !== void 0 ? { debounceMs: args.wake.debounceMs } : {}
|
|
849
850
|
};
|
|
850
|
-
await ctx.observe(source, { wake });
|
|
851
|
-
return
|
|
852
|
-
|
|
853
|
-
|
|
851
|
+
const handle = await ctx.observe(source, { wake });
|
|
852
|
+
if (!handle.streamUrl) throw new Error(`pg-sync observation did not return a stream URL for ${handle.sourceRef}`);
|
|
853
|
+
return asToolResult$1({
|
|
854
|
+
sourceRef: handle.sourceRef,
|
|
855
|
+
streamUrl: handle.streamUrl,
|
|
854
856
|
wake
|
|
855
857
|
});
|
|
856
858
|
}
|
|
857
859
|
};
|
|
858
860
|
}
|
|
859
861
|
|
|
862
|
+
//#endregion
|
|
863
|
+
//#region src/tools/unobserve-pg-sync.ts
|
|
864
|
+
function asToolResult(value) {
|
|
865
|
+
return {
|
|
866
|
+
content: [{
|
|
867
|
+
type: `text`,
|
|
868
|
+
text: typeof value === `string` ? value : JSON.stringify(value, null, 2)
|
|
869
|
+
}],
|
|
870
|
+
details: {}
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
function isRecord$1(value) {
|
|
874
|
+
return typeof value === `object` && value !== null && !Array.isArray(value);
|
|
875
|
+
}
|
|
876
|
+
function listPgSyncObservations(ctx) {
|
|
877
|
+
const manifests = ctx.db.collections.manifests?.toArray;
|
|
878
|
+
if (!Array.isArray(manifests)) return [];
|
|
879
|
+
const observations = [];
|
|
880
|
+
for (const entry of manifests) {
|
|
881
|
+
if (!isRecord$1(entry) || entry.kind !== `source` || entry.sourceType !== `pgSync` || typeof entry.sourceRef !== `string`) continue;
|
|
882
|
+
const config = isRecord$1(entry.config) ? entry.config : {};
|
|
883
|
+
observations.push({
|
|
884
|
+
sourceRef: entry.sourceRef,
|
|
885
|
+
...typeof config.table === `string` ? { table: config.table } : {},
|
|
886
|
+
...typeof config.url === `string` ? { url: config.url } : {},
|
|
887
|
+
...typeof entry.streamUrl === `string` ? { streamUrl: entry.streamUrl } : {}
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
return observations.sort((left, right) => left.sourceRef.localeCompare(right.sourceRef));
|
|
891
|
+
}
|
|
892
|
+
function createUnobservePgSyncTool(ctx) {
|
|
893
|
+
return {
|
|
894
|
+
name: `unobserve_pg_sync`,
|
|
895
|
+
label: `Stop Observing Postgres Sync`,
|
|
896
|
+
description: `Stop being woken by a Postgres shape stream you previously observed with observe_pg_sync. Identify the observation by its sourceRef (preferred) or table. Call with no arguments to list your active pg-sync observations. This only removes your own subscription; any other agents observing the same shape keep their stream.`,
|
|
897
|
+
parameters: Type.Object({
|
|
898
|
+
sourceRef: Type.Optional(Type.String({ description: `The sourceRef returned by observe_pg_sync. Preferred — unambiguous.` })),
|
|
899
|
+
table: Type.Optional(Type.String({ description: `The observed table name. Used only when sourceRef is not given; fails if more than one observation matches.` }))
|
|
900
|
+
}),
|
|
901
|
+
execute: async (_toolCallId, params) => {
|
|
902
|
+
const args = params;
|
|
903
|
+
const observations = listPgSyncObservations(ctx);
|
|
904
|
+
if (!args.sourceRef && !args.table) return asToolResult(observations.length > 0 ? { observations } : `You have no active pg-sync observations.`);
|
|
905
|
+
let sourceRef = args.sourceRef;
|
|
906
|
+
if (!sourceRef) {
|
|
907
|
+
const matches = observations.filter((o) => o.table === args.table);
|
|
908
|
+
if (matches.length === 0) return asToolResult(`No active pg-sync observation found for table "${args.table}".`);
|
|
909
|
+
if (matches.length > 1) return asToolResult({
|
|
910
|
+
error: `Multiple pg-sync observations match table "${args.table}"; pass a sourceRef instead.`,
|
|
911
|
+
matches
|
|
912
|
+
});
|
|
913
|
+
sourceRef = matches[0].sourceRef;
|
|
914
|
+
} else if (!observations.some((o) => o.sourceRef === sourceRef)) return asToolResult(`No active pg-sync observation found for sourceRef "${sourceRef}".`);
|
|
915
|
+
await ctx.unobserve(sourceRef);
|
|
916
|
+
return asToolResult({
|
|
917
|
+
unobserved: true,
|
|
918
|
+
sourceRef
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
|
|
860
924
|
//#endregion
|
|
861
925
|
//#region src/tools/fork.ts
|
|
862
926
|
function createForkTool(ctx) {
|
|
@@ -1289,7 +1353,7 @@ async function generateTitle(userMessage, llmCall, onFallback) {
|
|
|
1289
1353
|
}
|
|
1290
1354
|
function buildHortonSystemPrompt(workingDirectory, opts = {}) {
|
|
1291
1355
|
const docsTools = opts.hasDocsSupport ? `\n- search_electric_agents_docs: hybrid search over the built-in Electric Agents docs index` : ``;
|
|
1292
|
-
const
|
|
1356
|
+
const webhookSourceTools = opts.hasWebhookSourceTools ? `\n- list_webhook_sources: list external webhook feeds you can subscribe to, including available buckets and parameters\n- subscribe_webhook_source: subscribe yourself to one of those feeds or buckets so matching future webhooks wake you\n- list_webhook_source_subscriptions: list your active webhook source subscriptions\n- unsubscribe_webhook_source: remove one of your webhook source subscriptions by id` : ``;
|
|
1293
1357
|
const titleTool = `\n- set_title: set or rename this chat session's UI title`;
|
|
1294
1358
|
const scheduleTools = opts.hasScheduleTools ? `\n- upsert_cron_schedule: create or update a recurring cron wake for yourself. Always include payload with the concrete instruction/message you should receive when the cron fires.\n- delete_schedule: delete one of your cron or future-send schedules by stable id\n- list_schedules: list your manifest-backed cron and future-send schedules` : ``;
|
|
1295
1359
|
const skillsTools = opts.hasSkills ? `\n- use_skill: load a skill (knowledge, instructions, or a tutorial) into your context to help with the user's request\n- remove_skill: unload a skill from context when you're done with it` : ``;
|
|
@@ -1346,9 +1410,10 @@ When a user opens with a greeting ("hi", "hello", "hey", etc.) or a broad statem
|
|
|
1346
1410
|
- fetch_url: fetch and convert a URL to markdown
|
|
1347
1411
|
- spawn_worker: dispatch a subagent for an isolated task
|
|
1348
1412
|
- fork: spawn a child session that inherits this conversation's history up to the latest completed response. Same parent-ownership model as spawn_worker — when the fork's next run finishes, you'll wake with its response.
|
|
1349
|
-
- observe_pg_sync: observe an Electric Postgres sync stream and wake on matching changes
|
|
1413
|
+
- observe_pg_sync: observe an Electric Postgres sync stream and wake on matching changes (see "Observing Postgres tables")
|
|
1414
|
+
- unobserve_pg_sync: stop being woken by a pg-sync stream you previously observed (see "Observing Postgres tables")
|
|
1350
1415
|
- send: send a message to an Electric Agent/entity. To schedule future work for yourself, call send with self: true and afterMs.
|
|
1351
|
-
${
|
|
1416
|
+
${webhookSourceTools}${titleTool}${scheduleTools}${docsTools}${skillsTools}
|
|
1352
1417
|
|
|
1353
1418
|
# Working with files
|
|
1354
1419
|
- Prefer edit over write when modifying existing files.
|
|
@@ -1356,6 +1421,14 @@ ${eventSourceTools}${titleTool}${scheduleTools}${docsTools}${skillsTools}
|
|
|
1356
1421
|
- Use absolute paths or paths relative to the current working directory.
|
|
1357
1422
|
${modelGuidance}${docsGuidance}${skillsGuidance}${onboardingGuidance}${docsUrlGuidance}
|
|
1358
1423
|
|
|
1424
|
+
# Observing Postgres tables
|
|
1425
|
+
observe_pg_sync subscribes you to row changes in a Postgres table via an Electric shape stream:
|
|
1426
|
+
- The \`url\` parameter is the HTTP(S) URL of an Electric shape endpoint (e.g. \`http://localhost:3000/v1/shape\`). It is NOT a \`postgres://\` connection string and there is no default — if the user hasn't given you the endpoint URL, ask for it. Never guess or invent one.
|
|
1427
|
+
- Registration validates the endpoint by fetching the shape log first. If it fails, the error includes Electric's response or the failure reason — use it to correct the table name, where clause, or URL, or relay it to the user.
|
|
1428
|
+
- Use \`where\` and \`columns\` to narrow the shape so you only wake on changes you care about; use \`wake.ops\` to filter by operation and \`wake.debounceMs\` to batch bursts.
|
|
1429
|
+
- The observation persists across wakes — register it once, don't re-register on every wake.
|
|
1430
|
+
- To stop, call unobserve_pg_sync with the sourceRef from observe_pg_sync (or the table name). Call it with no arguments to list your active observations. This only ends your own subscription.
|
|
1431
|
+
|
|
1359
1432
|
# Risky actions
|
|
1360
1433
|
Pause and confirm with the user before:
|
|
1361
1434
|
- Destructive operations (deleting files, rm -rf, dropping data, force-pushing)
|
|
@@ -1427,6 +1500,7 @@ function createHortonTools(sandbox, ctx, readSet, opts = {}) {
|
|
|
1427
1500
|
createSpawnWorkerTool(ctx, opts.modelConfig),
|
|
1428
1501
|
createForkTool(ctx),
|
|
1429
1502
|
createObservePgSyncTool(ctx),
|
|
1503
|
+
createUnobservePgSyncTool(ctx),
|
|
1430
1504
|
createSetTitleTool(ctx),
|
|
1431
1505
|
createSendTool(ctx.send, { selfEntityUrl: ctx.entityUrl }),
|
|
1432
1506
|
...ctx.getGoal()?.status === `active` ? [createMarkGoalCompleteTool(ctx)] : [],
|
|
@@ -1567,7 +1641,7 @@ function createAssistantHandler(options) {
|
|
|
1567
1641
|
...loadedSkills.tools,
|
|
1568
1642
|
...mcp.tools()
|
|
1569
1643
|
];
|
|
1570
|
-
const
|
|
1644
|
+
const hasWebhookSourceTools = tools.some((tool) => getToolName(tool) === `list_webhook_sources`);
|
|
1571
1645
|
const hasScheduleTools = tools.some((tool) => getToolName(tool) === `upsert_cron_schedule`);
|
|
1572
1646
|
const titlePromise = !ctx.tags.title ? (async () => {
|
|
1573
1647
|
const firstUserMessage = await extractFirstUserMessage(ctx);
|
|
@@ -1669,7 +1743,7 @@ function createAssistantHandler(options) {
|
|
|
1669
1743
|
docsUrl,
|
|
1670
1744
|
modelProvider: modelConfig.provider,
|
|
1671
1745
|
modelId: String(modelConfig.model),
|
|
1672
|
-
|
|
1746
|
+
hasWebhookSourceTools,
|
|
1673
1747
|
hasScheduleTools,
|
|
1674
1748
|
...activeGoalPromptInfo && { activeGoal: activeGoalPromptInfo }
|
|
1675
1749
|
}),
|
|
@@ -1958,7 +2032,7 @@ function dedupeToolsByName(tools) {
|
|
|
1958
2032
|
}
|
|
1959
2033
|
function createBuiltinElectricTools(custom) {
|
|
1960
2034
|
return async (context) => {
|
|
1961
|
-
const builtinTools = [...
|
|
2035
|
+
const builtinTools = [...createWebhookSourceTools(context), ...createScheduleTools({
|
|
1962
2036
|
...context,
|
|
1963
2037
|
db: context.db
|
|
1964
2038
|
})];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@electric-ax/agents",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.19",
|
|
4
4
|
"description": "Built-in Electric Agents runtimes such as Horton and worker",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"sqlite-vec": "^0.1.9",
|
|
50
50
|
"undici": "^7.24.7",
|
|
51
51
|
"zod": "^4.3.6",
|
|
52
|
-
"@electric-ax/agents-
|
|
53
|
-
"@electric-ax/agents-
|
|
52
|
+
"@electric-ax/agents-runtime": "0.4.1",
|
|
53
|
+
"@electric-ax/agents-mcp": "0.2.3"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/better-sqlite3": "^7.6.13",
|