@on-mission/sdk 0.1.4 → 0.1.9

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 (54) hide show
  1. package/dist/chunk-N26TYEUP.js +50 -0
  2. package/dist/chunk-N26TYEUP.js.map +1 -0
  3. package/dist/debug-log-SHNBLZWL.js +9 -0
  4. package/dist/debug-log-SHNBLZWL.js.map +1 -0
  5. package/dist/index.d.ts +868 -18
  6. package/dist/index.js +15843 -11
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -2
  9. package/dist/api.d.ts +0 -20
  10. package/dist/api.d.ts.map +0 -1
  11. package/dist/api.js +0 -39
  12. package/dist/api.js.map +0 -1
  13. package/dist/auth-failure-tracker.d.ts +0 -22
  14. package/dist/auth-failure-tracker.d.ts.map +0 -1
  15. package/dist/auth-failure-tracker.js +0 -31
  16. package/dist/auth-failure-tracker.js.map +0 -1
  17. package/dist/auth.d.ts +0 -65
  18. package/dist/auth.d.ts.map +0 -1
  19. package/dist/auth.js +0 -45
  20. package/dist/auth.js.map +0 -1
  21. package/dist/config.d.ts +0 -39
  22. package/dist/config.d.ts.map +0 -1
  23. package/dist/config.js +0 -28
  24. package/dist/config.js.map +0 -1
  25. package/dist/context.d.ts +0 -24
  26. package/dist/context.d.ts.map +0 -1
  27. package/dist/context.js +0 -22
  28. package/dist/context.js.map +0 -1
  29. package/dist/event-handler.d.ts +0 -16
  30. package/dist/event-handler.d.ts.map +0 -1
  31. package/dist/event-handler.js +0 -27
  32. package/dist/event-handler.js.map +0 -1
  33. package/dist/http-server.d.ts +0 -51
  34. package/dist/http-server.d.ts.map +0 -1
  35. package/dist/http-server.js +0 -163
  36. package/dist/http-server.js.map +0 -1
  37. package/dist/index.d.ts.map +0 -1
  38. package/dist/ingress.d.ts +0 -133
  39. package/dist/ingress.d.ts.map +0 -1
  40. package/dist/ingress.js +0 -89
  41. package/dist/ingress.js.map +0 -1
  42. package/dist/logging.d.ts +0 -63
  43. package/dist/logging.d.ts.map +0 -1
  44. package/dist/logging.js +0 -171
  45. package/dist/logging.js.map +0 -1
  46. package/dist/sdk.d.ts +0 -197
  47. package/dist/sdk.d.ts.map +0 -1
  48. package/dist/sdk.js +0 -435
  49. package/dist/sdk.js.map +0 -1
  50. package/dist/tool-registry.d.ts +0 -49
  51. package/dist/tool-registry.d.ts.map +0 -1
  52. package/dist/tool-registry.js +0 -37
  53. package/dist/tool-registry.js.map +0 -1
  54. package/dist/tsconfig.tsbuildinfo +0 -1
package/dist/index.d.ts CHANGED
@@ -1,3 +1,862 @@
1
+ import { FileRef, StagingProvenance, ToolCallInput } from '@mission/models';
2
+ export { FileRef, FileRefStore, MAX_STAGING_UPLOAD_BYTES, StagingProvenance, Tool, ToolCallContext, ToolCallEnvelope, ToolCallInput, ToolFunction, ToolFunctionPermission, ToolKind, fileRefSchema } from '@mission/models';
3
+ import { createTRPCProxyClient } from '@trpc/client';
4
+
5
+ /**
6
+ * Skill Context
7
+ *
8
+ * Bootstrapped from environment variables injected by the Mission control plane
9
+ * when it starts the skill process inside the Daytona sandbox.
10
+ *
11
+ * See docs/technical/legacy/architecture/skills/architecture.md §4.5.
12
+ */
13
+ type SkillContext = {
14
+ /** Mission control plane API base URL. */
15
+ apiUrl: string;
16
+ /** JWT scoped to this skill installation (for outbound API calls). */
17
+ skillToken: string;
18
+ /** Stable identifier for this skill installation (per-workspace). */
19
+ installationId: string;
20
+ /** Port assigned by the control plane for this skill's HTTP server. */
21
+ skillPort: number;
22
+ };
23
+
24
+ /**
25
+ * API Client
26
+ *
27
+ * Typed tRPC client for all SDK -> Mission gateway communication.
28
+ * The public SDK surface stays the same (`mission.auth.get()`, etc.),
29
+ * while transport details are centralized here.
30
+ *
31
+ * Transient-error retry: the fetch link below retries connection-level
32
+ * failures (ECONNREFUSED, socket hang up, ECONNRESET, ETIMEDOUT, generic
33
+ * `fetch failed` TypeError) and 5xx responses with jittered exponential
34
+ * backoff for up to ~60 seconds. This is essential at skill startup, when
35
+ * a sandbox can race the gateway's readiness — without it, a single
36
+ * transient miss kills the whole skill process and triggers a permanent
37
+ * connection disconnect via sandboxRestartWorkflow.
38
+ */
39
+
40
+ type GatewayClient = ReturnType<typeof createTRPCProxyClient<GatewayRouter>>;
41
+ type ApiClient = {
42
+ trpc: GatewayClient;
43
+ baseUrl: string;
44
+ setToken: (token: string) => void;
45
+ getToken: () => string;
46
+ };
47
+
48
+ /**
49
+ * Auth Module
50
+ *
51
+ * Unified interface for all authentication credentials.
52
+ * `mission.auth.get()` returns a single object containing:
53
+ * - User-provided credentials (API keys, tokens, secrets from the connection form)
54
+ * - OAuth token data (for managed auth providers like Google, Slack)
55
+ *
56
+ * Skill authors always call `auth.get()` regardless of auth type.
57
+ *
58
+ * For managed auth (Nango-backed), the SDK caches tokens and refreshes
59
+ * transparently. See docs/technical/legacy/architecture/skills/authentication.md.
60
+ *
61
+ * For custom auth, credentials are collected via a connection form
62
+ * defined in mission.json's `auth.credentials` schema.
63
+ *
64
+ * Transport: tRPC mutation/query via Mission gateway API.
65
+ */
66
+
67
+ /**
68
+ * OAuth credentials returned for managed auth providers.
69
+ * The control plane handles token refresh (via Nango or equivalent).
70
+ */
71
+ type OAuthCredentials = {
72
+ /** The current access token string. */
73
+ accessToken: string;
74
+ /** OAuth 1.0a access token secret. Only present for OAuth 1.0a providers. */
75
+ accessTokenSecret?: string;
76
+ /** OAuth 1.0a / app-level consumer key. Only present for OAuth 1.0a providers. */
77
+ consumerKey?: string;
78
+ /** OAuth 1.0a / app-level consumer secret. Only present for OAuth 1.0a providers. */
79
+ consumerSecret?: string;
80
+ /** When this token expires (Unix milliseconds). */
81
+ expiresAt: number;
82
+ /** Scopes that were granted. */
83
+ scopes: string[];
84
+ /** The provider identifier (e.g. 'google', 'slack'). */
85
+ provider: string;
86
+ };
87
+ /**
88
+ * The unified auth response.
89
+ *
90
+ * For custom auth (Sentry, Twilio, etc.):
91
+ * { credentials: { authToken: 'sntrys_...', organization: 'my-org' } }
92
+ *
93
+ * For managed OAuth (Gmail, Slack, etc.):
94
+ * { credentials: {}, oauth: { accessToken: 'ya29...', ... } }
95
+ *
96
+ * For mixed (future: OAuth + user-provided extras):
97
+ * { credentials: { webhookSecret: '...' }, oauth: { accessToken: '...', ... } }
98
+ */
99
+ type AuthCredentials = {
100
+ /** User-provided credential values from the connection form. */
101
+ credentials: Record<string, string>;
102
+ /** OAuth token data. Present when auth.type is "managed". */
103
+ oauth?: OAuthCredentials;
104
+ };
105
+ type AuthModule = {
106
+ /**
107
+ * Get all authentication data for this skill installation.
108
+ *
109
+ * Returns user-provided credentials (API keys, tokens from the
110
+ * connection form) and, for managed OAuth providers, a current
111
+ * access token that the SDK keeps refreshed.
112
+ *
113
+ * This is safe to call repeatedly — the SDK caches internally.
114
+ */
115
+ get: () => Promise<AuthCredentials>;
116
+ };
117
+
118
+ /**
119
+ * Config Module
120
+ *
121
+ * Fetches per-installation configuration from the control plane.
122
+ * Accepts optional defaults so skills always get a complete config
123
+ * object without manual merging.
124
+ *
125
+ * The control plane stores config per Connection. Values are set
126
+ * by the user in the App's settings UI (defined by configSchema in
127
+ * mission.json). The SDK merges platform values over provided defaults
128
+ * so the returned object is always complete.
129
+ *
130
+ * Transport: tRPC query via Mission gateway API.
131
+ *
132
+ * See docs/product/apps.md § App configuration state.
133
+ */
134
+
135
+ type ConfigModule = {
136
+ /**
137
+ * Fetch the full configuration object for this skill installation.
138
+ *
139
+ * If `defaults` are provided, the SDK merges platform-stored values
140
+ * over them, so the result is always a complete `T` with no undefined
141
+ * fields. Without defaults, returns whatever the platform has stored.
142
+ *
143
+ * @example
144
+ * ```ts
145
+ * const config = await mission.config.get<GmailConfig>({
146
+ * watchLabels: ['INBOX'],
147
+ * maxResults: 20,
148
+ * signatureLine: ''
149
+ * })
150
+ * // config is guaranteed to have all three fields
151
+ * ```
152
+ */
153
+ get: <T = Record<string, unknown>>(defaults?: Partial<T>) => Promise<T>;
154
+ };
155
+
156
+ /**
157
+ * Debug Logging — Local Development
158
+ *
159
+ * Writes structured logs to /tmp/mission-debug.log for visibility into
160
+ * event flow in local sandboxes where console.log isn't visible.
161
+ *
162
+ * Only active when DEBUG_MISSION_EVENTS=1 env var is set.
163
+ */
164
+ declare const debugLog: (topic: string, message: string, data?: Record<string, unknown>) => void;
165
+ declare const clearDebugLog: () => void;
166
+
167
+ /**
168
+ * Files Module
169
+ *
170
+ * `mission.files` — the skill-facing seam for moving file bytes without ever
171
+ * routing them through the Mission platform, the model, the tool-call path, or
172
+ * any log.
173
+ *
174
+ * The contract:
175
+ *
176
+ * - The model only ever holds a `FileRef` handle — a PLAIN object reference
177
+ * `{store, key, filename, mimeType, sizeBytes}` — never bytes and never any
178
+ * authority. The handle is not signed.
179
+ * - The skill REDEEMS a `FileRef` for a read URL, never constructs one.
180
+ * References are produced server-side by a tool running where authz exists.
181
+ * The skill presents `{store, key}` to the gateway, which RE-AUTHORIZES the
182
+ * read server-side (first-party gate + workspace/object BOLA pin from the key
183
+ * grammar) and mints a signed GCS read URL.
184
+ * - Bytes move skill ⇄ GCS DIRECTLY over short-lived signed URLs. The platform
185
+ * NEVER proxies bytes.
186
+ * - A signed URL is a LIVE CREDENTIAL. It is redeemed once and discarded —
187
+ * never logged, never returned in a tool result, never put in model context.
188
+ *
189
+ * Authz lives entirely in the gateway endpoint (`skill.redeemFileRef`). The
190
+ * skill presents a reference; the platform re-authorizes and decides. Reads
191
+ * only — no write path exists (a write over a caller-named key was rejected by
192
+ * the security review; inbound bytes use the platform-allocates-key
193
+ * `stageInboundBytes` flow).
194
+ *
195
+ * Transport for the redeem verb: tRPC mutation via the Mission gateway API.
196
+ * Transport for the byte transfer: a plain `fetch` straight to GCS.
197
+ */
198
+
199
+ /**
200
+ * Thrown when an external byte transfer (a GCS GET/PUT) does not complete within
201
+ * its deadline. Distinct from an HTTP error so callers can tell a hung
202
+ * connection from a 4xx/5xx. A hung byte transfer otherwise wedges the sandbox
203
+ * holding the full attachment buffer until the max_duration kill — the dominant
204
+ * trigger for the response-lost double-send.
205
+ */
206
+ declare class FileTransferTimeoutError extends Error {
207
+ constructor(operation: string);
208
+ }
209
+ type FilesModule = {
210
+ /**
211
+ * Redeem a `FileRef` for a signed read URL. Presents `{store, key}` to the
212
+ * gateway, which re-authorizes the read server-side. The returned `readUrl` is
213
+ * a live credential — redeem and discard. Prefer `getBytes` unless you need to
214
+ * drive the GET yourself.
215
+ */
216
+ redeemForRead: (ref: FileRef) => Promise<{
217
+ readUrl: string;
218
+ }>;
219
+ /**
220
+ * Download the bytes a `ref` points at. Wraps `redeemForRead` + the GCS GET
221
+ * inside the SDK so the signed URL never leaves this module. Bytes stream
222
+ * GCS → skill; the platform never sees them.
223
+ */
224
+ getBytes: (ref: FileRef) => Promise<Buffer>;
225
+ /**
226
+ * Stage INBOUND bytes (e.g. an email attachment a skill just downloaded) and
227
+ * obtain a read `FileRef` over the result — the SAFE, platform-allocates-key
228
+ * flow.
229
+ *
230
+ * The skill NEVER names a storage key. The platform allocates a server-derived,
231
+ * content-addressed key, hands back a signed PUT URL + the plain allocated key;
232
+ * the SDK streams the bytes straight to GCS, then presents that key back to mint
233
+ * the read `FileRef`. The mint is gated server-side by CONTENT SCREENING — the
234
+ * platform reads the bytes back, screens them, and only mints on pass; on a
235
+ * screening flag/outage this REJECTS (throws) and no handle is produced.
236
+ *
237
+ * Provenance is REQUIRED — it is stamped onto the staged object so a
238
+ * downstream device-apply / library-promote can show where the bytes came
239
+ * from. Bytes move skill → GCS over the signed URL; they never enter a tool
240
+ * result, the model context, or any log.
241
+ */
242
+ stageInboundBytes: (input: {
243
+ bytes: Buffer | Uint8Array;
244
+ contentType: string;
245
+ filename: string;
246
+ provenance: StagingProvenance;
247
+ }) => Promise<FileRef>;
248
+ };
249
+
250
+ /**
251
+ * Shared email-attachment helpers for file-attaching skills (Gmail today;
252
+ * Outlook reuses these for its own slice). Two provider-agnostic concerns live
253
+ * here so neither skill hand-rolls them:
254
+ *
255
+ * 1. Redeeming `FileRef` attachments to bytes, with a per-provider size cap
256
+ * enforced BEFORE any bytes move, and sequential (bounded-concurrency)
257
+ * streaming so the base64 memory peak stays bounded.
258
+ * 2. RFC 2822 `multipart/mixed` MIME assembly (text/plain + optional
259
+ * text/html body, plus base64 attachment parts).
260
+ *
261
+ * Provider-specific framing (Gmail's base64url raw + resumable-upload threshold;
262
+ * Outlook's Graph upload session) stays in each skill's client — only the
263
+ * generic, reusable pieces are here.
264
+ *
265
+ * Bytes NEVER enter the model context, the tool-call/result JSON, or any log.
266
+ * The model holds only the small `FileRef` handle; the bytes are redeemed inside
267
+ * the SDK (`mission.files.getBytes`), assembled into MIME, and handed to the
268
+ * provider.
269
+ */
270
+
271
+ /** A redeemed attachment: the readable triple + its bytes. */
272
+ type RedeemedAttachment = {
273
+ filename: string;
274
+ mimeType: string;
275
+ bytes: Buffer;
276
+ };
277
+ /**
278
+ * Per-provider attachment limits. `maxTotalRawBytes` is the cap on the SUM of
279
+ * raw (pre-base64) attachment bytes — enforced before any byte moves. Gmail's
280
+ * documented limit is ~25 MB AFTER base64 encoding, which is ~18 MB of raw
281
+ * bytes (base64 inflates ~4/3). The error message degrades to "send a link".
282
+ */
283
+ type AttachmentLimits = {
284
+ /** Provider display name for the error message (e.g. "Gmail"). */
285
+ provider: string;
286
+ /** Cap on the sum of raw attachment bytes, pre-base64. */
287
+ maxTotalRawBytes: number;
288
+ };
289
+ type MimeBodyParts = {
290
+ to: string[];
291
+ cc?: string[];
292
+ bcc?: string[];
293
+ subject: string;
294
+ /** Plain-text body. */
295
+ text: string;
296
+ /** Optional HTML body. */
297
+ html?: string;
298
+ inReplyTo?: string;
299
+ references?: string;
300
+ /** Extra headers (e.g. a deterministic `Message-ID` for idempotency). */
301
+ extraHeaders?: Record<string, string>;
302
+ };
303
+ /** The identity that derives a send's deterministic Message-ID. Recipients are
304
+ * order-insensitive (sorted) so the same logical send hashes identically across
305
+ * retries; the per-attachment content sha256s fold the attachment set in. */
306
+ type SendMessageIdInput = {
307
+ workspaceId: string;
308
+ to: string[];
309
+ cc?: string[];
310
+ bcc?: string[];
311
+ subject: string;
312
+ body: string;
313
+ html?: string;
314
+ attachments: RedeemedAttachment[];
315
+ };
316
+ /** The deterministic Message-ID for a send, in both forms a provider needs. */
317
+ type SendMessageId = {
318
+ /** The bare sha256 hex (e.g. for Gmail's `rfc822msgid:<hash>@mission.send`). */
319
+ hash: string;
320
+ /** The framed `<hash@mission.send>` header value (Message-ID / internetMessageId). */
321
+ messageId: string;
322
+ };
323
+ /**
324
+ * The shared email-attachment domain. One object, dotted access — the loose
325
+ * procedure surface is collapsed here so a send tool reaches every reusable
326
+ * piece (caps, ref parsing, redemption, MIME assembly, header encoding, and the
327
+ * deterministic Message-ID kernel) through a single import.
328
+ */
329
+ declare const EmailAttachments: {
330
+ limits: {
331
+ gmail: AttachmentLimits;
332
+ outlook: AttachmentLimits;
333
+ };
334
+ parseRefs: (value: unknown) => FileRef[];
335
+ assertWithinCap: (refs: FileRef[], limits: AttachmentLimits) => void;
336
+ redeem: (files: FilesModule, refs: FileRef[], limits: AttachmentLimits) => Promise<RedeemedAttachment[]>;
337
+ assembleMime: (input: MimeBodyParts, attachments: RedeemedAttachment[]) => string;
338
+ sendMessageId: (input: SendMessageIdInput) => SendMessageId;
339
+ header: {
340
+ encode: (value: string, field: string) => string;
341
+ assertNoCRLF: (value: string, field: string) => string;
342
+ };
343
+ };
344
+
345
+ type SkillEvent = {
346
+ eventType: string;
347
+ payload: Record<string, unknown>;
348
+ platform: string;
349
+ tenantId: string;
350
+ };
351
+ type EventHandler = (event: SkillEvent) => Promise<void> | void;
352
+ type EventHandlerRegistry = {
353
+ on: (eventName: string, handler: EventHandler) => void;
354
+ dispatch: (event: SkillEvent) => Promise<{
355
+ handled: boolean;
356
+ }>;
357
+ hasHandlers: () => boolean;
358
+ };
359
+
360
+ /**
361
+ * Ingress Module
362
+ *
363
+ * Handles inbound traffic from external services (webhooks, WebSocket streams).
364
+ * Skills register handlers per endpoint name; the HTTP server routes traffic
365
+ * to them.
366
+ *
367
+ * Traffic flow (HTTP-native, no WebSocket control plane):
368
+ *
369
+ * Tool calls from Mission API:
370
+ * Mission API → Daytona Standard Preview URL → HTTP server → tool handler
371
+ *
372
+ * Webhooks from external services:
373
+ * External service → Daytona Signed Preview URL → HTTP server → ingress handler
374
+ *
375
+ * WebSocket streams (e.g., Twilio media):
376
+ * External service → Daytona Signed Preview URL → HTTP server → stream handler
377
+ *
378
+ * The HTTP server (started by mission.connect()) dispatches to the registered
379
+ * handler and sends the response back as a standard HTTP response.
380
+ *
381
+ * For WebSocket endpoints: event-based model (connect, message, disconnect).
382
+ * WebSocket support depends on Daytona Preview URL proxy supporting upgrades.
383
+ *
384
+ * Endpoint names are chosen by the skill author at registration time.
385
+ * There is no need to pre-declare them in mission.json — the SDK registers
386
+ * them dynamically and reports them to the control plane at ready().
387
+ *
388
+ * See docs/technical/legacy/architecture/skills/architecture.md §4.4.
389
+ * See docs/technical/legacy/architecture/skills/sandbox-runtime.md §2-3.
390
+ */
391
+
392
+ type IngressRequest = {
393
+ /** HTTP method (GET, POST, etc.). */
394
+ method: string;
395
+ /** Request path (relative to the ingress endpoint). */
396
+ path: string;
397
+ /** Request headers (lowercased keys). */
398
+ headers: Record<string, string>;
399
+ /** Raw request body as a string. */
400
+ body: string;
401
+ /** Parsed query parameters. */
402
+ query: Record<string, string>;
403
+ };
404
+ type IngressResponse = {
405
+ /** HTTP status code. */
406
+ status: number;
407
+ /** Response headers. */
408
+ headers?: Record<string, string>;
409
+ /** Response body. */
410
+ body?: string;
411
+ };
412
+ type IngressHandler = (request: IngressRequest) => Promise<IngressResponse>;
413
+ type IngressStreamEvents = {
414
+ /** Called when a new WebSocket connection is established. */
415
+ onConnect: (streamId: string, metadata: Record<string, string>) => void;
416
+ /** Called when a message is received on the WebSocket. */
417
+ onMessage: (streamId: string, data: string) => void;
418
+ /** Called when the WebSocket disconnects. */
419
+ onDisconnect: (streamId: string) => void;
420
+ };
421
+ /**
422
+ * Result from requesting a public ingress URL.
423
+ * The URL is a Daytona Signed Preview URL with a configurable TTL.
424
+ * The skill is responsible for monitoring expiry and re-requesting.
425
+ */
426
+ type IngressUrl = {
427
+ /** The public URL to register with external services. */
428
+ url: string;
429
+ /** When this URL expires (Unix milliseconds). */
430
+ expiresAt: number;
431
+ };
432
+ type IngressModule = {
433
+ /**
434
+ * Register a handler for an HTTP ingress endpoint.
435
+ * The endpoint name is chosen by the skill author.
436
+ *
437
+ * @example
438
+ * ```ts
439
+ * mission.ingress.on('webhook', async (request) => {
440
+ * const payload = JSON.parse(request.body)
441
+ * // process webhook...
442
+ * return { status: 200 }
443
+ * })
444
+ * ```
445
+ */
446
+ on: (endpointName: string, handler: IngressHandler) => void;
447
+ /**
448
+ * Register handlers for a WebSocket ingress endpoint.
449
+ * The endpoint name is chosen by the skill author.
450
+ *
451
+ * @example
452
+ * ```ts
453
+ * mission.ingress.onStream('media', {
454
+ * onConnect: (streamId, meta) => { startSession(streamId) },
455
+ * onMessage: (streamId, data) => { processAudio(streamId, data) },
456
+ * onDisconnect: (streamId) => { endSession(streamId) }
457
+ * })
458
+ * ```
459
+ */
460
+ onStream: (endpointName: string, handlers: IngressStreamEvents) => void;
461
+ /**
462
+ * Get a public URL for a named ingress endpoint.
463
+ *
464
+ * The control plane generates a Daytona Signed Preview URL with a
465
+ * configurable TTL. The skill is responsible for:
466
+ * 1. Registering this URL with the external service.
467
+ * 2. Monitoring `expiresAt` and calling this again before expiry.
468
+ * 3. Re-registering the new URL with the external service.
469
+ *
470
+ * On sandbox restart, skill processes restart and naturally
471
+ * re-register during their startup sequence.
472
+ *
473
+ * @example
474
+ * ```ts
475
+ * const { url, expiresAt } = await mission.ingress.getPublicUrl('webhook')
476
+ * await twilio.incomingPhoneNumbers(sid).update({ voiceUrl: url })
477
+ * ```
478
+ */
479
+ getPublicUrl: (endpointName: string) => Promise<IngressUrl>;
480
+ };
481
+
482
+ /**
483
+ * Logging Module
484
+ *
485
+ * Provides two logging mechanisms:
486
+ *
487
+ * 1. Console capture — overrides the global `console` object so that
488
+ * skill authors who use `console.log()` naturally get their logs
489
+ * forwarded to the Mission logging pipeline.
490
+ *
491
+ * 2. Structured logger — `mission.log.info(message, data?)` for skills
492
+ * that want explicit, structured logging with context.
493
+ *
494
+ * Both paths feed into the same transport:
495
+ * - **Production**: Buffered in memory and flushed every few seconds to the
496
+ * Gateway via `skill.ingestLogs`. The Gateway enriches entries with
497
+ * installation metadata and forwards them to Better Stack.
498
+ * - **Local dev**: Direct synchronous writes to `.logs/skill-port-*.log`
499
+ * (if LOCAL_DEV_MODE=1). This ensures logs survive crashes.
500
+ *
501
+ * Transport: skill.ingestLogs tRPC mutation (batched, fire-and-forget) OR
502
+ * local file write (sync, immediate, survives crashes).
503
+ */
504
+
505
+ type LogModule = {
506
+ /** Log at info level. */
507
+ info: (message: string, data?: Record<string, unknown>) => void;
508
+ /** Log at warn level. */
509
+ warn: (message: string, data?: Record<string, unknown>) => void;
510
+ /** Log at error level. */
511
+ error: (message: string, data?: Record<string, unknown>) => void;
512
+ /** Log at debug level. */
513
+ debug: (message: string, data?: Record<string, unknown>) => void;
514
+ };
515
+
516
+ /**
517
+ * Send-Intents Module
518
+ *
519
+ * `mission.sendIntents` — the skill-facing seam for the durable Outlook
520
+ * send-intent idempotency ledger. It is the SDK half of the exactly-once guard
521
+ * that closes the Outlook double-send-on-retry hole (B2): the skill claims a
522
+ * send's content-identity BEFORE dispatching to Microsoft Graph, marks it sent
523
+ * on a 2xx, and releases it only on a provable pre-dispatch failure.
524
+ *
525
+ * The skill runs in a sandbox with NO database access. Its only durable channel
526
+ * is the Mission gateway's `skill.*` tRPC router, so each method here is a single
527
+ * strongly-consistent round-trip to OUR store via the gateway — NEVER a read of
528
+ * Graph's eventually-consistent Sent Items.
529
+ *
530
+ * TENANCY: the skill never passes a workspaceId. The gateway resolves the
531
+ * authoritative owning workspace SERVER-SIDE from the authenticated connection
532
+ * (see `skill.outlookSendIntentClaim`). The workspace identity the skill DOES
533
+ * contribute rides INSIDE the `contentKey` hash, which the skill computes from
534
+ * its tool-context workspaceId — so the ledger key is workspace-scoped by
535
+ * construction without the skill asserting tenancy on the wire.
536
+ *
537
+ * This module's shape is the concrete backing for the outlook skill's
538
+ * `SendIntents` port (`skills/outlook-mail/src/send-intents.ts`): `claim`,
539
+ * `markSent`, `release`, keyed on `contentKey` alone.
540
+ *
541
+ * Transport: tRPC mutations via the Mission gateway API. The result shapes
542
+ * (`SendIntentRecord`, `SendIntentClaimOutcome`, …) are hand-declared here, but
543
+ * the load-bearing direction is drift-protected: every call goes through the
544
+ * fully-typed `ApiClient`, so a mutation INPUT that diverges from the server
545
+ * contract fails to compile, and `claim` returns the procedure's result
546
+ * directly — assignment-checked against `SendIntentClaimOutcome`. The result
547
+ * shape is therefore pinned by an assignment check, not derived by inference.
548
+ */
549
+
550
+ /** Lifecycle state of a send-intent claim in the durable ledger. */
551
+ type SendIntentStatus = 'in_flight' | 'sent';
552
+ /** A claimed (or already-resolved) send intent, read back from the ledger. */
553
+ type SendIntentRecord = {
554
+ /** The content-identity sha256 hex (the ledger primary key). */
555
+ contentKey: string;
556
+ status: SendIntentStatus;
557
+ /** Present once `status === 'sent'`: the provider message id we returned. */
558
+ providerMessageId: string | null;
559
+ /** Present once `status === 'sent'`: the Graph `conversationId`. */
560
+ conversationId: string | null;
561
+ };
562
+ /** The provider ids recorded against a claim when the send completes. */
563
+ type SendIntentCompletion = {
564
+ providerMessageId: string;
565
+ conversationId: string;
566
+ };
567
+ /** The atomic-claim outcome the gateway relays back to the skill. */
568
+ type SendIntentClaimOutcome = {
569
+ outcome: 'claimed';
570
+ } | {
571
+ outcome: 'already_sent';
572
+ record: SendIntentRecord;
573
+ } | {
574
+ outcome: 'in_flight';
575
+ record: SendIntentRecord;
576
+ };
577
+ type SendIntentsModule = {
578
+ /**
579
+ * Atomically claim a send's content-identity BEFORE the Graph send.
580
+ * - no row → insert `in_flight`, return `{ outcome: 'claimed' }`; the caller
581
+ * OWNS the send and proceeds.
582
+ * - `sent` row → `{ outcome: 'already_sent', record }`; the caller MUST NOT
583
+ * resend; it returns the recorded provider ids.
584
+ * - `in_flight` row → `{ outcome: 'in_flight', record }`; a prior attempt
585
+ * owns it (concurrent, or crashed before commit); the caller MUST NOT
586
+ * double-send.
587
+ */
588
+ claim: (contentKey: string) => Promise<SendIntentClaimOutcome>;
589
+ /**
590
+ * Mark a claimed intent as sent, recording the provider ids. Called once the
591
+ * Graph send returns 2xx. Idempotent server-side (guarded on `in_flight`).
592
+ */
593
+ markSent: (contentKey: string, completion: SendIntentCompletion) => Promise<void>;
594
+ /**
595
+ * Release an in-flight claim WITHOUT marking it sent. Called ONLY when the
596
+ * failure happened provably BEFORE the Graph send was issued, so a legitimate
597
+ * retry can re-claim and actually send. MUST NOT be called once the send may
598
+ * have reached Graph (the irreversible boundary).
599
+ */
600
+ release: (contentKey: string) => Promise<void>;
601
+ };
602
+
603
+ /**
604
+ * Tool Registry
605
+ *
606
+ * Skills register their tool handlers here. When the Gateway sends
607
+ * a "tools.call" command, the registry dispatches to the right handler.
608
+ *
609
+ * Usage:
610
+ * mission.tools.register('gmail_search', async ({ params, context }) => { ... })
611
+ * mission.tools.registerAll(handlers)
612
+ */
613
+
614
+ /**
615
+ * A tool handler function. Receives params and caller context, returns a result.
616
+ * The SDK handles wrapping the result in the gateway response protocol.
617
+ *
618
+ * `input.context.toolCallId` is a stable, retry-invariant identifier for the
619
+ * logical tool call: the same value arrives on every Temporal retry, so a
620
+ * handler can use it as an idempotency key to dedupe mutating effects (mint
621
+ * IDs once, claim-before-write external sends). Optional to consume — handlers
622
+ * that don't mutate ignore it. The envelope is decoded into `ToolCallInput` in
623
+ * `sdk.ts`, which passes `context` through whole, so the field reaches the
624
+ * handler unchanged.
625
+ */
626
+ type ToolHandler = (input: ToolCallInput) => Promise<unknown>;
627
+ type ToolsModule = {
628
+ /** Register a single tool handler by name. */
629
+ register: (name: string, handler: ToolHandler) => void;
630
+ /** Register multiple tool handlers at once. */
631
+ registerAll: (handlers: Record<string, ToolHandler>) => void;
632
+ };
633
+
634
+ /**
635
+ * Mission SDK — Main Entry
636
+ *
637
+ * Creates the SDK instance that a skill uses to interact with the
638
+ * Mission platform. Communication is HTTP-native:
639
+ *
640
+ * Outbound (Skill → Mission API):
641
+ * Standard REST calls via the API client.
642
+ *
643
+ * Inbound (Mission API → Skill):
644
+ * HTTP requests to the skill's HTTP server, exposed via
645
+ * Daytona Preview URLs.
646
+ *
647
+ * Lifecycle:
648
+ * 1. const mission = createMissionSDK() — create SDK (no connection yet)
649
+ * 2. await mission.connect() — start HTTP server, install logging
650
+ * 3. // auth, config are now usable
651
+ * 4. mission.tools.registerAll(handlers) — register tool handlers
652
+ * 5. mission.ingress.on('name', handler) — register ingress handlers
653
+ * 6. mission.on('event_type', handler) — register event handler
654
+ * 7. mission.onShutdown(() => cleanup()) — register shutdown hooks
655
+ * 8. await mission.ready() — signal readiness, accept traffic
656
+ *
657
+ * See docs/technical/legacy/architecture/skills/architecture.md §4 and §6.
658
+ * See docs/technical/legacy/architecture/skills/sandbox-runtime.md.
659
+ */
660
+
661
+ type MissionSDKOptions = {
662
+ /**
663
+ * Override the skill context instead of reading from env.
664
+ * Useful for testing or local development.
665
+ */
666
+ context?: Partial<SkillContext>;
667
+ /**
668
+ * Override the skill HTTP server port. Normally read from
669
+ * MISSION_SKILL_PORT (assigned by the control plane).
670
+ * Must be in the Daytona Preview URL range (3000-9999).
671
+ */
672
+ port?: number;
673
+ };
674
+ /**
675
+ * A message from a skill to the agent.
676
+ *
677
+ * Skills process their domain-specific inbound data and produce
678
+ * human-readable messages that agents can reason about.
679
+ */
680
+ type AgentMessage = {
681
+ /**
682
+ * Human-readable message describing what happened.
683
+ * This is what the agent sees and reasons about.
684
+ */
685
+ message: string;
686
+ /**
687
+ * Structured context data the agent can reference.
688
+ * Include IDs, metadata, and any data that helps the agent
689
+ * take follow-up actions using the skill's tools.
690
+ */
691
+ context?: Record<string, unknown>;
692
+ /**
693
+ * Optional deduplication key to prevent duplicate processing.
694
+ * If the platform has already processed a message with this key,
695
+ * it will be silently dropped.
696
+ */
697
+ deduplicationKey?: string;
698
+ };
699
+ type MissionSDK = {
700
+ /** The resolved skill context. */
701
+ context: SkillContext;
702
+ /** Get authentication credentials (user-provided secrets and/or OAuth tokens). */
703
+ auth: AuthModule;
704
+ /** Fetch per-installation configuration. */
705
+ config: ConfigModule;
706
+ /**
707
+ * Move file bytes between the skill and storage via platform-minted,
708
+ * authorized `FileRef` handles. Bytes stream skill ⇄ GCS directly — never
709
+ * through the platform, the model, or any log.
710
+ */
711
+ files: FilesModule;
712
+ /**
713
+ * Durable Outlook send-intent idempotency ledger. Claim a send's
714
+ * content-identity before dispatching to Graph, mark it sent on a 2xx, release
715
+ * it on a provable pre-dispatch failure — the exactly-once guard behind
716
+ * `outlook_mail_send`. Each call is a strongly-consistent round-trip to OUR
717
+ * store via the gateway, never a read of Graph's Sent Items.
718
+ */
719
+ sendIntents: SendIntentsModule;
720
+ /** Register ingress handlers and discover public URLs. */
721
+ ingress: IngressModule;
722
+ /** Structured logger — logs are batched and shipped to the Mission platform. */
723
+ log: LogModule;
724
+ /** Register tool handlers that the platform can invoke. */
725
+ tools: ToolsModule;
726
+ /**
727
+ * Subscribe to typed events delivered by the platform.
728
+ * Use '*' to subscribe to all event types.
729
+ *
730
+ * @example
731
+ * ```ts
732
+ * mission.on('mailbox_activity', async (event) => {
733
+ * await mission.emit({ message: '...', context: event.payload })
734
+ * })
735
+ * ```
736
+ */
737
+ on: (eventName: string, handler: EventHandler) => void;
738
+ /**
739
+ * Send a message to the agent about an external event.
740
+ * The platform routes it to the appropriate agent(s) based on
741
+ * the workspace's routing configuration.
742
+ *
743
+ * Sends an agent message to the platform for processing.
744
+ *
745
+ * @example
746
+ * ```ts
747
+ * await mission.emit({
748
+ * message: 'New email from john@example.com',
749
+ * context: { emailAddress: 'john@example.com' },
750
+ * deduplicationKey: 'gmail-push-12345'
751
+ * })
752
+ * ```
753
+ */
754
+ emit: (message: AgentMessage) => Promise<void>;
755
+ /**
756
+ * Connect to the Mission platform.
757
+ *
758
+ * This starts the skill's HTTP server (for inbound tool calls and
759
+ * ingress traffic), installs the console override for structured
760
+ * logging, and begins the keep-alive heartbeat loop.
761
+ *
762
+ * After this call, `auth`, `config`, `emit`, and `ingress.getPublicUrl`
763
+ * are usable.
764
+ */
765
+ connect: () => Promise<void>;
766
+ /**
767
+ * Signal readiness and start accepting tool calls and ingress traffic.
768
+ *
769
+ * Call this AFTER registering all tools, ingress handlers, and event handlers.
770
+ * The returned promise resolves when the process receives a shutdown
771
+ * signal (SIGTERM/SIGINT) and graceful shutdown completes.
772
+ */
773
+ ready: () => Promise<void>;
774
+ /**
775
+ * Register a callback to run during graceful shutdown.
776
+ * Multiple handlers can be registered; they run in order.
777
+ *
778
+ * Shutdown is triggered by SIGTERM or SIGINT (standard container signals).
779
+ * The SDK stops the HTTP server, drains in-flight requests, runs
780
+ * shutdown handlers, then exits.
781
+ *
782
+ * @example
783
+ * ```ts
784
+ * mission.onShutdown(async () => {
785
+ * await database.close()
786
+ * await cache.flush()
787
+ * })
788
+ * ```
789
+ */
790
+ onShutdown: (handler: () => Promise<void> | void) => void;
791
+ /**
792
+ * Register a health check handler for install-time smoke testing.
793
+ *
794
+ * The handler is called by the platform after the skill process starts
795
+ * during installation. It should verify that the skill can reach its
796
+ * external dependencies (e.g., API keys work, OAuth tokens are valid).
797
+ *
798
+ * This is NOT a tool — it is never exposed to the LLM or listed in
799
+ * reported tools. It is served on an internal HTTP endpoint
800
+ * (`POST /_mission/health-check`) that only the platform calls.
801
+ *
802
+ * If no handler is registered, the health check is skipped (not failed).
803
+ *
804
+ * @example
805
+ * ```ts
806
+ * mission.onHealthCheck(async () => {
807
+ * await gmail.listLabels()
808
+ * return { ok: true }
809
+ * })
810
+ * ```
811
+ */
812
+ onHealthCheck: (handler: () => Promise<unknown> | unknown) => void;
813
+ };
814
+ /**
815
+ * Create a Mission SDK instance.
816
+ *
817
+ * @example
818
+ * ```ts
819
+ * import { createMissionSDK } from '@mission/sdk'
820
+ *
821
+ * const mission = createMissionSDK()
822
+ * await mission.connect()
823
+ *
824
+ * const auth = await mission.auth.get()
825
+ * const config = await mission.config.get<MyConfig>(defaults)
826
+ *
827
+ * mission.tools.registerAll(handlers)
828
+ * mission.ingress.on('webhook', handler)
829
+ * mission.on('mailbox_activity', async (event) => {
830
+ * await mission.emit({ message: '...', context: event.payload })
831
+ * })
832
+ * mission.onShutdown(() => cleanup())
833
+ *
834
+ * await mission.ready()
835
+ * ```
836
+ */
837
+ declare function createMissionSDK(options?: MissionSDKOptions): MissionSDK;
838
+
839
+ /**
840
+ * A skill tool failure that carries the upstream HTTP status.
841
+ *
842
+ * Skill clients (Airtable, Gmail, …) call third-party REST APIs. When one of
843
+ * those APIs rejects a request — a 422 for a bad field name, a 404 for a
844
+ * missing record — the client throws. A plain `Error` loses the status: the
845
+ * skill runtime's tool dispatch (`POST /_mission/tools/:toolName`) then can't
846
+ * tell a client mistake from a server fault and blankets every throw to
847
+ * HTTP 500. That 500 propagates to the executor, which (a) stamps a misleading
848
+ * "HTTP 500" into the model-facing tool result and (b) treats a deterministic
849
+ * client error as a retryable server fault.
850
+ *
851
+ * Throw this instead of a bare `Error` so the real status survives the hop.
852
+ * The dispatch layer reads `statusCode` and returns it verbatim, so a 422
853
+ * stays a 422 all the way to the model.
854
+ */
855
+ declare class SkillToolHttpError extends Error {
856
+ readonly statusCode: number;
857
+ constructor(statusCode: number, message: string);
858
+ }
859
+
1
860
  /**
2
861
  * Mission SDK
3
862
  *
@@ -5,27 +864,17 @@
5
864
  * Communication is HTTP-native: outbound REST calls to the Mission API,
6
865
  * inbound HTTP requests via Daytona Preview URLs.
7
866
  *
8
- * See docs/technical/architecture/skills/architecture.md.
9
- * See docs/technical/architecture/skills/sandbox-runtime.md.
10
- */
11
- export type { Tool, ToolFunction, ToolFunctionPermission, ToolKind } from '@mission/models';
12
- export type { ApiClient } from './api';
13
- export type { AuthCredentials, AuthModule, OAuthCredentials } from './auth';
14
- export type { ConfigModule } from './config';
15
- export type { SkillContext } from './context';
16
- export type { EventHandler, EventHandlerRegistry, SkillEvent } from './event-handler';
17
- export type { IngressHandler, IngressModule, IngressRequest, IngressResponse, IngressStreamEvents, IngressUrl } from './ingress';
18
- export type { LogModule } from './logging';
19
- export type { AgentMessage, MissionSDK, MissionSDKOptions } from './sdk';
20
- export { createMissionSDK } from './sdk';
21
- export type { ToolCallContext, ToolCallEnvelope, ToolCallInput, ToolHandler, ToolsModule } from './tool-registry';
867
+ * See docs/technical/legacy/architecture/skills/architecture.md.
868
+ * See docs/technical/legacy/architecture/skills/sandbox-runtime.md.
869
+ */
870
+
22
871
  /** Incoming webhook request passed to a skill's `webhook()` handler. */
23
- export type WebhookRequest = {
872
+ type WebhookRequest = {
24
873
  body: unknown;
25
874
  headers: Record<string, string>;
26
875
  };
27
876
  /** Optional response a `webhook()` handler can return to the external service. */
28
- export type WebhookResponse = {
877
+ type WebhookResponse = {
29
878
  status: number;
30
879
  body: unknown;
31
880
  };
@@ -34,8 +883,9 @@ export type WebhookResponse = {
34
883
  * - `externalId`: tenant identifier used to route the event to the correct connection(s).
35
884
  * - `response`: optional custom HTTP response to send back to the caller.
36
885
  */
37
- export type WebhookResult = {
886
+ type WebhookResult = {
38
887
  externalId: string | null;
39
888
  response?: WebhookResponse;
40
889
  };
41
- //# sourceMappingURL=index.d.ts.map
890
+
891
+ export { type AgentMessage, type ApiClient, type AttachmentLimits, type AuthCredentials, type AuthModule, type ConfigModule, EmailAttachments, type EventHandler, type EventHandlerRegistry, FileTransferTimeoutError, type FilesModule, type IngressHandler, type IngressModule, type IngressRequest, type IngressResponse, type IngressStreamEvents, type IngressUrl, type LogModule, type MimeBodyParts, type MissionSDK, type MissionSDKOptions, type OAuthCredentials, type RedeemedAttachment, type SendIntentClaimOutcome, type SendIntentCompletion, type SendIntentRecord, type SendIntentStatus, type SendIntentsModule, type SendMessageId, type SendMessageIdInput, type SkillContext, type SkillEvent, SkillToolHttpError, type ToolHandler, type ToolsModule, type WebhookRequest, type WebhookResponse, type WebhookResult, clearDebugLog, createMissionSDK, debugLog };