@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
@@ -0,0 +1,21 @@
1
+ import { Context, Effect } from 'effect'
2
+ import type { MediaPayload, SendResult } from '../types/index.js'
3
+
4
+ export interface TransportService {
5
+ readonly send: (opts: {
6
+ from: string
7
+ to: string
8
+ text: string
9
+ media?: MediaPayload
10
+ }) => Effect.Effect<SendResult>
11
+
12
+ readonly getDeliveryStatus: (messageId: string) => Effect.Effect<{
13
+ state: 'delivered' | 'failed' | 'pending'
14
+ error?: string
15
+ }>
16
+ }
17
+
18
+ export class Transport extends Context.Tag('@onkeiki/agents/Transport')<
19
+ Transport,
20
+ TransportService
21
+ >() {}
@@ -0,0 +1,8 @@
1
+ export { Storage, StorageError, type StorageService, type NewMessage, type DeliveryClaim, type UsageEvent, type BillingPeriod, type UsageSummary } from './Storage.js'
2
+ export { Transport, type TransportService } from './Transport.js'
3
+ export { Platform, PlatformError, type PlatformService, type MemoryFact, type BrowseResult, type SandboxExecResult, type HandoffResult, type EscalationResult, type BackgroundJobResult } from './Platform.js'
4
+ export { Steering, type SteeringService, type SteeringDecision, type TurnState } from './Steering.js'
5
+ export { Blackboard, type BlackboardService } from './Blackboard.js'
6
+ export { Observability, ObservabilityError, braintrustExporter, axiomExporter, otelExporter, customExporter, type TraceExporter, type TraceEvent, type SpanEvent, type SpanKind, type SpanStatus } from './Observability.js'
7
+ export { SandboxProvider, type SandboxProviderService, type SandboxHandle, type SandboxExecResult as SandboxProviderExecResult } from './Sandbox.js'
8
+ export { PluginDb, type PluginDbService, type PluginMigration } from './PluginDb.js'
@@ -0,0 +1,531 @@
1
+ import type { Effect, Layer } from 'effect'
2
+ import type { UserProfile } from './user.js'
3
+ import type { InboundMessage, DeliveryStatus } from './message.js'
4
+ import type { Storage, StorageError } from '../services/Storage.js'
5
+ import type { Transport } from '../services/Transport.js'
6
+ import type { AgentKitPlugin } from '../Plugin.js'
7
+ import type { ToolDefinition } from '../Tool.js'
8
+ import type { AlertDefinition } from '../Alert.js'
9
+ import type { TraceExporter } from '../services/Observability.js'
10
+ import type { PluginDbService } from '../services/PluginDb.js'
11
+
12
+ export type DurationInput = `${number} ${'seconds' | 'minutes' | 'hours'}` | number
13
+
14
+ export type HarnessConfig = {
15
+ readonly type: 'flue' | 'hermes'
16
+ readonly dispatchUrl?: string
17
+ readonly timeoutMs?: DurationInput
18
+ readonly connectivity?: 'push' | 'relay'
19
+ }
20
+
21
+ export type PromptContext = {
22
+ readonly user: UserProfile
23
+ readonly now: Date
24
+ readonly phone: string
25
+ readonly context: RetrievedContext
26
+ /** True when this turn carries the user's first-ever message (no prior history). */
27
+ readonly isFirstInteraction: boolean
28
+ /** Conversation history given to the model this turn, oldest first. */
29
+ readonly history: ReadonlyArray<{ readonly content: string; readonly direction: 'inbound' | 'outbound' }>
30
+ /**
31
+ * True when the prompt is being rendered once for platform storage
32
+ * (kit.sync) rather than for a live turn. Omit time-of-day content in that
33
+ * case — the platform injects a fresh Current Date/Time section per turn.
34
+ */
35
+ readonly staticSync?: boolean
36
+ }
37
+
38
+ /** Turn-loop progress visible to {@link TurnHooks} after each step. */
39
+ export type TurnHookState = {
40
+ /** 0-based index of the step about to run (prepareStep) or just finished (stopWhen). */
41
+ readonly step: number
42
+ /** Tool calls executed so far this turn. */
43
+ readonly toolCalls: number
44
+ /** Name of the most recently executed tool, if any. */
45
+ readonly lastToolName?: string
46
+ /** Error message from the most recent tool call, when it failed. */
47
+ readonly lastToolError?: string
48
+ /** JSON-serialized input of the most recent tool call, if any. */
49
+ readonly lastToolInput?: string
50
+ /** Consecutive identical tool failures (same tool, input, and error) ending at the most recent call. 0 when the last call succeeded. */
51
+ readonly identicalFailureStreak: number
52
+ }
53
+
54
+ /** What {@link TurnHooks.reviewReply} sees when a candidate final reply is produced. */
55
+ export type TurnReviewState = {
56
+ /** Tool calls executed this turn, in order. */
57
+ readonly toolResults: ReadonlyArray<{ readonly name: string; readonly input: unknown; readonly output: unknown }>
58
+ /** 0-based count of review retries already spent this turn. */
59
+ readonly attempt: number
60
+ }
61
+
62
+ /**
63
+ * Developer hooks into the agent turn loop.
64
+ */
65
+ export type TurnHooks = {
66
+ /** Rewrite the final reply before it is returned (sanitizers, length caps, sign-offs). */
67
+ readonly transformReply?: (reply: string, ctx: PromptContext) => string
68
+ /** Called before each LLM step; may inject an extra system message for that step only. */
69
+ readonly prepareStep?: (state: TurnHookState, ctx: PromptContext) => { readonly appendSystem?: string } | undefined
70
+ /** Extra stop condition checked after each step's tool calls. Return true to end the turn. */
71
+ readonly stopWhen?: (state: TurnHookState, ctx: PromptContext) => boolean
72
+ /**
73
+ * Gate a candidate final reply (called on the post-`transformReply` text —
74
+ * what would actually be delivered). Return `{ retryWith }` to reject it:
75
+ * the corrective text is appended as a system message and the turn loop
76
+ * continues (bounded retries). A rejected reply is never delivered — if
77
+ * retries run out or the step budget ends on a rejected candidate, the
78
+ * turn returns an empty reply instead.
79
+ */
80
+ readonly reviewReply?: (reply: string, ctx: PromptContext, state: TurnReviewState) => { readonly retryWith?: string } | undefined
81
+ }
82
+
83
+ export type RetrievedContext = {
84
+ readonly memorySummary: string | null
85
+ readonly relevantFacts: ReadonlyArray<{ fact: string; category: string }>
86
+ }
87
+
88
+ export type ResolveNumberContext = {
89
+ readonly phone: string
90
+ readonly user: UserProfile
91
+ readonly direction: 'inbound' | 'outbound'
92
+ readonly platform: {
93
+ readonly getUserMeta: (phone: string, key: string) => Effect.Effect<string | null>
94
+ readonly setUserMeta: (phone: string, key: string, value: string) => Effect.Effect<void>
95
+ readonly acquireLine: (opts: AcquireLineOpts) => Effect.Effect<PhoneLine>
96
+ }
97
+ }
98
+
99
+ export type AcquireLineOpts = {
100
+ readonly pool: string
101
+ readonly strategy: 'least-active' | 'round-robin' | 'random' | 'geo-closest'
102
+ readonly sticky?: boolean
103
+ readonly region?: string
104
+ }
105
+
106
+ export type PhoneLine = {
107
+ readonly number: string
108
+ readonly pool: string
109
+ readonly region: string
110
+ readonly activeConversations: number
111
+ }
112
+
113
+ export type MemoryConfig = {
114
+ readonly enabled: boolean
115
+ readonly provider: 'supermemory'
116
+ readonly autoRetrieve?: boolean
117
+ readonly categories?: ReadonlyArray<string>
118
+ }
119
+
120
+ export type BrowserConfig = {
121
+ readonly enabled: boolean
122
+ readonly handoff?: boolean
123
+ readonly screenshotDelivery?: boolean
124
+ }
125
+
126
+ export type SandboxConfig = {
127
+ readonly enabled: boolean
128
+ readonly persistent?: boolean
129
+ readonly maxExecTime?: DurationInput
130
+ }
131
+
132
+ export type ExecutorConfig = {
133
+ readonly enabled: boolean
134
+ readonly maxConcurrent?: number
135
+ readonly timeout?: DurationInput
136
+ readonly onComplete?: 'notify' | 'silent'
137
+ }
138
+
139
+ /**
140
+ * Executor.sh–backed custom tool support (the `mcp()` plugin). Lets conversation
141
+ * users connect their own remote MCP servers and OpenAPI specs at runtime.
142
+ * Requires the Sandbox capability.
143
+ */
144
+ export type McpConfig = {
145
+ readonly enabled: boolean
146
+ /** Max sources (MCP servers + OpenAPI specs) a user can connect. */
147
+ readonly maxServers?: number
148
+ /** Whether conversation users may add sources themselves. */
149
+ readonly userCanAdd?: boolean
150
+ /**
151
+ * Developer-declared per-user connections. Each entry names an OpenAPI
152
+ * provider (e.g. Gmail) that every conversation user authorizes
153
+ * individually: the agent texts them a fresh connect link, they complete
154
+ * OAuth in the browser, and the resulting credential lives in that user's
155
+ * own Executor daemon — never shared across users or exposed to the model.
156
+ */
157
+ readonly userConnections?: ReadonlyArray<UserConnectionDefinition>
158
+ }
159
+
160
+ /**
161
+ * OAuth app config for a user connection. The client id/secret are named
162
+ * developer secrets (synced via `secrets`), resolved server-side at connect
163
+ * time — the values never appear in the agent config or model context.
164
+ */
165
+ export type UserConnectionOAuth = {
166
+ readonly authorizationUrl: string
167
+ readonly tokenUrl: string
168
+ /** Developer-secret name holding the OAuth client id. */
169
+ readonly clientIdSecret: string
170
+ /** Developer-secret name holding the OAuth client secret. */
171
+ readonly clientSecretSecret: string
172
+ }
173
+
174
+ /**
175
+ * The connection's liveness probe: one read-only operation the user's daemon
176
+ * runs against the real provider to answer "is this credential still alive?".
177
+ * Without it health falls back to local credential resolution, which cannot
178
+ * detect a token revoked upstream — declare one for every connection.
179
+ */
180
+ export type UserConnectionHealthCheck = {
181
+ /** Operation name as it appears in the spec (e.g. `users.getProfile`). */
182
+ readonly operation: string
183
+ /** Pinned arguments the operation needs (e.g. Gmail's `{ userId: "me" }`). */
184
+ readonly args?: Readonly<Record<string, unknown>>
185
+ /** Dot-path into the response shown as the connection's identity. */
186
+ readonly identityField?: string
187
+ }
188
+
189
+ export type UserConnectionDefinition = {
190
+ /** Stable connection name, also the Executor source slug (e.g. "gmail"). */
191
+ readonly name: string
192
+ /** Shown to the model so it knows when to offer connecting this provider. */
193
+ readonly description?: string
194
+ /** Alternate names users may say for THIS connection (e.g. "gcal"). */
195
+ readonly aliases?: ReadonlyArray<string>
196
+ /**
197
+ * The company behind the provider ("google", "microsoft"). Not an alias: two
198
+ * connections may share a vendor, and a shared vendor word is ambiguous
199
+ * rather than a match, so it can never silently select one of them.
200
+ */
201
+ readonly vendor?: string
202
+ /**
203
+ * What this connection lets the agent do, as `domain.verb` ("mail.read",
204
+ * "calendar.write"). Declaring these lets a caller that says a vendor word
205
+ * plus what it needs ("google", "calendar") resolve without being asked.
206
+ */
207
+ readonly capabilities?: ReadonlyArray<string>
208
+ /** OpenAPI spec URL registered as the Executor source. */
209
+ readonly spec: string
210
+ /** Override for the API base URL when the spec's `servers` entry is wrong. */
211
+ readonly baseUrl?: string
212
+ readonly oauth: UserConnectionOAuth
213
+ readonly healthCheck?: UserConnectionHealthCheck
214
+ }
215
+
216
+ export type SteeringConfig = {
217
+ readonly debounceWindow: DurationInput
218
+ readonly midTurnStrategy: 'interrupt' | 'append' | 'queue'
219
+ readonly maxBatch: number
220
+ readonly classify?: (
221
+ pending: ReadonlyArray<InboundMessage>,
222
+ incoming: InboundMessage
223
+ ) => Effect.Effect<'steer' | 'new-turn'>
224
+ }
225
+
226
+ export type MediaActionConfig = {
227
+ readonly action: 'transcribe' | 'describe' | 'extract' | 'ignore' | 'pass-url' | 'pass-raw'
228
+ readonly model?: string
229
+ readonly prompt?: string
230
+ readonly maxSize?: string
231
+ readonly format?: (content: string) => string
232
+ }
233
+
234
+ export type MediaConfig = {
235
+ readonly audio: MediaActionConfig
236
+ readonly image: MediaActionConfig
237
+ readonly video: MediaActionConfig
238
+ readonly document: MediaActionConfig
239
+ readonly fallback: string
240
+ }
241
+
242
+ export type EscalationRoute = {
243
+ readonly webhook?: string
244
+ readonly slack?: { readonly channel: string }
245
+ readonly email?: string
246
+ }
247
+
248
+ export type EscalationConfig = {
249
+ readonly enabled: boolean
250
+ readonly requireConfirmation?: boolean
251
+ readonly routes: EscalationRoute
252
+ }
253
+
254
+ export type EventHandlers = {
255
+ readonly onInbound?: (msg: InboundMessage) => Effect.Effect<{ proceed: boolean; enrichment?: Record<string, unknown> }>
256
+ readonly onOutbound?: (msg: { phone: string; text: string; messageId: string }) => Effect.Effect<void>
257
+ readonly onDeliveryStatus?: (status: DeliveryStatus) => Effect.Effect<void>
258
+ readonly onNewUser?: (event: { phone: string; firstMessage: string }) => Effect.Effect<void>
259
+ readonly onOptOut?: (event: { phone: string }) => Effect.Effect<void>
260
+ }
261
+
262
+ export type ObservabilityConfig = {
263
+ readonly enabled: boolean
264
+ readonly exporters: ReadonlyArray<TraceExporter>
265
+ readonly sampleRate?: number // 0.0 - 1.0, default 1.0 (export all traces)
266
+ readonly redactInputs?: boolean // Strip PII from span inputs before export
267
+ readonly redactOutputs?: boolean // Strip PII from span outputs before export
268
+ }
269
+
270
+ /**
271
+ * A developer-defined loop template synced to the platform with the agent.
272
+ * The model creates it with `create_loop { preset: <name> }` — the prompt and
273
+ * schedule come from this definition, so they never travel through a tool call.
274
+ */
275
+ export type LoopPresetDefinition = {
276
+ readonly name: string
277
+ /** Shown to the model so it knows when to offer this loop. */
278
+ readonly description: string
279
+ readonly prompt: string
280
+ readonly scheduleType: 'once' | 'cron' | 'interval'
281
+ readonly scheduleExpression: string
282
+ readonly timezone?: string
283
+ readonly quiet?: boolean
284
+ }
285
+
286
+ export type LoopsConfig = {
287
+ readonly enabled: boolean
288
+ readonly presets?: ReadonlyArray<LoopPresetDefinition>
289
+ }
290
+
291
+ /**
292
+ * A reference to an already-published capability, pinned to an exact version.
293
+ * `publisher` defaults to `official`. See docs/capabilities.md.
294
+ */
295
+ export type CapabilityRef = {
296
+ readonly slug: string
297
+ readonly version: string
298
+ readonly publisher?: string
299
+ }
300
+
301
+ export type CapabilityRuntime = 'render-template' | 'openapi' | 'codemode' | 'mcp' | 'builtin'
302
+
303
+ /** Host namespaces a capability may declare in `uses` (optionally `name@range`). */
304
+ export type CapabilityNamespace = 'loops' | 'entities' | 'sandbox' | 'browser' | 'mcp' | 'cards' | 'http'
305
+
306
+ export type CapabilitySecret = {
307
+ readonly name: string
308
+ readonly required?: boolean
309
+ readonly description?: string
310
+ }
311
+
312
+ export type CapabilityField = {
313
+ readonly name: string
314
+ readonly type: string
315
+ readonly required?: boolean
316
+ readonly description?: string
317
+ }
318
+
319
+ /** How a tool runs; `kind` always equals the manifest `runtime`. */
320
+ export type CapabilityToolRun<R extends CapabilityRuntime = CapabilityRuntime> = R extends 'render-template'
321
+ ? {
322
+ readonly kind: R
323
+ /** Name of an entry in the manifest's `templates`. */
324
+ readonly template: string
325
+ /** Tool argument sent as plain text if rendering fails (default `fallback_text`). */
326
+ readonly textFallbackArg?: string
327
+ }
328
+ : { readonly kind: R }
329
+
330
+ /** A tool the installing agent sees; it only ever reads `description` and `inputSchema`. */
331
+ export type CapabilityTool<R extends CapabilityRuntime = CapabilityRuntime> = {
332
+ readonly name: string
333
+ readonly description: string
334
+ readonly inputSchema: Record<string, unknown>
335
+ readonly run: CapabilityToolRun<R>
336
+ }
337
+
338
+ export type CapabilityTemplate = {
339
+ readonly name: string
340
+ readonly source: string
341
+ readonly inputSchema?: { readonly fields: ReadonlyArray<CapabilityField> }
342
+ }
343
+
344
+ /** A durable, per-user table the capability owns (requires `uses: ["entities"]`). */
345
+ export type CapabilityEntity = {
346
+ readonly name: string
347
+ readonly description?: string
348
+ readonly fields?: ReadonlyArray<CapabilityField>
349
+ readonly maxRowsPerUser?: number
350
+ }
351
+
352
+ /** The check the platform runs before the agent on every loop tick. */
353
+ export type CapabilityLoopProbe =
354
+ | { readonly kind: 'execute'; readonly code: string; readonly connections?: 'personal' | 'shared' }
355
+ | {
356
+ readonly kind: 'gmail.history' | 'calendar.sync' | 'calendar.upcoming' | 'outlook.delta' | 'outlook-calendar.upcoming'
357
+ readonly integration?: string
358
+ readonly connections?: 'personal' | 'shared'
359
+ readonly leadMinutes?: number
360
+ readonly labelId?: string | null
361
+ }
362
+
363
+ /** When a probe's changes start the agent; anything but `always` needs a `probe`. */
364
+ export type CapabilityLoopTrigger =
365
+ | { readonly kind: 'always' }
366
+ | { readonly kind: 'any_change' }
367
+ | { readonly kind: 'added'; readonly min?: number }
368
+ | { readonly kind: 'match'; readonly pattern: string; readonly on?: 'added' | 'any' }
369
+ | { readonly kind: 'model'; readonly question: string; readonly model?: string }
370
+
371
+ /** A loop installed with the capability (requires `uses: ["loops"]`). */
372
+ export type CapabilityLoop = {
373
+ readonly name: string
374
+ readonly description?: string
375
+ readonly prompt: string
376
+ readonly scheduleType: 'once' | 'cron' | 'interval'
377
+ readonly scheduleExpression: string
378
+ readonly timezone?: string
379
+ readonly quiet?: boolean
380
+ readonly requiresConnections?: ReadonlyArray<string>
381
+ readonly probe?: CapabilityLoopProbe
382
+ readonly trigger?: CapabilityLoopTrigger
383
+ }
384
+
385
+ /**
386
+ * A versioned capability manifest, published privately under the developer's
387
+ * organization from `agent.capabilities` at sync time. Discriminated on
388
+ * `runtime`, so every tool's `run.kind` must match it and `templates` only
389
+ * exist on `render-template`. The platform validates the same shape; see
390
+ * docs/capabilities.md.
391
+ */
392
+ export type CapabilityManifest = {
393
+ [R in CapabilityRuntime]: {
394
+ /** `[a-z0-9-]`, max 64 chars, unique within the publisher. */
395
+ readonly slug: string
396
+ /** Exact semver, e.g. `1.0.0`. */
397
+ readonly version: string
398
+ readonly title: string
399
+ readonly description: string
400
+ readonly runtime: R
401
+ readonly uses?: ReadonlyArray<CapabilityNamespace | `${CapabilityNamespace}@${string}`>
402
+ readonly secrets?: ReadonlyArray<CapabilitySecret>
403
+ readonly tools?: ReadonlyArray<CapabilityTool<R>>
404
+ /** Guidance for the installing agent on when to use the tools. */
405
+ readonly prompt?: string
406
+ readonly loops?: ReadonlyArray<CapabilityLoop>
407
+ readonly entities?: ReadonlyArray<string | CapabilityEntity>
408
+ /** Hosts the `http` namespace may reach, `host` (port 443) or `host:port` (required with `uses: ["http"]`). */
409
+ readonly http?: { readonly allow: ReadonlyArray<string> }
410
+ } & (R extends 'render-template' ? { readonly templates?: ReadonlyArray<CapabilityTemplate> } : { readonly templates?: never })
411
+ }[CapabilityRuntime]
412
+
413
+ export type CapabilityInput = CapabilityRef | CapabilityManifest
414
+
415
+ export type AgentConfig = {
416
+ readonly model: string
417
+ readonly maxSteps: number
418
+ readonly historyLimit: number
419
+ readonly systemPrompt: (ctx: PromptContext) => string
420
+ readonly tools: ReadonlyArray<ToolDefinition<any, any>>
421
+ readonly alerts?: ReadonlyArray<AlertDefinition>
422
+ readonly hooks?: TurnHooks
423
+
424
+ readonly memory?: MemoryConfig
425
+ readonly browser?: BrowserConfig
426
+ readonly sandbox?: SandboxConfig
427
+ readonly executor?: ExecutorConfig
428
+ readonly mcp?: McpConfig
429
+ readonly blackboard?: boolean
430
+ readonly promptCache?: boolean
431
+ readonly escalation?: EscalationConfig
432
+ readonly steering?: SteeringConfig
433
+ readonly media?: MediaConfig
434
+ readonly observability?: ObservabilityConfig
435
+ readonly loops?: LoopsConfig
436
+ /** Capabilities installed on this agent: refs to published capabilities or full manifests. */
437
+ readonly capabilities?: ReadonlyArray<CapabilityInput>
438
+ }
439
+
440
+ /**
441
+ * A storage adapter bundled with a PluginDb service.
442
+ * Returned by adapter factories like `PostgresStorage()`.
443
+ */
444
+ export type StorageBundle = {
445
+ readonly layer: Layer.Layer<Storage, StorageError>
446
+ readonly pluginDb: PluginDbService
447
+ }
448
+
449
+ /**
450
+ * Channel type identifiers for multi-channel agent support.
451
+ */
452
+ export type ChannelType = 'sms' | 'telegram' | (string & {})
453
+
454
+ /**
455
+ * Telegram channel configuration.
456
+ */
457
+ export type TelegramChannelConfig = {
458
+ readonly type: 'telegram'
459
+ /** Telegram Bot API token. */
460
+ readonly botToken: string
461
+ /** Optional: restrict to specific chat IDs. */
462
+ readonly allowedChatIds?: ReadonlyArray<number>
463
+ }
464
+
465
+ /**
466
+ * SMS channel configuration (wraps the legacy resolveNumber pattern).
467
+ */
468
+ export type SmsChannelConfig = {
469
+ readonly type: 'sms'
470
+ /** Resolve which outbound phone line to use for a given context. */
471
+ readonly resolveNumber: (ctx: ResolveNumberContext) => Effect.Effect<string>
472
+ }
473
+
474
+ /**
475
+ * Union of supported channel configurations.
476
+ */
477
+ export type ChannelConfig = SmsChannelConfig | TelegramChannelConfig
478
+
479
+ export type AgentKitConfig = {
480
+ readonly apiKey: string
481
+ readonly name: string
482
+ /**
483
+ * @deprecated Use `channels` array with an SMS channel config instead.
484
+ * Kept for backward compatibility with SMS-only agents.
485
+ */
486
+ readonly resolveNumber?: (ctx: ResolveNumberContext) => Effect.Effect<string>
487
+ /**
488
+ * Channel configurations. Each channel handles its own inbound routing
489
+ * and outbound delivery. If omitted with a `resolveNumber`, a single
490
+ * SMS channel is implied.
491
+ *
492
+ * @example
493
+ * ```ts
494
+ * channels: [
495
+ * { type: 'sms', resolveNumber: (ctx) => Effect.succeed('+14155551234') },
496
+ * { type: 'telegram', botToken: process.env.TG_BOT_TOKEN! },
497
+ * ]
498
+ * ```
499
+ */
500
+ readonly channels?: ReadonlyArray<ChannelConfig>
501
+ /**
502
+ * Storage adapter for a self-run loop. Pass either:
503
+ * - A raw `Layer` (backward compat, no plugin DB support)
504
+ * - A `StorageBundle` from a factory like `PostgresStorage()` (includes both storage and pluginDb)
505
+ * Cloud agents (the default harness) need none: the platform stores history and users.
506
+ */
507
+ readonly storage?: Layer.Layer<Storage, StorageError> | StorageBundle
508
+ readonly agent: AgentConfig
509
+ /** Select the turn loop implementation. Defaults to the platform Flue harness. */
510
+ readonly harness?: HarnessConfig
511
+ readonly events?: EventHandlers
512
+ /** Secrets to sync to the platform (name → value). Accessible at runtime via ctx.secrets. */
513
+ readonly secrets?: Readonly<Record<string, string>>
514
+ /**
515
+ * Plugins extend the agent with tools, hooks, routes, and layers.
516
+ * Each plugin is self-contained and composable — capabilities that
517
+ * were previously part of AgentConfig (memory, browser, sandbox,
518
+ * steering, etc.) can now be isolated into plugins.
519
+ *
520
+ * Order matters: plugins initialize in array order, and each plugin's
521
+ * `init` sees the config contributions of the plugins before it — so a
522
+ * plugin that depends on another's config (e.g. `mcp()` requiring an
523
+ * enabled sandbox) must come after it.
524
+ */
525
+ readonly plugins?: ReadonlyArray<AgentKitPlugin>
526
+ }
527
+
528
+ /**
529
+ * @deprecated Use {@link AgentKitConfig} instead. Kept for backwards compatibility.
530
+ */
531
+ export type SmsKitConfig = AgentKitConfig
@@ -0,0 +1,3 @@
1
+ export * from './message.js'
2
+ export * from './user.js'
3
+ export * from './config.js'
@@ -0,0 +1,60 @@
1
+ import { Schema } from 'effect'
2
+
3
+ export const Direction = Schema.Literal('inbound', 'outbound')
4
+ export type Direction = typeof Direction.Type
5
+
6
+ export const InboundMessage = Schema.Struct({
7
+ phone: Schema.String,
8
+ chatId: Schema.String,
9
+ messageId: Schema.String,
10
+ text: Schema.String,
11
+ mediaUrl: Schema.optional(Schema.String),
12
+ mediaMimeType: Schema.optional(Schema.String),
13
+ timestamp: Schema.Date,
14
+ })
15
+ export type InboundMessage = typeof InboundMessage.Type
16
+
17
+ export const OutboundMessage = Schema.Struct({
18
+ phone: Schema.String,
19
+ chatId: Schema.String,
20
+ text: Schema.String,
21
+ mediaUrl: Schema.optional(Schema.String),
22
+ externalMessageId: Schema.optional(Schema.String),
23
+ })
24
+ export type OutboundMessage = typeof OutboundMessage.Type
25
+
26
+ export const StoredMessage = Schema.Struct({
27
+ id: Schema.String,
28
+ phone: Schema.String,
29
+ chatId: Schema.String,
30
+ direction: Direction,
31
+ content: Schema.String,
32
+ mediaUrl: Schema.optional(Schema.String),
33
+ mediaMimeType: Schema.optional(Schema.String),
34
+ externalMessageId: Schema.optional(Schema.String),
35
+ createdAt: Schema.Date,
36
+ })
37
+ export type StoredMessage = typeof StoredMessage.Type
38
+
39
+ export const MediaPayload = Schema.Struct({
40
+ url: Schema.optional(Schema.String),
41
+ base64: Schema.optional(Schema.String),
42
+ mimeType: Schema.String,
43
+ filename: Schema.optional(Schema.String),
44
+ })
45
+ export type MediaPayload = typeof MediaPayload.Type
46
+
47
+ export const SendResult = Schema.Struct({
48
+ chatId: Schema.String,
49
+ messageId: Schema.String,
50
+ })
51
+ export type SendResult = typeof SendResult.Type
52
+
53
+ export const DeliveryStatus = Schema.Struct({
54
+ messageId: Schema.String,
55
+ phone: Schema.String,
56
+ state: Schema.Literal('delivered', 'failed', 'pending'),
57
+ error: Schema.optional(Schema.String),
58
+ timestamp: Schema.Date,
59
+ })
60
+ export type DeliveryStatus = typeof DeliveryStatus.Type
@@ -0,0 +1,17 @@
1
+ import { Schema } from 'effect'
2
+
3
+ export const UserProfile = Schema.Struct({
4
+ phone: Schema.String,
5
+ timezone: Schema.NullOr(Schema.String),
6
+ currency: Schema.NullOr(Schema.String),
7
+ metadata: Schema.Record({ key: Schema.String, value: Schema.Unknown }),
8
+ firstSeenAt: Schema.Date,
9
+ })
10
+ export type UserProfile = typeof UserProfile.Type
11
+
12
+ export const Subscriber = Schema.Struct({
13
+ phone: Schema.String,
14
+ chatId: Schema.String,
15
+ preferences: Schema.Record({ key: Schema.String, value: Schema.Unknown }),
16
+ })
17
+ export type Subscriber = typeof Subscriber.Type