@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.
- package/dist/chunk-N26TYEUP.js +50 -0
- package/dist/chunk-N26TYEUP.js.map +1 -0
- package/dist/debug-log-SHNBLZWL.js +9 -0
- package/dist/debug-log-SHNBLZWL.js.map +1 -0
- package/dist/index.d.ts +868 -18
- package/dist/index.js +15843 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
- package/dist/api.d.ts +0 -20
- package/dist/api.d.ts.map +0 -1
- package/dist/api.js +0 -39
- package/dist/api.js.map +0 -1
- package/dist/auth-failure-tracker.d.ts +0 -22
- package/dist/auth-failure-tracker.d.ts.map +0 -1
- package/dist/auth-failure-tracker.js +0 -31
- package/dist/auth-failure-tracker.js.map +0 -1
- package/dist/auth.d.ts +0 -65
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js +0 -45
- package/dist/auth.js.map +0 -1
- package/dist/config.d.ts +0 -39
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -28
- package/dist/config.js.map +0 -1
- package/dist/context.d.ts +0 -24
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -22
- package/dist/context.js.map +0 -1
- package/dist/event-handler.d.ts +0 -16
- package/dist/event-handler.d.ts.map +0 -1
- package/dist/event-handler.js +0 -27
- package/dist/event-handler.js.map +0 -1
- package/dist/http-server.d.ts +0 -51
- package/dist/http-server.d.ts.map +0 -1
- package/dist/http-server.js +0 -163
- package/dist/http-server.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/ingress.d.ts +0 -133
- package/dist/ingress.d.ts.map +0 -1
- package/dist/ingress.js +0 -89
- package/dist/ingress.js.map +0 -1
- package/dist/logging.d.ts +0 -63
- package/dist/logging.d.ts.map +0 -1
- package/dist/logging.js +0 -171
- package/dist/logging.js.map +0 -1
- package/dist/sdk.d.ts +0 -197
- package/dist/sdk.d.ts.map +0 -1
- package/dist/sdk.js +0 -435
- package/dist/sdk.js.map +0 -1
- package/dist/tool-registry.d.ts +0 -49
- package/dist/tool-registry.d.ts.map +0 -1
- package/dist/tool-registry.js +0 -37
- package/dist/tool-registry.js.map +0 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
886
|
+
type WebhookResult = {
|
|
38
887
|
externalId: string | null;
|
|
39
888
|
response?: WebhookResponse;
|
|
40
889
|
};
|
|
41
|
-
|
|
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 };
|