@onkeiki/agents 0.1.0

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.
Files changed (111) hide show
  1. package/dist/Alert.d.ts +56 -0
  2. package/dist/Alert.d.ts.map +1 -0
  3. package/dist/Alert.js +8 -0
  4. package/dist/Alert.js.map +1 -0
  5. package/dist/Entity.d.ts +158 -0
  6. package/dist/Entity.d.ts.map +1 -0
  7. package/dist/Entity.js +289 -0
  8. package/dist/Entity.js.map +1 -0
  9. package/dist/Kit.d.ts +138 -0
  10. package/dist/Kit.d.ts.map +1 -0
  11. package/dist/Kit.js +517 -0
  12. package/dist/Kit.js.map +1 -0
  13. package/dist/OpenApi.d.ts +151 -0
  14. package/dist/OpenApi.d.ts.map +1 -0
  15. package/dist/OpenApi.js +222 -0
  16. package/dist/OpenApi.js.map +1 -0
  17. package/dist/Plugin.d.ts +183 -0
  18. package/dist/Plugin.d.ts.map +1 -0
  19. package/dist/Plugin.js +10 -0
  20. package/dist/Plugin.js.map +1 -0
  21. package/dist/Server.d.ts +71 -0
  22. package/dist/Server.d.ts.map +1 -0
  23. package/dist/Server.js +476 -0
  24. package/dist/Server.js.map +1 -0
  25. package/dist/Tool.d.ts +86 -0
  26. package/dist/Tool.d.ts.map +1 -0
  27. package/dist/Tool.js +27 -0
  28. package/dist/Tool.js.map +1 -0
  29. package/dist/bundle.d.ts +17 -0
  30. package/dist/bundle.d.ts.map +1 -0
  31. package/dist/bundle.js +101 -0
  32. package/dist/bundle.js.map +1 -0
  33. package/dist/index.d.ts +19 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +18 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/services/Blackboard.d.ts +18 -0
  38. package/dist/services/Blackboard.d.ts.map +1 -0
  39. package/dist/services/Blackboard.js +4 -0
  40. package/dist/services/Blackboard.js.map +1 -0
  41. package/dist/services/Observability.d.ts +102 -0
  42. package/dist/services/Observability.d.ts.map +1 -0
  43. package/dist/services/Observability.js +238 -0
  44. package/dist/services/Observability.js.map +1 -0
  45. package/dist/services/Platform.d.ts +112 -0
  46. package/dist/services/Platform.d.ts.map +1 -0
  47. package/dist/services/Platform.js +13 -0
  48. package/dist/services/Platform.js.map +1 -0
  49. package/dist/services/PluginDb.d.ts +44 -0
  50. package/dist/services/PluginDb.d.ts.map +1 -0
  51. package/dist/services/PluginDb.js +5 -0
  52. package/dist/services/PluginDb.js.map +1 -0
  53. package/dist/services/Sandbox.d.ts +68 -0
  54. package/dist/services/Sandbox.d.ts.map +1 -0
  55. package/dist/services/Sandbox.js +4 -0
  56. package/dist/services/Sandbox.js.map +1 -0
  57. package/dist/services/Steering.d.ts +46 -0
  58. package/dist/services/Steering.d.ts.map +1 -0
  59. package/dist/services/Steering.js +4 -0
  60. package/dist/services/Steering.js.map +1 -0
  61. package/dist/services/Storage.d.ts +63 -0
  62. package/dist/services/Storage.d.ts.map +1 -0
  63. package/dist/services/Storage.js +13 -0
  64. package/dist/services/Storage.js.map +1 -0
  65. package/dist/services/Transport.d.ts +19 -0
  66. package/dist/services/Transport.d.ts.map +1 -0
  67. package/dist/services/Transport.js +4 -0
  68. package/dist/services/Transport.js.map +1 -0
  69. package/dist/services/index.d.ts +9 -0
  70. package/dist/services/index.d.ts.map +1 -0
  71. package/dist/services/index.js +9 -0
  72. package/dist/services/index.js.map +1 -0
  73. package/dist/types/config.d.ts +526 -0
  74. package/dist/types/config.d.ts.map +1 -0
  75. package/dist/types/config.js +2 -0
  76. package/dist/types/config.js.map +1 -0
  77. package/dist/types/index.d.ts +4 -0
  78. package/dist/types/index.d.ts.map +1 -0
  79. package/dist/types/index.js +4 -0
  80. package/dist/types/index.js.map +1 -0
  81. package/dist/types/message.d.ts +54 -0
  82. package/dist/types/message.d.ts.map +1 -0
  83. package/dist/types/message.js +47 -0
  84. package/dist/types/message.js.map +1 -0
  85. package/dist/types/user.d.ts +16 -0
  86. package/dist/types/user.d.ts.map +1 -0
  87. package/dist/types/user.js +14 -0
  88. package/dist/types/user.js.map +1 -0
  89. package/package.json +39 -0
  90. package/src/Alert.ts +51 -0
  91. package/src/Entity.ts +543 -0
  92. package/src/Kit.ts +705 -0
  93. package/src/OpenApi.ts +343 -0
  94. package/src/Plugin.ts +209 -0
  95. package/src/Server.ts +618 -0
  96. package/src/Tool.ts +110 -0
  97. package/src/bundle.ts +114 -0
  98. package/src/index.ts +209 -0
  99. package/src/services/Blackboard.ts +19 -0
  100. package/src/services/Observability.ts +320 -0
  101. package/src/services/Platform.ts +125 -0
  102. package/src/services/PluginDb.ts +48 -0
  103. package/src/services/Sandbox.ts +74 -0
  104. package/src/services/Steering.ts +45 -0
  105. package/src/services/Storage.ts +76 -0
  106. package/src/services/Transport.ts +21 -0
  107. package/src/services/index.ts +8 -0
  108. package/src/types/config.ts +531 -0
  109. package/src/types/index.ts +3 -0
  110. package/src/types/message.ts +60 -0
  111. package/src/types/user.ts +17 -0
package/dist/Kit.d.ts ADDED
@@ -0,0 +1,138 @@
1
+ import { Layer } from 'effect';
2
+ import type { AgentKitConfig, AgentConfig } from './types/config.js';
3
+ import type { AlertDefinition } from './Alert.js';
4
+ import type { AgentKitPlugin, SyncPayload } from './Plugin.js';
5
+ import type { PluginMigration } from './services/PluginDb.js';
6
+ import type { InboundMessage } from './types/index.js';
7
+ import type { ToolDefinition } from './Tool.js';
8
+ export type AgentKitInstance = {
9
+ /**
10
+ * Sync local agent config + tools to the platform (infra-as-code).
11
+ * Pushes model, systemPrompt, tools, and execution mode to the platform DB.
12
+ */
13
+ readonly sync: () => Promise<void>;
14
+ /**
15
+ * Start the HTTP server that receives turn requests from the platform.
16
+ * Also calls sync() to push config to platform.
17
+ */
18
+ readonly serve: (opts?: {
19
+ port?: number;
20
+ host?: string;
21
+ }) => Promise<void>;
22
+ /**
23
+ * Process a single inbound message programmatically (useful for testing).
24
+ */
25
+ readonly handleInbound: (msg: InboundMessage) => Promise<{
26
+ response: string;
27
+ messageId: string;
28
+ }>;
29
+ /**
30
+ * Graceful shutdown.
31
+ */
32
+ readonly shutdown: () => Promise<void>;
33
+ };
34
+ /**
35
+ * @deprecated Use {@link AgentKitInstance} instead. Kept for backwards compatibility.
36
+ */
37
+ export type SmsKitInstance = AgentKitInstance;
38
+ /**
39
+ * Resolved plugin state — the merged result of all plugins.
40
+ * Used internally by Kit, Server, and sync.
41
+ */
42
+ export type ResolvedPlugins = {
43
+ /** Agent config with plugin `init` contributions applied (explicit config wins). */
44
+ readonly agentConfig: AgentConfig;
45
+ /** Merged `PluginInitResult.extra` values, seeded into the sync payload's `extra`. */
46
+ readonly initExtra: Record<string, unknown>;
47
+ readonly tools: ReadonlyArray<ToolDefinition<any, any>>;
48
+ /** Alert definitions contributed by plugins, additive to `agent.alerts`. */
49
+ readonly alerts: ReadonlyArray<AlertDefinition>;
50
+ readonly routes: Readonly<Record<string, (req: Request, ctx: any) => Promise<Response>>>;
51
+ readonly hooks: {
52
+ readonly beforeTurn: ReadonlyArray<NonNullable<NonNullable<AgentKitPlugin['hooks']>['beforeTurn']>>;
53
+ readonly afterTurn: ReadonlyArray<NonNullable<NonNullable<AgentKitPlugin['hooks']>['afterTurn']>>;
54
+ readonly beforeToolCall: ReadonlyArray<NonNullable<NonNullable<AgentKitPlugin['hooks']>['beforeToolCall']>>;
55
+ readonly afterToolCall: ReadonlyArray<NonNullable<NonNullable<AgentKitPlugin['hooks']>['afterToolCall']>>;
56
+ };
57
+ readonly layers: ReadonlyArray<Layer.Layer<any>>;
58
+ readonly onSync: ReadonlyArray<NonNullable<AgentKitPlugin['onSync']>>;
59
+ readonly onRequest: ReadonlyArray<NonNullable<AgentKitPlugin['onRequest']>>;
60
+ readonly onResponse: ReadonlyArray<NonNullable<AgentKitPlugin['onResponse']>>;
61
+ readonly middleware: ReadonlyArray<NonNullable<AgentKitPlugin['middleware']>[number]>;
62
+ readonly migrations: ReadonlyArray<{
63
+ readonly pluginId: string;
64
+ readonly migrations: ReadonlyArray<PluginMigration>;
65
+ }>;
66
+ };
67
+ /**
68
+ * Merge a plugin's `init` config contribution into the agent config.
69
+ * Explicitly-set agent config keys always win over plugin defaults.
70
+ */
71
+ export declare function applyPluginConfig(agent: AgentConfig, overrides: Partial<AgentConfig>): AgentConfig;
72
+ /**
73
+ * Resolve all plugins into a single merged structure.
74
+ */
75
+ export declare function resolvePlugins(config: AgentKitConfig): ResolvedPlugins;
76
+ /**
77
+ * The agent's own alerts plus plugin-contributed ones, deduped by name
78
+ * (first definition wins, so agent config beats plugins). Used by both
79
+ * alert registration in `sync` and the `/alerts/execute` handler so the
80
+ * two cannot diverge.
81
+ */
82
+ export declare function mergedAlerts(config: AgentKitConfig, resolved: ResolvedPlugins): AlertDefinition[];
83
+ /**
84
+ * Run all plugin `onSync` hooks over the initial sync payload.
85
+ */
86
+ export declare function applySyncHooks(initial: SyncPayload, hooks: ReadonlyArray<NonNullable<AgentKitPlugin['onSync']>>): SyncPayload;
87
+ /**
88
+ * Create an AgentKit instance from config.
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * import { AgentKit } from '@onkeiki/agents'
93
+ * import { PostgresStorageLive } from '@onkeiki/agents-storage-postgres'
94
+ * import { memory, browser, steering } from '@onkeiki/agents-plugins'
95
+ *
96
+ * const kit = AgentKit.create({
97
+ * apiKey: process.env.KEIKI_API_KEY!,
98
+ * name: 'My Agent',
99
+ * resolveNumber: (ctx) => Effect.succeed('+14155551234'),
100
+ * storage: PostgresStorageLive({ connectionString: process.env.DATABASE_URL! }),
101
+ * plugins: [
102
+ * memory({ provider: 'supermemory', autoRetrieve: true }),
103
+ * browser({ handoff: true }),
104
+ * steering({ debounceWindow: '2 seconds', midTurnStrategy: 'interrupt' }),
105
+ * ],
106
+ * agent: {
107
+ * model: 'google/gemini-3.5-flash',
108
+ * maxSteps: 10,
109
+ * historyLimit: 40,
110
+ * systemPrompt: ({ user }) => `You are an assistant for ${user.phone}`,
111
+ * tools: [],
112
+ * },
113
+ * })
114
+ *
115
+ * kit.serve({ port: 8789 })
116
+ * ```
117
+ */
118
+ export declare const AgentKit: {
119
+ create(config: AgentKitConfig): AgentKitInstance;
120
+ };
121
+ /**
122
+ * @deprecated Use {@link AgentKit} instead. Kept for backwards compatibility.
123
+ */
124
+ export declare const SmsKit: {
125
+ create(config: AgentKitConfig): AgentKitInstance;
126
+ };
127
+ /**
128
+ * Channel-neutral alias of `AgentKit.create()`.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * import { create } from '@onkeiki/agents'
133
+ *
134
+ * const kit = create({ ... })
135
+ * ```
136
+ */
137
+ export declare const create: (config: AgentKitConfig) => AgentKitInstance;
138
+ //# sourceMappingURL=Kit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Kit.d.ts","sourceRoot":"","sources":["../src/Kit.ts"],"names":[],"mappings":"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"}
package/dist/Kit.js ADDED
@@ -0,0 +1,517 @@
1
+ import { Effect, Layer, Queue, Ref, pipe, JSONSchema } from 'effect';
2
+ import { StorageError } from './services/Storage.js';
3
+ import { Storage } from './services/Storage.js';
4
+ import { Platform, PlatformError } from './services/Platform.js';
5
+ import { Steering } from './services/Steering.js';
6
+ import { Blackboard } from './services/Blackboard.js';
7
+ import { bundleTool } from './bundle.js';
8
+ /**
9
+ * Merge a plugin's `init` config contribution into the agent config.
10
+ * Explicitly-set agent config keys always win over plugin defaults.
11
+ */
12
+ export function applyPluginConfig(agent, overrides) {
13
+ const explicit = Object.fromEntries(Object.entries(agent).filter(([, value]) => value !== undefined));
14
+ return { ...agent, ...overrides, ...explicit };
15
+ }
16
+ /**
17
+ * Resolve all plugins into a single merged structure.
18
+ */
19
+ export function resolvePlugins(config) {
20
+ const plugins = config.plugins ?? [];
21
+ let agentConfig = config.agent;
22
+ const initExtra = {};
23
+ const tools = [];
24
+ const routes = {};
25
+ const beforeTurn = [];
26
+ const afterTurn = [];
27
+ const beforeToolCall = [];
28
+ const afterToolCall = [];
29
+ const layers = [];
30
+ const onSync = [];
31
+ const onRequest = [];
32
+ const onResponse = [];
33
+ const middleware = [];
34
+ const migrations = [];
35
+ const alerts = [];
36
+ for (const plugin of plugins) {
37
+ // Run init — may contribute config overrides (not mutating original).
38
+ // Later plugins see earlier plugins' contributions.
39
+ const ctx = { config, agentConfig };
40
+ const initResult = plugin.init?.(ctx);
41
+ if (initResult?.config) {
42
+ agentConfig = applyPluginConfig(agentConfig, initResult.config);
43
+ }
44
+ if (initResult?.extra) {
45
+ Object.assign(initExtra, initResult.extra);
46
+ }
47
+ // Collect static migrations declared on the plugin
48
+ if (plugin.migrations && plugin.migrations.length > 0) {
49
+ migrations.push({ pluginId: plugin.id, migrations: plugin.migrations });
50
+ }
51
+ if (plugin.tools)
52
+ tools.push(...plugin.tools);
53
+ if (plugin.alerts)
54
+ alerts.push(...plugin.alerts);
55
+ if (plugin.routes)
56
+ Object.assign(routes, plugin.routes);
57
+ if (plugin.hooks?.beforeTurn)
58
+ beforeTurn.push(plugin.hooks.beforeTurn);
59
+ if (plugin.hooks?.afterTurn)
60
+ afterTurn.push(plugin.hooks.afterTurn);
61
+ if (plugin.hooks?.beforeToolCall)
62
+ beforeToolCall.push(plugin.hooks.beforeToolCall);
63
+ if (plugin.hooks?.afterToolCall)
64
+ afterToolCall.push(plugin.hooks.afterToolCall);
65
+ if (plugin.layers)
66
+ layers.push(...plugin.layers);
67
+ if (plugin.onSync)
68
+ onSync.push(plugin.onSync);
69
+ if (plugin.onRequest)
70
+ onRequest.push(plugin.onRequest);
71
+ if (plugin.onResponse)
72
+ onResponse.push(plugin.onResponse);
73
+ if (plugin.middleware)
74
+ middleware.push(...plugin.middleware);
75
+ }
76
+ return {
77
+ agentConfig,
78
+ initExtra,
79
+ tools,
80
+ alerts,
81
+ routes,
82
+ hooks: { beforeTurn, afterTurn, beforeToolCall, afterToolCall },
83
+ layers,
84
+ onSync,
85
+ onRequest,
86
+ onResponse,
87
+ middleware,
88
+ migrations,
89
+ };
90
+ }
91
+ /**
92
+ * The agent's own alerts plus plugin-contributed ones, deduped by name
93
+ * (first definition wins, so agent config beats plugins). Used by both
94
+ * alert registration in `sync` and the `/alerts/execute` handler so the
95
+ * two cannot diverge.
96
+ */
97
+ export function mergedAlerts(config, resolved) {
98
+ const seen = new Set();
99
+ return [...(config.agent.alerts ?? []), ...resolved.alerts].filter((a) => {
100
+ if (seen.has(a.name))
101
+ return false;
102
+ seen.add(a.name);
103
+ return true;
104
+ });
105
+ }
106
+ /**
107
+ * Run all plugin `onSync` hooks over the initial sync payload.
108
+ */
109
+ export function applySyncHooks(initial, hooks) {
110
+ let payload = initial;
111
+ for (const onSync of hooks) {
112
+ payload = onSync(payload);
113
+ }
114
+ return payload;
115
+ }
116
+ function isStorageBundle(s) {
117
+ return 'layer' in s && 'pluginDb' in s;
118
+ }
119
+ /** Cloud agents keep history and users on the platform; every local storage call fails loudly. */
120
+ const NoStorage = Layer.succeed(Storage, new Proxy({}, {
121
+ get: (_, method) => () => Effect.fail(new StorageError(`Storage.${String(method)} called but no \`storage\` was passed to AgentKit.create`)),
122
+ }));
123
+ function unwrapStorage(config) {
124
+ if (!config.storage)
125
+ return { layer: NoStorage, pluginDb: undefined };
126
+ if (isStorageBundle(config.storage)) {
127
+ return { layer: config.storage.layer, pluginDb: config.storage.pluginDb };
128
+ }
129
+ return { layer: config.storage, pluginDb: undefined };
130
+ }
131
+ /**
132
+ * Run plugin-owned migrations using the configured PluginDb.
133
+ * Each plugin's migrations are tracked in `smskit_plugin_migrations`.
134
+ * Migrations are additive only — tables are NEVER dropped when a plugin is disabled.
135
+ */
136
+ async function runPluginMigrations(pluginDb, migrations) {
137
+ if (migrations.length === 0 || !pluginDb)
138
+ return;
139
+ const db = pluginDb;
140
+ await Effect.runPromise(db.execute(`
141
+ CREATE TABLE IF NOT EXISTS smskit_plugin_migrations (
142
+ plugin_id TEXT NOT NULL,
143
+ version INT NOT NULL,
144
+ name TEXT NOT NULL,
145
+ applied_at TIMESTAMPTZ DEFAULT now(),
146
+ PRIMARY KEY (plugin_id, version)
147
+ )
148
+ `));
149
+ for (const { pluginId, pluginMigrations } of migrations.map((m) => ({ pluginId: m.pluginId, pluginMigrations: m.migrations }))) {
150
+ const applied = await Effect.runPromise(db.query(`SELECT version FROM smskit_plugin_migrations WHERE plugin_id = $1 ORDER BY version`, [pluginId]));
151
+ const appliedSet = new Set(applied.map((r) => r.version));
152
+ for (const migration of [...pluginMigrations].sort((a, b) => a.version - b.version)) {
153
+ if (appliedSet.has(migration.version))
154
+ continue;
155
+ await Effect.runPromise(db.execute(migration.up));
156
+ await Effect.runPromise(db.execute(`INSERT INTO smskit_plugin_migrations (plugin_id, version, name) VALUES ($1, $2, $3)`, [pluginId, migration.version, migration.name]));
157
+ }
158
+ }
159
+ }
160
+ /**
161
+ * Create an AgentKit instance from config.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * import { AgentKit } from '@onkeiki/agents'
166
+ * import { PostgresStorageLive } from '@onkeiki/agents-storage-postgres'
167
+ * import { memory, browser, steering } from '@onkeiki/agents-plugins'
168
+ *
169
+ * const kit = AgentKit.create({
170
+ * apiKey: process.env.KEIKI_API_KEY!,
171
+ * name: 'My Agent',
172
+ * resolveNumber: (ctx) => Effect.succeed('+14155551234'),
173
+ * storage: PostgresStorageLive({ connectionString: process.env.DATABASE_URL! }),
174
+ * plugins: [
175
+ * memory({ provider: 'supermemory', autoRetrieve: true }),
176
+ * browser({ handoff: true }),
177
+ * steering({ debounceWindow: '2 seconds', midTurnStrategy: 'interrupt' }),
178
+ * ],
179
+ * agent: {
180
+ * model: 'google/gemini-3.5-flash',
181
+ * maxSteps: 10,
182
+ * historyLimit: 40,
183
+ * systemPrompt: ({ user }) => `You are an assistant for ${user.phone}`,
184
+ * tools: [],
185
+ * },
186
+ * })
187
+ *
188
+ * kit.serve({ port: 8789 })
189
+ * ```
190
+ */
191
+ export const AgentKit = {
192
+ create(config) {
193
+ const resolved = resolvePlugins(config);
194
+ const effectiveConfig = { ...config, agent: resolved.agentConfig };
195
+ const { layer: storageLayer, pluginDb } = unwrapStorage(effectiveConfig);
196
+ const platformLayer = makePlatformLayer(effectiveConfig);
197
+ const steeringLayer = makeSteeringLayer(effectiveConfig.agent);
198
+ const blackboardLayer = makeBlackboardLayer();
199
+ const fullLayer = pipe(storageLayer, Layer.merge(platformLayer), Layer.merge(steeringLayer), Layer.merge(blackboardLayer));
200
+ return {
201
+ sync: async () => {
202
+ await runPluginMigrations(pluginDb, resolved.migrations);
203
+ await syncToPlatform(effectiveConfig, resolved);
204
+ },
205
+ serve: async (opts) => {
206
+ const port = opts?.port ?? 8789;
207
+ const host = opts?.host ?? '0.0.0.0';
208
+ // Start server first so it's ready to receive turns
209
+ const { createServer } = await import('./Server.js');
210
+ const server = createServer(effectiveConfig, resolved);
211
+ Bun.serve({
212
+ port,
213
+ hostname: host,
214
+ fetch: server.fetch,
215
+ });
216
+ console.log(`@onkeiki/agents serving on ${host}:${port}`);
217
+ // Run plugin migrations and sync config
218
+ await runPluginMigrations(pluginDb, resolved.migrations);
219
+ await syncToPlatform(effectiveConfig, resolved, port);
220
+ },
221
+ handleInbound: async (msg) => {
222
+ return Effect.runPromise(Effect.gen(function* () {
223
+ const steering = yield* Steering;
224
+ yield* steering.enqueue(msg);
225
+ return { response: '', messageId: '' };
226
+ }).pipe(Effect.provide(fullLayer)));
227
+ },
228
+ shutdown: async () => {
229
+ console.log('@onkeiki/agents shutting down');
230
+ },
231
+ };
232
+ },
233
+ };
234
+ /**
235
+ * @deprecated Use {@link AgentKit} instead. Kept for backwards compatibility.
236
+ */
237
+ export const SmsKit = AgentKit;
238
+ /**
239
+ * Channel-neutral alias of `AgentKit.create()`.
240
+ *
241
+ * @example
242
+ * ```ts
243
+ * import { create } from '@onkeiki/agents'
244
+ *
245
+ * const kit = create({ ... })
246
+ * ```
247
+ */
248
+ export const create = (config) => AgentKit.create(config);
249
+ // --- Internal helpers ---
250
+ function platformFetch(config, path, body) {
251
+ return Effect.tryPromise({
252
+ try: async () => {
253
+ const res = await fetch(`${getPlatformUrl()}${path}`, {
254
+ method: body ? 'POST' : 'GET',
255
+ headers: {
256
+ 'Authorization': `Bearer ${config.apiKey}`,
257
+ 'Content-Type': 'application/json',
258
+ },
259
+ ...(body ? { body: JSON.stringify(body) } : {}),
260
+ });
261
+ if (!res.ok) {
262
+ throw new Error(`Platform API error ${res.status}: ${await res.text()}`);
263
+ }
264
+ return await res.json();
265
+ },
266
+ catch: (e) => new PlatformError(String(e), e),
267
+ });
268
+ }
269
+ function makePlatformLayer(config) {
270
+ return Layer.succeed(Platform, {
271
+ memoryRecall: (phone, query, opts) => platformFetch(config, '/v1/memory/recall', { phone, query, ...opts }).pipe(Effect.map((r) => r.facts ?? [])),
272
+ memoryLearn: (phone, facts) => platformFetch(config, '/v1/memory/learn', { phone, facts }).pipe(Effect.asVoid),
273
+ browse: (instruction, opts) => platformFetch(config, '/v1/browser/browse', { instruction, ...opts }),
274
+ handoff: (opts) => platformFetch(config, '/v1/browser/handoff', opts),
275
+ exec: (command, opts) => platformFetch(config, '/v1/sandbox/exec', { command, ...opts }),
276
+ writeFile: (path, content, opts) => platformFetch(config, '/v1/sandbox/write', { path, content, ...opts }).pipe(Effect.asVoid),
277
+ readFile: (path, opts) => platformFetch(config, '/v1/sandbox/read', { path, ...opts }).pipe(Effect.map((r) => r.content)),
278
+ runJob: (opts) => platformFetch(config, '/v1/executor/run', opts),
279
+ getJobStatus: (jobId) => platformFetch(config, `/v1/executor/status/${jobId}`),
280
+ escalate: (opts) => platformFetch(config, '/v1/escalation/create', opts),
281
+ extractFacts: (phone, messages) => platformFetch(config, '/v1/learning/extract', { phone, messages }).pipe(Effect.map((r) => r.facts ?? [])),
282
+ transcribeAudio: (url) => platformFetch(config, '/v1/media/transcribe', { url }).pipe(Effect.map((r) => r.text)),
283
+ describeImage: (url, prompt) => platformFetch(config, '/v1/media/describe', { url, prompt }).pipe(Effect.map((r) => r.description)),
284
+ extractDocument: (url) => platformFetch(config, '/v1/media/extract-document', { url }).pipe(Effect.map((r) => r.content)),
285
+ });
286
+ }
287
+ function makeSteeringLayer(agent) {
288
+ return Layer.effect(Steering, Effect.gen(function* () {
289
+ const decisionQueue = yield* Queue.unbounded();
290
+ const states = yield* Ref.make(new Map());
291
+ return {
292
+ enqueue: (msg) => Effect.gen(function* () {
293
+ const stateMap = yield* Ref.get(states);
294
+ const existing = stateMap.get(msg.phone);
295
+ if (!existing || existing.status === 'idle') {
296
+ yield* Ref.update(states, (m) => {
297
+ const updated = new Map(m);
298
+ updated.set(msg.phone, { phone: msg.phone, status: 'debouncing', pending: [msg] });
299
+ return updated;
300
+ });
301
+ }
302
+ else if (existing.status === 'debouncing') {
303
+ yield* Ref.update(states, (m) => {
304
+ const updated = new Map(m);
305
+ updated.set(msg.phone, { ...existing, pending: [...existing.pending, msg] });
306
+ return updated;
307
+ });
308
+ }
309
+ else if (existing.status === 'processing') {
310
+ const strategy = agent.steering?.midTurnStrategy ?? 'append';
311
+ if (strategy === 'interrupt') {
312
+ yield* Queue.offer(decisionQueue, { type: 'interrupt', messages: [msg] });
313
+ }
314
+ else {
315
+ yield* Queue.offer(decisionQueue, { type: 'append', message: msg });
316
+ }
317
+ }
318
+ }),
319
+ decisions: Effect.succeed(decisionQueue),
320
+ turnCompleted: (phone) => Ref.update(states, (m) => {
321
+ const updated = new Map(m);
322
+ updated.set(phone, { phone, status: 'idle', pending: [] });
323
+ return updated;
324
+ }),
325
+ getState: (phone) => Ref.get(states).pipe(Effect.map((m) => m.get(phone) ?? { phone, status: 'idle', pending: [] })),
326
+ };
327
+ }));
328
+ }
329
+ function makeBlackboardLayer() {
330
+ return Layer.effect(Blackboard, Effect.gen(function* () {
331
+ const store = yield* Ref.make(new Map());
332
+ return {
333
+ read: (key) => Ref.get(store).pipe(Effect.map((m) => m.get(key) ?? null)),
334
+ write: (key, value) => Ref.update(store, (m) => { const n = new Map(m); n.set(key, value); return n; }),
335
+ delete: (key) => Ref.update(store, (m) => { const n = new Map(m); n.delete(key); return n; }),
336
+ entries: () => Ref.get(store).pipe(Effect.map((m) => [...m.entries()])),
337
+ clear: () => Ref.set(store, new Map()),
338
+ };
339
+ }));
340
+ }
341
+ /**
342
+ * Sync local agent config + tools + alerts + secrets to the platform (infra-as-code).
343
+ */
344
+ async function syncToPlatform(config, resolved, port) {
345
+ const platformUrl = getPlatformUrl();
346
+ console.log(`[agents] Syncing to platform at ${platformUrl}`);
347
+ const headers = {
348
+ 'Authorization': `Bearer ${config.apiKey}`,
349
+ 'Content-Type': 'application/json',
350
+ };
351
+ // Build a static system prompt for platform storage
352
+ const staticPrompt = config.agent.systemPrompt({
353
+ user: { phone: '', timezone: null, currency: null, metadata: {}, firstSeenAt: new Date() },
354
+ now: new Date(),
355
+ phone: '',
356
+ context: { memorySummary: null, relevantFacts: [] },
357
+ isFirstInteraction: false,
358
+ history: [],
359
+ staticSync: true,
360
+ });
361
+ // Merge agent tools + plugin tools
362
+ const allTools = [...config.agent.tools, ...resolved.tools];
363
+ // Build tool definitions for the sync payload
364
+ const isSdkMode = port !== undefined;
365
+ const toolDefs = await Promise.all(allTools.map(async (tool) => ({
366
+ name: tool.name,
367
+ description: tool.description,
368
+ inputSchema: JSONSchema.make(tool.input),
369
+ executionType: 'code',
370
+ code: isSdkMode ? null : await bundleTool(tool),
371
+ })));
372
+ // Derive features flags from agent config
373
+ const features = {
374
+ memory: !!config.agent.memory?.enabled,
375
+ browser: !!config.agent.browser?.enabled,
376
+ sandbox: !!config.agent.sandbox?.enabled,
377
+ executor: !!config.agent.executor?.enabled,
378
+ mcp: !!config.agent.mcp?.enabled,
379
+ blackboard: !!config.agent.blackboard,
380
+ promptCache: !!config.agent.promptCache,
381
+ escalation: !!config.agent.escalation?.enabled,
382
+ steering: !!config.agent.steering,
383
+ media: !!config.agent.media,
384
+ loops: !!config.agent.loops?.enabled,
385
+ };
386
+ const loopPresets = (config.agent.loops?.presets ?? []).map((p) => ({
387
+ name: p.name,
388
+ description: p.description,
389
+ prompt: p.prompt,
390
+ schedule_type: p.scheduleType,
391
+ schedule_expression: p.scheduleExpression,
392
+ timezone: p.timezone ?? null,
393
+ quiet: p.quiet ?? false,
394
+ }));
395
+ const userConnections = (config.agent.mcp?.userConnections ?? []).map((c) => ({
396
+ name: c.name,
397
+ description: c.description ?? null,
398
+ aliases: c.aliases ?? [],
399
+ vendor: c.vendor ?? null,
400
+ capabilities: c.capabilities ?? [],
401
+ spec: c.spec,
402
+ base_url: c.baseUrl ?? null,
403
+ oauth: {
404
+ authorization_url: c.oauth.authorizationUrl,
405
+ token_url: c.oauth.tokenUrl,
406
+ client_id_secret: c.oauth.clientIdSecret,
407
+ client_secret_secret: c.oauth.clientSecretSecret,
408
+ },
409
+ health_check: c.healthCheck
410
+ ? {
411
+ operation: c.healthCheck.operation,
412
+ args: c.healthCheck.args ?? null,
413
+ identity_field: c.healthCheck.identityField ?? null,
414
+ }
415
+ : null,
416
+ }));
417
+ // Single atomic sync call
418
+ const syncBody = {
419
+ name: config.name,
420
+ model: config.agent.model,
421
+ max_steps: config.agent.maxSteps,
422
+ history_limit: config.agent.historyLimit,
423
+ system_prompt: staticPrompt,
424
+ execution_mode: port ? 'sdk' : 'platform',
425
+ webhook_url: port ? `http://localhost:${port}` : undefined,
426
+ harness: config.harness?.type ?? 'flue',
427
+ harness_config: config.harness ? {
428
+ mode: config.harness.connectivity,
429
+ dispatchUrl: config.harness.dispatchUrl,
430
+ timeoutMs: config.harness.timeoutMs === undefined
431
+ ? undefined
432
+ : durationInputToMs(config.harness.timeoutMs),
433
+ } : undefined,
434
+ features,
435
+ loop_presets: loopPresets,
436
+ user_connections: userConnections,
437
+ capabilities: config.agent.capabilities ?? undefined,
438
+ tools: toolDefs,
439
+ };
440
+ // Let plugins augment the sync payload
441
+ const payload = applySyncHooks({
442
+ name: syncBody.name,
443
+ model: syncBody.model,
444
+ tools: toolDefs.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
445
+ features,
446
+ extra: { ...resolved.initExtra },
447
+ }, resolved.onSync);
448
+ const syncRes = await fetch(`${platformUrl}/api/v1/developer/sync`, {
449
+ method: 'POST',
450
+ headers,
451
+ body: JSON.stringify({
452
+ ...syncBody,
453
+ name: payload.name,
454
+ model: payload.model,
455
+ features: payload.features,
456
+ extra: payload.extra,
457
+ }),
458
+ });
459
+ let devAgentId;
460
+ if (!syncRes.ok) {
461
+ console.warn(`[agents] Sync failed: ${syncRes.status} ${await syncRes.text()}`);
462
+ }
463
+ else {
464
+ const result = await syncRes.json();
465
+ devAgentId = result.devAgentId;
466
+ console.log(`[agents] Sync complete: agent="${config.name}" id=${devAgentId} tools=${allTools.length}`);
467
+ }
468
+ // Sync alerts (if any defined) — the agent's own plus plugin-contributed.
469
+ const alerts = mergedAlerts(config, resolved);
470
+ if (alerts.length > 0) {
471
+ const alertDefs = alerts.map((alert) => ({
472
+ name: alert.name,
473
+ description: alert.description,
474
+ schedule: alert.schedule,
475
+ }));
476
+ const alertsRes = await fetch(`${platformUrl}/api/v1/developer/alerts/sync`, {
477
+ method: 'POST',
478
+ headers,
479
+ body: JSON.stringify({ devAgentId, alerts: alertDefs }),
480
+ });
481
+ if (!alertsRes.ok) {
482
+ console.warn(`[agents] Alerts sync failed: ${alertsRes.status} ${await alertsRes.text()}`);
483
+ }
484
+ else {
485
+ console.log(`[agents] Alerts synced: ${alerts.length} alert(s)`);
486
+ }
487
+ }
488
+ // Sync secrets (if any defined)
489
+ if (config.secrets && Object.keys(config.secrets).length > 0) {
490
+ const secretsRes = await fetch(`${platformUrl}/api/v1/developer/secrets/sync`, {
491
+ method: 'POST',
492
+ headers,
493
+ body: JSON.stringify({ secrets: config.secrets }),
494
+ });
495
+ if (!secretsRes.ok) {
496
+ console.warn(`[agents] Secrets sync failed: ${secretsRes.status} ${await secretsRes.text()}`);
497
+ }
498
+ else {
499
+ console.log(`[agents] Secrets synced: ${Object.keys(config.secrets).length} secret(s)`);
500
+ }
501
+ }
502
+ }
503
+ function durationInputToMs(value) {
504
+ if (typeof value === 'number')
505
+ return value;
506
+ const [amountText, unit] = value.split(' ');
507
+ const amount = Number(amountText);
508
+ if (unit === 'hours')
509
+ return amount * 3_600_000;
510
+ if (unit === 'minutes')
511
+ return amount * 60_000;
512
+ return amount * 1_000;
513
+ }
514
+ function getPlatformUrl() {
515
+ return process.env.KEIKI_PLATFORM_URL ?? 'https://smskit-production.up.railway.app';
516
+ }
517
+ //# sourceMappingURL=Kit.js.map