@databricks/appkit 0.47.1 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CLAUDE.md +11 -1
  2. package/dist/agents/databricks.d.ts +24 -7
  3. package/dist/agents/databricks.d.ts.map +1 -1
  4. package/dist/agents/databricks.js +25 -0
  5. package/dist/agents/databricks.js.map +1 -1
  6. package/dist/agents/supervisor-api.d.ts +362 -0
  7. package/dist/agents/supervisor-api.d.ts.map +1 -0
  8. package/dist/agents/supervisor-api.js +498 -0
  9. package/dist/agents/supervisor-api.js.map +1 -0
  10. package/dist/appkit/package.js +1 -1
  11. package/dist/beta.d.ts +2 -1
  12. package/dist/beta.js +2 -1
  13. package/dist/cli/commands/lint.js +6 -0
  14. package/dist/cli/commands/lint.js.map +1 -1
  15. package/dist/connectors/serving/client.d.ts +24 -0
  16. package/dist/connectors/serving/client.d.ts.map +1 -0
  17. package/dist/connectors/serving/client.js +34 -14
  18. package/dist/connectors/serving/client.js.map +1 -1
  19. package/dist/core/agent/run-agent.d.ts.map +1 -1
  20. package/dist/core/agent/run-agent.js +51 -2
  21. package/dist/core/agent/run-agent.js.map +1 -1
  22. package/dist/core/agent/types.d.ts +19 -3
  23. package/dist/core/agent/types.d.ts.map +1 -1
  24. package/dist/core/agent/types.js.map +1 -1
  25. package/dist/core/appkit.d.ts.map +1 -1
  26. package/dist/core/appkit.js +39 -1
  27. package/dist/core/appkit.js.map +1 -1
  28. package/dist/index.js +2 -2
  29. package/dist/plugins/agents/agents.d.ts.map +1 -1
  30. package/dist/plugins/agents/agents.js +65 -5
  31. package/dist/plugins/agents/agents.js.map +1 -1
  32. package/dist/plugins/files/plugin.js +2 -2
  33. package/dist/plugins/jobs/plugin.js +2 -2
  34. package/dist/plugins/serving/serving.js +2 -2
  35. package/dist/plugins/ui-variants/choice-sink.js +70 -0
  36. package/dist/plugins/ui-variants/choice-sink.js.map +1 -0
  37. package/dist/plugins/ui-variants/index.js +94 -0
  38. package/dist/plugins/ui-variants/index.js.map +1 -0
  39. package/dist/plugins/ui-variants/manifest.js +17 -0
  40. package/dist/plugins/ui-variants/manifest.js.map +1 -0
  41. package/dist/registry/manifest-loader.d.ts +1 -1
  42. package/dist/schemas/manifest.d.ts +1 -0
  43. package/dist/schemas/manifest.d.ts.map +1 -1
  44. package/dist/schemas/manifest.js +1 -0
  45. package/dist/schemas/manifest.js.map +1 -1
  46. package/dist/shared/src/agent.d.ts +28 -0
  47. package/dist/shared/src/agent.d.ts.map +1 -1
  48. package/dist/shared/src/plugin.d.ts +3 -1
  49. package/dist/shared/src/plugin.d.ts.map +1 -1
  50. package/dist/shared/src/schemas/manifest.d.ts +3 -2
  51. package/dist/shared/src/schemas/manifest.d.ts.map +1 -1
  52. package/dist/stream/index.js +1 -0
  53. package/dist/stream/sse-reader.js +86 -0
  54. package/dist/stream/sse-reader.js.map +1 -0
  55. package/docs/api/appkit/Class.DatabricksAdapter.md +34 -0
  56. package/docs/api/appkit/Class.SupervisorApiAdapter.md +121 -0
  57. package/docs/api/appkit/Function.fromSupervisorApi.md +63 -0
  58. package/docs/api/appkit/Function.isSupervisorTool.md +18 -0
  59. package/docs/api/appkit/Interface.AgentAdapter.md +24 -0
  60. package/docs/api/appkit/Interface.AgentInput.md +13 -0
  61. package/docs/api/appkit/Interface.HostedSupervisorTool.md +21 -0
  62. package/docs/api/appkit/Interface.PluginManifest.md +27 -9
  63. package/docs/api/appkit/Interface.SupervisorApiAdapterOptions.md +38 -0
  64. package/docs/api/appkit/Interface.SupervisorExtension.md +12 -0
  65. package/docs/api/appkit/Interface.WorkspaceClientLike.md +67 -0
  66. package/docs/api/appkit/TypeAlias.AgentTool.md +3 -2
  67. package/docs/api/appkit/TypeAlias.ResolvedToolEntry.md +167 -0
  68. package/docs/api/appkit/TypeAlias.SupervisorTool.md +45 -0
  69. package/docs/api/appkit/Variable.SUPERVISOR_EXTENSION_KEY.md +8 -0
  70. package/docs/api/appkit/Variable.supervisorTools.md +176 -0
  71. package/docs/api/appkit.md +118 -108
  72. package/docs/plugins/agents.md +131 -1
  73. package/docs/plugins/manifest.md +12 -11
  74. package/llms.txt +11 -1
  75. package/package.json +2 -1
  76. package/sbom.cdx.json +1 -1
  77. package/scripts/postinstall.js +0 -1
  78. package/skills/appkit-ui-variants/SKILL.md +183 -0
@@ -0,0 +1,498 @@
1
+ import { createLogger } from "../logging/logger.js";
2
+ import { readSseEvents } from "../stream/sse-reader.js";
3
+ import "../stream/index.js";
4
+ import { streamPath } from "../connectors/serving/client.js";
5
+
6
+ //#region src/agents/supervisor-api.ts
7
+ const logger = createLogger("agents:supervisor-api");
8
+ /**
9
+ * Total wall-clock budget for a single `run()` before the adapter aborts the
10
+ * SSE stream and surfaces a terminal `transport` error. Guards against an
11
+ * upstream that stalls indefinitely (the agent run path does not otherwise
12
+ * wrap `adapter.run()` in a timeout). Override via
13
+ * {@link SupervisorApiAdapterOptions.timeoutMs}.
14
+ */
15
+ const DEFAULT_STREAM_TIMEOUT_MS = 3e5;
16
+ /**
17
+ * Single sink for all error events emitted by the adapter. Logs the verbose
18
+ * detail (stack, upstream payload, etc.) at `warn` level and returns a
19
+ * sanitised {@link AgentEvent} carrying only a stable code so the client
20
+ * never sees raw upstream text.
21
+ */
22
+ function emitError(code, detail) {
23
+ logger.warn("supervisor-api error code=%s detail=%s", code, summariseErrorPayload(detail));
24
+ logger.debug("supervisor-api error code=%s detail=%O", code, detail);
25
+ return {
26
+ type: "status",
27
+ status: "error",
28
+ error: `Supervisor API error (${code})`
29
+ };
30
+ }
31
+ /**
32
+ * Renders an upstream error / incomplete_details payload as a short
33
+ * single-line string for log lines. Avoids dumping the full JSON tree
34
+ * (CWE-532): we keep the discriminator (`type`/`code`) plus a trimmed
35
+ * message, and that's it. Full payloads are still available via
36
+ * `DEBUG=appkit:agents:supervisor-api`.
37
+ */
38
+ function summariseErrorPayload(payload) {
39
+ if (payload == null) return "<none>";
40
+ if (typeof payload === "string") return payload.length > 80 ? `${payload.slice(0, 80)}…` : payload;
41
+ if (typeof payload !== "object") return String(payload);
42
+ const obj = payload;
43
+ const kind = typeof obj.type === "string" && obj.type || typeof obj.code === "string" && obj.code || typeof obj.reason === "string" && obj.reason || "object";
44
+ const message = typeof obj.message === "string" && obj.message || typeof obj.detail === "string" && obj.detail || "";
45
+ const trimmed = message.length > 80 ? `${message.slice(0, 80)}…` : message;
46
+ return trimmed ? `${kind}: ${trimmed}` : kind;
47
+ }
48
+ /**
49
+ * Type guard for {@link HostedSupervisorTool}. Used by the agents plugin
50
+ * (`buildToolIndex`) and standalone `runAgent` (`classifyTool`) to route
51
+ * supervisor-hosted tools to the extensions payload rather than the
52
+ * adapter's `tools` array.
53
+ */
54
+ function isSupervisorTool(value) {
55
+ return typeof value === "object" && value !== null && value.__kind === "hosted-supervisor";
56
+ }
57
+ /**
58
+ * Concise factories for declaring Supervisor API tools.
59
+ *
60
+ * Each factory accepts a single named-options object: routing-critical
61
+ * strings (`id`, `name`, `description`) get labels at the call site so
62
+ * "we swapped the args and didn't notice for two weeks" bugs are
63
+ * impossible.
64
+ *
65
+ * `description` is required: SA's protobuf validation rejects `null`/`""`,
66
+ * AND the LLM running on SA reads this string to decide when to route to
67
+ * the tool. Two genie spaces both labelled "Genie space" give the model
68
+ * nothing to discriminate on, so callers always own the routing hint.
69
+ *
70
+ * ⚠ The `description` is read by the LLM at routing time — it is a
71
+ * prompt-injection sink. Do **not** derive it from untrusted input (user
72
+ * messages, request bodies, external systems). Treat it as application
73
+ * configuration. (CWE-1427)
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * import { createAgent } from "@databricks/appkit";
78
+ * import {
79
+ * agents,
80
+ * DatabricksAdapter,
81
+ * supervisorTools,
82
+ * } from "@databricks/appkit/beta";
83
+ *
84
+ * const assistant = createAgent({
85
+ * instructions: "You are a helpful assistant.",
86
+ * model: DatabricksAdapter.fromSupervisorApi({
87
+ * model: "databricks-claude-sonnet-4",
88
+ * }),
89
+ * tools: () => ({
90
+ * nyc: supervisorTools.genieSpace({
91
+ * id: "01ABCDEF12345678",
92
+ * description: "NYC taxi trip records and zones",
93
+ * }),
94
+ * add: supervisorTools.ucFunction({
95
+ * name: "main.default.add",
96
+ * description: "Adds two integers and returns the sum.",
97
+ * }),
98
+ * }),
99
+ * });
100
+ * ```
101
+ */
102
+ const supervisorTools = {
103
+ genieSpace: ({ id, description }) => ({
104
+ __kind: "hosted-supervisor",
105
+ spec: {
106
+ type: "genie_space",
107
+ genie_space: {
108
+ id,
109
+ description
110
+ }
111
+ }
112
+ }),
113
+ ucFunction: ({ name, description }) => ({
114
+ __kind: "hosted-supervisor",
115
+ spec: {
116
+ type: "uc_function",
117
+ uc_function: {
118
+ name,
119
+ description
120
+ }
121
+ }
122
+ }),
123
+ knowledgeAssistant: ({ knowledgeAssistantId, description }) => ({
124
+ __kind: "hosted-supervisor",
125
+ spec: {
126
+ type: "knowledge_assistant",
127
+ knowledge_assistant: {
128
+ knowledge_assistant_id: knowledgeAssistantId,
129
+ description
130
+ }
131
+ }
132
+ }),
133
+ app: ({ name, description }) => ({
134
+ __kind: "hosted-supervisor",
135
+ spec: {
136
+ type: "app",
137
+ app: {
138
+ name,
139
+ description
140
+ }
141
+ }
142
+ }),
143
+ ucConnection: ({ name, description }) => ({
144
+ __kind: "hosted-supervisor",
145
+ spec: {
146
+ type: "uc_connection",
147
+ uc_connection: {
148
+ name,
149
+ description
150
+ }
151
+ }
152
+ })
153
+ };
154
+ /**
155
+ * Namespace key under which the adapter reads its hosted-tool payload
156
+ * from {@link AgentInput.extensions}. Exported so the agents plugin and
157
+ * standalone `runAgent` (the producers) can write under the same key the
158
+ * adapter reads.
159
+ */
160
+ const SUPERVISOR_EXTENSION_KEY = "databricks.supervisor";
161
+ function readSupervisorExtension(input) {
162
+ const raw = input.extensions?.[SUPERVISOR_EXTENSION_KEY];
163
+ if (!raw || typeof raw !== "object") return {};
164
+ return raw;
165
+ }
166
+ /**
167
+ * Adapter that calls the Databricks AI Gateway Responses API
168
+ * (`/ai-gateway/mlflow/v1/responses`).
169
+ *
170
+ * Streams SSE events in the OpenAI Responses API wire format and maps them
171
+ * to the AppKit `AgentEvent` protocol. Tool execution is handled
172
+ * server-side, so the adapter ignores the agents-plugin tool index.
173
+ *
174
+ * Authentication is handled via the Databricks SDK credential chain — the
175
+ * same mechanism used by `DatabricksAdapter.fromModelServing`. The transport
176
+ * is injected via {@link SupervisorApiAdapterCtorOptions.streamBody}; the
177
+ * {@link fromSupervisorApi} factory wires it through the SDK's
178
+ * `apiClient.request({ raw: true })`.
179
+ *
180
+ * Set `DEBUG=appkit:agents:supervisor-api` to log the outbound request
181
+ * shape (model, instructions length, input shape, tool count) and to be
182
+ * notified when the recovery path engages (no incremental deltas, text
183
+ * pulled from `response.completed.output[]`). The no-delta warning includes
184
+ * a per-turn event-type histogram and the SA-reported status/error/
185
+ * incomplete_details, so it's already actionable without DEBUG.
186
+ *
187
+ * Tools are not configured on the adapter. Declare them via
188
+ * `createAgent({ tools: () => ({ key: supervisorTools.genieSpace({...}) }) })`
189
+ * (or markdown frontmatter referencing an ambient `supervisorTools.*` entry);
190
+ * the agents plugin / standalone `runAgent` aggregates hosted-supervisor
191
+ * entries and routes them to the adapter via
192
+ * `AgentInput.extensions[SUPERVISOR_EXTENSION_KEY]`. Advanced callers
193
+ * invoking `adapter.run(...)` directly populate that key themselves.
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * import { createApp, createAgent } from "@databricks/appkit";
198
+ * import {
199
+ * agents,
200
+ * DatabricksAdapter,
201
+ * supervisorTools,
202
+ * } from "@databricks/appkit/beta";
203
+ *
204
+ * await createApp({
205
+ * plugins: [
206
+ * agents({
207
+ * agents: {
208
+ * assistant: createAgent({
209
+ * instructions: "You are a helpful assistant.",
210
+ * model: DatabricksAdapter.fromSupervisorApi({
211
+ * model: "databricks-claude-sonnet-4",
212
+ * }),
213
+ * tools: () => ({
214
+ * nyc: supervisorTools.genieSpace({
215
+ * id: "01ABCDEF12345678",
216
+ * description: "NYC taxi trip records and zones",
217
+ * }),
218
+ * }),
219
+ * }),
220
+ * },
221
+ * }),
222
+ * ],
223
+ * });
224
+ * ```
225
+ */
226
+ var SupervisorApiAdapter = class {
227
+ streamBody;
228
+ model;
229
+ timeoutMs;
230
+ /**
231
+ * Capability negotiation: the adapter reads its hosted-tool payload
232
+ * from {@link AgentInput.extensions} under {@link SUPERVISOR_EXTENSION_KEY}.
233
+ * The agents plugin uses this list to warn at registration when the tool
234
+ * index produces extensions the adapter wouldn't consume.
235
+ */
236
+ acceptsExtensions = [SUPERVISOR_EXTENSION_KEY];
237
+ /**
238
+ * Capability negotiation: the adapter does not consume `input.tools`.
239
+ * Tool execution is owned by the Databricks AI Gateway server-side, so
240
+ * any function tools or local sub-agents declared on this agent would
241
+ * be silently dropped — the agents plugin warns at registration when
242
+ * that combination is detected.
243
+ */
244
+ consumesInputTools = false;
245
+ constructor(options) {
246
+ this.streamBody = options.streamBody;
247
+ this.model = options.model;
248
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_STREAM_TIMEOUT_MS;
249
+ }
250
+ async *run(input, context) {
251
+ if (context.signal?.aborted) return;
252
+ yield {
253
+ type: "status",
254
+ status: "running"
255
+ };
256
+ const { instructions, input: payloadInput } = this.buildInput(input.messages);
257
+ const hostedTools = readSupervisorExtension(input).hostedTools ?? [];
258
+ yield* this.streamResponse(instructions, payloadInput, hostedTools, context.signal);
259
+ }
260
+ async *streamResponse(instructions, input, hostedTools, signal) {
261
+ const body = {
262
+ model: this.model,
263
+ input,
264
+ stream: true
265
+ };
266
+ if (instructions) body.instructions = instructions;
267
+ if (hostedTools.length > 0) body.tools = hostedTools;
268
+ logger.debug("model=%s instructionsLen=%d inputType=%s tools=%d", this.model, instructions?.length ?? 0, typeof input === "string" ? "string" : `array[${input.length}]`, hostedTools.length);
269
+ const timeoutSignal = AbortSignal.timeout(this.timeoutMs);
270
+ const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
271
+ let stream;
272
+ try {
273
+ stream = await this.streamBody(body, combinedSignal);
274
+ } catch (err) {
275
+ if (signal?.aborted) return;
276
+ yield emitError("transport", err);
277
+ return;
278
+ }
279
+ let receivedAnyDelta = false;
280
+ const streamedItemIds = /* @__PURE__ */ new Set();
281
+ const eventCounts = /* @__PURE__ */ new Map();
282
+ let terminated = false;
283
+ let lastCompleted;
284
+ try {
285
+ for await (const { event, data } of readSseEvents(stream, combinedSignal)) {
286
+ if (data === "[DONE]") continue;
287
+ let parsed;
288
+ try {
289
+ parsed = JSON.parse(data);
290
+ } catch (err) {
291
+ logger.debug("Failed to parse SSE data line: %s (%O)", data.slice(0, 200), err);
292
+ continue;
293
+ }
294
+ const eventType = event || (typeof parsed.type === "string" ? parsed.type : "");
295
+ eventCounts.set(eventType, (eventCounts.get(eventType) ?? 0) + 1);
296
+ if (eventType === "response.completed") {
297
+ lastCompleted = parsed.response;
298
+ continue;
299
+ }
300
+ const out = mapEvent(eventType, parsed, streamedItemIds);
301
+ if (out) {
302
+ if (out.type === "message_delta") receivedAnyDelta = true;
303
+ yield out;
304
+ if (out.type === "status" && out.status === "error") {
305
+ terminated = true;
306
+ break;
307
+ }
308
+ }
309
+ }
310
+ } catch (err) {
311
+ if (signal?.aborted) return;
312
+ yield emitError("transport", err);
313
+ return;
314
+ }
315
+ if (signal?.aborted) return;
316
+ if (timeoutSignal.aborted) {
317
+ yield emitError("transport", `stream timed out after ${this.timeoutMs}ms`);
318
+ return;
319
+ }
320
+ if (eventCounts.size === 0) {
321
+ yield emitError("transport", "stream closed without events");
322
+ return;
323
+ }
324
+ if (terminated) return;
325
+ if (!receivedAnyDelta) {
326
+ const recovered = extractTextFromCompletedResponse(lastCompleted);
327
+ if (recovered) {
328
+ logger.debug("Recovered %d chars from response.completed.output[]", recovered.length);
329
+ yield {
330
+ type: "message_delta",
331
+ content: recovered
332
+ };
333
+ receivedAnyDelta = true;
334
+ }
335
+ }
336
+ if (eventCounts.has("response.completed")) {
337
+ if (lastCompleted?.status === "failed" || lastCompleted?.error != null) {
338
+ yield emitError("upstream_failed", {
339
+ status: lastCompleted?.status,
340
+ error: lastCompleted?.error,
341
+ incomplete_details: lastCompleted?.incomplete_details
342
+ });
343
+ return;
344
+ }
345
+ yield {
346
+ type: "status",
347
+ status: "complete"
348
+ };
349
+ }
350
+ if (!receivedAnyDelta) {
351
+ const histogram = [...eventCounts.entries()].map(([t, n]) => `${t}=${n}`).join(", ");
352
+ logger.warn("Supervisor API stream completed without any output_text deltas. events={%s} completed.status=%s completed.error=%s completed.incomplete=%s", histogram, lastCompleted?.status ?? "<none>", summariseErrorPayload(lastCompleted?.error), summariseErrorPayload(lastCompleted?.incomplete_details));
353
+ logger.debug("Supervisor API no-delta full payload: error=%O incomplete=%O", lastCompleted?.error, lastCompleted?.incomplete_details);
354
+ }
355
+ }
356
+ /**
357
+ * Splits the agent's message list into a Responses-API payload. System
358
+ * messages are concatenated (in order) into the top-level `instructions`
359
+ * field; user/assistant turns become `input` (as a plain string for the
360
+ * common single-user-turn case, otherwise as `{role,content}[]`). Tool-role
361
+ * messages are skipped — SA owns its own tool history server-side, so
362
+ * re-feeding our tool-result records would only confuse it.
363
+ */
364
+ buildInput(messages) {
365
+ const instructionsParts = [];
366
+ const turns = [];
367
+ for (const m of messages) if (m.role === "system") instructionsParts.push(m.content);
368
+ else if (m.role !== "tool") turns.push({
369
+ role: m.role,
370
+ content: m.content
371
+ });
372
+ const instructions = instructionsParts.length ? instructionsParts.join("\n\n") : void 0;
373
+ if (turns.length === 1 && turns[0].role === "user") return {
374
+ instructions,
375
+ input: turns[0].content
376
+ };
377
+ return {
378
+ instructions,
379
+ input: turns
380
+ };
381
+ }
382
+ };
383
+ /**
384
+ * Pulls the final assistant text out of the `response` payload attached to a
385
+ * `response.completed` event. SA always materialises the full response there,
386
+ * so this is our last-resort recovery path when the stream produced neither
387
+ * `output_text.delta` nor an actionable `output_item.done` (observed
388
+ * intermittently with tool-enabled SA agents).
389
+ */
390
+ function extractTextFromCompletedResponse(response) {
391
+ if (!response?.output) return "";
392
+ let text = "";
393
+ for (const item of response.output) {
394
+ if (item?.type !== "message" || !Array.isArray(item.content)) continue;
395
+ for (const part of item.content) if (part?.type === "output_text" && typeof part.text === "string") text += part.text;
396
+ }
397
+ return text;
398
+ }
399
+ function mapEvent(eventType, data, streamedItemIds) {
400
+ switch (eventType) {
401
+ case "response.output_text.delta": {
402
+ const itemId = typeof data.item_id === "string" ? data.item_id : void 0;
403
+ if (itemId) streamedItemIds.add(itemId);
404
+ return {
405
+ type: "message_delta",
406
+ content: typeof data.delta === "string" ? data.delta : ""
407
+ };
408
+ }
409
+ case "response.failed": return emitError("upstream_failed", data);
410
+ case "error": return emitError("upstream_unknown", typeof data.error === "string" ? data.error : data.error == null ? "Unknown error" : data.error);
411
+ case "response.output_item.done": {
412
+ const item = data.item;
413
+ if (item?.type === "error" || item?.id === "error" && item?.type !== "message") return emitError("upstream_tool", item);
414
+ if (item?.type === "message" && item.id && !streamedItemIds.has(item.id)) {
415
+ const text = (item.content ?? []).map((c) => c.type === "output_text" ? c.text ?? "" : "").join("");
416
+ if (text.length > 0) {
417
+ streamedItemIds.add(item.id);
418
+ return {
419
+ type: "message_delta",
420
+ content: text
421
+ };
422
+ }
423
+ }
424
+ return null;
425
+ }
426
+ default: return null;
427
+ }
428
+ }
429
+ /**
430
+ * Creates an {@link AgentAdapter} backed by the Databricks AI Gateway
431
+ * Responses API (`/ai-gateway/mlflow/v1/responses`).
432
+ *
433
+ * Uses the SDK's default credential chain for auth (reads DATABRICKS_HOST,
434
+ * DATABRICKS_TOKEN, OAuth config, etc.). Tools are declared on the agent
435
+ * (via `createAgent({ tools })`), not on this factory.
436
+ *
437
+ * Application code should prefer the
438
+ * {@link DatabricksAdapter.fromSupervisorApi} static — it delegates here
439
+ * and keeps a single `DatabricksAdapter.from*` autocomplete root for all
440
+ * Databricks-backed adapters. This free function is the implementation
441
+ * behind the static and remains exported for callers that want to import
442
+ * it directly without pulling in {@link DatabricksAdapter}.
443
+ *
444
+ * @example
445
+ * ```ts
446
+ * import { createApp, createAgent } from "@databricks/appkit";
447
+ * import {
448
+ * agents,
449
+ * DatabricksAdapter,
450
+ * supervisorTools,
451
+ * } from "@databricks/appkit/beta";
452
+ *
453
+ * await createApp({
454
+ * plugins: [
455
+ * agents({
456
+ * agents: {
457
+ * assistant: createAgent({
458
+ * instructions: "You are a helpful assistant.",
459
+ * model: DatabricksAdapter.fromSupervisorApi({
460
+ * model: "databricks-claude-sonnet-4",
461
+ * }),
462
+ * tools: () => ({
463
+ * nyc: supervisorTools.genieSpace({
464
+ * id: "01ABCDEF12345678",
465
+ * description: "NYC taxi trip records and zones",
466
+ * }),
467
+ * }),
468
+ * }),
469
+ * },
470
+ * }),
471
+ * ],
472
+ * });
473
+ * ```
474
+ *
475
+ * @remarks
476
+ * ⚠ When passing your own `workspaceClient`, see the warning on
477
+ * {@link SupervisorApiAdapterOptions.workspaceClient} — the client is
478
+ * captured once and reused, so per-request OBO clients would leak
479
+ * identity across requests.
480
+ *
481
+ * @see {@link DatabricksAdapter.fromSupervisorApi} — the recommended
482
+ * application-facing entry point.
483
+ */
484
+ async function fromSupervisorApi(options) {
485
+ let client = options.workspaceClient;
486
+ if (!client) client = new (await (import("@databricks/sdk-experimental"))).WorkspaceClient({});
487
+ await client.config.ensureResolved();
488
+ const resolved = client;
489
+ return new SupervisorApiAdapter({
490
+ streamBody: (body, signal) => streamPath(resolved, "/ai-gateway/mlflow/v1/responses", body, signal),
491
+ model: options.model,
492
+ timeoutMs: options.timeoutMs
493
+ });
494
+ }
495
+
496
+ //#endregion
497
+ export { SUPERVISOR_EXTENSION_KEY, SupervisorApiAdapter, fromSupervisorApi, isSupervisorTool, supervisorTools };
498
+ //# sourceMappingURL=supervisor-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supervisor-api.js","names":[],"sources":["../../src/agents/supervisor-api.ts"],"sourcesContent":["import type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n AgentRunContext,\n Message,\n ResponseStreamEvent,\n} from \"shared\";\nimport {\n type ApiClientLike,\n type StreamBody,\n streamPath,\n} from \"../connectors/serving/client\";\nimport { createLogger } from \"../logging/logger\";\nimport { readSseEvents } from \"../stream\";\n\nconst logger = createLogger(\"agents:supervisor-api\");\n\n/**\n * Total wall-clock budget for a single `run()` before the adapter aborts the\n * SSE stream and surfaces a terminal `transport` error. Guards against an\n * upstream that stalls indefinitely (the agent run path does not otherwise\n * wrap `adapter.run()` in a timeout). Override via\n * {@link SupervisorApiAdapterOptions.timeoutMs}.\n */\nconst DEFAULT_STREAM_TIMEOUT_MS = 300_000;\n\n/**\n * Stable client-facing error codes. We never surface raw upstream error\n * strings to the client (CWE-209) — the helper logs the verbose detail\n * server-side and returns one of these codes in the {@link AgentEvent}.\n */\ntype SupervisorErrorCode =\n | \"transport\"\n | \"upstream_failed\"\n | \"upstream_tool\"\n | \"upstream_unknown\";\n\n/**\n * Single sink for all error events emitted by the adapter. Logs the verbose\n * detail (stack, upstream payload, etc.) at `warn` level and returns a\n * sanitised {@link AgentEvent} carrying only a stable code so the client\n * never sees raw upstream text.\n */\nfunction emitError(code: SupervisorErrorCode, detail: unknown): AgentEvent {\n // Summarise at `warn` (CWE-532: never dump the full upstream payload to\n // the default log level); the verbose object is only available via\n // `DEBUG=appkit:agents:supervisor-api`.\n logger.warn(\n \"supervisor-api error code=%s detail=%s\",\n code,\n summariseErrorPayload(detail),\n );\n logger.debug(\"supervisor-api error code=%s detail=%O\", code, detail);\n return {\n type: \"status\",\n status: \"error\",\n error: `Supervisor API error (${code})`,\n };\n}\n\n/**\n * Renders an upstream error / incomplete_details payload as a short\n * single-line string for log lines. Avoids dumping the full JSON tree\n * (CWE-532): we keep the discriminator (`type`/`code`) plus a trimmed\n * message, and that's it. Full payloads are still available via\n * `DEBUG=appkit:agents:supervisor-api`.\n */\nfunction summariseErrorPayload(payload: unknown): string {\n if (payload == null) return \"<none>\";\n if (typeof payload === \"string\") {\n return payload.length > 80 ? `${payload.slice(0, 80)}…` : payload;\n }\n if (typeof payload !== \"object\") return String(payload);\n const obj = payload as Record<string, unknown>;\n const kind =\n (typeof obj.type === \"string\" && obj.type) ||\n (typeof obj.code === \"string\" && obj.code) ||\n (typeof obj.reason === \"string\" && obj.reason) ||\n \"object\";\n const message =\n (typeof obj.message === \"string\" && obj.message) ||\n (typeof obj.detail === \"string\" && obj.detail) ||\n \"\";\n const trimmed = message.length > 80 ? `${message.slice(0, 80)}…` : message;\n return trimmed ? `${kind}: ${trimmed}` : kind;\n}\n\n/**\n * Structural shape of a Databricks SDK client used by {@link fromSupervisorApi}.\n * Only what we need: `apiClient.request` for streaming and\n * `config.ensureResolved` to materialise the host/credentials.\n *\n * Exported because {@link SupervisorApiAdapterOptions.workspaceClient} (a\n * public type) references it — callers passing their own client can name\n * the shape they need to satisfy.\n */\nexport interface WorkspaceClientLike extends ApiClientLike {\n config: { ensureResolved(): Promise<void> };\n}\n\n// ---------------------------------------------------------------------------\n// Supervisor API tool surface (wire format)\n// ---------------------------------------------------------------------------\n\n/**\n * Tools supported by the Databricks AI Gateway Responses API. The shapes match\n * the wire format the endpoint expects, so the adapter passes the array\n * straight into the request body.\n *\n * This is an adapter-internal wire type. Application code authors tools via\n * the {@link supervisorTools} factories, which return tagged\n * {@link HostedSupervisorTool} records — the agents plugin then unwraps\n * the `.spec` when routing through {@link AgentInput.extensions}.\n */\nexport type SupervisorTool =\n | { type: \"genie_space\"; genie_space: { id: string; description: string } }\n | { type: \"uc_function\"; uc_function: { name: string; description: string } }\n | {\n type: \"knowledge_assistant\";\n knowledge_assistant: {\n knowledge_assistant_id: string;\n description: string;\n };\n }\n | { type: \"app\"; app: { name: string; description: string } }\n | {\n type: \"uc_connection\";\n uc_connection: { name: string; description: string };\n };\n\n/**\n * Tagged record returned by every {@link supervisorTools} factory. The\n * `__kind` discriminator lets the agents plugin (and standalone\n * `runAgent`) classify these tools without a structural match against the\n * wire format — keeps the SA wire shape free to evolve and avoids\n * namespace collisions with MCP hosted tools (which use `type: \"genie-space\"`\n * hyphenated, vs SA's `type: \"genie_space\"` underscored).\n */\nexport interface HostedSupervisorTool {\n readonly __kind: \"hosted-supervisor\";\n readonly spec: SupervisorTool;\n}\n\n/**\n * Type guard for {@link HostedSupervisorTool}. Used by the agents plugin\n * (`buildToolIndex`) and standalone `runAgent` (`classifyTool`) to route\n * supervisor-hosted tools to the extensions payload rather than the\n * adapter's `tools` array.\n */\nexport function isSupervisorTool(\n value: unknown,\n): value is HostedSupervisorTool {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as Record<string, unknown>).__kind === \"hosted-supervisor\"\n );\n}\n\n/**\n * Concise factories for declaring Supervisor API tools.\n *\n * Each factory accepts a single named-options object: routing-critical\n * strings (`id`, `name`, `description`) get labels at the call site so\n * \"we swapped the args and didn't notice for two weeks\" bugs are\n * impossible.\n *\n * `description` is required: SA's protobuf validation rejects `null`/`\"\"`,\n * AND the LLM running on SA reads this string to decide when to route to\n * the tool. Two genie spaces both labelled \"Genie space\" give the model\n * nothing to discriminate on, so callers always own the routing hint.\n *\n * ⚠ The `description` is read by the LLM at routing time — it is a\n * prompt-injection sink. Do **not** derive it from untrusted input (user\n * messages, request bodies, external systems). Treat it as application\n * configuration. (CWE-1427)\n *\n * @example\n * ```ts\n * import { createAgent } from \"@databricks/appkit\";\n * import {\n * agents,\n * DatabricksAdapter,\n * supervisorTools,\n * } from \"@databricks/appkit/beta\";\n *\n * const assistant = createAgent({\n * instructions: \"You are a helpful assistant.\",\n * model: DatabricksAdapter.fromSupervisorApi({\n * model: \"databricks-claude-sonnet-4\",\n * }),\n * tools: () => ({\n * nyc: supervisorTools.genieSpace({\n * id: \"01ABCDEF12345678\",\n * description: \"NYC taxi trip records and zones\",\n * }),\n * add: supervisorTools.ucFunction({\n * name: \"main.default.add\",\n * description: \"Adds two integers and returns the sum.\",\n * }),\n * }),\n * });\n * ```\n */\nexport const supervisorTools = {\n genieSpace: ({\n id,\n description,\n }: {\n id: string;\n description: string;\n }): HostedSupervisorTool => ({\n __kind: \"hosted-supervisor\",\n spec: { type: \"genie_space\", genie_space: { id, description } },\n }),\n ucFunction: ({\n name,\n description,\n }: {\n name: string;\n description: string;\n }): HostedSupervisorTool => ({\n __kind: \"hosted-supervisor\",\n spec: { type: \"uc_function\", uc_function: { name, description } },\n }),\n knowledgeAssistant: ({\n knowledgeAssistantId,\n description,\n }: {\n knowledgeAssistantId: string;\n description: string;\n }): HostedSupervisorTool => ({\n __kind: \"hosted-supervisor\",\n spec: {\n type: \"knowledge_assistant\",\n knowledge_assistant: {\n knowledge_assistant_id: knowledgeAssistantId,\n description,\n },\n },\n }),\n app: ({\n name,\n description,\n }: {\n name: string;\n description: string;\n }): HostedSupervisorTool => ({\n __kind: \"hosted-supervisor\",\n spec: { type: \"app\", app: { name, description } },\n }),\n ucConnection: ({\n name,\n description,\n }: {\n name: string;\n description: string;\n }): HostedSupervisorTool => ({\n __kind: \"hosted-supervisor\",\n spec: { type: \"uc_connection\", uc_connection: { name, description } },\n }),\n};\n\n// ---------------------------------------------------------------------------\n// AgentInput.extensions integration\n// ---------------------------------------------------------------------------\n\n/**\n * Namespace key under which the adapter reads its hosted-tool payload\n * from {@link AgentInput.extensions}. Exported so the agents plugin and\n * standalone `runAgent` (the producers) can write under the same key the\n * adapter reads.\n */\nexport const SUPERVISOR_EXTENSION_KEY = \"databricks.supervisor\" as const;\n\n/**\n * Shape of the value at `AgentInput.extensions[SUPERVISOR_EXTENSION_KEY]`.\n * The agents plugin / `runAgent` build this from the tool index; advanced\n * callers invoking `adapter.run(...)` directly populate it themselves.\n */\nexport interface SupervisorExtension {\n hostedTools?: SupervisorTool[];\n}\n\nfunction readSupervisorExtension(input: AgentInput): SupervisorExtension {\n const raw = input.extensions?.[SUPERVISOR_EXTENSION_KEY];\n // Single cast at the boundary. The contract on `extensions` is opaque;\n // we trust the producer (agents plugin / runAgent / caller) to use the\n // shape declared here.\n if (!raw || typeof raw !== \"object\") return {};\n return raw as SupervisorExtension;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter\n// ---------------------------------------------------------------------------\n\nexport interface SupervisorApiAdapterOptions {\n /**\n * Model identifier to pass in the request body\n * (e.g. \"databricks-claude-sonnet-4\").\n */\n model: string;\n /**\n * A WorkspaceClient (or structural equivalent) used for host resolution\n * and per-request authentication. When omitted, a `WorkspaceClient({})`\n * is created internally using the default SDK credential chain\n * (`DATABRICKS_HOST`, OAuth, PAT, etc.).\n *\n * ⚠ The `workspaceClient` is captured at construction and reused across\n * every request. Passing a per-request OBO (On-Behalf-Of) client here\n * would silently leak the first request's identity into all subsequent\n * requests served by this adapter instance. Use the default credential\n * chain or pass a service-principal client. (CWE-664)\n */\n workspaceClient?: WorkspaceClientLike;\n /**\n * Total wall-clock budget (ms) for a single `run()`. When the SSE stream\n * runs longer than this — e.g. an upstream that stalls without closing —\n * the adapter aborts it and emits a terminal `transport` error rather than\n * hanging the request indefinitely.\n *\n * This is a total-duration cap, not an idle cap. Defaults to 5 minutes,\n * generous enough for multi-tool server-side orchestration.\n */\n timeoutMs?: number;\n}\n\ninterface SupervisorApiAdapterCtorOptions {\n streamBody: StreamBody;\n model: string;\n timeoutMs?: number;\n}\n\n/**\n * Adapter that calls the Databricks AI Gateway Responses API\n * (`/ai-gateway/mlflow/v1/responses`).\n *\n * Streams SSE events in the OpenAI Responses API wire format and maps them\n * to the AppKit `AgentEvent` protocol. Tool execution is handled\n * server-side, so the adapter ignores the agents-plugin tool index.\n *\n * Authentication is handled via the Databricks SDK credential chain — the\n * same mechanism used by `DatabricksAdapter.fromModelServing`. The transport\n * is injected via {@link SupervisorApiAdapterCtorOptions.streamBody}; the\n * {@link fromSupervisorApi} factory wires it through the SDK's\n * `apiClient.request({ raw: true })`.\n *\n * Set `DEBUG=appkit:agents:supervisor-api` to log the outbound request\n * shape (model, instructions length, input shape, tool count) and to be\n * notified when the recovery path engages (no incremental deltas, text\n * pulled from `response.completed.output[]`). The no-delta warning includes\n * a per-turn event-type histogram and the SA-reported status/error/\n * incomplete_details, so it's already actionable without DEBUG.\n *\n * Tools are not configured on the adapter. Declare them via\n * `createAgent({ tools: () => ({ key: supervisorTools.genieSpace({...}) }) })`\n * (or markdown frontmatter referencing an ambient `supervisorTools.*` entry);\n * the agents plugin / standalone `runAgent` aggregates hosted-supervisor\n * entries and routes them to the adapter via\n * `AgentInput.extensions[SUPERVISOR_EXTENSION_KEY]`. Advanced callers\n * invoking `adapter.run(...)` directly populate that key themselves.\n *\n * @example\n * ```ts\n * import { createApp, createAgent } from \"@databricks/appkit\";\n * import {\n * agents,\n * DatabricksAdapter,\n * supervisorTools,\n * } from \"@databricks/appkit/beta\";\n *\n * await createApp({\n * plugins: [\n * agents({\n * agents: {\n * assistant: createAgent({\n * instructions: \"You are a helpful assistant.\",\n * model: DatabricksAdapter.fromSupervisorApi({\n * model: \"databricks-claude-sonnet-4\",\n * }),\n * tools: () => ({\n * nyc: supervisorTools.genieSpace({\n * id: \"01ABCDEF12345678\",\n * description: \"NYC taxi trip records and zones\",\n * }),\n * }),\n * }),\n * },\n * }),\n * ],\n * });\n * ```\n */\nexport class SupervisorApiAdapter implements AgentAdapter {\n private streamBody: StreamBody;\n private model: string;\n private timeoutMs: number;\n\n /**\n * Capability negotiation: the adapter reads its hosted-tool payload\n * from {@link AgentInput.extensions} under {@link SUPERVISOR_EXTENSION_KEY}.\n * The agents plugin uses this list to warn at registration when the tool\n * index produces extensions the adapter wouldn't consume.\n */\n readonly acceptsExtensions = [SUPERVISOR_EXTENSION_KEY] as const;\n\n /**\n * Capability negotiation: the adapter does not consume `input.tools`.\n * Tool execution is owned by the Databricks AI Gateway server-side, so\n * any function tools or local sub-agents declared on this agent would\n * be silently dropped — the agents plugin warns at registration when\n * that combination is detected.\n */\n readonly consumesInputTools = false;\n\n constructor(options: SupervisorApiAdapterCtorOptions) {\n this.streamBody = options.streamBody;\n this.model = options.model;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_STREAM_TIMEOUT_MS;\n }\n\n async *run(\n input: AgentInput,\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n if (context.signal?.aborted) return;\n\n yield { type: \"status\", status: \"running\" };\n\n const { instructions, input: payloadInput } = this.buildInput(\n input.messages,\n );\n const hostedTools = readSupervisorExtension(input).hostedTools ?? [];\n yield* this.streamResponse(\n instructions,\n payloadInput,\n hostedTools,\n context.signal,\n );\n }\n\n private async *streamResponse(\n instructions: string | undefined,\n input: ResponseInput,\n hostedTools: SupervisorTool[],\n signal?: AbortSignal,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n const body: Record<string, unknown> = {\n model: this.model,\n input,\n stream: true,\n };\n if (instructions) {\n body.instructions = instructions;\n }\n // SA's protobuf validation rejects `tools: []` and `tools: null`. Only\n // include the field when at least one tool is configured.\n if (hostedTools.length > 0) {\n body.tools = hostedTools;\n }\n\n logger.debug(\n \"model=%s instructionsLen=%d inputType=%s tools=%d\",\n this.model,\n instructions?.length ?? 0,\n typeof input === \"string\" ? \"string\" : `array[${input.length}]`,\n hostedTools.length,\n );\n\n // Compose a total-duration timeout with the consumer's abort signal. The\n // agent run path drives `run()` directly without a TimeoutInterceptor, so\n // without this the adapter would hang forever on a stalled upstream. We\n // hand the combined signal to the transport + reader, but keep checking\n // the consumer's `signal` separately: a consumer-initiated abort is a\n // clean stop, whereas a timeout is a failure that must surface an error.\n const timeoutSignal = AbortSignal.timeout(this.timeoutMs);\n const combinedSignal = signal\n ? AbortSignal.any([signal, timeoutSignal])\n : timeoutSignal;\n\n let stream: ReadableStream<Uint8Array>;\n try {\n stream = await this.streamBody(body, combinedSignal);\n } catch (err) {\n // Aborts surface as exceptions thrown by `fetch`/SDK transports when\n // the consumer cancels mid-request. Treat as a clean stop so consumers\n // don't see a contradictory terminal `error` after their own abort. A\n // timeout (consumer signal not aborted) falls through to `emitError`.\n if (signal?.aborted) return;\n yield emitError(\"transport\", err);\n return;\n }\n\n let receivedAnyDelta = false;\n // Tracks `item_id`s we've already streamed text deltas for. Used by\n // `mapEvent` to fall back to the final item text on `output_item.done`\n // only when no incremental deltas streamed for that item — avoids\n // double-emitting text when SA does both delta and done.\n const streamedItemIds = new Set<string>();\n // Histogram of received event types — surfaced in the no-delta warning\n // so it's actionable without re-running with DEBUG.\n const eventCounts = new Map<string, number>();\n // Set to true once we've yielded a terminal `{status:\"error\"}` event so\n // the recovery / completion / no-delta-warning blocks below all bail\n // out — the consumer's already seen the terminal status, anything\n // further would contradict the protocol's terminal-event semantics.\n let terminated = false;\n // Diagnostic snapshot of the last `response.completed` event. SA stuffs\n // the final assistant message into `response.output[]` even when it\n // didn't emit any deltas (e.g. when a tool failed or the model produced\n // nothing). Keeping it lets us recover the text and surface useful\n // errors instead of a silent empty turn.\n let lastCompleted:\n | {\n status?: string;\n output?: Array<{\n type?: string;\n content?: Array<{ type?: string; text?: string }>;\n }>;\n error?: unknown;\n incomplete_details?: unknown;\n }\n | undefined;\n\n // `readSseEvents` throws on transport errors and on the DoS caps\n // (maxLineChars / maxBufferChars). Without this guard the rejection\n // propagates out of `run()` and tears down the request. Treat a\n // consumer-initiated abort as a clean stop; everything else becomes a\n // sanitised terminal `transport` error.\n try {\n for await (const { event, data } of readSseEvents(\n stream,\n combinedSignal,\n )) {\n if (data === \"[DONE]\") continue;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(data);\n } catch (err) {\n logger.debug(\n \"Failed to parse SSE data line: %s (%O)\",\n data.slice(0, 200),\n err,\n );\n continue;\n }\n\n const eventType =\n event || (typeof parsed.type === \"string\" ? parsed.type : \"\");\n eventCounts.set(eventType, (eventCounts.get(eventType) ?? 0) + 1);\n\n // `response.completed` is held back until after the loop so we can\n // synthesise a `message_delta` from `response.output[]` when the\n // stream produced no incremental deltas (intermittent SA behaviour).\n // Emitting `complete` first would let UIs finalise the turn before the\n // recovered text arrives.\n if (eventType === \"response.completed\") {\n lastCompleted = parsed.response as typeof lastCompleted;\n continue;\n }\n\n const out = mapEvent(eventType, parsed, streamedItemIds);\n if (out) {\n if (out.type === \"message_delta\") receivedAnyDelta = true;\n yield out;\n if (out.type === \"status\" && out.status === \"error\") {\n terminated = true;\n break;\n }\n }\n }\n } catch (err) {\n if (signal?.aborted) return;\n yield emitError(\"transport\", err);\n return;\n }\n\n // Consumer-initiated abort: clean stop, no terminal error.\n if (signal?.aborted) return;\n\n // Timeout fired while reading (the reader may break cleanly rather than\n // throw, depending on where the abort lands), so check it explicitly.\n if (timeoutSignal.aborted) {\n yield emitError(\n \"transport\",\n `stream timed out after ${this.timeoutMs}ms`,\n );\n return;\n }\n\n if (eventCounts.size === 0) {\n // A stream that closes without a single event leaves the consumer\n // stuck in `running`. Surface a terminal `transport` error so the\n // turn ends.\n yield emitError(\"transport\", \"stream closed without events\");\n return;\n }\n\n if (terminated) return;\n\n // Recovery path: no deltas streamed but SA finished — pull the assistant\n // text out of `response.completed.response.output[]`.\n if (!receivedAnyDelta) {\n const recovered = extractTextFromCompletedResponse(lastCompleted);\n if (recovered) {\n logger.debug(\n \"Recovered %d chars from response.completed.output[]\",\n recovered.length,\n );\n yield { type: \"message_delta\", content: recovered };\n receivedAnyDelta = true;\n }\n }\n\n if (eventCounts.has(\"response.completed\")) {\n // SA sometimes signals a failed turn via `response.completed` with a\n // nested `status: \"failed\"` (or a populated `error`) rather than\n // emitting `response.failed`. Without this gate the adapter would\n // silently yield `complete` on a server-side failure.\n //\n // `incomplete_details` on its own is NOT fatal: a benign\n // `max_output_tokens` truncation populates it while still producing\n // usable partial output. In that case we fall through to `complete`\n // and let the recovered text above stand as the turn result.\n if (lastCompleted?.status === \"failed\" || lastCompleted?.error != null) {\n yield emitError(\"upstream_failed\", {\n status: lastCompleted?.status,\n error: lastCompleted?.error,\n incomplete_details: lastCompleted?.incomplete_details,\n });\n return;\n }\n yield { type: \"status\", status: \"complete\" };\n }\n\n if (!receivedAnyDelta) {\n const histogram = [...eventCounts.entries()]\n .map(([t, n]) => `${t}=${n}`)\n .join(\", \");\n logger.warn(\n \"Supervisor API stream completed without any output_text deltas. \" +\n \"events={%s} completed.status=%s completed.error=%s completed.incomplete=%s\",\n histogram,\n lastCompleted?.status ?? \"<none>\",\n summariseErrorPayload(lastCompleted?.error),\n summariseErrorPayload(lastCompleted?.incomplete_details),\n );\n logger.debug(\n \"Supervisor API no-delta full payload: error=%O incomplete=%O\",\n lastCompleted?.error,\n lastCompleted?.incomplete_details,\n );\n }\n }\n\n /**\n * Splits the agent's message list into a Responses-API payload. System\n * messages are concatenated (in order) into the top-level `instructions`\n * field; user/assistant turns become `input` (as a plain string for the\n * common single-user-turn case, otherwise as `{role,content}[]`). Tool-role\n * messages are skipped — SA owns its own tool history server-side, so\n * re-feeding our tool-result records would only confuse it.\n */\n private buildInput(messages: Message[]): {\n instructions: string | undefined;\n input: ResponseInput;\n } {\n const instructionsParts: string[] = [];\n const turns: Array<{\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n }> = [];\n\n for (const m of messages) {\n if (m.role === \"system\") instructionsParts.push(m.content);\n else if (m.role !== \"tool\")\n turns.push({ role: m.role, content: m.content });\n }\n\n const instructions = instructionsParts.length\n ? instructionsParts.join(\"\\n\\n\")\n : undefined;\n\n if (turns.length === 1 && turns[0].role === \"user\") {\n return { instructions, input: turns[0].content };\n }\n return { instructions, input: turns };\n }\n}\n\ntype ResponseInput =\n | string\n | Array<{ role: \"user\" | \"assistant\" | \"system\"; content: string }>;\n\n/**\n * Pulls the final assistant text out of the `response` payload attached to a\n * `response.completed` event. SA always materialises the full response there,\n * so this is our last-resort recovery path when the stream produced neither\n * `output_text.delta` nor an actionable `output_item.done` (observed\n * intermittently with tool-enabled SA agents).\n */\nfunction extractTextFromCompletedResponse(\n response:\n | {\n output?: Array<{\n type?: string;\n content?: Array<{ type?: string; text?: string }>;\n }>;\n }\n | undefined,\n): string {\n if (!response?.output) return \"\";\n let text = \"\";\n for (const item of response.output) {\n if (item?.type !== \"message\" || !Array.isArray(item.content)) continue;\n for (const part of item.content) {\n if (part?.type === \"output_text\" && typeof part.text === \"string\") {\n text += part.text;\n }\n }\n }\n return text;\n}\n\nfunction mapEvent(\n eventType: string,\n data: Record<string, unknown>,\n streamedItemIds: Set<string>,\n): AgentEvent | null {\n // The cast restricts the switch domain to the closed wire-event union\n // exported by `shared`, so typos in case clauses (e.g. `response.faled`)\n // become compile errors instead of silent string mismatches. Unknown\n // event names still fall through to `default` at runtime — we don't\n // require exhaustive matching since SA emits more lifecycle events\n // than we care to map.\n switch (eventType as ResponseStreamEvent[\"type\"]) {\n case \"response.output_text.delta\": {\n const itemId =\n typeof data.item_id === \"string\" ? data.item_id : undefined;\n if (itemId) streamedItemIds.add(itemId);\n return {\n type: \"message_delta\",\n content: typeof data.delta === \"string\" ? data.delta : \"\",\n };\n }\n\n // `response.completed` is intentionally absent: `streamResponse` holds\n // it back so it can synthesise a delta from `response.output[]` when\n // the stream produced none, then emits `{status:\"complete\"}` itself.\n\n case \"response.failed\":\n return emitError(\"upstream_failed\", data);\n\n case \"error\": {\n // Branch detail extraction so a missing `error` field doesn't surface\n // the JSON-stringified literal `'\"Unknown error\"'` (with quotes) in\n // server logs. The client never sees this string — `emitError`\n // sanitises it to a stable code.\n const detail =\n typeof data.error === \"string\"\n ? data.error\n : data.error == null\n ? \"Unknown error\"\n : data.error;\n return emitError(\"upstream_unknown\", detail);\n }\n\n case \"response.output_item.done\": {\n const item = data.item as\n | {\n id?: string;\n type?: string;\n content?: Array<{ text?: string; type?: string }>;\n }\n | undefined;\n\n // SA's contract reserves `item.id === \"error\"` for tool failures, but\n // a 5-char identifier collision is too small a margin. Require either\n // an explicit `type === \"error\"` or pair the reserved id with a\n // non-message type (a normal assistant message uses `type: \"message\"`).\n if (\n item?.type === \"error\" ||\n (item?.id === \"error\" && item?.type !== \"message\")\n ) {\n return emitError(\"upstream_tool\", item);\n }\n\n // Fallback: when SA produces a tool-driven response (e.g. Genie space),\n // it often omits `response.output_text.delta` events and only emits the\n // final assistant message via `output_item.done`. Surface that text as\n // a single delta so the UI sees the answer.\n if (\n item?.type === \"message\" &&\n item.id &&\n !streamedItemIds.has(item.id)\n ) {\n const text = (item.content ?? [])\n .map((c) => (c.type === \"output_text\" ? (c.text ?? \"\") : \"\"))\n .join(\"\");\n if (text.length > 0) {\n streamedItemIds.add(item.id);\n return { type: \"message_delta\", content: text };\n }\n }\n return null;\n }\n\n // All other event types are intentionally ignored. Notable lifecycle\n // events we drop on the floor: `response.created`, `response.in_progress`,\n // `response.output_text.done`, `response.output_item.added`,\n // `response.content_part.added`, `response.content_part.done`.\n default:\n return null;\n }\n}\n\n/**\n * Creates an {@link AgentAdapter} backed by the Databricks AI Gateway\n * Responses API (`/ai-gateway/mlflow/v1/responses`).\n *\n * Uses the SDK's default credential chain for auth (reads DATABRICKS_HOST,\n * DATABRICKS_TOKEN, OAuth config, etc.). Tools are declared on the agent\n * (via `createAgent({ tools })`), not on this factory.\n *\n * Application code should prefer the\n * {@link DatabricksAdapter.fromSupervisorApi} static — it delegates here\n * and keeps a single `DatabricksAdapter.from*` autocomplete root for all\n * Databricks-backed adapters. This free function is the implementation\n * behind the static and remains exported for callers that want to import\n * it directly without pulling in {@link DatabricksAdapter}.\n *\n * @example\n * ```ts\n * import { createApp, createAgent } from \"@databricks/appkit\";\n * import {\n * agents,\n * DatabricksAdapter,\n * supervisorTools,\n * } from \"@databricks/appkit/beta\";\n *\n * await createApp({\n * plugins: [\n * agents({\n * agents: {\n * assistant: createAgent({\n * instructions: \"You are a helpful assistant.\",\n * model: DatabricksAdapter.fromSupervisorApi({\n * model: \"databricks-claude-sonnet-4\",\n * }),\n * tools: () => ({\n * nyc: supervisorTools.genieSpace({\n * id: \"01ABCDEF12345678\",\n * description: \"NYC taxi trip records and zones\",\n * }),\n * }),\n * }),\n * },\n * }),\n * ],\n * });\n * ```\n *\n * @remarks\n * ⚠ When passing your own `workspaceClient`, see the warning on\n * {@link SupervisorApiAdapterOptions.workspaceClient} — the client is\n * captured once and reused, so per-request OBO clients would leak\n * identity across requests.\n *\n * @see {@link DatabricksAdapter.fromSupervisorApi} — the recommended\n * application-facing entry point.\n */\nexport async function fromSupervisorApi(\n options: SupervisorApiAdapterOptions,\n): Promise<AgentAdapter> {\n let client = options.workspaceClient;\n if (!client) {\n const sdk = await import(\"@databricks/sdk-experimental\");\n // The SDK's concrete `WorkspaceClient` provides everything\n // `WorkspaceClientLike` needs (`apiClient.request` + `config.ensureResolved`)\n // but its `apiClient.request` signature is narrower than our structural\n // `Record<string, unknown>` shape, so a direct assignment doesn't type.\n // The cast bridges the structural gap — same pattern the serving\n // connector uses for `ApiClientLike`.\n client = new sdk.WorkspaceClient({}) as unknown as WorkspaceClientLike;\n }\n\n await client.config.ensureResolved();\n\n // Capture the resolved client so the closure doesn't depend on the outer\n // `let` binding being reassigned later.\n const resolved = client;\n return new SupervisorApiAdapter({\n streamBody: (body, signal) =>\n streamPath(resolved, \"/ai-gateway/mlflow/v1/responses\", body, signal),\n model: options.model,\n timeoutMs: options.timeoutMs,\n });\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  //#region package.json
2
2
  var name = "@databricks/appkit";
3
- var version = "0.47.1";
3
+ var version = "0.49.0";
4
4
 
5
5
  //#endregion
6
6
  export { name, version };
package/dist/beta.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { AgentAdapter, AgentEvent, AgentInput, AgentRunContext, AgentToolDefinition, Message, Thread, ThreadStore, ToolAnnotations, ToolProvider } from "./shared/src/agent.js";
2
2
  import "./shared/src/index.js";
3
+ import { HostedSupervisorTool, SUPERVISOR_EXTENSION_KEY, SupervisorApiAdapter, SupervisorApiAdapterOptions, SupervisorExtension, SupervisorTool, WorkspaceClientLike, fromSupervisorApi, isSupervisorTool, supervisorTools } from "./agents/supervisor-api.js";
3
4
  import { DatabricksAdapter, GenerationParams, parseTextToolCalls } from "./agents/databricks.js";
4
5
  import { AppKitMcpClient, McpConnectAllResult } from "./connectors/mcp/client.js";
5
6
  import { FunctionTool, functionToolToDefinition, isFunctionTool } from "./core/agent/tools/function-tool.js";
@@ -14,4 +15,4 @@ import { agentIdFromMarkdownPath, loadAgentFromFile, loadAgentsFromDir } from ".
14
15
  import { agents } from "./plugins/agents/agents.js";
15
16
  import "./plugins/agents/index.js";
16
17
  import "./plugins/beta-exports.generated.js";
17
- export { type AgentAdapter, type AgentDefinition, type AgentEvent, type AgentInput, type AgentRunContext, type AgentTool, type AgentToolDefinition, type AgentTools, type AgentToolsFn, type AgentsPluginConfig, AppKitMcpClient, type AutoInheritToolsConfig, type BaseSystemPromptOption, DatabricksAdapter, type FunctionTool, type GenerationParams, type HostedTool, type McpConnectAllResult, type Message, type PluginToolkitProvider, type Plugins, type PromptContext, type RegisteredAgent, type ResolvedToolEntry, type RunAgentInput, type RunAgentResult, type Thread, type ThreadStore, type ToolAnnotations, type ToolConfig, type ToolEntry, type ToolProvider, type ToolRegistry, type ToolkitEntry, type ToolkitOptions, agentIdFromMarkdownPath, agents, createAgent, defineTool, executeFromRegistry, functionToolToDefinition, isFunctionTool, isHostedTool, isToolkitEntry, loadAgentFromFile, loadAgentsFromDir, mcpServer, parseTextToolCalls, resolveHostedTools, runAgent, tool, toolsFromRegistry };
18
+ export { type AgentAdapter, type AgentDefinition, type AgentEvent, type AgentInput, type AgentRunContext, type AgentTool, type AgentToolDefinition, type AgentTools, type AgentToolsFn, type AgentsPluginConfig, AppKitMcpClient, type AutoInheritToolsConfig, type BaseSystemPromptOption, DatabricksAdapter, type FunctionTool, type GenerationParams, type HostedSupervisorTool, type HostedTool, type McpConnectAllResult, type Message, type PluginToolkitProvider, type Plugins, type PromptContext, type RegisteredAgent, type ResolvedToolEntry, type RunAgentInput, type RunAgentResult, SUPERVISOR_EXTENSION_KEY, SupervisorApiAdapter, type SupervisorApiAdapterOptions, type SupervisorExtension, type SupervisorTool, type Thread, type ThreadStore, type ToolAnnotations, type ToolConfig, type ToolEntry, type ToolProvider, type ToolRegistry, type ToolkitEntry, type ToolkitOptions, type WorkspaceClientLike, agentIdFromMarkdownPath, agents, createAgent, defineTool, executeFromRegistry, fromSupervisorApi, functionToolToDefinition, isFunctionTool, isHostedTool, isSupervisorTool, isToolkitEntry, loadAgentFromFile, loadAgentsFromDir, mcpServer, parseTextToolCalls, resolveHostedTools, runAgent, supervisorTools, tool, toolsFromRegistry };
package/dist/beta.js CHANGED
@@ -2,6 +2,7 @@ import { AppKitMcpClient } from "./connectors/mcp/client.js";
2
2
  import { tool } from "./core/agent/tools/tool.js";
3
3
  import { defineTool, executeFromRegistry, toolsFromRegistry } from "./core/agent/tools/define-tool.js";
4
4
  import { DatabricksAdapter, parseTextToolCalls } from "./agents/databricks.js";
5
+ import { SUPERVISOR_EXTENSION_KEY, SupervisorApiAdapter, fromSupervisorApi, isSupervisorTool, supervisorTools } from "./agents/supervisor-api.js";
5
6
  import { createAgent } from "./core/agent/create-agent.js";
6
7
  import { functionToolToDefinition, isFunctionTool } from "./core/agent/tools/function-tool.js";
7
8
  import { isHostedTool, mcpServer, resolveHostedTools } from "./core/agent/tools/hosted-tools.js";
@@ -13,4 +14,4 @@ import { agents } from "./plugins/agents/agents.js";
13
14
  import "./plugins/agents/index.js";
14
15
  import "./plugins/beta-exports.generated.js";
15
16
 
16
- export { AppKitMcpClient, DatabricksAdapter, agentIdFromMarkdownPath, agents, createAgent, defineTool, executeFromRegistry, functionToolToDefinition, isFunctionTool, isHostedTool, isToolkitEntry, loadAgentFromFile, loadAgentsFromDir, mcpServer, parseTextToolCalls, resolveHostedTools, runAgent, tool, toolsFromRegistry };
17
+ export { AppKitMcpClient, DatabricksAdapter, SUPERVISOR_EXTENSION_KEY, SupervisorApiAdapter, agentIdFromMarkdownPath, agents, createAgent, defineTool, executeFromRegistry, fromSupervisorApi, functionToolToDefinition, isFunctionTool, isHostedTool, isSupervisorTool, isToolkitEntry, loadAgentFromFile, loadAgentsFromDir, mcpServer, parseTextToolCalls, resolveHostedTools, runAgent, supervisorTools, tool, toolsFromRegistry };
@@ -26,6 +26,12 @@ const rules = [
26
26
  id: "no-parse-float-without-validation",
27
27
  pattern: "parseFloat($X).toFixed($Y)",
28
28
  message: "parseFloat can return NaN. Validate input or use toNumber() helper from shared/types.ts."
29
+ },
30
+ {
31
+ id: "no-variants-in-prod",
32
+ pattern: "<Variants $$$P>$$$C</Variants>",
33
+ message: "<Variants> is a development-only variant picker and must not be shipped. Finalize the chosen <Variant> before deploying.",
34
+ includeTests: false
29
35
  }
30
36
  ];
31
37
  function isTestFile(filePath, rootDir) {