@electric-ax/agents 0.4.0 → 0.4.1
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 +8 -14
- package/dist/index.cjs +8 -14
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +8 -14
- package/docs/index.md +3 -3
- package/docs/reference/built-in-collections.md +1 -1
- package/docs/reference/wake-event.md +4 -4
- package/docs/usage/spawning-and-coordinating.md +1 -1
- package/docs/usage/waking-entities.md +5 -5
- package/docs/usage/writing-handlers.md +1 -1
- package/package.json +4 -4
package/dist/entrypoint.js
CHANGED
|
@@ -625,8 +625,8 @@ function createHortonDocsSupport(workingDirectory, opts = {}) {
|
|
|
625
625
|
logPrefix: `[horton-docs]`
|
|
626
626
|
});
|
|
627
627
|
function resolveCurrentQuestion(wake, events, inbox) {
|
|
628
|
-
if (wake.type === `
|
|
629
|
-
const eventQuestion = findLatestQuestion(events.filter((event) => event.type === `
|
|
628
|
+
if (wake.type === `inbox`) {
|
|
629
|
+
const eventQuestion = findLatestQuestion(events.filter((event) => event.type === `inbox`).map((event) => event.value));
|
|
630
630
|
if (eventQuestion) return eventQuestion;
|
|
631
631
|
}
|
|
632
632
|
const wakeQuestion = payloadToText(wake.payload).trim();
|
|
@@ -1426,7 +1426,8 @@ function registerHorton(registry, options) {
|
|
|
1426
1426
|
const { workingDirectory, streamFn, skillsRegistry = null, modelCatalog } = options;
|
|
1427
1427
|
const docsUrl = options.docsUrl ?? process.env.HORTON_DOCS_URL;
|
|
1428
1428
|
if (process.env.BRAVE_SEARCH_API_KEY) serverLog.info(`[horton] Web search: using Brave Search API`);
|
|
1429
|
-
else serverLog.warn(`[horton] BRAVE_SEARCH_API_KEY not set — web search will fall back to Anthropic built-in search
|
|
1429
|
+
else if (process.env.ANTHROPIC_API_KEY) serverLog.warn(`[horton] BRAVE_SEARCH_API_KEY not set — web search will fall back to Anthropic built-in search`);
|
|
1430
|
+
else serverLog.warn(`[horton] BRAVE_SEARCH_API_KEY and ANTHROPIC_API_KEY not set — web search tool will be unavailable`);
|
|
1430
1431
|
const docsSupport = createHortonDocsSupport(workingDirectory);
|
|
1431
1432
|
const docsSearchTool = docsSupport?.createSearchTool();
|
|
1432
1433
|
docsSupport?.ensureReady().catch((error) => {
|
|
@@ -1902,7 +1903,7 @@ function truncate(str, max) {
|
|
|
1902
1903
|
//#endregion
|
|
1903
1904
|
//#region src/bootstrap.ts
|
|
1904
1905
|
async function createBuiltinAgentHandler(options) {
|
|
1905
|
-
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1906
|
+
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1906
1907
|
const modelCatalog = await createBuiltinModelCatalog({ allowMockFallback: Boolean(streamFn) });
|
|
1907
1908
|
if (!modelCatalog) {
|
|
1908
1909
|
serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY or OPENAI_API_KEY`);
|
|
@@ -1910,7 +1911,7 @@ async function createBuiltinAgentHandler(options) {
|
|
|
1910
1911
|
}
|
|
1911
1912
|
const cwd = workingDirectory ?? process.cwd();
|
|
1912
1913
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
1913
|
-
const baseSkillsDir = path.resolve(here, `../skills`);
|
|
1914
|
+
const baseSkillsDir = baseSkillsDirOverride ?? path.resolve(here, `../skills`);
|
|
1914
1915
|
let skillsRegistry = null;
|
|
1915
1916
|
try {
|
|
1916
1917
|
skillsRegistry = await createSkillsRegistry({
|
|
@@ -2098,6 +2099,7 @@ var BuiltinAgentsServer = class {
|
|
|
2098
2099
|
createElectricTools: this.options.createElectricTools,
|
|
2099
2100
|
publicUrl,
|
|
2100
2101
|
runtimeName: `builtin-agents`,
|
|
2102
|
+
baseSkillsDir: this.options.baseSkillsDir,
|
|
2101
2103
|
serverHeaders: pullWake.headers
|
|
2102
2104
|
});
|
|
2103
2105
|
if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY or OPENAI_API_KEY must be set before starting builtin agents`);
|
|
@@ -2203,14 +2205,6 @@ function validateUrl(name, value) {
|
|
|
2203
2205
|
throw new Error(`Invalid ${name}: "${value}"`);
|
|
2204
2206
|
}
|
|
2205
2207
|
}
|
|
2206
|
-
function buildAssertedAuthHeaders(env) {
|
|
2207
|
-
const headers = {};
|
|
2208
|
-
const email = readEnv(env, [`ELECTRIC_ASSERTED_AUTH_EMAIL`]);
|
|
2209
|
-
const name = readEnv(env, [`ELECTRIC_ASSERTED_AUTH_NAME`]);
|
|
2210
|
-
if (email) headers[`X-Electric-Asserted-Email`] = email;
|
|
2211
|
-
if (name) headers[`X-Electric-Asserted-Name`] = name;
|
|
2212
|
-
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
2213
|
-
}
|
|
2214
2208
|
function parseAdditionalServerHeaders(env) {
|
|
2215
2209
|
const raw = readEnv(env, [`ELECTRIC_AGENTS_SERVER_HEADERS`]);
|
|
2216
2210
|
if (!raw) return void 0;
|
|
@@ -2244,7 +2238,7 @@ function hasHeader(headers, name) {
|
|
|
2244
2238
|
function resolveBuiltinAgentsEntrypointOptions(env = process.env, cwd = process.cwd()) {
|
|
2245
2239
|
const agentServerUrl = validateUrl(`agent server URL`, readRequiredEnv(env, [`ELECTRIC_AGENTS_SERVER_URL`, `ELECTRIC_AGENTS_BASE_URL`], `agent server base URL`));
|
|
2246
2240
|
const runnerId = readRequiredEnv(env, [`ELECTRIC_AGENTS_PULL_WAKE_RUNNER_ID`, `PULL_WAKE_RUNNER_ID`], `pull-wake runner id`);
|
|
2247
|
-
const serverHeaders = mergeHeaders(
|
|
2241
|
+
const serverHeaders = mergeHeaders(parseAdditionalServerHeaders(env));
|
|
2248
2242
|
return {
|
|
2249
2243
|
agentServerUrl,
|
|
2250
2244
|
workingDirectory: readEnv(env, [`ELECTRIC_AGENTS_WORKING_DIRECTORY`, `WORKING_DIRECTORY`]) ?? cwd,
|
package/dist/index.cjs
CHANGED
|
@@ -648,8 +648,8 @@ function createHortonDocsSupport(workingDirectory, opts = {}) {
|
|
|
648
648
|
logPrefix: `[horton-docs]`
|
|
649
649
|
});
|
|
650
650
|
function resolveCurrentQuestion(wake, events, inbox) {
|
|
651
|
-
if (wake.type === `
|
|
652
|
-
const eventQuestion = findLatestQuestion(events.filter((event) => event.type === `
|
|
651
|
+
if (wake.type === `inbox`) {
|
|
652
|
+
const eventQuestion = findLatestQuestion(events.filter((event) => event.type === `inbox`).map((event) => event.value));
|
|
653
653
|
if (eventQuestion) return eventQuestion;
|
|
654
654
|
}
|
|
655
655
|
const wakeQuestion = payloadToText(wake.payload).trim();
|
|
@@ -1450,7 +1450,8 @@ function registerHorton(registry, options) {
|
|
|
1450
1450
|
const { workingDirectory, streamFn, skillsRegistry = null, modelCatalog } = options;
|
|
1451
1451
|
const docsUrl = options.docsUrl ?? process.env.HORTON_DOCS_URL;
|
|
1452
1452
|
if (process.env.BRAVE_SEARCH_API_KEY) serverLog.info(`[horton] Web search: using Brave Search API`);
|
|
1453
|
-
else serverLog.warn(`[horton] BRAVE_SEARCH_API_KEY not set — web search will fall back to Anthropic built-in search
|
|
1453
|
+
else if (process.env.ANTHROPIC_API_KEY) serverLog.warn(`[horton] BRAVE_SEARCH_API_KEY not set — web search will fall back to Anthropic built-in search`);
|
|
1454
|
+
else serverLog.warn(`[horton] BRAVE_SEARCH_API_KEY and ANTHROPIC_API_KEY not set — web search tool will be unavailable`);
|
|
1454
1455
|
const docsSupport = createHortonDocsSupport(workingDirectory);
|
|
1455
1456
|
const docsSearchTool = docsSupport?.createSearchTool();
|
|
1456
1457
|
docsSupport?.ensureReady().catch((error) => {
|
|
@@ -1927,7 +1928,7 @@ function truncate(str, max) {
|
|
|
1927
1928
|
//#region src/bootstrap.ts
|
|
1928
1929
|
const DEFAULT_BUILTIN_AGENT_HANDLER_PATH = `/_electric/builtin-agent-handler`;
|
|
1929
1930
|
async function createBuiltinAgentHandler(options) {
|
|
1930
|
-
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1931
|
+
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1931
1932
|
const modelCatalog = await createBuiltinModelCatalog({ allowMockFallback: Boolean(streamFn) });
|
|
1932
1933
|
if (!modelCatalog) {
|
|
1933
1934
|
serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY or OPENAI_API_KEY`);
|
|
@@ -1935,7 +1936,7 @@ async function createBuiltinAgentHandler(options) {
|
|
|
1935
1936
|
}
|
|
1936
1937
|
const cwd = workingDirectory ?? process.cwd();
|
|
1937
1938
|
const here = node_path.default.dirname((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
1938
|
-
const baseSkillsDir = node_path.default.resolve(here, `../skills`);
|
|
1939
|
+
const baseSkillsDir = baseSkillsDirOverride ?? node_path.default.resolve(here, `../skills`);
|
|
1939
1940
|
let skillsRegistry = null;
|
|
1940
1941
|
try {
|
|
1941
1942
|
skillsRegistry = await createSkillsRegistry({
|
|
@@ -2133,6 +2134,7 @@ var BuiltinAgentsServer = class {
|
|
|
2133
2134
|
createElectricTools: this.options.createElectricTools,
|
|
2134
2135
|
publicUrl,
|
|
2135
2136
|
runtimeName: `builtin-agents`,
|
|
2137
|
+
baseSkillsDir: this.options.baseSkillsDir,
|
|
2136
2138
|
serverHeaders: pullWake.headers
|
|
2137
2139
|
});
|
|
2138
2140
|
if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY or OPENAI_API_KEY must be set before starting builtin agents`);
|
|
@@ -2238,14 +2240,6 @@ function validateUrl(name, value) {
|
|
|
2238
2240
|
throw new Error(`Invalid ${name}: "${value}"`);
|
|
2239
2241
|
}
|
|
2240
2242
|
}
|
|
2241
|
-
function buildAssertedAuthHeaders(env) {
|
|
2242
|
-
const headers = {};
|
|
2243
|
-
const email = readEnv(env, [`ELECTRIC_ASSERTED_AUTH_EMAIL`]);
|
|
2244
|
-
const name = readEnv(env, [`ELECTRIC_ASSERTED_AUTH_NAME`]);
|
|
2245
|
-
if (email) headers[`X-Electric-Asserted-Email`] = email;
|
|
2246
|
-
if (name) headers[`X-Electric-Asserted-Name`] = name;
|
|
2247
|
-
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
2248
|
-
}
|
|
2249
2243
|
function parseAdditionalServerHeaders(env) {
|
|
2250
2244
|
const raw = readEnv(env, [`ELECTRIC_AGENTS_SERVER_HEADERS`]);
|
|
2251
2245
|
if (!raw) return void 0;
|
|
@@ -2279,7 +2273,7 @@ function hasHeader(headers, name) {
|
|
|
2279
2273
|
function resolveBuiltinAgentsEntrypointOptions(env = process.env, cwd = process.cwd()) {
|
|
2280
2274
|
const agentServerUrl = validateUrl(`agent server URL`, readRequiredEnv(env, [`ELECTRIC_AGENTS_SERVER_URL`, `ELECTRIC_AGENTS_BASE_URL`], `agent server base URL`));
|
|
2281
2275
|
const runnerId = readRequiredEnv(env, [`ELECTRIC_AGENTS_PULL_WAKE_RUNNER_ID`, `PULL_WAKE_RUNNER_ID`], `pull-wake runner id`);
|
|
2282
|
-
const serverHeaders = mergeHeaders(
|
|
2276
|
+
const serverHeaders = mergeHeaders(parseAdditionalServerHeaders(env));
|
|
2283
2277
|
return {
|
|
2284
2278
|
agentServerUrl,
|
|
2285
2279
|
workingDirectory: readEnv(env, [`ELECTRIC_AGENTS_WORKING_DIRECTORY`, `WORKING_DIRECTORY`]) ?? cwd,
|
package/dist/index.d.cts
CHANGED
|
@@ -43,6 +43,8 @@ interface BuiltinAgentHandlerOptions {
|
|
|
43
43
|
streamFn?: StreamFn;
|
|
44
44
|
publicUrl?: string;
|
|
45
45
|
runtimeName?: string;
|
|
46
|
+
/** Override for the built-in skills directory; required when embedders bundle this package. */
|
|
47
|
+
baseSkillsDir?: string;
|
|
46
48
|
serverHeaders?: HeadersProvider;
|
|
47
49
|
defaultDispatchPolicyForType?: (typeName: string) => DispatchPolicy | undefined;
|
|
48
50
|
createElectricTools?: (context: {
|
|
@@ -125,6 +127,8 @@ interface BuiltinAgentsServerOptions {
|
|
|
125
127
|
* so the embedder must opt in.
|
|
126
128
|
*/
|
|
127
129
|
loadProjectMcpConfig?: boolean;
|
|
130
|
+
/** Override for the built-in skills directory; required when embedders bundle this package. */
|
|
131
|
+
baseSkillsDir?: string;
|
|
128
132
|
createElectricTools?: (context: {
|
|
129
133
|
entityUrl: string;
|
|
130
134
|
entityType: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -43,6 +43,8 @@ interface BuiltinAgentHandlerOptions {
|
|
|
43
43
|
streamFn?: StreamFn;
|
|
44
44
|
publicUrl?: string;
|
|
45
45
|
runtimeName?: string;
|
|
46
|
+
/** Override for the built-in skills directory; required when embedders bundle this package. */
|
|
47
|
+
baseSkillsDir?: string;
|
|
46
48
|
serverHeaders?: HeadersProvider;
|
|
47
49
|
defaultDispatchPolicyForType?: (typeName: string) => DispatchPolicy | undefined;
|
|
48
50
|
createElectricTools?: (context: {
|
|
@@ -125,6 +127,8 @@ interface BuiltinAgentsServerOptions {
|
|
|
125
127
|
* so the embedder must opt in.
|
|
126
128
|
*/
|
|
127
129
|
loadProjectMcpConfig?: boolean;
|
|
130
|
+
/** Override for the built-in skills directory; required when embedders bundle this package. */
|
|
131
|
+
baseSkillsDir?: string;
|
|
128
132
|
createElectricTools?: (context: {
|
|
129
133
|
entityUrl: string;
|
|
130
134
|
entityType: string;
|
package/dist/index.js
CHANGED
|
@@ -624,8 +624,8 @@ function createHortonDocsSupport(workingDirectory, opts = {}) {
|
|
|
624
624
|
logPrefix: `[horton-docs]`
|
|
625
625
|
});
|
|
626
626
|
function resolveCurrentQuestion(wake, events, inbox) {
|
|
627
|
-
if (wake.type === `
|
|
628
|
-
const eventQuestion = findLatestQuestion(events.filter((event) => event.type === `
|
|
627
|
+
if (wake.type === `inbox`) {
|
|
628
|
+
const eventQuestion = findLatestQuestion(events.filter((event) => event.type === `inbox`).map((event) => event.value));
|
|
629
629
|
if (eventQuestion) return eventQuestion;
|
|
630
630
|
}
|
|
631
631
|
const wakeQuestion = payloadToText(wake.payload).trim();
|
|
@@ -1426,7 +1426,8 @@ function registerHorton(registry, options) {
|
|
|
1426
1426
|
const { workingDirectory, streamFn, skillsRegistry = null, modelCatalog } = options;
|
|
1427
1427
|
const docsUrl = options.docsUrl ?? process.env.HORTON_DOCS_URL;
|
|
1428
1428
|
if (process.env.BRAVE_SEARCH_API_KEY) serverLog.info(`[horton] Web search: using Brave Search API`);
|
|
1429
|
-
else serverLog.warn(`[horton] BRAVE_SEARCH_API_KEY not set — web search will fall back to Anthropic built-in search
|
|
1429
|
+
else if (process.env.ANTHROPIC_API_KEY) serverLog.warn(`[horton] BRAVE_SEARCH_API_KEY not set — web search will fall back to Anthropic built-in search`);
|
|
1430
|
+
else serverLog.warn(`[horton] BRAVE_SEARCH_API_KEY and ANTHROPIC_API_KEY not set — web search tool will be unavailable`);
|
|
1430
1431
|
const docsSupport = createHortonDocsSupport(workingDirectory);
|
|
1431
1432
|
const docsSearchTool = docsSupport?.createSearchTool();
|
|
1432
1433
|
docsSupport?.ensureReady().catch((error) => {
|
|
@@ -1903,7 +1904,7 @@ function truncate(str, max) {
|
|
|
1903
1904
|
//#region src/bootstrap.ts
|
|
1904
1905
|
const DEFAULT_BUILTIN_AGENT_HANDLER_PATH = `/_electric/builtin-agent-handler`;
|
|
1905
1906
|
async function createBuiltinAgentHandler(options) {
|
|
1906
|
-
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1907
|
+
const { agentServerUrl, serveEndpoint, workingDirectory, streamFn, createElectricTools, publicUrl, runtimeName, baseSkillsDir: baseSkillsDirOverride, serverHeaders, defaultDispatchPolicyForType } = options;
|
|
1907
1908
|
const modelCatalog = await createBuiltinModelCatalog({ allowMockFallback: Boolean(streamFn) });
|
|
1908
1909
|
if (!modelCatalog) {
|
|
1909
1910
|
serverLog.warn(`[builtin-agents] no supported model provider API key found — set ANTHROPIC_API_KEY or OPENAI_API_KEY`);
|
|
@@ -1911,7 +1912,7 @@ async function createBuiltinAgentHandler(options) {
|
|
|
1911
1912
|
}
|
|
1912
1913
|
const cwd = workingDirectory ?? process.cwd();
|
|
1913
1914
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
1914
|
-
const baseSkillsDir = path.resolve(here, `../skills`);
|
|
1915
|
+
const baseSkillsDir = baseSkillsDirOverride ?? path.resolve(here, `../skills`);
|
|
1915
1916
|
let skillsRegistry = null;
|
|
1916
1917
|
try {
|
|
1917
1918
|
skillsRegistry = await createSkillsRegistry({
|
|
@@ -2109,6 +2110,7 @@ var BuiltinAgentsServer = class {
|
|
|
2109
2110
|
createElectricTools: this.options.createElectricTools,
|
|
2110
2111
|
publicUrl,
|
|
2111
2112
|
runtimeName: `builtin-agents`,
|
|
2113
|
+
baseSkillsDir: this.options.baseSkillsDir,
|
|
2112
2114
|
serverHeaders: pullWake.headers
|
|
2113
2115
|
});
|
|
2114
2116
|
if (!this.bootstrap) throw new Error(`ANTHROPIC_API_KEY or OPENAI_API_KEY must be set before starting builtin agents`);
|
|
@@ -2214,14 +2216,6 @@ function validateUrl(name, value) {
|
|
|
2214
2216
|
throw new Error(`Invalid ${name}: "${value}"`);
|
|
2215
2217
|
}
|
|
2216
2218
|
}
|
|
2217
|
-
function buildAssertedAuthHeaders(env) {
|
|
2218
|
-
const headers = {};
|
|
2219
|
-
const email = readEnv(env, [`ELECTRIC_ASSERTED_AUTH_EMAIL`]);
|
|
2220
|
-
const name = readEnv(env, [`ELECTRIC_ASSERTED_AUTH_NAME`]);
|
|
2221
|
-
if (email) headers[`X-Electric-Asserted-Email`] = email;
|
|
2222
|
-
if (name) headers[`X-Electric-Asserted-Name`] = name;
|
|
2223
|
-
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
2224
|
-
}
|
|
2225
2219
|
function parseAdditionalServerHeaders(env) {
|
|
2226
2220
|
const raw = readEnv(env, [`ELECTRIC_AGENTS_SERVER_HEADERS`]);
|
|
2227
2221
|
if (!raw) return void 0;
|
|
@@ -2255,7 +2249,7 @@ function hasHeader(headers, name) {
|
|
|
2255
2249
|
function resolveBuiltinAgentsEntrypointOptions(env = process.env, cwd = process.cwd()) {
|
|
2256
2250
|
const agentServerUrl = validateUrl(`agent server URL`, readRequiredEnv(env, [`ELECTRIC_AGENTS_SERVER_URL`, `ELECTRIC_AGENTS_BASE_URL`], `agent server base URL`));
|
|
2257
2251
|
const runnerId = readRequiredEnv(env, [`ELECTRIC_AGENTS_PULL_WAKE_RUNNER_ID`, `PULL_WAKE_RUNNER_ID`], `pull-wake runner id`);
|
|
2258
|
-
const serverHeaders = mergeHeaders(
|
|
2252
|
+
const serverHeaders = mergeHeaders(parseAdditionalServerHeaders(env));
|
|
2259
2253
|
return {
|
|
2260
2254
|
agentServerUrl,
|
|
2261
2255
|
workingDirectory: readEnv(env, [`ELECTRIC_AGENTS_WORKING_DIRECTORY`, `WORKING_DIRECTORY`]) ?? cwd,
|
package/docs/index.md
CHANGED
|
@@ -60,7 +60,7 @@ The function that runs when an entity wakes. Receives a [`HandlerContext`](/docs
|
|
|
60
60
|
```ts
|
|
61
61
|
registry.define("support", {
|
|
62
62
|
async handler(ctx, wake) {
|
|
63
|
-
if (wake.type === "
|
|
63
|
+
if (wake.type === "inbox") {
|
|
64
64
|
ctx.useAgent({
|
|
65
65
|
systemPrompt: "You are a support agent.",
|
|
66
66
|
model: "claude-sonnet-4-5-20250929",
|
|
@@ -78,11 +78,11 @@ Events that trigger a handler invocation. Wake sources include incoming messages
|
|
|
78
78
|
|
|
79
79
|
```ts
|
|
80
80
|
async handler(ctx, wake) {
|
|
81
|
-
// wake.type — "
|
|
81
|
+
// wake.type — "inbox", "wake", etc.
|
|
82
82
|
// wake.source — who triggered the wake
|
|
83
83
|
// wake.payload — message content or wake data
|
|
84
84
|
|
|
85
|
-
if (wake.type === "
|
|
85
|
+
if (wake.type === "inbox") {
|
|
86
86
|
const userMessage = wake.payload
|
|
87
87
|
// handle incoming message
|
|
88
88
|
}
|
|
@@ -23,7 +23,7 @@ Every entity automatically has these 17 collections, populated by the runtime as
|
|
|
23
23
|
| `toolCalls` | `tool_call` | `ToolCall` | Tool call lifecycle |
|
|
24
24
|
| `reasoning` | `reasoning` | `Reasoning` | Reasoning block lifecycle |
|
|
25
25
|
| `errors` | `error` | `ErrorEvent` | Diagnostic errors |
|
|
26
|
-
| `inbox` | `
|
|
26
|
+
| `inbox` | `inbox` | `MessageReceived` | Inbound messages |
|
|
27
27
|
| `wakes` | `wake` | `WakeEntry` | Wake delivery records |
|
|
28
28
|
| `entityCreated` | `entity_created` | `EntityCreated` | Entity bootstrap metadata |
|
|
29
29
|
| `entityStopped` | `entity_stopped` | `EntityStopped` | Entity shutdown signal |
|
|
@@ -30,7 +30,7 @@ type WakeEvent = {
|
|
|
30
30
|
| Field | Type | Description |
|
|
31
31
|
| ------------ | --------- | ------------------------------------------------------------------------ |
|
|
32
32
|
| `source` | `string` | URL or identifier of the stream that triggered the wake. |
|
|
33
|
-
| `type` | `string` | Wake type. Usually `"
|
|
33
|
+
| `type` | `string` | Wake type. Usually `"inbox"` or `"wake"`; fallback webhook events can use `triggerEvent` or `"message"`. See catalog. |
|
|
34
34
|
| `fromOffset` | `number` | Start offset of new events in the source stream. |
|
|
35
35
|
| `toOffset` | `number` | End offset (exclusive) of new events. |
|
|
36
36
|
| `eventCount` | `number` | Number of new events in this wake. |
|
|
@@ -40,9 +40,9 @@ type WakeEvent = {
|
|
|
40
40
|
|
|
41
41
|
## Wake-type catalog
|
|
42
42
|
|
|
43
|
-
Handlers usually see two values for `wake.type`. Direct inbox messages arrive as `"
|
|
43
|
+
Handlers usually see two values for `wake.type`. Direct inbox messages arrive as `"inbox"`. Most non-message triggers are flattened into `"wake"`, with the specifics carried on `wake.payload`. Low-level webhook fallbacks can surface `triggerEvent` directly, or `"message"` when no trigger event is provided.
|
|
44
44
|
|
|
45
|
-
### `"
|
|
45
|
+
### `"inbox"`
|
|
46
46
|
|
|
47
47
|
An external message landed in the entity's inbox — from `ctx.send()`, the CLI's `electric agents send`, or any direct `/send` HTTP call.
|
|
48
48
|
|
|
@@ -89,7 +89,7 @@ Inspect the payload to distinguish the sub-kind:
|
|
|
89
89
|
| Observed change | `ctx.observe(..., { wake: { on: 'change' } })` or `observe(db(...))` | `payload.changes` is non-empty |
|
|
90
90
|
| Shared-state change | `await ctx.observe(db(...), { wake: { on: 'change' } })` | `payload.changes` is non-empty, `payload.source` identifies the shared-state stream |
|
|
91
91
|
| Cron fired | A cron schedule entry on the entity's manifest | `payload.source` identifies the schedule; `payload.changes` is empty |
|
|
92
|
-
| Scheduled send | A `future_send` schedule fires | Arrives as `"
|
|
92
|
+
| Scheduled send | A `future_send` schedule fires | Arrives as `"inbox"` (not `"wake"`) — the schedule produces a message delivery |
|
|
93
93
|
| Timeout | `timeoutMs` on a `change` wake config elapsed with no changes | `payload.timeout === true`, `payload.changes` is empty |
|
|
94
94
|
|
|
95
95
|
For the narrative on how these are produced, see [Waking entities](../usage/waking-entities).
|
|
@@ -31,13 +31,13 @@ There are five things that can wake an entity:
|
|
|
31
31
|
|
|
32
32
|
### 1. An incoming message
|
|
33
33
|
|
|
34
|
-
Any external `/send` (via the CLI, HTTP, or another entity's `ctx.send()`) appends a `
|
|
34
|
+
Any external `/send` (via the CLI, HTTP, or another entity's `ctx.send()`) appends a `inbox` event to the entity's stream, which wakes the handler:
|
|
35
35
|
|
|
36
36
|
```ts
|
|
37
37
|
ctx.send("/assistant/peer", { text: "hello" })
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
The receiving handler sees `wake.type === "
|
|
40
|
+
The receiving handler sees `wake.type === "inbox"` and finds the payload on `wake.payload`.
|
|
41
41
|
|
|
42
42
|
### 2. A spawned child
|
|
43
43
|
|
|
@@ -97,7 +97,7 @@ The minimum useful pattern is to branch on `wake.type`:
|
|
|
97
97
|
|
|
98
98
|
```ts
|
|
99
99
|
async handler(ctx, wake) {
|
|
100
|
-
if (wake.type === "
|
|
100
|
+
if (wake.type === "inbox") {
|
|
101
101
|
// external input - reply, dispatch, etc.
|
|
102
102
|
ctx.useAgent({ ... })
|
|
103
103
|
await ctx.agent.run()
|
|
@@ -112,8 +112,8 @@ async handler(ctx, wake) {
|
|
|
112
112
|
|
|
113
113
|
Two wake types reach handlers directly:
|
|
114
114
|
|
|
115
|
-
- `"
|
|
116
|
-
- `"wake"` — a synthesised wake for anything else (child finished, collection change, cron, timeout). The specifics are on `wake.payload`. A future-send schedule delivers a message, so it arrives as `"
|
|
115
|
+
- `"inbox"` — an external message was delivered to this entity's inbox.
|
|
116
|
+
- `"wake"` — a synthesised wake for anything else (child finished, collection change, cron, timeout). The specifics are on `wake.payload`. A future-send schedule delivers a message, so it arrives as `"inbox"`.
|
|
117
117
|
|
|
118
118
|
For the full payload shape (`changes[]`, `finished_child`, `other_children`, `timeout`), see the [wake-type catalog](../reference/wake-event#wake-type-catalog) in the reference.
|
|
119
119
|
|
|
@@ -120,7 +120,7 @@ type WakeEvent = {
|
|
|
120
120
|
| Field | Description |
|
|
121
121
|
| ------------ | -------------------------------------------------------------- |
|
|
122
122
|
| `source` | The stream or entity that caused the wake. |
|
|
123
|
-
| `type` | The wake type: `"
|
|
123
|
+
| `type` | The wake type: `"inbox"` for inbox messages or `"wake"` for child completion, observed changes, cron, and timeouts. |
|
|
124
124
|
| `fromOffset` | Start offset of the events that triggered this wake. |
|
|
125
125
|
| `toOffset` | End offset of the events that triggered this wake. |
|
|
126
126
|
| `eventCount` | Number of new events since last wake. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@electric-ax/agents",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Built-in Electric Agents runtimes such as Horton and worker",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
"@mariozechner/pi-agent-core": "^0.70.2",
|
|
33
33
|
"@mariozechner/pi-ai": "^0.70.2",
|
|
34
34
|
"@sinclair/typebox": "^0.34.48",
|
|
35
|
-
"better-sqlite3": "^
|
|
35
|
+
"better-sqlite3": "^12.9.0",
|
|
36
36
|
"nanoid": "^3.3.11",
|
|
37
37
|
"pino": "^10.3.1",
|
|
38
38
|
"pino-pretty": "^13.0.0",
|
|
39
39
|
"sqlite-vec": "^0.1.9",
|
|
40
40
|
"zod": "^4.3.6",
|
|
41
|
-
"@electric-ax/agents-mcp": "0.2.
|
|
42
|
-
"@electric-ax/agents-runtime": "0.2.
|
|
41
|
+
"@electric-ax/agents-mcp": "0.2.2",
|
|
42
|
+
"@electric-ax/agents-runtime": "0.2.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/better-sqlite3": "^7.6.13",
|