@frockbot/applet-sdk 0.7.111 → 0.7.113

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/README.md CHANGED
@@ -44,9 +44,14 @@ which `applet_create` writes through the Workspace.
44
44
 
45
45
  A Plugin (ADR 0026) is written against `@frockbot/applet-sdk/plugin`, which
46
46
  is declarations only: `plugin.ts` exports `tools` and `execute`, and may
47
- export `hooks`, `services`, `triggers` and `views`, beside a `plugin.json` descriptor.
47
+ export `hooks`, `services`, `triggers`, `views` and `modelProviders`, beside a `plugin.json` descriptor.
48
48
  `tools` may be empty — a Plugin that only serves hooks is admissible, because
49
- the kernel's own descriptor contract admits one.
49
+ the kernel's own descriptor contract admits one. A model provider
50
+ (`PluginModelProvider`, ADR 0032) answers a normalized model request with
51
+ normalized stream events and makes its one upstream call through
52
+ `ctx.modelTransport`; the deployment serves it only from the artifact its own
53
+ provider catalog names, so this is not a way for a Bot-written Plugin to reach
54
+ a provider.
50
55
  `runPluginBuildV1(directory, { mode, id })` is four stages — `descriptor`,
51
56
  `typecheck`, `bundle`, `describe` — with no lint stage, because a Plugin's
52
57
  reach is a grant the descriptor declares and the kernel enforces. The bundle
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frockbot/applet-sdk",
3
- "version": "0.7.111",
3
+ "version": "0.7.113",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Authoring SDK for FrockBot Applets: schema-first Durable Object server, TanStack DB client, component kit, linter, and the build pipeline.",
package/plugin/index.d.ts CHANGED
@@ -157,6 +157,129 @@ export interface PluginModel {
157
157
  >;
158
158
  }
159
159
 
160
+ /** One message in the normalized request a model provider is handed. */
161
+ export interface PluginModelMessage {
162
+ role: "user" | "assistant" | "tool";
163
+ [key: string]: unknown;
164
+ }
165
+
166
+ /**
167
+ * One model request, normalized by the kernel: the same shape a Package's
168
+ * provider receives, minus the Connection the host holds on the Plugin's
169
+ * behalf.
170
+ */
171
+ export interface PluginModelRequest {
172
+ requestId: string;
173
+ provider: string;
174
+ model: string;
175
+ system: string;
176
+ messages: PluginModelMessage[];
177
+ tools: { name: string; description: string; inputSchema: JsonSchema }[];
178
+ responseFormat?: { type: string; [key: string]: unknown };
179
+ }
180
+
181
+ /**
182
+ * One normalized stream event a provider answers with. The kernel decodes
183
+ * every field strictly, so a provider that invents an event fails its call
184
+ * rather than half-rendering a reply.
185
+ */
186
+ export type PluginModelStreamEvent =
187
+ | {
188
+ type: "provider-state";
189
+ /** Opaque provider content the kernel replays on later turns. */
190
+ state: { [key: string]: unknown };
191
+ }
192
+ | { type: "text-delta"; text: string }
193
+ | {
194
+ /**
195
+ * A heartbeat: real upstream bytes that are not the reply — a reasoning
196
+ * model thinking, arguments still arriving. It reaches no one and shows
197
+ * nothing; it is how a long stretch with no visible output is told
198
+ * apart from a dead socket.
199
+ */
200
+ type: "progress";
201
+ }
202
+ | { type: "tool-call"; call: { id: string; name: string; input: unknown } }
203
+ | {
204
+ type: "usage";
205
+ usage: {
206
+ inputTokens: number;
207
+ outputTokens: number;
208
+ cachedInputTokens?: number;
209
+ reasoningTokens?: number;
210
+ };
211
+ }
212
+ | { type: "response-format-note"; note: { [key: string]: unknown } }
213
+ | { type: "structured-output-failure"; failure: { [key: string]: unknown } }
214
+ | { type: "finish"; reason: "completed" | "tool-calls" | "max-tokens" }
215
+ | {
216
+ /**
217
+ * The provider refused, in the Plugin's own words. The classification
218
+ * tells the kernel's retry policy what to do: `permanent` is not
219
+ * retried, `transient` is, and `unknown` takes the kernel's default.
220
+ */
221
+ type: "provider-failure";
222
+ classification: "transient" | "permanent" | "unknown";
223
+ reason: string;
224
+ retryAfterMs?: number;
225
+ };
226
+
227
+ /** What the host transport answers with. */
228
+ export type PluginModelTransportOutcome =
229
+ | {
230
+ status: "streaming";
231
+ httpStatus: number;
232
+ /** The provider's body, streamed. Decode it as the provider frames it. */
233
+ body: ReadableStream<Uint8Array>;
234
+ }
235
+ | {
236
+ status: "refused";
237
+ httpStatus: number;
238
+ /** The host's own words; an upstream error body is never forwarded. */
239
+ reason: string;
240
+ retryAfterMs?: number;
241
+ }
242
+ | { status: "unavailable"; reason: string };
243
+
244
+ /**
245
+ * The model transport a provider contribution may call, exactly once per
246
+ * model call. The host resolves the Connection, sends to the one endpoint and
247
+ * route the deployment serves the provider on, attaches the credential
248
+ * server-side and streams the provider's bytes back. A Plugin never sees the
249
+ * credential, cannot name a Connection or a destination, and cannot follow a
250
+ * redirect.
251
+ */
252
+ export type PluginModelTransport = (request: {
253
+ /** The request body, exactly as the upstream should receive it. */
254
+ body: string;
255
+ }) => Promise<PluginModelTransportOutcome>;
256
+
257
+ /** `ctx` inside one model call served by this Plugin's provider contribution. */
258
+ export interface PluginModelContext extends PluginContext {
259
+ /** This call's one credentialed upstream call. */
260
+ readonly modelTransport: PluginModelTransport;
261
+ }
262
+
263
+ /**
264
+ * One model provider a Plugin serves (ADR 0032). The `id` is the provider
265
+ * type a Bot's model selection names; the descriptor declares it too, with
266
+ * the protocol version, the credential scheme and the endpoint the host
267
+ * attaches them to, and the two are checked against each other at mount.
268
+ */
269
+ export interface PluginModelProvider {
270
+ /**
271
+ * One model call: the kernel hands a normalized request and the Plugin
272
+ * answers with normalized events, reached through `ctx.modelTransport`.
273
+ */
274
+ stream(
275
+ request: PluginModelRequest,
276
+ ctx: PluginModelContext,
277
+ ): AsyncIterable<PluginModelStreamEvent>;
278
+ }
279
+
280
+ /** Every model provider the module serves, by provider id. */
281
+ export type PluginModelProviders = Record<string, PluginModelProvider>;
282
+
160
283
  export type MemoryScope = "bot" | "user" | "project";
161
284
  export type MemoryTier = "profile" | "log" | "note";
162
285
 
@@ -253,6 +376,11 @@ export interface PluginContext {
253
376
  readonly settings: PluginSettings;
254
377
  /** The `ai` grant. */
255
378
  readonly model?: PluginModel;
379
+ /**
380
+ * The credentialed transport, present exactly while this Plugin is serving
381
+ * one of its model provider contributions. A tool call's `ctx` has none.
382
+ */
383
+ readonly modelTransport?: PluginModelTransport;
256
384
  /** The `memory` grant. */
257
385
  readonly memory?: PluginMemory;
258
386
  /** The `workspace` grant. */
@@ -611,4 +739,10 @@ export interface PluginModule {
611
739
  * the card's `dataSchema` and `render` answers with the surface.
612
740
  */
613
741
  cards?: Record<string, PluginCard>;
742
+ /**
743
+ * One entry per model provider declared under `modelProviders` in
744
+ * `plugin.json` (ADR 0032). A Bot that selects that provider runs this
745
+ * contribution; the host serves the credential through `ctx.modelTransport`.
746
+ */
747
+ modelProviders?: PluginModelProviders;
614
748
  }
@@ -54,6 +54,8 @@ export interface PluginDescriptionV1 {
54
54
  views: string[];
55
55
  /** The cards the module draws, by id (ADR 0030). */
56
56
  cards: string[];
57
+ /** The model providers the module serves, by provider id (ADR 0032). */
58
+ modelProviders: string[];
57
59
  }
58
60
 
59
61
  export interface PluginBuildManifestV1 extends PluginDescriptionV1 {
@@ -279,6 +281,20 @@ function names(value, label) {
279
281
  });
280
282
  }
281
283
 
284
+ function modelProviderNames(value) {
285
+ if (value === undefined) return [];
286
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
287
+ throw new Error('"modelProviders" must be an object');
288
+ }
289
+ return Object.keys(value).map(function (providerId) {
290
+ var provider = value[providerId];
291
+ if (!provider || typeof provider !== "object" || typeof provider.stream !== "function") {
292
+ throw new Error('model provider "' + providerId + '" must export a stream function');
293
+ }
294
+ return providerId;
295
+ });
296
+ }
297
+
282
298
  function cardNames(value) {
283
299
  if (value === undefined) return [];
284
300
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -320,6 +336,7 @@ function describe() {
320
336
  triggers: names(plugin.triggers, "triggers"),
321
337
  views: names(plugin.views, "views"),
322
338
  cards: cardNames(plugin.cards),
339
+ modelProviders: modelProviderNames(plugin.modelProviders),
323
340
  };
324
341
  }
325
342
 
@@ -414,6 +431,7 @@ function validateDescription(input: PluginDescriptionV1): PluginDescriptionV1 {
414
431
  triggers: [...input.triggers],
415
432
  views: [...input.views],
416
433
  cards: [...(input.cards ?? [])],
434
+ modelProviders: [...(input.modelProviders ?? [])],
417
435
  };
418
436
  }
419
437