@electric-ax/agents 0.4.17 → 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 +269 -44
- package/dist/index.cjs +268 -42
- package/dist/index.d.cts +76 -32
- package/dist/index.d.ts +76 -32
- package/dist/index.js +270 -45
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,36 @@ import { AgentTool as AgentTool$1, StreamFn } from "@mariozechner/pi-agent-core"
|
|
|
6
6
|
import { IncomingMessage, ServerResponse } from "node:http";
|
|
7
7
|
import { ChangeEvent } from "@durable-streams/state";
|
|
8
8
|
|
|
9
|
+
//#region src/model-catalog.d.ts
|
|
10
|
+
type BuiltinModelProvider = AvailableProvider;
|
|
11
|
+
type BuiltinModelInput = `text` | `image`;
|
|
12
|
+
interface BuiltinModelChoice {
|
|
13
|
+
provider: BuiltinModelProvider;
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
value: string;
|
|
17
|
+
reasoning: boolean;
|
|
18
|
+
input: Array<BuiltinModelInput>;
|
|
19
|
+
}
|
|
20
|
+
interface BuiltinModelCatalog {
|
|
21
|
+
choices: Array<BuiltinModelChoice>;
|
|
22
|
+
defaultChoice: BuiltinModelChoice;
|
|
23
|
+
}
|
|
24
|
+
interface BuiltinModelCatalogOptions {
|
|
25
|
+
allowMockFallback?: boolean;
|
|
26
|
+
enabledModelValues?: ReadonlyArray<string> | null;
|
|
27
|
+
}
|
|
28
|
+
declare const REASONING_EFFORT_VALUES: readonly ["auto", "minimal", "low", "medium", "high"];
|
|
29
|
+
type BuiltinReasoningEffort = (typeof REASONING_EFFORT_VALUES)[number];
|
|
30
|
+
type ExplicitReasoningEffort = Exclude<BuiltinReasoningEffort, `auto`>;
|
|
31
|
+
type BuiltinAgentModelConfig = Pick<AgentConfig, `model` | `provider` | `onPayload` | `getApiKey`> & {
|
|
32
|
+
reasoningEffort?: ExplicitReasoningEffort;
|
|
33
|
+
};
|
|
34
|
+
declare function builtinModelProviderLabel(provider: BuiltinModelProvider): string;
|
|
35
|
+
declare function listBuiltinModelChoices(providers: ReadonlyArray<BuiltinModelProvider>): Array<BuiltinModelChoice>;
|
|
36
|
+
declare function resolveBuiltinModelConfig(catalog: BuiltinModelCatalog, args: Readonly<Record<string, unknown>>): BuiltinAgentModelConfig;
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
9
39
|
//#region src/bootstrap.d.ts
|
|
10
40
|
declare const DEFAULT_BUILTIN_AGENT_HANDLER_PATH = "/_electric/builtin-agent-handler";
|
|
11
41
|
interface AgentHandlerResult {
|
|
@@ -21,8 +51,38 @@ interface AgentHandlerResult {
|
|
|
21
51
|
* die with the process, which would leave containers running.
|
|
22
52
|
*/
|
|
23
53
|
shutdownSandboxes: (() => Promise<void>) | null;
|
|
54
|
+
/**
|
|
55
|
+
* Model catalog the built-in agents resolve `model` args against — lets
|
|
56
|
+
* embedders register sibling agent types with the same model resolution.
|
|
57
|
+
*/
|
|
58
|
+
modelCatalog: BuiltinModelCatalog;
|
|
24
59
|
}
|
|
25
60
|
type BuiltinElectricToolsFactory = NonNullable<ProcessWakeConfig[`createElectricTools`]>;
|
|
61
|
+
/** Mount spec mirroring `DockerSandboxOpts['extraMounts']` items. */
|
|
62
|
+
interface BuiltinDockerSandboxMount {
|
|
63
|
+
hostPath: string;
|
|
64
|
+
containerPath: string;
|
|
65
|
+
readOnly?: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Embedder customization for the built-in `docker` sandbox profile.
|
|
69
|
+
* Threads straight into `dockerSandbox()` (which already supports these);
|
|
70
|
+
* custom `extraMounts` are appended after the working-directory mount.
|
|
71
|
+
* These are embedder/operator-trust inputs: `extraMounts` is subject to the
|
|
72
|
+
* runtime's docker-socket guard, and `env` is passed verbatim into the
|
|
73
|
+
* container.
|
|
74
|
+
*
|
|
75
|
+
* Note: custom `extraMounts` must not target the working-directory container
|
|
76
|
+
* path (`/work`) — it collides with the cwd mount and fails at container-create
|
|
77
|
+
* time with an opaque docker error.
|
|
78
|
+
*/
|
|
79
|
+
interface BuiltinDockerSandboxOptions {
|
|
80
|
+
/** Digest-pinned image unless `allowFloatingTag` is set. */
|
|
81
|
+
image?: string;
|
|
82
|
+
allowFloatingTag?: boolean;
|
|
83
|
+
env?: Record<string, string>;
|
|
84
|
+
extraMounts?: Array<BuiltinDockerSandboxMount>;
|
|
85
|
+
}
|
|
26
86
|
interface BuiltinAgentHandlerOptions {
|
|
27
87
|
agentServerUrl: string;
|
|
28
88
|
serveEndpoint?: string;
|
|
@@ -36,6 +96,8 @@ interface BuiltinAgentHandlerOptions {
|
|
|
36
96
|
serverHeaders?: HeadersProvider;
|
|
37
97
|
defaultDispatchPolicyForType?: (typeName: string) => DispatchPolicy | undefined;
|
|
38
98
|
createElectricTools?: BuiltinElectricToolsFactory;
|
|
99
|
+
/** Customize the built-in `docker` sandbox profile (image, env, mounts). */
|
|
100
|
+
dockerSandbox?: BuiltinDockerSandboxOptions;
|
|
39
101
|
}
|
|
40
102
|
declare function createBuiltinElectricTools(custom?: BuiltinElectricToolsFactory): BuiltinElectricToolsFactory;
|
|
41
103
|
declare function createBuiltinAgentHandler(options: BuiltinAgentHandlerOptions): Promise<AgentHandlerResult | null>;
|
|
@@ -45,6 +107,12 @@ declare const registerAgentTypes: typeof registerBuiltinAgentTypes;
|
|
|
45
107
|
|
|
46
108
|
//#endregion
|
|
47
109
|
//#region src/durable-streams-cache.d.ts
|
|
110
|
+
/**
|
|
111
|
+
* Merge the profile's working-directory mount with embedder docker options
|
|
112
|
+
* into the option fragment spread into `dockerSandbox()`. An internal helper:
|
|
113
|
+
* exported from this module so the unit test can import it, but intentionally
|
|
114
|
+
* not re-exported from `index.ts` (not part of the package's public API).
|
|
115
|
+
*/
|
|
48
116
|
type DurableStreamsFetchCacheOptions = false | {
|
|
49
117
|
store?: `memory` | `sqlite`;
|
|
50
118
|
sqliteLocation?: string;
|
|
@@ -160,48 +228,24 @@ declare function runBuiltinAgentsEntrypoint({
|
|
|
160
228
|
url: string;
|
|
161
229
|
}>;
|
|
162
230
|
|
|
163
|
-
//#endregion
|
|
164
|
-
//#region src/model-catalog.d.ts
|
|
165
|
-
type BuiltinModelProvider = AvailableProvider;
|
|
166
|
-
type BuiltinModelInput = `text` | `image`;
|
|
167
|
-
interface BuiltinModelChoice {
|
|
168
|
-
provider: BuiltinModelProvider;
|
|
169
|
-
id: string;
|
|
170
|
-
label: string;
|
|
171
|
-
value: string;
|
|
172
|
-
reasoning: boolean;
|
|
173
|
-
input: Array<BuiltinModelInput>;
|
|
174
|
-
}
|
|
175
|
-
interface BuiltinModelCatalog {
|
|
176
|
-
choices: Array<BuiltinModelChoice>;
|
|
177
|
-
defaultChoice: BuiltinModelChoice;
|
|
178
|
-
}
|
|
179
|
-
interface BuiltinModelCatalogOptions {
|
|
180
|
-
allowMockFallback?: boolean;
|
|
181
|
-
enabledModelValues?: ReadonlyArray<string> | null;
|
|
182
|
-
}
|
|
183
|
-
declare const REASONING_EFFORT_VALUES: readonly ["auto", "minimal", "low", "medium", "high"];
|
|
184
|
-
type BuiltinReasoningEffort = (typeof REASONING_EFFORT_VALUES)[number];
|
|
185
|
-
type ExplicitReasoningEffort = Exclude<BuiltinReasoningEffort, `auto`>;
|
|
186
|
-
type BuiltinAgentModelConfig = Pick<AgentConfig, `model` | `provider` | `onPayload` | `getApiKey`> & {
|
|
187
|
-
reasoningEffort?: ExplicitReasoningEffort;
|
|
188
|
-
};
|
|
189
|
-
declare function builtinModelProviderLabel(provider: BuiltinModelProvider): string;
|
|
190
|
-
declare function listBuiltinModelChoices(providers: ReadonlyArray<BuiltinModelProvider>): Array<BuiltinModelChoice>;
|
|
191
|
-
declare function resolveBuiltinModelConfig(catalog: BuiltinModelCatalog, args: Readonly<Record<string, unknown>>): BuiltinAgentModelConfig;
|
|
192
|
-
|
|
193
231
|
//#endregion
|
|
194
232
|
//#region src/agents/horton.d.ts
|
|
195
233
|
declare const HORTON_MODEL = "claude-sonnet-4-6";
|
|
196
234
|
declare function generateTitle(userMessage: string, llmCall: (prompt: string) => Promise<string>, onFallback?: (reason: string) => void): Promise<string>;
|
|
235
|
+
interface ActiveGoalPromptInfo {
|
|
236
|
+
objective: string;
|
|
237
|
+
tokenBudget: number | null;
|
|
238
|
+
tokensUsed: number;
|
|
239
|
+
}
|
|
197
240
|
declare function buildHortonSystemPrompt(workingDirectory: string, opts?: {
|
|
198
241
|
hasDocsSupport?: boolean;
|
|
199
|
-
|
|
242
|
+
hasWebhookSourceTools?: boolean;
|
|
200
243
|
hasScheduleTools?: boolean;
|
|
201
244
|
hasSkills?: boolean;
|
|
202
245
|
docsUrl?: string;
|
|
203
246
|
modelProvider?: string;
|
|
204
247
|
modelId?: string;
|
|
248
|
+
activeGoal?: ActiveGoalPromptInfo;
|
|
205
249
|
}): string;
|
|
206
250
|
declare function createHortonTools(sandbox: Sandbox, ctx: HandlerContext, readSet: Set<string>, opts?: {
|
|
207
251
|
docsSearchTool?: AgentTool$1;
|
|
@@ -254,4 +298,4 @@ declare function createHortonDocsSupport(workingDirectory: string, opts?: {
|
|
|
254
298
|
}): HortonDocsSupport | null;
|
|
255
299
|
|
|
256
300
|
//#endregion
|
|
257
|
-
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, createForkTool, createHortonDocsSupport, createHortonTools, createSpawnWorkerTool, generateTitle, listBuiltinModelChoices, registerAgentTypes, registerBuiltinAgentTypes, registerHorton, registerWorker, resolveBuiltinAgentsEntrypointOptions, runBuiltinAgentsEntrypoint };
|
|
301
|
+
export { AgentHandlerResult, BuiltinAgentHandlerOptions, BuiltinAgentModelConfig, BuiltinAgentsEntrypointOptions, BuiltinAgentsEntrypointServer, BuiltinAgentsServer, BuiltinAgentsServerOptions, BuiltinDockerSandboxMount, BuiltinDockerSandboxOptions, BuiltinElectricToolsFactory, BuiltinModelCatalog, 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, createForkTool, createHortonDocsSupport, createHortonTools, createSpawnWorkerTool, generateTitle, listBuiltinModelChoices, registerAgentTypes, registerBuiltinAgentTypes, registerHorton, registerWorker, resolveBuiltinAgentsEntrypointOptions, resolveBuiltinModelConfig, runBuiltinAgentsEntrypoint };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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 { MOONSHOT_API_BASE_URL, MOONSHOT_PROVIDER, appendPathToUrl, buildSkillSlashCommands, completeWithLowCostModel, createContextSkillLoader, createEntityRegistry, createPullWakeRunner, createRuntimeHandler, createSkillsRegistry, db, detectAvailableProviders, getMoonshotApiKey, getMoonshotModel, getMoonshotModels, pgSync, readCodexAccessToken, registerToolProvider, unregisterToolProvider } from "@electric-ax/agents-runtime";
|
|
5
|
-
import { braveSearchTool, braveSearchTool as braveSearchTool$1, createBashTool, createEditTool,
|
|
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, 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) {
|
|
@@ -1069,25 +1133,66 @@ function filterChoicesByEnabledModels(choices, values) {
|
|
|
1069
1133
|
const filtered = choices.filter((choice) => enabled.has(choice.value));
|
|
1070
1134
|
return filtered.length > 0 ? filtered : choices;
|
|
1071
1135
|
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Anthropic-specific budget mapping for `reasoningEffort`.
|
|
1138
|
+
*
|
|
1139
|
+
* Anthropic's `thinking.budget_tokens` is a hard cap on tokens spent
|
|
1140
|
+
* inside the thinking block before the model must commit to its
|
|
1141
|
+
* answer. Docs require ≥ 1024; we scale from there. Numbers tuned so
|
|
1142
|
+
* `medium` is the spot most "show your work" requests land, and
|
|
1143
|
+
* `high` covers tougher reasoning without uncapped spend.
|
|
1144
|
+
*
|
|
1145
|
+
* Keep in sync with provider doc updates — Anthropic has shifted the
|
|
1146
|
+
* minimum once already (older models capped lower).
|
|
1147
|
+
*/
|
|
1148
|
+
const ANTHROPIC_THINKING_BUDGET_BY_EFFORT = {
|
|
1149
|
+
minimal: 1024,
|
|
1150
|
+
low: 2048,
|
|
1151
|
+
medium: 8192,
|
|
1152
|
+
high: 24576
|
|
1153
|
+
};
|
|
1072
1154
|
function withProviderPayloadDefaults(config, choice, reasoningEffort) {
|
|
1073
|
-
if (
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1155
|
+
if (!choice.reasoning) return config;
|
|
1156
|
+
if (choice.provider === `openai` || choice.provider === `openai-codex`) {
|
|
1157
|
+
const defaultEffort = choice.provider === `openai-codex` ? `low` : `minimal`;
|
|
1158
|
+
const effort = reasoningEffort === `minimal` && choice.provider === `openai-codex` ? `low` : reasoningEffort ?? defaultEffort;
|
|
1159
|
+
return {
|
|
1160
|
+
...config,
|
|
1161
|
+
onPayload: (payload) => {
|
|
1162
|
+
if (typeof payload !== `object` || payload === null) return void 0;
|
|
1163
|
+
const body = payload;
|
|
1164
|
+
const existingReasoning = typeof body.reasoning === `object` && body.reasoning !== null ? body.reasoning : {};
|
|
1165
|
+
return {
|
|
1166
|
+
...body,
|
|
1167
|
+
reasoning: {
|
|
1168
|
+
...existingReasoning,
|
|
1169
|
+
effort
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
if (choice.provider === `anthropic`) {
|
|
1176
|
+
const effectiveEffort = reasoningEffort ?? `minimal`;
|
|
1177
|
+
const budgetTokens = ANTHROPIC_THINKING_BUDGET_BY_EFFORT[effectiveEffort];
|
|
1178
|
+
return {
|
|
1179
|
+
...config,
|
|
1180
|
+
onPayload: (payload) => {
|
|
1181
|
+
if (typeof payload !== `object` || payload === null) return void 0;
|
|
1182
|
+
const body = payload;
|
|
1183
|
+
const existingThinking = typeof body.thinking === `object` && body.thinking !== null ? body.thinking : {};
|
|
1184
|
+
return {
|
|
1185
|
+
...body,
|
|
1186
|
+
thinking: {
|
|
1187
|
+
...existingThinking,
|
|
1188
|
+
type: `enabled`,
|
|
1189
|
+
budget_tokens: budgetTokens
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
return config;
|
|
1091
1196
|
}
|
|
1092
1197
|
function parseReasoningEffort(value) {
|
|
1093
1198
|
return value === `minimal` || value === `low` || value === `medium` || value === `high` ? value : null;
|
|
@@ -1248,7 +1353,7 @@ async function generateTitle(userMessage, llmCall, onFallback) {
|
|
|
1248
1353
|
}
|
|
1249
1354
|
function buildHortonSystemPrompt(workingDirectory, opts = {}) {
|
|
1250
1355
|
const docsTools = opts.hasDocsSupport ? `\n- search_electric_agents_docs: hybrid search over the built-in Electric Agents docs index` : ``;
|
|
1251
|
-
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` : ``;
|
|
1252
1357
|
const titleTool = `\n- set_title: set or rename this chat session's UI title`;
|
|
1253
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` : ``;
|
|
1254
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` : ``;
|
|
@@ -1305,9 +1410,10 @@ When a user opens with a greeting ("hi", "hello", "hey", etc.) or a broad statem
|
|
|
1305
1410
|
- fetch_url: fetch and convert a URL to markdown
|
|
1306
1411
|
- spawn_worker: dispatch a subagent for an isolated task
|
|
1307
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.
|
|
1308
|
-
- 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")
|
|
1309
1415
|
- send: send a message to an Electric Agent/entity. To schedule future work for yourself, call send with self: true and afterMs.
|
|
1310
|
-
${
|
|
1416
|
+
${webhookSourceTools}${titleTool}${scheduleTools}${docsTools}${skillsTools}
|
|
1311
1417
|
|
|
1312
1418
|
# Working with files
|
|
1313
1419
|
- Prefer edit over write when modifying existing files.
|
|
@@ -1315,6 +1421,14 @@ ${eventSourceTools}${titleTool}${scheduleTools}${docsTools}${skillsTools}
|
|
|
1315
1421
|
- Use absolute paths or paths relative to the current working directory.
|
|
1316
1422
|
${modelGuidance}${docsGuidance}${skillsGuidance}${onboardingGuidance}${docsUrlGuidance}
|
|
1317
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
|
+
|
|
1318
1432
|
# Risky actions
|
|
1319
1433
|
Pause and confirm with the user before:
|
|
1320
1434
|
- Destructive operations (deleting files, rm -rf, dropping data, force-pushing)
|
|
@@ -1352,7 +1466,18 @@ Workflow when forking yourself for parallel exploration:
|
|
|
1352
1466
|
Report outcomes faithfully. If a command failed, say so with the relevant output. If you didn't run a verification step, say that rather than implying you did. Don't hedge confirmed results with unnecessary disclaimers.
|
|
1353
1467
|
|
|
1354
1468
|
Working directory: ${workingDirectory}
|
|
1355
|
-
The current year is ${new Date().getFullYear()}
|
|
1469
|
+
The current year is ${new Date().getFullYear()}.${buildGoalGuidance(opts.activeGoal)}`;
|
|
1470
|
+
}
|
|
1471
|
+
function buildGoalGuidance(goal) {
|
|
1472
|
+
if (!goal) return ``;
|
|
1473
|
+
const budgetLine = goal.tokenBudget === null ? `unlimited` : `${goal.tokensUsed} / ${goal.tokenBudget} tokens used`;
|
|
1474
|
+
return `
|
|
1475
|
+
|
|
1476
|
+
# Active goal
|
|
1477
|
+
- Objective: ${goal.objective}
|
|
1478
|
+
- Token budget: ${budgetLine}
|
|
1479
|
+
|
|
1480
|
+
The user set this goal with /goal set. Work autonomously toward it: do NOT ask the user clarifying questions or pause for confirmation — make reasonable assumptions and proceed. When you believe the goal is met, call the \`mark_goal_complete\` tool. If you hit a blocker that genuinely requires the user (e.g. credentials, a destructive action), call \`mark_goal_complete\` with a summary explaining what's needed. The runtime will abort this run automatically if you exceed the token budget.`;
|
|
1356
1481
|
}
|
|
1357
1482
|
function getToolName(tool) {
|
|
1358
1483
|
if (typeof tool !== `object` || tool === null) return null;
|
|
@@ -1375,8 +1500,10 @@ function createHortonTools(sandbox, ctx, readSet, opts = {}) {
|
|
|
1375
1500
|
createSpawnWorkerTool(ctx, opts.modelConfig),
|
|
1376
1501
|
createForkTool(ctx),
|
|
1377
1502
|
createObservePgSyncTool(ctx),
|
|
1503
|
+
createUnobservePgSyncTool(ctx),
|
|
1378
1504
|
createSetTitleTool(ctx),
|
|
1379
1505
|
createSendTool(ctx.send, { selfEntityUrl: ctx.entityUrl }),
|
|
1506
|
+
...ctx.getGoal()?.status === `active` ? [createMarkGoalCompleteTool(ctx)] : [],
|
|
1380
1507
|
...opts.docsSearchTool ? [opts.docsSearchTool] : []
|
|
1381
1508
|
];
|
|
1382
1509
|
}
|
|
@@ -1445,11 +1572,58 @@ async function readAgentsMd(sandbox) {
|
|
|
1445
1572
|
return null;
|
|
1446
1573
|
}
|
|
1447
1574
|
}
|
|
1575
|
+
function extractWakeText(wake) {
|
|
1576
|
+
if (wake.type !== `inbox`) return null;
|
|
1577
|
+
const payload = wake.payload;
|
|
1578
|
+
if (typeof payload === `string`) return payload;
|
|
1579
|
+
if (payload && typeof payload === `object`) {
|
|
1580
|
+
const record = payload;
|
|
1581
|
+
if (typeof record.text === `string`) return record.text;
|
|
1582
|
+
if (typeof record.source === `string`) return record.source;
|
|
1583
|
+
}
|
|
1584
|
+
return null;
|
|
1585
|
+
}
|
|
1586
|
+
async function tryHandleSlashCommand(ctx, wake) {
|
|
1587
|
+
const text = extractWakeText(wake);
|
|
1588
|
+
if (text === null) return false;
|
|
1589
|
+
if (isGoalCommandText(text)) {
|
|
1590
|
+
const command = parseGoalCommand(text);
|
|
1591
|
+
const result = dispatchGoalCommand(ctx, command);
|
|
1592
|
+
if (result.message) {
|
|
1593
|
+
serverLog.info(`[horton ${ctx.entityUrl}] ${result.message}`);
|
|
1594
|
+
writeSlashCommandReply(ctx, result.message);
|
|
1595
|
+
}
|
|
1596
|
+
if (command.kind === `set`) await kickoffGoalRun(ctx);
|
|
1597
|
+
return result.handled;
|
|
1598
|
+
}
|
|
1599
|
+
return false;
|
|
1600
|
+
}
|
|
1601
|
+
const GOAL_KICKOFF_TEXT = `Start working toward the active goal now. Call \`mark_goal_complete\` when you believe it is done.`;
|
|
1602
|
+
async function kickoffGoalRun(ctx) {
|
|
1603
|
+
const goal = ctx.getGoal();
|
|
1604
|
+
if (!goal || goal.status !== `active`) return;
|
|
1605
|
+
try {
|
|
1606
|
+
await ctx.send(ctx.entityUrl, {
|
|
1607
|
+
kind: `goal_kickoff`,
|
|
1608
|
+
text: GOAL_KICKOFF_TEXT
|
|
1609
|
+
}, { type: `inbox` });
|
|
1610
|
+
} catch (err) {
|
|
1611
|
+
serverLog.warn(`[horton ${ctx.entityUrl}] failed to enqueue goal kickoff: ${err instanceof Error ? err.message : String(err)}`);
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
function writeSlashCommandReply(ctx, text) {
|
|
1615
|
+
try {
|
|
1616
|
+
ctx.replyText(text);
|
|
1617
|
+
} catch (err) {
|
|
1618
|
+
serverLog.warn(`[horton ${ctx.entityUrl}] failed to render slash command reply: ${err instanceof Error ? err.message : String(err)}`);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1448
1621
|
function createAssistantHandler(options) {
|
|
1449
1622
|
const { streamFn, docsSupport, docsSearchTool, skillsRegistry, modelCatalog, docsUrl } = options;
|
|
1450
1623
|
const skillLoader = createContextSkillLoader(skillsRegistry, { slashCommandOwner: HORTON_SKILLS_SLASH_COMMAND_OWNER });
|
|
1451
1624
|
const hasSkills = skillLoader.hasSkills;
|
|
1452
1625
|
return async function assistantHandler(ctx, wake) {
|
|
1626
|
+
if (await tryHandleSlashCommand(ctx, wake)) return;
|
|
1453
1627
|
const loadedSkills = await skillLoader.load(ctx);
|
|
1454
1628
|
const readSet = new Set();
|
|
1455
1629
|
const modelConfig = resolveBuiltinModelConfig(modelCatalog, ctx.args);
|
|
@@ -1467,7 +1641,7 @@ function createAssistantHandler(options) {
|
|
|
1467
1641
|
...loadedSkills.tools,
|
|
1468
1642
|
...mcp.tools()
|
|
1469
1643
|
];
|
|
1470
|
-
const
|
|
1644
|
+
const hasWebhookSourceTools = tools.some((tool) => getToolName(tool) === `list_webhook_sources`);
|
|
1471
1645
|
const hasScheduleTools = tools.some((tool) => getToolName(tool) === `upsert_cron_schedule`);
|
|
1472
1646
|
const titlePromise = !ctx.tags.title ? (async () => {
|
|
1473
1647
|
const firstUserMessage = await extractFirstUserMessage(ctx);
|
|
@@ -1542,6 +1716,26 @@ function createAssistantHandler(options) {
|
|
|
1542
1716
|
}
|
|
1543
1717
|
}
|
|
1544
1718
|
});
|
|
1719
|
+
const goal = ctx.getGoal();
|
|
1720
|
+
const enforcedGoal = goal && goal.status === `active` ? goal : void 0;
|
|
1721
|
+
const activeGoalPromptInfo = enforcedGoal ? {
|
|
1722
|
+
objective: enforcedGoal.objective,
|
|
1723
|
+
tokenBudget: enforcedGoal.tokenBudget,
|
|
1724
|
+
tokensUsed: enforcedGoal.tokensUsed
|
|
1725
|
+
} : void 0;
|
|
1726
|
+
const budgetAbort = new AbortController();
|
|
1727
|
+
let runTokensUsed = enforcedGoal?.tokensUsed ?? 0;
|
|
1728
|
+
let budgetTripped = false;
|
|
1729
|
+
const onStepEnd = enforcedGoal ? (stats) => {
|
|
1730
|
+
if (budgetTripped) return;
|
|
1731
|
+
runTokensUsed += stats.uncachedInput + stats.output;
|
|
1732
|
+
ctx.updateGoalUsage(runTokensUsed);
|
|
1733
|
+
if (enforcedGoal.tokenBudget !== null && runTokensUsed >= enforcedGoal.tokenBudget) {
|
|
1734
|
+
budgetTripped = true;
|
|
1735
|
+
serverLog.info(`[horton ${ctx.entityUrl}] goal budget exhausted (${runTokensUsed} tokens) — aborting run`);
|
|
1736
|
+
budgetAbort.abort();
|
|
1737
|
+
}
|
|
1738
|
+
} : void 0;
|
|
1545
1739
|
ctx.useAgent({
|
|
1546
1740
|
systemPrompt: buildHortonSystemPrompt(sandboxCwd, {
|
|
1547
1741
|
hasDocsSupport: Boolean(docsSupport),
|
|
@@ -1549,14 +1743,27 @@ function createAssistantHandler(options) {
|
|
|
1549
1743
|
docsUrl,
|
|
1550
1744
|
modelProvider: modelConfig.provider,
|
|
1551
1745
|
modelId: String(modelConfig.model),
|
|
1552
|
-
|
|
1553
|
-
hasScheduleTools
|
|
1746
|
+
hasWebhookSourceTools,
|
|
1747
|
+
hasScheduleTools,
|
|
1748
|
+
...activeGoalPromptInfo && { activeGoal: activeGoalPromptInfo }
|
|
1554
1749
|
}),
|
|
1555
1750
|
...modelConfig,
|
|
1556
1751
|
tools,
|
|
1557
|
-
...streamFn && { streamFn }
|
|
1752
|
+
...streamFn && { streamFn },
|
|
1753
|
+
...onStepEnd && { onStepEnd }
|
|
1558
1754
|
});
|
|
1559
|
-
|
|
1755
|
+
try {
|
|
1756
|
+
await ctx.agent.run(void 0, budgetAbort.signal);
|
|
1757
|
+
} catch (err) {
|
|
1758
|
+
if (!budgetTripped) throw err;
|
|
1759
|
+
serverLog.info(`[horton ${ctx.entityUrl}] agent.run aborted by budget enforcement`);
|
|
1760
|
+
}
|
|
1761
|
+
if (enforcedGoal) ctx.updateGoalUsage(runTokensUsed, budgetTripped ? { status: `budget_limited` } : void 0);
|
|
1762
|
+
if (budgetTripped && enforcedGoal && enforcedGoal.tokenBudget !== null) {
|
|
1763
|
+
const budget = enforcedGoal.tokenBudget;
|
|
1764
|
+
const suggestedNext = Math.max(budget * 2, budget + 1e4);
|
|
1765
|
+
writeSlashCommandReply(ctx, `⚠️ Stopped — goal hit the token budget (${formatTokenCount(runTokensUsed)} / ${formatTokenCount(budget)} tokens used). Raise the budget with \`/goal set "..." --tokens ${formatTokenCount(suggestedNext)}\`, or call \`/goal complete\` to finalize.`);
|
|
1766
|
+
}
|
|
1560
1767
|
await titlePromise;
|
|
1561
1768
|
};
|
|
1562
1769
|
}
|
|
@@ -1596,7 +1803,8 @@ function registerHorton(registry, options) {
|
|
|
1596
1803
|
subject_value: `user`,
|
|
1597
1804
|
permission: `manage`
|
|
1598
1805
|
}],
|
|
1599
|
-
|
|
1806
|
+
state: { comments: commentsCollection },
|
|
1807
|
+
slashCommands: [GOAL_SLASH_COMMAND, ...buildSkillSlashCommands(skillsRegistry)],
|
|
1600
1808
|
handler: assistantHandler
|
|
1601
1809
|
});
|
|
1602
1810
|
return [`horton`];
|
|
@@ -1780,6 +1988,7 @@ function registerWorker(registry, options) {
|
|
|
1780
1988
|
subject_value: `user`,
|
|
1781
1989
|
permission: `manage`
|
|
1782
1990
|
}],
|
|
1991
|
+
state: { comments: commentsCollection },
|
|
1783
1992
|
async handler(ctx) {
|
|
1784
1993
|
const args = parseWorkerArgs(ctx.args);
|
|
1785
1994
|
const readSet = new Set();
|
|
@@ -1823,7 +2032,7 @@ function dedupeToolsByName(tools) {
|
|
|
1823
2032
|
}
|
|
1824
2033
|
function createBuiltinElectricTools(custom) {
|
|
1825
2034
|
return async (context) => {
|
|
1826
|
-
const builtinTools = [...
|
|
2035
|
+
const builtinTools = [...createWebhookSourceTools(context), ...createScheduleTools({
|
|
1827
2036
|
...context,
|
|
1828
2037
|
db: context.db
|
|
1829
2038
|
})];
|
|
@@ -1832,7 +2041,7 @@ function createBuiltinElectricTools(custom) {
|
|
|
1832
2041
|
};
|
|
1833
2042
|
}
|
|
1834
2043
|
async function createBuiltinAgentHandler(options) {
|
|
1835
|
-
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, enabledModelValues, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
2044
|
+
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, enabledModelValues, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType, dockerSandbox: dockerSandboxOpts } = options;
|
|
1836
2045
|
const modelCatalog = await createBuiltinModelCatalog({
|
|
1837
2046
|
allowMockFallback: Boolean(streamFn),
|
|
1838
2047
|
enabledModelValues
|
|
@@ -1868,7 +2077,7 @@ async function createBuiltinAgentHandler(options) {
|
|
|
1868
2077
|
modelCatalog
|
|
1869
2078
|
});
|
|
1870
2079
|
typeNames.push(`worker`);
|
|
1871
|
-
const { profiles: sandboxProfiles, shutdownSandboxes } = await buildBuiltinSandboxProfiles(cwd);
|
|
2080
|
+
const { profiles: sandboxProfiles, shutdownSandboxes } = await buildBuiltinSandboxProfiles(cwd, dockerSandboxOpts);
|
|
1872
2081
|
const runtime = createRuntimeHandler({
|
|
1873
2082
|
baseUrl: agentServerUrl,
|
|
1874
2083
|
serveEndpoint,
|
|
@@ -1888,7 +2097,8 @@ async function createBuiltinAgentHandler(options) {
|
|
|
1888
2097
|
registry,
|
|
1889
2098
|
typeNames,
|
|
1890
2099
|
skillsRegistry,
|
|
1891
|
-
shutdownSandboxes
|
|
2100
|
+
shutdownSandboxes,
|
|
2101
|
+
modelCatalog
|
|
1892
2102
|
};
|
|
1893
2103
|
}
|
|
1894
2104
|
async function createAgentHandler(agentServerUrl, workingDirectory, streamFn, createElectricTools, serveEndpoint) {
|
|
@@ -1917,6 +2127,21 @@ function sweepOrphanedDockerSandboxesOnce(sweep) {
|
|
|
1917
2127
|
return dockerBootSweep;
|
|
1918
2128
|
}
|
|
1919
2129
|
/**
|
|
2130
|
+
* Merge the profile's working-directory mount with embedder docker options
|
|
2131
|
+
* into the option fragment spread into `dockerSandbox()`. An internal helper:
|
|
2132
|
+
* exported from this module so the unit test can import it, but intentionally
|
|
2133
|
+
* not re-exported from `index.ts` (not part of the package's public API).
|
|
2134
|
+
*/
|
|
2135
|
+
function resolveDockerSandboxOpts(cwdMount, custom) {
|
|
2136
|
+
const extraMounts = [...cwdMount ? [cwdMount] : [], ...custom?.extraMounts ?? []];
|
|
2137
|
+
return {
|
|
2138
|
+
...custom?.image !== void 0 && { image: custom.image },
|
|
2139
|
+
...custom?.allowFloatingTag !== void 0 && { allowFloatingTag: custom.allowFloatingTag },
|
|
2140
|
+
...custom?.env !== void 0 && { env: custom.env },
|
|
2141
|
+
...extraMounts.length > 0 && { extraMounts }
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
1920
2145
|
* Built-in sandbox profiles. `local` is always available. `docker` is
|
|
1921
2146
|
* gated on Docker being reachable so a user without Docker installed
|
|
1922
2147
|
* sees only what works — the UI never offers a non-functional choice.
|
|
@@ -1926,7 +2151,7 @@ function sweepOrphanedDockerSandboxesOnce(sweep) {
|
|
|
1926
2151
|
* server must run on shutdown (the providers' debounced idle teardowns die
|
|
1927
2152
|
* with the process).
|
|
1928
2153
|
*/
|
|
1929
|
-
async function buildBuiltinSandboxProfiles(workingDirectory) {
|
|
2154
|
+
async function buildBuiltinSandboxProfiles(workingDirectory, dockerOpts) {
|
|
1930
2155
|
const profiles = [{
|
|
1931
2156
|
name: `local`,
|
|
1932
2157
|
label: `Local`,
|
|
@@ -1951,11 +2176,11 @@ async function buildBuiltinSandboxProfiles(workingDirectory) {
|
|
|
1951
2176
|
workingDirectory: `/work`,
|
|
1952
2177
|
factory: () => dockerSandbox({
|
|
1953
2178
|
initialNetworkPolicy: { mode: `allow-all` },
|
|
1954
|
-
|
|
2179
|
+
...resolveDockerSandboxOpts(cwd ? {
|
|
1955
2180
|
hostPath: cwd,
|
|
1956
2181
|
containerPath: `/work`,
|
|
1957
2182
|
readOnly: false
|
|
1958
|
-
}
|
|
2183
|
+
} : void 0, dockerOpts),
|
|
1959
2184
|
sandboxKey,
|
|
1960
2185
|
persistent,
|
|
1961
2186
|
owner,
|
|
@@ -2341,4 +2566,4 @@ async function runBuiltinAgentsEntrypoint({ env = process.env, cwd = process.cwd
|
|
|
2341
2566
|
}
|
|
2342
2567
|
|
|
2343
2568
|
//#endregion
|
|
2344
|
-
export { BuiltinAgentsServer, DEFAULT_BUILTIN_AGENT_HANDLER_PATH, HORTON_MODEL, WORKER_TOOL_NAMES, braveSearchTool, buildHortonSystemPrompt, builtinModelProviderLabel, createAgentHandler, createBuiltinAgentHandler, createBuiltinElectricTools, createForkTool, createHortonDocsSupport, createHortonTools, createSpawnWorkerTool, generateTitle, listBuiltinModelChoices, registerAgentTypes, registerBuiltinAgentTypes, registerHorton, registerWorker, resolveBuiltinAgentsEntrypointOptions, runBuiltinAgentsEntrypoint };
|
|
2569
|
+
export { BuiltinAgentsServer, DEFAULT_BUILTIN_AGENT_HANDLER_PATH, HORTON_MODEL, WORKER_TOOL_NAMES, braveSearchTool, buildHortonSystemPrompt, builtinModelProviderLabel, createAgentHandler, createBuiltinAgentHandler, createBuiltinElectricTools, createForkTool, createHortonDocsSupport, createHortonTools, createSpawnWorkerTool, generateTitle, listBuiltinModelChoices, registerAgentTypes, registerBuiltinAgentTypes, registerHorton, registerWorker, resolveBuiltinAgentsEntrypointOptions, resolveBuiltinModelConfig, runBuiltinAgentsEntrypoint };
|