Commit 4e6da4d
feat(appkit): supervisor API adapter for agents plugin (#400)
* feat(appkit): supervisor api adapter
* fix(appkit): address PR #345 review findings (section 9)
Apply Mario's defensive/correctness fixes from the supervisor API adapter
review without touching the public API shape (sections 1-8 will land in a
stacked branch). Highlights:
High
- Route the three SSE error-leak sites in supervisor-api.ts (streamBody
catch, mapEvent "error", output_item.done with id="error") through a
single emitError helper that returns a stable client-facing code
(`Supervisor API error (transport|upstream_failed|upstream_tool|
upstream_unknown)`) and logs the verbose detail server-side only.
Addresses CWE-209 verbatim-upstream-error-text leak.
Medium
- Gate the terminal {status:"complete"} emission on lastCompleted.status
/ .error / .incomplete_details so a `response.completed` with a nested
failed status no longer silently succeeds; surface as upstream_failed
instead. Regression tests added.
- Skip the terminal error in the streamBody catch when signal.aborted —
consumer-initiated aborts now end with a clean stop, not a contradictory
terminal error event. Regression test added.
- Tighten the output_item.done error match: require item.type === "error"
(or pair the reserved id="error" with a non-message type) so a stray
assistant message with id="error" is not mis-classified.
- Add maxLineChars / maxBufferChars caps to readSseEvents with 1 MiB / 8
MiB defaults; throw on overflow. Addresses CWE-770. Tests added.
- Docs: add a CWE-1427 callout warning that hosted-tool `description` is a
prompt-injection sink — do not derive it from untrusted input.
- Redact the no-delta warning log: summariseErrorPayload extracts a short
`type: message` line; full payload only via DEBUG. Addresses CWE-532.
- Gate the buffer-level CRLF normalize in sse-reader on `\r` presence to
skip the regex on LF-only steady state.
Low
- mapEvent("error") fallback no longer wraps "Unknown error" with literal
JSON quotes (uses string branch).
- Drop the misleading "we await the factory at module init" comment in
dev-playground; the code never awaits.
- Fix @example imports in supervisor-api.ts JSDoc to use
@databricks/appkit/beta (the actual public re-export).
- Replace trimStart() with single-U+0020 strip in sse-reader per the SSE
spec; remove the now-dead per-line `\r$` strip after the buffer-level
CRLF normalise.
- Flag streamPath as @internal in connectors/serving/client.ts noting the
CWE-918 SSRF risk if it ever leaks to user-controlled input.
- Add JSDoc warning to workspaceClient on SupervisorApiAdapterOptions:
passing a per-request OBO client would leak identity across requests
(CWE-664).
Signed-off-by: Hubert Zub <hubert.zub@databricks.com>
* fix(appkit): address pkosiec feedback
Signed-off-by: Hubert Zub <hubert.zub@databricks.com>
* refactor(appkit): restructure supervisor-api adapter per PR #345 review
Address structural feedback from Mario Cadenas' review of PR #345 (sections
1-8). Stacked on top of the §9 defensive fixes commit.
API changes (BETA surface only):
- Add `DatabricksAdapter.fromSupervisorApi` static factory for discoverability
alongside `.fromChatCompletions`.
- Shrink `SupervisorApiAdapterOptions` to `{ model, workspaceClient? }`; tools
no longer live on the adapter.
- Hosted tools (`supervisorTools.*`) now return tagged `HostedSupervisorTool`
records and accept named options instead of positional args.
- Declare hosted tools on the agent's `tools` map (same place as function
tools / sub-agents); the agents plugin and `runAgent` route them to the
adapter via the new `AgentInput.extensions[SUPERVISOR_EXTENSION_KEY]`.
- Add capability-negotiation fields to `AgentAdapter`:
`acceptsExtensions?` + `consumesInputTools?`. The agents plugin and
`runAgent` warn at registration when adapter capabilities don't match
declared tools.
Internals:
- Extend `ResolvedToolEntry` / `StandaloneEntry` with a `hosted-supervisor`
branch; `classifyTool` matches it before MCP hosted-tool rejection so
standalone `runAgent` supports supervisor tools.
- Defense-in-depth: both indexers throw if a `hosted-supervisor` entry is
ever dispatched as a callable function.
- `DatabricksAdapter.fromSupervisorApi` uses a dynamic import to avoid
load-time cycles.
Docs:
- Rewrite supervisor-API section in docs/plugins/agents.md for the new shape.
- Add cross-adapter sub-agent composition note (one-directional: chat
parents can call supervisor children, not vice-versa, until SA's
function-call events are routed back through `context.executeTool`).
Playground:
- Update dev-playground supervisor agent to the new shape
(`DatabricksAdapter.fromSupervisorApi(...)` + tools on `createAgent`).
Tests:
- Rewrite supervisor-api.test.ts factory + adapter tests for the new shape.
- Add `isSupervisorTool` and `DatabricksAdapter.fromSupervisorApi` tests.
- New regression tests in `run-agent.test.ts` covering the
hosted-supervisor extension-routing path and both capability-mismatch
warnings.
- New agents-plugin tests covering the same warning paths and the new
`hosted-supervisor` tool-index branch.
Signed-off-by: Hubert Zub <hubert.zub@databricks.com>
* fix(appkit): pkosiec's feedback
Signed-off-by: Hubert Zub <hubert.zub@databricks.com>
* fix(appkit): address supervisor adapter feedback 2026-07-28: first round
Signed-off-by: Hubert Zub <hubert.zub@databricks.com>
* fix(appkit): add agent selector to dev-playground
Signed-off-by: Hubert Zub <hubert.zub@databricks.com>
* Apply suggestions from code review
Co-authored-by: Pawel Kosiec <pawel.kosiec@gmail.com>
---------
Signed-off-by: Hubert Zub <hubert.zub@databricks.com>
Co-authored-by: Pawel Kosiec <pawel.kosiec@gmail.com>1 parent af65ac3 commit 4e6da4d
35 files changed
Lines changed: 4257 additions & 58 deletions
File tree
- apps/dev-playground
- client/src/routes
- server
- docs/docs
- api/appkit
- plugins
- packages
- appkit/src
- agents
- tests
- connectors/serving
- core/agent
- tests
- plugins/agents
- tests
- stream
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
3 | 10 | | |
4 | 11 | | |
5 | 12 | | |
6 | 13 | | |
7 | 14 | | |
8 | 15 | | |
9 | 16 | | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
10 | 23 | | |
11 | 24 | | |
12 | 25 | | |
| |||
142 | 155 | | |
143 | 156 | | |
144 | 157 | | |
| 158 | + | |
145 | 159 | | |
146 | 160 | | |
147 | 161 | | |
| |||
211 | 225 | | |
212 | 226 | | |
213 | 227 | | |
| 228 | + | |
214 | 229 | | |
215 | 230 | | |
216 | 231 | | |
| |||
310 | 325 | | |
311 | 326 | | |
312 | 327 | | |
313 | | - | |
| 328 | + | |
314 | 329 | | |
315 | 330 | | |
316 | 331 | | |
| |||
328 | 343 | | |
329 | 344 | | |
330 | 345 | | |
331 | | - | |
332 | | - | |
333 | | - | |
334 | | - | |
335 | | - | |
336 | | - | |
337 | | - | |
338 | | - | |
339 | | - | |
340 | | - | |
341 | | - | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
342 | 381 | | |
343 | | - | |
344 | | - | |
345 | | - | |
346 | | - | |
347 | | - | |
348 | 382 | | |
349 | 383 | | |
350 | 384 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
15 | 21 | | |
16 | 22 | | |
17 | 23 | | |
| |||
68 | 74 | | |
69 | 75 | | |
70 | 76 | | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
71 | 106 | | |
72 | 107 | | |
73 | 108 | | |
| |||
385 | 420 | | |
386 | 421 | | |
387 | 422 | | |
388 | | - | |
| 423 | + | |
389 | 424 | | |
390 | 425 | | |
391 | 426 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
0 commit comments