@databricks/appkit 0.61.1 → 0.62.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 (194) hide show
  1. package/CLAUDE.md +1 -0
  2. package/dist/agents/databricks.d.ts.map +1 -1
  3. package/dist/agents/databricks.js.map +1 -1
  4. package/dist/agents/supervisor-api.d.ts.map +1 -1
  5. package/dist/agents/supervisor-api.js.map +1 -1
  6. package/dist/app/index.d.ts.map +1 -1
  7. package/dist/app/index.js.map +1 -1
  8. package/dist/appkit/package.js +1 -1
  9. package/dist/cache/index.d.ts.map +1 -1
  10. package/dist/cache/index.js.map +1 -1
  11. package/dist/cache/storage/memory.js.map +1 -1
  12. package/dist/cache/storage/persistent.js.map +1 -1
  13. package/dist/cli/commands/codemod/index.js.map +1 -1
  14. package/dist/cli/commands/codemod/on-plugins-ready.js.map +1 -1
  15. package/dist/cli/commands/docs.js.map +1 -1
  16. package/dist/cli/commands/doctor/bundle.js.map +1 -1
  17. package/dist/cli/commands/doctor/index.js.map +1 -1
  18. package/dist/cli/commands/doctor/report.js.map +1 -1
  19. package/dist/cli/commands/doctor/resolve-targets.js.map +1 -1
  20. package/dist/cli/commands/doctor/run.js.map +1 -1
  21. package/dist/cli/commands/generate-types.js.map +1 -1
  22. package/dist/cli/commands/lint.js.map +1 -1
  23. package/dist/cli/commands/plugin/add-resource/add-resource.js.map +1 -1
  24. package/dist/cli/commands/plugin/create/create.js.map +1 -1
  25. package/dist/cli/commands/plugin/create/prompt-resource.js.map +1 -1
  26. package/dist/cli/commands/plugin/create/scaffold.js.map +1 -1
  27. package/dist/cli/commands/plugin/index.js.map +1 -1
  28. package/dist/cli/commands/plugin/list/list.js.map +1 -1
  29. package/dist/cli/commands/plugin/promote/promote.js.map +1 -1
  30. package/dist/cli/commands/plugin/sync/sync.js.map +1 -1
  31. package/dist/cli/commands/plugin/validate/validate-manifest.js.map +1 -1
  32. package/dist/cli/commands/plugin/validate/validate.js.map +1 -1
  33. package/dist/cli/commands/registry/add.js.map +1 -1
  34. package/dist/cli/commands/registry/client.js.map +1 -1
  35. package/dist/cli/commands/registry/config-writer.js.map +1 -1
  36. package/dist/cli/commands/registry/env-reconcile.js.map +1 -1
  37. package/dist/cli/commands/registry/env-writer.js.map +1 -1
  38. package/dist/cli/commands/registry/index.js.map +1 -1
  39. package/dist/cli/commands/registry/info.js.map +1 -1
  40. package/dist/cli/commands/registry/list.js.map +1 -1
  41. package/dist/cli/commands/registry/requirements.js.map +1 -1
  42. package/dist/cli/commands/registry/server-register.js.map +1 -1
  43. package/dist/cli/commands/registry/workspace-picker.js.map +1 -1
  44. package/dist/cli/commands/setup.js.map +1 -1
  45. package/dist/cli/index.js.map +1 -1
  46. package/dist/connectors/files/client.js.map +1 -1
  47. package/dist/connectors/lakebase/index.d.ts.map +1 -1
  48. package/dist/connectors/lakebase/index.js.map +1 -1
  49. package/dist/connectors/lakebase/pool-manager.d.ts.map +1 -1
  50. package/dist/connectors/lakebase/pool-manager.js.map +1 -1
  51. package/dist/connectors/lakebase/routing-pool.d.ts.map +1 -1
  52. package/dist/connectors/lakebase/routing-pool.js.map +1 -1
  53. package/dist/connectors/mcp/client.d.ts.map +1 -1
  54. package/dist/connectors/mcp/client.js.map +1 -1
  55. package/dist/connectors/sql-warehouse/client.js.map +1 -1
  56. package/dist/context/client-options.js.map +1 -1
  57. package/dist/context/execution-context.d.ts.map +1 -1
  58. package/dist/context/execution-context.js.map +1 -1
  59. package/dist/context/index.d.ts +1 -1
  60. package/dist/context/service-context.d.ts +55 -3
  61. package/dist/context/service-context.d.ts.map +1 -1
  62. package/dist/context/service-context.js.map +1 -1
  63. package/dist/core/agent/build-toolkit.js.map +1 -1
  64. package/dist/core/agent/load-agents.d.ts.map +1 -1
  65. package/dist/core/agent/load-agents.js.map +1 -1
  66. package/dist/core/agent/run-agent.d.ts.map +1 -1
  67. package/dist/core/agent/run-agent.js.map +1 -1
  68. package/dist/core/agent/toolkit-resolver.js.map +1 -1
  69. package/dist/core/agent/tools/define-tool.d.ts.map +1 -1
  70. package/dist/core/agent/tools/define-tool.js.map +1 -1
  71. package/dist/core/agent/tools/tool.d.ts.map +1 -1
  72. package/dist/core/agent/tools/tool.js.map +1 -1
  73. package/dist/core/agent/types.d.ts.map +1 -1
  74. package/dist/core/agent/types.js.map +1 -1
  75. package/dist/core/appkit.d.ts.map +1 -1
  76. package/dist/core/appkit.js.map +1 -1
  77. package/dist/core/lifecycle-manager.js.map +1 -1
  78. package/dist/core/plugin-context.d.ts +13 -0
  79. package/dist/core/plugin-context.d.ts.map +1 -1
  80. package/dist/core/plugin-context.js +12 -1
  81. package/dist/core/plugin-context.js.map +1 -1
  82. package/dist/errors/configuration.d.ts.map +1 -1
  83. package/dist/errors/configuration.js.map +1 -1
  84. package/dist/logging/logger.js.map +1 -1
  85. package/dist/logging/wide-event-emitter.js.map +1 -1
  86. package/dist/plugin/dev-reader.d.ts.map +1 -1
  87. package/dist/plugin/dev-reader.js.map +1 -1
  88. package/dist/plugin/interceptors/cache.js.map +1 -1
  89. package/dist/plugin/interceptors/retry.js.map +1 -1
  90. package/dist/plugin/interceptors/telemetry.js.map +1 -1
  91. package/dist/plugin/plugin.d.ts +2 -2
  92. package/dist/plugin/plugin.d.ts.map +1 -1
  93. package/dist/plugin/plugin.js.map +1 -1
  94. package/dist/plugins/agents/agents.d.ts.map +1 -1
  95. package/dist/plugins/agents/agents.js.map +1 -1
  96. package/dist/plugins/agents/event-translator.js.map +1 -1
  97. package/dist/plugins/agents/thread-store.js.map +1 -1
  98. package/dist/plugins/ai-search/ai-search.d.ts.map +1 -1
  99. package/dist/plugins/ai-search/ai-search.js.map +1 -1
  100. package/dist/plugins/analytics/analytics.d.ts.map +1 -1
  101. package/dist/plugins/analytics/analytics.js.map +1 -1
  102. package/dist/plugins/analytics/mv/cache.js.map +1 -1
  103. package/dist/plugins/analytics/mv/constants.js.map +1 -1
  104. package/dist/plugins/analytics/mv/formatters.js.map +1 -1
  105. package/dist/plugins/analytics/mv/metadata.js.map +1 -1
  106. package/dist/plugins/analytics/mv/registry.js.map +1 -1
  107. package/dist/plugins/analytics/mv/schemas.js.map +1 -1
  108. package/dist/plugins/analytics/query.js.map +1 -1
  109. package/dist/plugins/analytics/result-delivery.js.map +1 -1
  110. package/dist/plugins/files/helpers.js.map +1 -1
  111. package/dist/plugins/files/plugin.d.ts.map +1 -1
  112. package/dist/plugins/files/plugin.js.map +1 -1
  113. package/dist/plugins/files/types.d.ts.map +1 -1
  114. package/dist/plugins/genie/genie.d.ts.map +1 -1
  115. package/dist/plugins/genie/genie.js.map +1 -1
  116. package/dist/plugins/jobs/plugin.d.ts.map +1 -1
  117. package/dist/plugins/jobs/plugin.js.map +1 -1
  118. package/dist/plugins/jobs/types.d.ts.map +1 -1
  119. package/dist/plugins/lakebase/lakebase.d.ts.map +1 -1
  120. package/dist/plugins/lakebase/lakebase.js.map +1 -1
  121. package/dist/plugins/lakebase/types.d.ts.map +1 -1
  122. package/dist/plugins/server/base-server.js.map +1 -1
  123. package/dist/plugins/server/client-config-sanitizer.js.map +1 -1
  124. package/dist/plugins/server/index.d.ts.map +1 -1
  125. package/dist/plugins/server/index.js.map +1 -1
  126. package/dist/plugins/server/react-source-loc-vite-plugin.js.map +1 -1
  127. package/dist/plugins/server/remote-tunnel/remote-tunnel-controller.js.map +1 -1
  128. package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
  129. package/dist/plugins/server/static-server.js.map +1 -1
  130. package/dist/plugins/server/utils.js.map +1 -1
  131. package/dist/plugins/server/vite-dev-server.js.map +1 -1
  132. package/dist/plugins/serving/schema-filter.js.map +1 -1
  133. package/dist/plugins/serving/serving.d.ts.map +1 -1
  134. package/dist/plugins/serving/serving.js.map +1 -1
  135. package/dist/plugins/ui-variants/index.js.map +1 -1
  136. package/dist/registry/manifest-loader.d.ts.map +1 -1
  137. package/dist/registry/manifest-loader.js.map +1 -1
  138. package/dist/registry/resource-registry.d.ts.map +1 -1
  139. package/dist/registry/resource-registry.js.map +1 -1
  140. package/dist/registry/types.d.ts.map +1 -1
  141. package/dist/registry/types.js.map +1 -1
  142. package/dist/schemas/manifest.d.ts.map +1 -1
  143. package/dist/schemas/manifest.js.map +1 -1
  144. package/dist/schemas/metric-fqn.js.map +1 -1
  145. package/dist/shared/src/plugin.d.ts.map +1 -1
  146. package/dist/shared/src/schemas/manifest.d.ts.map +1 -1
  147. package/dist/shared/src/schemas/metric-fqn.js.map +1 -1
  148. package/dist/shared/src/schemas/metric-metadata-bundle.js.map +1 -1
  149. package/dist/shared/src/schemas/metric-source.js.map +1 -1
  150. package/dist/shared/src/sse/analytics.js.map +1 -1
  151. package/dist/shared/src/workspace-client/legacy.js.map +1 -1
  152. package/dist/shared/src/workspace-client/types.d.ts.map +1 -1
  153. package/dist/stream/sse-writer.js.map +1 -1
  154. package/dist/stream/stream-manager.d.ts.map +1 -1
  155. package/dist/stream/stream-manager.js.map +1 -1
  156. package/dist/stream/types.js.map +1 -1
  157. package/dist/telemetry/instrumentations.js.map +1 -1
  158. package/dist/telemetry/telemetry-manager.js.map +1 -1
  159. package/dist/telemetry/telemetry-provider.js.map +1 -1
  160. package/dist/telemetry/trace-sampler.js.map +1 -1
  161. package/dist/testing/expect-stream.d.ts +111 -0
  162. package/dist/testing/expect-stream.d.ts.map +1 -0
  163. package/dist/testing/expect-stream.js +154 -0
  164. package/dist/testing/expect-stream.js.map +1 -0
  165. package/dist/testing/fixtures.d.ts +257 -0
  166. package/dist/testing/fixtures.d.ts.map +1 -0
  167. package/dist/testing/fixtures.js +385 -0
  168. package/dist/testing/fixtures.js.map +1 -0
  169. package/dist/testing/index.d.ts +5 -0
  170. package/dist/testing/index.js +5 -0
  171. package/dist/testing/test-plugin-context.d.ts +158 -0
  172. package/dist/testing/test-plugin-context.d.ts.map +1 -0
  173. package/dist/testing/test-plugin-context.js +139 -0
  174. package/dist/testing/test-plugin-context.js.map +1 -0
  175. package/dist/type-generator/cache.js.map +1 -1
  176. package/dist/type-generator/index.js.map +1 -1
  177. package/dist/type-generator/migration.js.map +1 -1
  178. package/dist/type-generator/mv-registry/config.js.map +1 -1
  179. package/dist/type-generator/query-registry.js.map +1 -1
  180. package/dist/type-generator/serving/cache.js.map +1 -1
  181. package/dist/type-generator/serving/generator.js.map +1 -1
  182. package/dist/type-generator/serving/server-file-extractor.d.ts.map +1 -1
  183. package/dist/type-generator/serving/server-file-extractor.js.map +1 -1
  184. package/dist/type-generator/serving/vite-plugin.d.ts.map +1 -1
  185. package/dist/type-generator/serving/vite-plugin.js.map +1 -1
  186. package/dist/type-generator/vite-plugin.d.ts.map +1 -1
  187. package/dist/type-generator/vite-plugin.js.map +1 -1
  188. package/dist/workspace-client/legacy.js.map +1 -1
  189. package/dist/workspace-client/types.d.ts.map +1 -1
  190. package/docs/plugins/testing.md +245 -0
  191. package/llms.txt +1 -0
  192. package/package.json +15 -2
  193. package/sbom.cdx.json +1 -1
  194. package/skills/appkit-ui-variants/SKILL.md +1 -1
package/CLAUDE.md CHANGED
@@ -58,6 +58,7 @@ npx @databricks/appkit docs <query>
58
58
  - [Plugin management](./docs/plugins/plugin-management.md): AppKit includes a CLI for managing plugins. All commands are available under npx @databricks/appkit plugin.
59
59
  - [Server plugin](./docs/plugins/server.md): Provides HTTP server capabilities with development and production modes.
60
60
  - [Plugin Stability Tiers](./docs/plugins/stability.md): AppKit plugins have a two-tier stability system that communicates API maturity and breaking-change expectations.
61
+ - [Testing](./docs/plugins/testing.md): AppKit ships a testing kit at @databricks/appkit/testing so you can test a plugin — including its cross-plugin tool calls and streaming responses — without a live Databricks workspace, credentials, or network access. That makes plugin tests fast and lets them run in CI, where no workspace is available.
61
62
 
62
63
  ## appkit API reference [collapsed]
63
64
 
@@ -1 +1 @@
1
- {"version":3,"file":"databricks.d.ts","names":[],"sources":["../../src/agents/databricks.ts"],"mappings":";;;;;;;;;;AAuCA;;UAAiB,gBAAA;EAAgB;EAE/B,WAAA;EAEA;EAAA,KAAA;EAIA;EAFA,IAAA;EAIgB;EAFhB,iBAAA;EAiEQ;EA/DR,gBAAA;AAAA;;;;;;UA+DQ,sBAAA;EACR,WAAA;EACA,YAAA,QAAoB,OAAA,CAAQ,MAAA;EAC5B,QAAA;EACA,SAAA;EAAA;EAEA,gBAAA,GAAmB,gBAAA;EAAA;EAEnB,eAAA;EAEA;EAAA,kBAAA;EAEqB;EAArB,qBAAA;AAAA;;;;;;;UASQ,wBAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,KAGG,wBAAA,GACD,sBAAA,GACA,wBAAA;;;;AAAwB;;;;UAelB,mBAAA;EACR,SAAA;IACE,OAAA,CAAQ,OAAA,EAAS,MAAA,oBAA0B,OAAA;EAAA;AAAA;AAAA,UAIrC,sBAAA;EACR,eAAA,EAAiB,mBAAA;EACjB,YAAA;EACA,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,UAGQ,mBAAA;EACR,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA,GAAkB,mBAAA;EAClB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;;;;;;;;;;;;;;;AAuFF;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,iBAAA,YAA6B,YAAA;EAAA,QAChC,UAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAAA,QACA,gBAAA;EAAA,QACA,eAAA;EAAA,QACA,kBAAA;EAAA,QACA,qBAAA;cAEI,OAAA,EAAS,wBAAA;EAuGnB;;;;;;;;EAAA,OAtDW,mBAAA,CACX,OAAA,EAAS,sBAAA,GACR,OAAA,CAAQ,iBAAA;EA4GA;;;;;;;;;;;;;;;AAoYb;;;;;EApYa,OAzDE,gBAAA,CACX,YAAA,WACA,OAAA,GAAU,mBAAA,GACT,OAAA,CAAQ,iBAAA;EA4bF;;;;;;;;;;;;;;;;;;;;;EAAA,OAxYI,iBAAA,CACX,OAAA,EArDQ,2BAAA,GAsDP,OAAA,CAAQ,YAAA;EAKJ,GAAA,CACL,KAAA,EAAO,UAAA,EACP,OAAA,EAAS,eAAA,GACR,cAAA,CAAe,UAAA;;UAuDH,iBAAA;EAAA,QA8CA,gBAAA;EAAA,QAyKA,gBAAA;;;;;;UAoCP,aAAA;EAAA,QAgDA,UAAA;AAAA;;;;;;;;iBA0BM,kBAAA,CACd,IAAA,WACC,KAAA;EAAQ,IAAA;EAAc,IAAA;AAAA"}
1
+ {"version":3,"file":"databricks.d.ts","names":[],"sources":["../../src/agents/databricks.ts"],"mappings":";;;;;;;;;;AAwCA;;UAAiB,gBAAA;EAAgB;EAE/B,WAAA;EAEA;EAAA,KAAA;EAIA;EAFA,IAAA;EAIgB;EAFhB,iBAAA;EAiEQ;EA/DR,gBAAA;AAAA;;;;;;UA+DQ,sBAAA;EACR,WAAA;EACA,YAAA,QAAoB,OAAA,CAAQ,MAAA;EAC5B,QAAA;EACA,SAAA;EAAA;EAEA,gBAAA,GAAmB,gBAAA;EAAA;EAEnB,eAAA;EAEA;EAAA,kBAAA;EAEqB;EAArB,qBAAA;AAAA;;;;;;;UASQ,wBAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,KAGG,wBAAA,GACD,sBAAA,GACA,wBAAA;;;;AAAwB;;;;UAelB,mBAAA;EACR,SAAA;IACE,OAAA,CAAQ,OAAA,EAAS,MAAA,oBAA0B,OAAA;EAAA;AAAA;AAAA,UAIrC,sBAAA;EACR,eAAA,EAAiB,mBAAA;EACjB,YAAA;EACA,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,UAGQ,mBAAA;EACR,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA,GAAkB,mBAAA;EAClB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;;;;;;;;;;;;;;;AAuFF;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,iBAAA,YAA6B,YAAA;EAAA,QAChC,UAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAAA,QACA,gBAAA;EAAA,QACA,eAAA;EAAA,QACA,kBAAA;EAAA,QACA,qBAAA;cAEI,OAAA,EAAS,wBAAA;EAuGnB;;;;;;;;EAAA,OAtDW,mBAAA,CACX,OAAA,EAAS,sBAAA,GACR,OAAA,CAAQ,iBAAA;EA4GA;;;;;;;;;;;;;;;AAoYb;;;;;EApYa,OAzDE,gBAAA,CACX,YAAA,WACA,OAAA,GAAU,mBAAA,GACT,OAAA,CAAQ,iBAAA;EA4bF;;;;;;;;;;;;;;;;;;;;;EAAA,OAxYI,iBAAA,CACX,OAAA,EArDQ,2BAAA,GAsDP,OAAA,CAAQ,YAAA;EAKJ,GAAA,CACL,KAAA,EAAO,UAAA,EACP,OAAA,EAAS,eAAA,GACR,cAAA,CAAe,UAAA;;UAuDH,iBAAA;EAAA,QA8CA,gBAAA;EAAA,QAyKA,gBAAA;;;;;;UAoCP,aAAA;EAAA,QAgDA,UAAA;AAAA;;;;;;;;iBA0BM,kBAAA,CACd,IAAA,WACC,KAAA;EAAQ,IAAA;EAAc,IAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"databricks.js","names":["servingStream"],"sources":["../../src/agents/databricks.ts"],"sourcesContent":["import type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n AgentRunContext,\n AgentToolDefinition,\n} from \"shared\";\nimport {\n type StreamBody,\n stream as servingStream,\n} from \"../connectors/serving/client\";\nimport { APPKIT_USER_AGENT, getClientOptions } from \"../context/client-options\";\nimport { createWorkspaceClient } from \"../workspace-client\";\n\n/** Default cap for a single incomplete SSE line tail (DoS guard). */\nconst DEFAULT_MAX_SSE_LINE_CHARS = 1024 * 1024;\n\n/** Default cap for accumulated assistant text from `delta.content`. */\nconst DEFAULT_MAX_STREAM_TEXT_CHARS = 4 * 1024 * 1024;\n\n/** Default cap for accumulated JSON arguments per streamed tool call index. */\nconst DEFAULT_MAX_TOOL_ARGUMENT_CHARS = 2 * 1024 * 1024;\n\n/** Cap text length before running Python-style tool-call regex (ReDoS guard). */\nconst PYTHON_STYLE_TOOL_PARSE_MAX_INPUT = 64 * 1024;\n\n/** Fallback HTTP timeout when the raw fetch adapter path receives no AbortSignal from the runner. */\nconst RAW_FETCH_DEFAULT_TIMEOUT_MS = 120_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Optional generation parameters forwarded to the OpenAI-compatible serving\n * request body. Names match the serving API wire keys. Only keys that are set\n * are sent — undefined values are omitted so the endpoint applies its own\n * defaults. Ranges are not validated here; the serving endpoint validates.\n */\nexport interface GenerationParams {\n /** Sampling temperature. */\n temperature?: number;\n /** Nucleus sampling probability mass (`top_p`). */\n top_p?: number;\n /** Stop sequence(s) that end generation. */\n stop?: string | string[];\n /** Penalize tokens by frequency. */\n frequency_penalty?: number;\n /** Penalize tokens by prior presence. */\n presence_penalty?: number;\n}\n\nconst GENERATION_PARAM_KEYS = [\n \"temperature\",\n \"top_p\",\n \"stop\",\n \"frequency_penalty\",\n \"presence_penalty\",\n] as const satisfies readonly (keyof GenerationParams)[];\n\n/** Copy only the set generation params onto the request body. */\nfunction applyGenerationParams(\n body: Record<string, unknown>,\n params: GenerationParams,\n): void {\n for (const key of GENERATION_PARAM_KEYS) {\n const value = params[key];\n if (value !== undefined) body[key] = value;\n }\n}\n\nfunction extractLlamaToolJsonSlice(text: string): string | undefined {\n const start = text.indexOf(\"[{\");\n if (start < 0) return undefined;\n const endBracket = text.lastIndexOf(\"}]\");\n if (endBracket < start) return undefined;\n return text.slice(start, endBracket + 2);\n}\n\n/** OpenAI SSE payload: `{ choices: [{ delta }] }`. */\nfunction openAiChoicesDelta(parsed: unknown): unknown {\n if (!isRecord(parsed)) return undefined;\n const choices = parsed.choices;\n if (!Array.isArray(choices) || choices.length < 1) return undefined;\n const first = choices[0];\n if (!isRecord(first)) return undefined;\n return first.delta;\n}\n\nfunction isStreamingDeltaToolCall(value: unknown): value is DeltaToolCall {\n if (!isRecord(value)) return false;\n return typeof value.index === \"number\";\n}\n\nfunction throwIfExceedsStreamLimit(\n label: string,\n currentLength: number,\n chunk: string,\n max: number,\n): void {\n if (currentLength + chunk.length > max) {\n throw new Error(\n `DatabricksAdapter: ${label} exceeds configured limit (${max} UTF-16 code units)`,\n );\n }\n}\n\n/**\n * Escape-hatch options: provide an `endpointUrl` + `authenticate()` and the\n * adapter uses a bare `fetch()` to call it. Useful for tests and for pointing\n * the adapter at non-workspace endpoints (reverse proxies, mocks).\n */\ninterface RawFetchAdapterOptions {\n endpointUrl: string;\n authenticate: () => Promise<Record<string, string>>;\n maxSteps?: number;\n maxTokens?: number;\n /** Optional generation params forwarded to the serving request body. */\n generationParams?: GenerationParams;\n /** Max length of one SSE line (including an incomplete tail in the buffer). */\n maxSseLineChars?: number;\n /** Max total length of assistant `delta.content` across the stream. */\n maxStreamTextChars?: number;\n /** Max length of streamed `function.arguments` per tool call index. */\n maxToolArgumentsChars?: number;\n}\n\n/**\n * Preferred options: caller provides the transport function directly.\n * The `fromServingEndpoint` / `fromModelServing` factories use this to route\n * through `connectors/serving/stream`, which centralises URL encoding, auth\n * via the SDK's `apiClient.request`, and any future retries/telemetry.\n */\ninterface StreamBodyAdapterOptions {\n streamBody: StreamBody;\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ntype DatabricksAdapterOptions =\n | RawFetchAdapterOptions\n | StreamBodyAdapterOptions;\n\nfunction isStreamBodyOptions(\n o: DatabricksAdapterOptions,\n): o is StreamBodyAdapterOptions {\n return \"streamBody\" in o;\n}\n\n/**\n * Duck-typed subset of the Databricks SDK `WorkspaceClient`. Callers of\n * `fromServingEndpoint` and `fromModelServing` pass a real `WorkspaceClient`,\n * but we only need the `apiClient.request` surface — so we declare the minimal\n * interface rather than importing the SDK type directly. This keeps the adapter\n * free of a hard compile-time dependency on `@databricks/sdk-experimental`.\n */\ninterface WorkspaceClientLike {\n apiClient: {\n request(options: Record<string, unknown>): Promise<unknown>;\n };\n}\n\ninterface ServingEndpointOptions {\n workspaceClient: WorkspaceClientLike;\n endpointName: string;\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface ModelServingOptions {\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n workspaceClient?: WorkspaceClientLike;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface OpenAIMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | null;\n tool_calls?: OpenAIToolCall[];\n tool_call_id?: string;\n}\n\ninterface OpenAIToolCall {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n /**\n * Opaque Vertex/Gemini \"thought signature\" blob the request must echo\n * back verbatim on the next turn. Vertex's OpenAI-compat proxy emits\n * this as `thoughtSignature` (camelCase) at the top level of the\n * tool_call delta (verified against `gemini-3.1-flash-lite-preview`),\n * and accepts the same spelling back on outbound. Non-Gemini endpoints\n * (Claude on Databricks, external OpenAI-compat models, Llama, etc.)\n * leave this undefined and the serializer omits the key.\n * See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/thought-signatures\n */\n thoughtSignature?: string;\n}\n\ninterface OpenAITool {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: unknown;\n };\n}\n\ninterface DeltaToolCall {\n index: number;\n id?: string;\n type?: string;\n function?: { name?: string; arguments?: string };\n /** See {@link OpenAIToolCall.thoughtSignature}. */\n thoughtSignature?: string;\n}\n\n/**\n * Adapter that talks directly to Databricks Model Serving `/invocations` endpoint.\n *\n * No dependency on the Vercel AI SDK or LangChain. Uses raw `fetch()` to POST\n * OpenAI-compatible payloads and parses the SSE stream itself. Calls\n * `authenticate()` per-request so tokens are always fresh.\n *\n * Handles both structured `tool_calls` responses and text-based tool call\n * fallback parsing for models that output tool calls as text.\n *\n * @example Using the factory (recommended)\n * ```ts\n * import { createApp, createAgent, agents, createWorkspaceClient } from \"@databricks/appkit\";\n * import { DatabricksAdapter } from \"@databricks/appkit/beta\";\n *\n * const adapter = DatabricksAdapter.fromServingEndpoint({\n * workspaceClient: createWorkspaceClient(),\n * endpointName: \"my-endpoint\",\n * });\n *\n * await createApp({\n * plugins: [\n * agents({\n * agents: {\n * assistant: createAgent({\n * instructions: \"You are a helpful assistant.\",\n * model: adapter,\n * }),\n * },\n * }),\n * ],\n * });\n * ```\n *\n * @example Using the raw constructor\n * ```ts\n * const adapter = new DatabricksAdapter({\n * endpointUrl: \"https://host/serving-endpoints/my-endpoint/invocations\",\n * authenticate: async () => ({ Authorization: `Bearer ${token}` }),\n * });\n * ```\n */\nexport class DatabricksAdapter implements AgentAdapter {\n private streamBody: StreamBody;\n private maxSteps: number;\n private maxTokens: number;\n private generationParams: GenerationParams;\n private maxSseLineChars: number;\n private maxStreamTextChars: number;\n private maxToolArgumentsChars: number;\n\n constructor(options: DatabricksAdapterOptions) {\n this.maxSteps = options.maxSteps ?? 10;\n this.maxTokens = options.maxTokens ?? 4096;\n this.generationParams = options.generationParams ?? {};\n this.maxSseLineChars =\n options.maxSseLineChars ?? DEFAULT_MAX_SSE_LINE_CHARS;\n this.maxStreamTextChars =\n options.maxStreamTextChars ?? DEFAULT_MAX_STREAM_TEXT_CHARS;\n this.maxToolArgumentsChars =\n options.maxToolArgumentsChars ?? DEFAULT_MAX_TOOL_ARGUMENT_CHARS;\n\n if (isStreamBodyOptions(options)) {\n this.streamBody = options.streamBody;\n } else {\n const { endpointUrl, authenticate } = options;\n this.streamBody = async (body, signal) => {\n const fetchSignal =\n signal ?? AbortSignal.timeout(RAW_FETCH_DEFAULT_TIMEOUT_MS);\n const authHeaders = await authenticate();\n const response = await fetch(endpointUrl, {\n method: \"POST\",\n headers: {\n \"User-Agent\": APPKIT_USER_AGENT,\n \"Content-Type\": \"application/json\",\n ...authHeaders,\n },\n body: JSON.stringify(body),\n signal: fetchSignal,\n });\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n throw new Error(\n `Databricks API error (${response.status}): ${errorText}`,\n );\n }\n if (!response.body) throw new Error(\"No response body\");\n return response.body;\n };\n }\n }\n\n /**\n * Creates a DatabricksAdapter for a Databricks Model Serving endpoint.\n *\n * Routes through the shared `connectors/serving/stream` helper, which\n * delegates to the SDK's `apiClient.request({ raw: true })`. That gives the\n * adapter centralised URL encoding + authentication with the rest of the\n * serving surface — no bespoke `fetch()` + `authenticate()` plumbing.\n */\n static async fromServingEndpoint(\n options: ServingEndpointOptions,\n ): Promise<DatabricksAdapter> {\n const {\n workspaceClient,\n endpointName,\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n } = options;\n return new DatabricksAdapter({\n streamBody: (body, signal) =>\n // Cast through the structural shape: the connector types\n // `workspaceClient` as the SDK's concrete `WorkspaceClient`, but we\n // only need `apiClient.request`.\n servingStream(\n workspaceClient as unknown as Parameters<typeof servingStream>[0],\n endpointName,\n body,\n signal,\n ),\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n });\n }\n\n /**\n * Creates a DatabricksAdapter from a Model Serving endpoint name.\n * Auto-creates a WorkspaceClient internally. Reads the endpoint name\n * from the argument or the `DATABRICKS_SERVING_ENDPOINT_NAME` env var.\n *\n * @example\n * ```ts\n * // Reads endpoint from DATABRICKS_SERVING_ENDPOINT_NAME env var\n * const adapter = await DatabricksAdapter.fromModelServing();\n *\n * // Explicit endpoint\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\");\n *\n * // With options\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\", {\n * maxSteps: 5,\n * maxTokens: 2048,\n * });\n * ```\n */\n static async fromModelServing(\n endpointName?: string,\n options?: ModelServingOptions,\n ): Promise<DatabricksAdapter> {\n const resolvedEndpoint =\n endpointName ?? process.env.DATABRICKS_SERVING_ENDPOINT_NAME;\n\n if (!resolvedEndpoint) {\n throw new Error(\n \"No endpoint name provided and DATABRICKS_SERVING_ENDPOINT_NAME env var is not set. \" +\n \"Pass an endpoint name or set DATABRICKS_SERVING_ENDPOINT_NAME.\",\n );\n }\n\n let workspaceClient: WorkspaceClientLike | undefined =\n options?.workspaceClient;\n if (!workspaceClient) {\n workspaceClient = createWorkspaceClient({\n clientOptions: getClientOptions(),\n }) as unknown as WorkspaceClientLike;\n }\n\n return DatabricksAdapter.fromServingEndpoint({\n workspaceClient,\n endpointName: resolvedEndpoint,\n maxSteps: options?.maxSteps,\n maxTokens: options?.maxTokens,\n generationParams: options?.generationParams,\n maxSseLineChars: options?.maxSseLineChars,\n maxStreamTextChars: options?.maxStreamTextChars,\n maxToolArgumentsChars: options?.maxToolArgumentsChars,\n });\n }\n\n /**\n * Discoverability shim for the Supervisor API adapter. Returns an\n * {@link AgentAdapter} (a `SupervisorApiAdapter` at runtime), NOT a\n * {@link DatabricksAdapter} — the two are separate classes (different\n * wire formats, different lifecycle). The return type is the\n * {@link AgentAdapter} interface so callers aren't bound to the concrete\n * class. Surfaced here so application developers see a single\n * `DatabricksAdapter.from*` autocomplete root.\n *\n * Dynamic-imports `./supervisor-api` to avoid forming a load-time cycle:\n * both files share `connectors/serving/client.ts`.\n *\n * @example\n * ```ts\n * import { DatabricksAdapter } from \"@databricks/appkit/beta\";\n *\n * const model = await DatabricksAdapter.fromSupervisorApi({\n * model: \"databricks-claude-sonnet-4-5\",\n * });\n * ```\n */\n static async fromSupervisorApi(\n options: import(\"./supervisor-api\").SupervisorApiAdapterOptions,\n ): Promise<AgentAdapter> {\n const { fromSupervisorApi } = await import(\"./supervisor-api\");\n return fromSupervisorApi(options);\n }\n\n async *run(\n input: AgentInput,\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n // Databricks API requires tool names to match [a-zA-Z0-9_-].\n // Our tool names use dots (e.g. \"analytics.query\"), so we swap dots\n // for double-underscores in the wire format and map back on receipt.\n const nameToWire = new Map<string, string>();\n const wireToName = new Map<string, string>();\n for (const tool of input.tools) {\n const wire = tool.name.replace(/\\./g, \"__\");\n if (wireToName.has(wire) && wireToName.get(wire) !== tool.name) {\n throw new Error(\n `Tool name collision: '${tool.name}' and '${wireToName.get(wire)}' both map to wire name '${wire}'`,\n );\n }\n nameToWire.set(tool.name, wire);\n wireToName.set(wire, tool.name);\n }\n\n const tools = this.buildTools(input.tools, nameToWire);\n const messages = this.buildMessages(input.messages, nameToWire);\n\n yield { type: \"status\", status: \"running\" };\n\n for (let step = 0; step < this.maxSteps; step++) {\n if (context.signal?.aborted) break;\n\n const { text, toolCalls } = yield* this.streamCompletion(\n messages,\n tools,\n context,\n );\n\n if (toolCalls.length === 0) {\n const parsed = parseTextToolCalls(text);\n if (parsed.length > 0) {\n yield* this.executeToolCalls(parsed, messages, context, nameToWire);\n continue;\n }\n break;\n }\n\n messages.push({\n role: \"assistant\",\n content: text || null,\n tool_calls: toolCalls,\n });\n\n for (const tc of toolCalls) {\n const wireName = tc.function.name;\n const originalName = wireToName.get(wireName) ?? wireName;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n }\n\n /** Parse wire arguments, emit tool_call / tool_result, append tool messages. */\n private async *executeSingleTool(\n tc: OpenAIToolCall,\n originalName: string,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n let args: unknown;\n try {\n args = JSON.parse(tc.function.arguments);\n } catch {\n args = {};\n }\n\n yield { type: \"tool_call\", callId: tc.id, name: originalName, args };\n\n try {\n const result = await context.executeTool(originalName, args);\n const resultStr =\n typeof result === \"string\" ? result : JSON.stringify(result);\n\n yield { type: \"tool_result\", callId: tc.id, result };\n\n messages.push({\n role: \"tool\",\n content: resultStr,\n tool_call_id: tc.id,\n });\n } catch (error) {\n const errMsg =\n error instanceof Error ? error.message : \"Tool execution failed\";\n\n yield {\n type: \"tool_result\",\n callId: tc.id,\n result: null,\n error: errMsg,\n };\n\n messages.push({\n role: \"tool\",\n content: JSON.stringify({ error: errMsg }),\n tool_call_id: tc.id,\n });\n }\n }\n\n private async *streamCompletion(\n messages: OpenAIMessage[],\n tools: OpenAITool[],\n context: AgentRunContext,\n ): AsyncGenerator<\n AgentEvent,\n { text: string; toolCalls: OpenAIToolCall[] },\n unknown\n > {\n const body: Record<string, unknown> = {\n messages,\n stream: true,\n max_tokens: this.maxTokens,\n };\n\n applyGenerationParams(body, this.generationParams);\n\n if (tools.length > 0) {\n body.tools = tools;\n }\n\n let responseBody: ReadableStream<Uint8Array>;\n try {\n responseBody = await this.streamBody(body, context.signal);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Stream request failed\";\n yield { type: \"status\", status: \"error\", error: msg };\n throw err;\n }\n\n const reader = responseBody.getReader();\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let fullText = \"\";\n const toolCallAccumulator = new Map<\n number,\n {\n id: string;\n name: string;\n arguments: string;\n thoughtSignature?: string;\n }\n >();\n\n try {\n while (true) {\n if (context.signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n if (buffer.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line buffer exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n for (const line of lines) {\n if (line.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n const trimmed = line.trim();\n if (!trimmed.startsWith(\"data: \")) continue;\n const data = trimmed.slice(6);\n if (data === \"[DONE]\") continue;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch (parseErr) {\n console.debug(\n \"[DatabricksAdapter] malformed SSE data line JSON\",\n { line: `${data.slice(0, 256)}${data.length > 256 ? \"…\" : \"\"}` },\n parseErr,\n );\n continue;\n }\n\n const deltaUnknown = openAiChoicesDelta(parsed);\n if (!isRecord(deltaUnknown)) continue;\n\n if (typeof deltaUnknown.content === \"string\") {\n const content = deltaUnknown.content;\n throwIfExceedsStreamLimit(\n \"streamed assistant text\",\n fullText.length,\n content,\n this.maxStreamTextChars,\n );\n fullText += content;\n yield { type: \"message_delta\" as const, content };\n }\n\n const toolCallsRaw = deltaUnknown.tool_calls;\n if (!Array.isArray(toolCallsRaw)) continue;\n\n for (const tc of toolCallsRaw) {\n if (!isStreamingDeltaToolCall(tc)) continue;\n const sig = tc.thoughtSignature;\n const existing = toolCallAccumulator.get(tc.index);\n if (existing) {\n if (tc.function?.arguments) {\n throwIfExceedsStreamLimit(\n \"tool call arguments\",\n existing.arguments.length,\n tc.function.arguments,\n this.maxToolArgumentsChars,\n );\n existing.arguments += tc.function.arguments;\n }\n if (sig && !existing.thoughtSignature) {\n existing.thoughtSignature = sig;\n }\n } else {\n const initial = tc.function?.arguments ?? \"\";\n if (initial.length > this.maxToolArgumentsChars) {\n throw new Error(\n `DatabricksAdapter: tool call arguments exceed configured limit (${this.maxToolArgumentsChars} UTF-16 code units)`,\n );\n }\n toolCallAccumulator.set(tc.index, {\n id: tc.id ?? `call_${tc.index}`,\n name: tc.function?.name ?? \"\",\n arguments: initial,\n ...(sig ? { thoughtSignature: sig } : {}),\n });\n }\n }\n }\n }\n } finally {\n try {\n await reader.cancel();\n } catch (cancelErr) {\n console.debug(\n \"[DatabricksAdapter] reader.cancel() failed during teardown\",\n cancelErr,\n );\n }\n try {\n reader.releaseLock();\n } catch (unlockErr) {\n console.debug(\n \"[DatabricksAdapter] reader.releaseLock() failed during teardown\",\n unlockErr,\n );\n }\n }\n\n const toolCalls: OpenAIToolCall[] = Array.from(\n toolCallAccumulator.values(),\n ).map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments || \"{}\" },\n ...(tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}),\n }));\n\n return { text: fullText, toolCalls };\n }\n\n private async *executeToolCalls(\n calls: Array<{ name: string; args: unknown }>,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n nameToWire: Map<string, string>,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n const toolCallObjs: OpenAIToolCall[] = calls.map((c, i) => ({\n id: `text_call_${i}`,\n type: \"function\" as const,\n function: {\n name: wireToolName(c.name),\n arguments: JSON.stringify(c.args),\n },\n }));\n\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: toolCallObjs,\n });\n\n for (let i = 0; i < toolCallObjs.length; i++) {\n const tc = toolCallObjs[i];\n const originalName = calls[i]?.name ?? tc.function.name;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n\n /**\n * Maps AppKit {@link AgentInput} messages into OpenAI-compatible wire messages.\n * Preserves multi-turn tool state (`toolCalls` → `tool_calls`, `toolCallId` →\n * `tool_call_id`) so resumed threads and hydrated history reach the model.\n */\n private buildMessages(\n messages: AgentInput[\"messages\"],\n nameToWire: Map<string, string>,\n ): OpenAIMessage[] {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n return messages.map((m) => {\n let content: string | null = m.content;\n if (\n m.role === \"assistant\" &&\n m.toolCalls &&\n m.toolCalls.length > 0 &&\n (!m.content || m.content.trim() === \"\")\n ) {\n content = null;\n }\n\n const out: OpenAIMessage = {\n role: m.role as OpenAIMessage[\"role\"],\n content,\n };\n\n if (m.toolCallId) {\n out.tool_call_id = m.toolCallId;\n }\n\n if (m.toolCalls && m.toolCalls.length > 0) {\n out.tool_calls = m.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: wireToolName(tc.name),\n arguments:\n typeof tc.args === \"string\"\n ? tc.args\n : JSON.stringify(tc.args ?? {}),\n },\n ...(tc.thoughtSignature\n ? { thoughtSignature: tc.thoughtSignature }\n : {}),\n }));\n }\n\n return out;\n });\n }\n\n private buildTools(\n definitions: AgentToolDefinition[],\n nameToWire: Map<string, string>,\n ): OpenAITool[] {\n return definitions.map((def) => ({\n type: \"function\" as const,\n function: {\n name: nameToWire.get(def.name) ?? def.name,\n description: def.description,\n parameters: def.parameters,\n },\n }));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Text-based tool call parsing (fallback)\n// ---------------------------------------------------------------------------\n\n/**\n * Parses text-based tool calls from model output.\n *\n * Handles two formats:\n * 1. Llama native: `[{\"name\": \"tool_name\", \"parameters\": {\"arg\": \"val\"}}]`\n * 2. Python-style: `[tool_name(arg1='val1', arg2='val2')]`\n */\nexport function parseTextToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const trimmed = text.trim();\n\n const jsonResult = tryParseLlamaJsonToolCalls(trimmed);\n if (jsonResult.length > 0) return jsonResult;\n\n const pyResult = tryParsePythonStyleToolCalls(trimmed);\n if (pyResult.length > 0) return pyResult;\n\n return [];\n}\n\nfunction isLlamaToolJsonItem(value: unknown): value is Record<\n string,\n unknown\n> & {\n name: string;\n} {\n if (!isRecord(value)) return false;\n return typeof value.name === \"string\";\n}\n\nfunction tryParseLlamaJsonToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const slice = extractLlamaToolJsonSlice(text);\n if (!slice) return [];\n\n try {\n const parsed: unknown = JSON.parse(slice);\n if (!Array.isArray(parsed)) return [];\n\n return parsed.filter(isLlamaToolJsonItem).map((item) => ({\n name: item.name,\n args: item.parameters ?? item.arguments ?? item.args ?? {},\n }));\n } catch {\n return [];\n }\n}\n\nfunction tryParsePythonStyleToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n if (text.length > PYTHON_STYLE_TOOL_PARSE_MAX_INPUT) {\n return [];\n }\n\n const pattern = /\\[?([a-zA-Z_][\\w.]*)\\(([^)]*)\\)\\]?/g;\n const results: Array<{ name: string; args: unknown }> = [];\n\n for (const match of text.matchAll(pattern)) {\n const name = match[1];\n const argsStr = match[2];\n\n const args: Record<string, unknown> = {};\n const argPattern = /(\\w+)\\s*=\\s*(?:'([^']*)'|\"([^\"]*)\"|(\\S+))/g;\n for (const argMatch of argsStr.matchAll(argPattern)) {\n const key = argMatch[1];\n const value = argMatch[2] ?? argMatch[3] ?? argMatch[4];\n args[key] = value;\n }\n\n results.push({ name, args });\n }\n\n return results;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"databricks.js","names":["servingStream"],"sources":["../../src/agents/databricks.ts"],"sourcesContent":["import type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n AgentRunContext,\n AgentToolDefinition,\n} from \"shared\";\n\nimport {\n type StreamBody,\n stream as servingStream,\n} from \"../connectors/serving/client\";\nimport { APPKIT_USER_AGENT, getClientOptions } from \"../context/client-options\";\nimport { createWorkspaceClient } from \"../workspace-client\";\n\n/** Default cap for a single incomplete SSE line tail (DoS guard). */\nconst DEFAULT_MAX_SSE_LINE_CHARS = 1024 * 1024;\n\n/** Default cap for accumulated assistant text from `delta.content`. */\nconst DEFAULT_MAX_STREAM_TEXT_CHARS = 4 * 1024 * 1024;\n\n/** Default cap for accumulated JSON arguments per streamed tool call index. */\nconst DEFAULT_MAX_TOOL_ARGUMENT_CHARS = 2 * 1024 * 1024;\n\n/** Cap text length before running Python-style tool-call regex (ReDoS guard). */\nconst PYTHON_STYLE_TOOL_PARSE_MAX_INPUT = 64 * 1024;\n\n/** Fallback HTTP timeout when the raw fetch adapter path receives no AbortSignal from the runner. */\nconst RAW_FETCH_DEFAULT_TIMEOUT_MS = 120_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Optional generation parameters forwarded to the OpenAI-compatible serving\n * request body. Names match the serving API wire keys. Only keys that are set\n * are sent — undefined values are omitted so the endpoint applies its own\n * defaults. Ranges are not validated here; the serving endpoint validates.\n */\nexport interface GenerationParams {\n /** Sampling temperature. */\n temperature?: number;\n /** Nucleus sampling probability mass (`top_p`). */\n top_p?: number;\n /** Stop sequence(s) that end generation. */\n stop?: string | string[];\n /** Penalize tokens by frequency. */\n frequency_penalty?: number;\n /** Penalize tokens by prior presence. */\n presence_penalty?: number;\n}\n\nconst GENERATION_PARAM_KEYS = [\n \"temperature\",\n \"top_p\",\n \"stop\",\n \"frequency_penalty\",\n \"presence_penalty\",\n] as const satisfies readonly (keyof GenerationParams)[];\n\n/** Copy only the set generation params onto the request body. */\nfunction applyGenerationParams(\n body: Record<string, unknown>,\n params: GenerationParams,\n): void {\n for (const key of GENERATION_PARAM_KEYS) {\n const value = params[key];\n if (value !== undefined) body[key] = value;\n }\n}\n\nfunction extractLlamaToolJsonSlice(text: string): string | undefined {\n const start = text.indexOf(\"[{\");\n if (start < 0) return undefined;\n const endBracket = text.lastIndexOf(\"}]\");\n if (endBracket < start) return undefined;\n return text.slice(start, endBracket + 2);\n}\n\n/** OpenAI SSE payload: `{ choices: [{ delta }] }`. */\nfunction openAiChoicesDelta(parsed: unknown): unknown {\n if (!isRecord(parsed)) return undefined;\n const choices = parsed.choices;\n if (!Array.isArray(choices) || choices.length < 1) return undefined;\n const first = choices[0];\n if (!isRecord(first)) return undefined;\n return first.delta;\n}\n\nfunction isStreamingDeltaToolCall(value: unknown): value is DeltaToolCall {\n if (!isRecord(value)) return false;\n return typeof value.index === \"number\";\n}\n\nfunction throwIfExceedsStreamLimit(\n label: string,\n currentLength: number,\n chunk: string,\n max: number,\n): void {\n if (currentLength + chunk.length > max) {\n throw new Error(\n `DatabricksAdapter: ${label} exceeds configured limit (${max} UTF-16 code units)`,\n );\n }\n}\n\n/**\n * Escape-hatch options: provide an `endpointUrl` + `authenticate()` and the\n * adapter uses a bare `fetch()` to call it. Useful for tests and for pointing\n * the adapter at non-workspace endpoints (reverse proxies, mocks).\n */\ninterface RawFetchAdapterOptions {\n endpointUrl: string;\n authenticate: () => Promise<Record<string, string>>;\n maxSteps?: number;\n maxTokens?: number;\n /** Optional generation params forwarded to the serving request body. */\n generationParams?: GenerationParams;\n /** Max length of one SSE line (including an incomplete tail in the buffer). */\n maxSseLineChars?: number;\n /** Max total length of assistant `delta.content` across the stream. */\n maxStreamTextChars?: number;\n /** Max length of streamed `function.arguments` per tool call index. */\n maxToolArgumentsChars?: number;\n}\n\n/**\n * Preferred options: caller provides the transport function directly.\n * The `fromServingEndpoint` / `fromModelServing` factories use this to route\n * through `connectors/serving/stream`, which centralises URL encoding, auth\n * via the SDK's `apiClient.request`, and any future retries/telemetry.\n */\ninterface StreamBodyAdapterOptions {\n streamBody: StreamBody;\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ntype DatabricksAdapterOptions =\n | RawFetchAdapterOptions\n | StreamBodyAdapterOptions;\n\nfunction isStreamBodyOptions(\n o: DatabricksAdapterOptions,\n): o is StreamBodyAdapterOptions {\n return \"streamBody\" in o;\n}\n\n/**\n * Duck-typed subset of the Databricks SDK `WorkspaceClient`. Callers of\n * `fromServingEndpoint` and `fromModelServing` pass a real `WorkspaceClient`,\n * but we only need the `apiClient.request` surface — so we declare the minimal\n * interface rather than importing the SDK type directly. This keeps the adapter\n * free of a hard compile-time dependency on `@databricks/sdk-experimental`.\n */\ninterface WorkspaceClientLike {\n apiClient: {\n request(options: Record<string, unknown>): Promise<unknown>;\n };\n}\n\ninterface ServingEndpointOptions {\n workspaceClient: WorkspaceClientLike;\n endpointName: string;\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface ModelServingOptions {\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n workspaceClient?: WorkspaceClientLike;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface OpenAIMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | null;\n tool_calls?: OpenAIToolCall[];\n tool_call_id?: string;\n}\n\ninterface OpenAIToolCall {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n /**\n * Opaque Vertex/Gemini \"thought signature\" blob the request must echo\n * back verbatim on the next turn. Vertex's OpenAI-compat proxy emits\n * this as `thoughtSignature` (camelCase) at the top level of the\n * tool_call delta (verified against `gemini-3.1-flash-lite-preview`),\n * and accepts the same spelling back on outbound. Non-Gemini endpoints\n * (Claude on Databricks, external OpenAI-compat models, Llama, etc.)\n * leave this undefined and the serializer omits the key.\n * See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/thought-signatures\n */\n thoughtSignature?: string;\n}\n\ninterface OpenAITool {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: unknown;\n };\n}\n\ninterface DeltaToolCall {\n index: number;\n id?: string;\n type?: string;\n function?: { name?: string; arguments?: string };\n /** See {@link OpenAIToolCall.thoughtSignature}. */\n thoughtSignature?: string;\n}\n\n/**\n * Adapter that talks directly to Databricks Model Serving `/invocations` endpoint.\n *\n * No dependency on the Vercel AI SDK or LangChain. Uses raw `fetch()` to POST\n * OpenAI-compatible payloads and parses the SSE stream itself. Calls\n * `authenticate()` per-request so tokens are always fresh.\n *\n * Handles both structured `tool_calls` responses and text-based tool call\n * fallback parsing for models that output tool calls as text.\n *\n * @example Using the factory (recommended)\n * ```ts\n * import { createApp, createAgent, agents, createWorkspaceClient } from \"@databricks/appkit\";\n * import { DatabricksAdapter } from \"@databricks/appkit/beta\";\n *\n * const adapter = DatabricksAdapter.fromServingEndpoint({\n * workspaceClient: createWorkspaceClient(),\n * endpointName: \"my-endpoint\",\n * });\n *\n * await createApp({\n * plugins: [\n * agents({\n * agents: {\n * assistant: createAgent({\n * instructions: \"You are a helpful assistant.\",\n * model: adapter,\n * }),\n * },\n * }),\n * ],\n * });\n * ```\n *\n * @example Using the raw constructor\n * ```ts\n * const adapter = new DatabricksAdapter({\n * endpointUrl: \"https://host/serving-endpoints/my-endpoint/invocations\",\n * authenticate: async () => ({ Authorization: `Bearer ${token}` }),\n * });\n * ```\n */\nexport class DatabricksAdapter implements AgentAdapter {\n private streamBody: StreamBody;\n private maxSteps: number;\n private maxTokens: number;\n private generationParams: GenerationParams;\n private maxSseLineChars: number;\n private maxStreamTextChars: number;\n private maxToolArgumentsChars: number;\n\n constructor(options: DatabricksAdapterOptions) {\n this.maxSteps = options.maxSteps ?? 10;\n this.maxTokens = options.maxTokens ?? 4096;\n this.generationParams = options.generationParams ?? {};\n this.maxSseLineChars =\n options.maxSseLineChars ?? DEFAULT_MAX_SSE_LINE_CHARS;\n this.maxStreamTextChars =\n options.maxStreamTextChars ?? DEFAULT_MAX_STREAM_TEXT_CHARS;\n this.maxToolArgumentsChars =\n options.maxToolArgumentsChars ?? DEFAULT_MAX_TOOL_ARGUMENT_CHARS;\n\n if (isStreamBodyOptions(options)) {\n this.streamBody = options.streamBody;\n } else {\n const { endpointUrl, authenticate } = options;\n this.streamBody = async (body, signal) => {\n const fetchSignal =\n signal ?? AbortSignal.timeout(RAW_FETCH_DEFAULT_TIMEOUT_MS);\n const authHeaders = await authenticate();\n const response = await fetch(endpointUrl, {\n method: \"POST\",\n headers: {\n \"User-Agent\": APPKIT_USER_AGENT,\n \"Content-Type\": \"application/json\",\n ...authHeaders,\n },\n body: JSON.stringify(body),\n signal: fetchSignal,\n });\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n throw new Error(\n `Databricks API error (${response.status}): ${errorText}`,\n );\n }\n if (!response.body) throw new Error(\"No response body\");\n return response.body;\n };\n }\n }\n\n /**\n * Creates a DatabricksAdapter for a Databricks Model Serving endpoint.\n *\n * Routes through the shared `connectors/serving/stream` helper, which\n * delegates to the SDK's `apiClient.request({ raw: true })`. That gives the\n * adapter centralised URL encoding + authentication with the rest of the\n * serving surface — no bespoke `fetch()` + `authenticate()` plumbing.\n */\n static async fromServingEndpoint(\n options: ServingEndpointOptions,\n ): Promise<DatabricksAdapter> {\n const {\n workspaceClient,\n endpointName,\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n } = options;\n return new DatabricksAdapter({\n streamBody: (body, signal) =>\n // Cast through the structural shape: the connector types\n // `workspaceClient` as the SDK's concrete `WorkspaceClient`, but we\n // only need `apiClient.request`.\n servingStream(\n workspaceClient as unknown as Parameters<typeof servingStream>[0],\n endpointName,\n body,\n signal,\n ),\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n });\n }\n\n /**\n * Creates a DatabricksAdapter from a Model Serving endpoint name.\n * Auto-creates a WorkspaceClient internally. Reads the endpoint name\n * from the argument or the `DATABRICKS_SERVING_ENDPOINT_NAME` env var.\n *\n * @example\n * ```ts\n * // Reads endpoint from DATABRICKS_SERVING_ENDPOINT_NAME env var\n * const adapter = await DatabricksAdapter.fromModelServing();\n *\n * // Explicit endpoint\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\");\n *\n * // With options\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\", {\n * maxSteps: 5,\n * maxTokens: 2048,\n * });\n * ```\n */\n static async fromModelServing(\n endpointName?: string,\n options?: ModelServingOptions,\n ): Promise<DatabricksAdapter> {\n const resolvedEndpoint =\n endpointName ?? process.env.DATABRICKS_SERVING_ENDPOINT_NAME;\n\n if (!resolvedEndpoint) {\n throw new Error(\n \"No endpoint name provided and DATABRICKS_SERVING_ENDPOINT_NAME env var is not set. \" +\n \"Pass an endpoint name or set DATABRICKS_SERVING_ENDPOINT_NAME.\",\n );\n }\n\n let workspaceClient: WorkspaceClientLike | undefined =\n options?.workspaceClient;\n if (!workspaceClient) {\n workspaceClient = createWorkspaceClient({\n clientOptions: getClientOptions(),\n }) as unknown as WorkspaceClientLike;\n }\n\n return DatabricksAdapter.fromServingEndpoint({\n workspaceClient,\n endpointName: resolvedEndpoint,\n maxSteps: options?.maxSteps,\n maxTokens: options?.maxTokens,\n generationParams: options?.generationParams,\n maxSseLineChars: options?.maxSseLineChars,\n maxStreamTextChars: options?.maxStreamTextChars,\n maxToolArgumentsChars: options?.maxToolArgumentsChars,\n });\n }\n\n /**\n * Discoverability shim for the Supervisor API adapter. Returns an\n * {@link AgentAdapter} (a `SupervisorApiAdapter` at runtime), NOT a\n * {@link DatabricksAdapter} — the two are separate classes (different\n * wire formats, different lifecycle). The return type is the\n * {@link AgentAdapter} interface so callers aren't bound to the concrete\n * class. Surfaced here so application developers see a single\n * `DatabricksAdapter.from*` autocomplete root.\n *\n * Dynamic-imports `./supervisor-api` to avoid forming a load-time cycle:\n * both files share `connectors/serving/client.ts`.\n *\n * @example\n * ```ts\n * import { DatabricksAdapter } from \"@databricks/appkit/beta\";\n *\n * const model = await DatabricksAdapter.fromSupervisorApi({\n * model: \"databricks-claude-sonnet-4-5\",\n * });\n * ```\n */\n static async fromSupervisorApi(\n options: import(\"./supervisor-api\").SupervisorApiAdapterOptions,\n ): Promise<AgentAdapter> {\n const { fromSupervisorApi } = await import(\"./supervisor-api\");\n return fromSupervisorApi(options);\n }\n\n async *run(\n input: AgentInput,\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n // Databricks API requires tool names to match [a-zA-Z0-9_-].\n // Our tool names use dots (e.g. \"analytics.query\"), so we swap dots\n // for double-underscores in the wire format and map back on receipt.\n const nameToWire = new Map<string, string>();\n const wireToName = new Map<string, string>();\n for (const tool of input.tools) {\n const wire = tool.name.replace(/\\./g, \"__\");\n if (wireToName.has(wire) && wireToName.get(wire) !== tool.name) {\n throw new Error(\n `Tool name collision: '${tool.name}' and '${wireToName.get(wire)}' both map to wire name '${wire}'`,\n );\n }\n nameToWire.set(tool.name, wire);\n wireToName.set(wire, tool.name);\n }\n\n const tools = this.buildTools(input.tools, nameToWire);\n const messages = this.buildMessages(input.messages, nameToWire);\n\n yield { type: \"status\", status: \"running\" };\n\n for (let step = 0; step < this.maxSteps; step++) {\n if (context.signal?.aborted) break;\n\n const { text, toolCalls } = yield* this.streamCompletion(\n messages,\n tools,\n context,\n );\n\n if (toolCalls.length === 0) {\n const parsed = parseTextToolCalls(text);\n if (parsed.length > 0) {\n yield* this.executeToolCalls(parsed, messages, context, nameToWire);\n continue;\n }\n break;\n }\n\n messages.push({\n role: \"assistant\",\n content: text || null,\n tool_calls: toolCalls,\n });\n\n for (const tc of toolCalls) {\n const wireName = tc.function.name;\n const originalName = wireToName.get(wireName) ?? wireName;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n }\n\n /** Parse wire arguments, emit tool_call / tool_result, append tool messages. */\n private async *executeSingleTool(\n tc: OpenAIToolCall,\n originalName: string,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n let args: unknown;\n try {\n args = JSON.parse(tc.function.arguments);\n } catch {\n args = {};\n }\n\n yield { type: \"tool_call\", callId: tc.id, name: originalName, args };\n\n try {\n const result = await context.executeTool(originalName, args);\n const resultStr =\n typeof result === \"string\" ? result : JSON.stringify(result);\n\n yield { type: \"tool_result\", callId: tc.id, result };\n\n messages.push({\n role: \"tool\",\n content: resultStr,\n tool_call_id: tc.id,\n });\n } catch (error) {\n const errMsg =\n error instanceof Error ? error.message : \"Tool execution failed\";\n\n yield {\n type: \"tool_result\",\n callId: tc.id,\n result: null,\n error: errMsg,\n };\n\n messages.push({\n role: \"tool\",\n content: JSON.stringify({ error: errMsg }),\n tool_call_id: tc.id,\n });\n }\n }\n\n private async *streamCompletion(\n messages: OpenAIMessage[],\n tools: OpenAITool[],\n context: AgentRunContext,\n ): AsyncGenerator<\n AgentEvent,\n { text: string; toolCalls: OpenAIToolCall[] },\n unknown\n > {\n const body: Record<string, unknown> = {\n messages,\n stream: true,\n max_tokens: this.maxTokens,\n };\n\n applyGenerationParams(body, this.generationParams);\n\n if (tools.length > 0) {\n body.tools = tools;\n }\n\n let responseBody: ReadableStream<Uint8Array>;\n try {\n responseBody = await this.streamBody(body, context.signal);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Stream request failed\";\n yield { type: \"status\", status: \"error\", error: msg };\n throw err;\n }\n\n const reader = responseBody.getReader();\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let fullText = \"\";\n const toolCallAccumulator = new Map<\n number,\n {\n id: string;\n name: string;\n arguments: string;\n thoughtSignature?: string;\n }\n >();\n\n try {\n while (true) {\n if (context.signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n if (buffer.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line buffer exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n for (const line of lines) {\n if (line.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n const trimmed = line.trim();\n if (!trimmed.startsWith(\"data: \")) continue;\n const data = trimmed.slice(6);\n if (data === \"[DONE]\") continue;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch (parseErr) {\n console.debug(\n \"[DatabricksAdapter] malformed SSE data line JSON\",\n { line: `${data.slice(0, 256)}${data.length > 256 ? \"…\" : \"\"}` },\n parseErr,\n );\n continue;\n }\n\n const deltaUnknown = openAiChoicesDelta(parsed);\n if (!isRecord(deltaUnknown)) continue;\n\n if (typeof deltaUnknown.content === \"string\") {\n const content = deltaUnknown.content;\n throwIfExceedsStreamLimit(\n \"streamed assistant text\",\n fullText.length,\n content,\n this.maxStreamTextChars,\n );\n fullText += content;\n yield { type: \"message_delta\" as const, content };\n }\n\n const toolCallsRaw = deltaUnknown.tool_calls;\n if (!Array.isArray(toolCallsRaw)) continue;\n\n for (const tc of toolCallsRaw) {\n if (!isStreamingDeltaToolCall(tc)) continue;\n const sig = tc.thoughtSignature;\n const existing = toolCallAccumulator.get(tc.index);\n if (existing) {\n if (tc.function?.arguments) {\n throwIfExceedsStreamLimit(\n \"tool call arguments\",\n existing.arguments.length,\n tc.function.arguments,\n this.maxToolArgumentsChars,\n );\n existing.arguments += tc.function.arguments;\n }\n if (sig && !existing.thoughtSignature) {\n existing.thoughtSignature = sig;\n }\n } else {\n const initial = tc.function?.arguments ?? \"\";\n if (initial.length > this.maxToolArgumentsChars) {\n throw new Error(\n `DatabricksAdapter: tool call arguments exceed configured limit (${this.maxToolArgumentsChars} UTF-16 code units)`,\n );\n }\n toolCallAccumulator.set(tc.index, {\n id: tc.id ?? `call_${tc.index}`,\n name: tc.function?.name ?? \"\",\n arguments: initial,\n ...(sig ? { thoughtSignature: sig } : {}),\n });\n }\n }\n }\n }\n } finally {\n try {\n await reader.cancel();\n } catch (cancelErr) {\n console.debug(\n \"[DatabricksAdapter] reader.cancel() failed during teardown\",\n cancelErr,\n );\n }\n try {\n reader.releaseLock();\n } catch (unlockErr) {\n console.debug(\n \"[DatabricksAdapter] reader.releaseLock() failed during teardown\",\n unlockErr,\n );\n }\n }\n\n const toolCalls: OpenAIToolCall[] = Array.from(\n toolCallAccumulator.values(),\n ).map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments || \"{}\" },\n ...(tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}),\n }));\n\n return { text: fullText, toolCalls };\n }\n\n private async *executeToolCalls(\n calls: Array<{ name: string; args: unknown }>,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n nameToWire: Map<string, string>,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n const toolCallObjs: OpenAIToolCall[] = calls.map((c, i) => ({\n id: `text_call_${i}`,\n type: \"function\" as const,\n function: {\n name: wireToolName(c.name),\n arguments: JSON.stringify(c.args),\n },\n }));\n\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: toolCallObjs,\n });\n\n for (let i = 0; i < toolCallObjs.length; i++) {\n const tc = toolCallObjs[i];\n const originalName = calls[i]?.name ?? tc.function.name;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n\n /**\n * Maps AppKit {@link AgentInput} messages into OpenAI-compatible wire messages.\n * Preserves multi-turn tool state (`toolCalls` → `tool_calls`, `toolCallId` →\n * `tool_call_id`) so resumed threads and hydrated history reach the model.\n */\n private buildMessages(\n messages: AgentInput[\"messages\"],\n nameToWire: Map<string, string>,\n ): OpenAIMessage[] {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n return messages.map((m) => {\n let content: string | null = m.content;\n if (\n m.role === \"assistant\" &&\n m.toolCalls &&\n m.toolCalls.length > 0 &&\n (!m.content || m.content.trim() === \"\")\n ) {\n content = null;\n }\n\n const out: OpenAIMessage = {\n role: m.role as OpenAIMessage[\"role\"],\n content,\n };\n\n if (m.toolCallId) {\n out.tool_call_id = m.toolCallId;\n }\n\n if (m.toolCalls && m.toolCalls.length > 0) {\n out.tool_calls = m.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: wireToolName(tc.name),\n arguments:\n typeof tc.args === \"string\"\n ? tc.args\n : JSON.stringify(tc.args ?? {}),\n },\n ...(tc.thoughtSignature\n ? { thoughtSignature: tc.thoughtSignature }\n : {}),\n }));\n }\n\n return out;\n });\n }\n\n private buildTools(\n definitions: AgentToolDefinition[],\n nameToWire: Map<string, string>,\n ): OpenAITool[] {\n return definitions.map((def) => ({\n type: \"function\" as const,\n function: {\n name: nameToWire.get(def.name) ?? def.name,\n description: def.description,\n parameters: def.parameters,\n },\n }));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Text-based tool call parsing (fallback)\n// ---------------------------------------------------------------------------\n\n/**\n * Parses text-based tool calls from model output.\n *\n * Handles two formats:\n * 1. Llama native: `[{\"name\": \"tool_name\", \"parameters\": {\"arg\": \"val\"}}]`\n * 2. Python-style: `[tool_name(arg1='val1', arg2='val2')]`\n */\nexport function parseTextToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const trimmed = text.trim();\n\n const jsonResult = tryParseLlamaJsonToolCalls(trimmed);\n if (jsonResult.length > 0) return jsonResult;\n\n const pyResult = tryParsePythonStyleToolCalls(trimmed);\n if (pyResult.length > 0) return pyResult;\n\n return [];\n}\n\nfunction isLlamaToolJsonItem(value: unknown): value is Record<\n string,\n unknown\n> & {\n name: string;\n} {\n if (!isRecord(value)) return false;\n return typeof value.name === \"string\";\n}\n\nfunction tryParseLlamaJsonToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const slice = extractLlamaToolJsonSlice(text);\n if (!slice) return [];\n\n try {\n const parsed: unknown = JSON.parse(slice);\n if (!Array.isArray(parsed)) return [];\n\n return parsed.filter(isLlamaToolJsonItem).map((item) => ({\n name: item.name,\n args: item.parameters ?? item.arguments ?? item.args ?? {},\n }));\n } catch {\n return [];\n }\n}\n\nfunction tryParsePythonStyleToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n if (text.length > PYTHON_STYLE_TOOL_PARSE_MAX_INPUT) {\n return [];\n }\n\n const pattern = /\\[?([a-zA-Z_][\\w.]*)\\(([^)]*)\\)\\]?/g;\n const results: Array<{ name: string; args: unknown }> = [];\n\n for (const match of text.matchAll(pattern)) {\n const name = match[1];\n const argsStr = match[2];\n\n const args: Record<string, unknown> = {};\n const argPattern = /(\\w+)\\s*=\\s*(?:'([^']*)'|\"([^\"]*)\"|(\\S+))/g;\n for (const argMatch of argsStr.matchAll(argPattern)) {\n const key = argMatch[1];\n const value = argMatch[2] ?? argMatch[3] ?? argMatch[4];\n args[key] = value;\n }\n\n results.push({ name, args });\n }\n\n return results;\n}\n"],"mappings":";;;;;;AAgBA,MAAM,6BAA6B,OAAO;;AAG1C,MAAM,gCAAgC,IAAI,OAAO;;AAGjD,MAAM,kCAAkC,IAAI,OAAO;;AAGnD,MAAM,oCAAoC,KAAK;;AAG/C,MAAM,+BAA+B;AAErC,SAAS,SAAS,OAAkD;AAClE,QAAO,OAAO,UAAU,YAAY,UAAU;;AAsBhD,MAAM,wBAAwB;CAC5B;CACA;CACA;CACA;CACA;CACD;;AAGD,SAAS,sBACP,MACA,QACM;AACN,MAAK,MAAM,OAAO,uBAAuB;EACvC,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,OAAW,MAAK,OAAO;;;AAIzC,SAAS,0BAA0B,MAAkC;CACnE,MAAM,QAAQ,KAAK,QAAQ,KAAK;AAChC,KAAI,QAAQ,EAAG,QAAO;CACtB,MAAM,aAAa,KAAK,YAAY,KAAK;AACzC,KAAI,aAAa,MAAO,QAAO;AAC/B,QAAO,KAAK,MAAM,OAAO,aAAa,EAAE;;;AAI1C,SAAS,mBAAmB,QAA0B;AACpD,KAAI,CAAC,SAAS,OAAO,CAAE,QAAO;CAC9B,MAAM,UAAU,OAAO;AACvB,KAAI,CAAC,MAAM,QAAQ,QAAQ,IAAI,QAAQ,SAAS,EAAG,QAAO;CAC1D,MAAM,QAAQ,QAAQ;AACtB,KAAI,CAAC,SAAS,MAAM,CAAE,QAAO;AAC7B,QAAO,MAAM;;AAGf,SAAS,yBAAyB,OAAwC;AACxE,KAAI,CAAC,SAAS,MAAM,CAAE,QAAO;AAC7B,QAAO,OAAO,MAAM,UAAU;;AAGhC,SAAS,0BACP,OACA,eACA,OACA,KACM;AACN,KAAI,gBAAgB,MAAM,SAAS,IACjC,OAAM,IAAI,MACR,sBAAsB,MAAM,6BAA6B,IAAI,qBAC9D;;AA4CL,SAAS,oBACP,GAC+B;AAC/B,QAAO,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyHzB,IAAa,oBAAb,MAAa,kBAA0C;CACrD,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,SAAmC;AAC7C,OAAK,WAAW,QAAQ,YAAY;AACpC,OAAK,YAAY,QAAQ,aAAa;AACtC,OAAK,mBAAmB,QAAQ,oBAAoB,EAAE;AACtD,OAAK,kBACH,QAAQ,mBAAmB;AAC7B,OAAK,qBACH,QAAQ,sBAAsB;AAChC,OAAK,wBACH,QAAQ,yBAAyB;AAEnC,MAAI,oBAAoB,QAAQ,CAC9B,MAAK,aAAa,QAAQ;OACrB;GACL,MAAM,EAAE,aAAa,iBAAiB;AACtC,QAAK,aAAa,OAAO,MAAM,WAAW;IACxC,MAAM,cACJ,UAAU,YAAY,QAAQ,6BAA6B;IAC7D,MAAM,cAAc,MAAM,cAAc;IACxC,MAAM,WAAW,MAAM,MAAM,aAAa;KACxC,QAAQ;KACR,SAAS;MACP,cAAc;MACd,gBAAgB;MAChB,GAAG;MACJ;KACD,MAAM,KAAK,UAAU,KAAK;KAC1B,QAAQ;KACT,CAAC;AACF,QAAI,CAAC,SAAS,IAAI;KAChB,MAAM,YAAY,MAAM,SAAS,MAAM,CAAC,YAAY,gBAAgB;AACpE,WAAM,IAAI,MACR,yBAAyB,SAAS,OAAO,KAAK,YAC/C;;AAEH,QAAI,CAAC,SAAS,KAAM,OAAM,IAAI,MAAM,mBAAmB;AACvD,WAAO,SAAS;;;;;;;;;;;;CAatB,aAAa,oBACX,SAC4B;EAC5B,MAAM,EACJ,iBACA,cACA,UACA,WACA,kBACA,iBACA,oBACA,0BACE;AACJ,SAAO,IAAI,kBAAkB;GAC3B,aAAa,MAAM,WAIjBA,OACE,iBACA,cACA,MACA,OACD;GACH;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;;;;;;;;;;;;;;;;;;;;;;CAuBJ,aAAa,iBACX,cACA,SAC4B;EAC5B,MAAM,mBACJ,gBAAgB,QAAQ,IAAI;AAE9B,MAAI,CAAC,iBACH,OAAM,IAAI,MACR,oJAED;EAGH,IAAI,kBACF,SAAS;AACX,MAAI,CAAC,gBACH,mBAAkB,sBAAsB,EACtC,eAAe,kBAAkB,EAClC,CAAC;AAGJ,SAAO,kBAAkB,oBAAoB;GAC3C;GACA,cAAc;GACd,UAAU,SAAS;GACnB,WAAW,SAAS;GACpB,kBAAkB,SAAS;GAC3B,iBAAiB,SAAS;GAC1B,oBAAoB,SAAS;GAC7B,uBAAuB,SAAS;GACjC,CAAC;;;;;;;;;;;;;;;;;;;;;;;CAwBJ,aAAa,kBACX,SACuB;EACvB,MAAM,EAAE,sBAAsB,MAAM,OAAO;AAC3C,SAAO,kBAAkB,QAAQ;;CAGnC,OAAO,IACL,OACA,SAC2C;EAI3C,MAAM,6BAAa,IAAI,KAAqB;EAC5C,MAAM,6BAAa,IAAI,KAAqB;AAC5C,OAAK,MAAM,QAAQ,MAAM,OAAO;GAC9B,MAAM,OAAO,KAAK,KAAK,QAAQ,OAAO,KAAK;AAC3C,OAAI,WAAW,IAAI,KAAK,IAAI,WAAW,IAAI,KAAK,KAAK,KAAK,KACxD,OAAM,IAAI,MACR,yBAAyB,KAAK,KAAK,SAAS,WAAW,IAAI,KAAK,CAAC,2BAA2B,KAAK,GAClG;AAEH,cAAW,IAAI,KAAK,MAAM,KAAK;AAC/B,cAAW,IAAI,MAAM,KAAK,KAAK;;EAGjC,MAAM,QAAQ,KAAK,WAAW,MAAM,OAAO,WAAW;EACtD,MAAM,WAAW,KAAK,cAAc,MAAM,UAAU,WAAW;AAE/D,QAAM;GAAE,MAAM;GAAU,QAAQ;GAAW;AAE3C,OAAK,IAAI,OAAO,GAAG,OAAO,KAAK,UAAU,QAAQ;AAC/C,OAAI,QAAQ,QAAQ,QAAS;GAE7B,MAAM,EAAE,MAAM,cAAc,OAAO,KAAK,iBACtC,UACA,OACA,QACD;AAED,OAAI,UAAU,WAAW,GAAG;IAC1B,MAAM,SAAS,mBAAmB,KAAK;AACvC,QAAI,OAAO,SAAS,GAAG;AACrB,YAAO,KAAK,iBAAiB,QAAQ,UAAU,SAAS,WAAW;AACnE;;AAEF;;AAGF,YAAS,KAAK;IACZ,MAAM;IACN,SAAS,QAAQ;IACjB,YAAY;IACb,CAAC;AAEF,QAAK,MAAM,MAAM,WAAW;IAC1B,MAAM,WAAW,GAAG,SAAS;IAC7B,MAAM,eAAe,WAAW,IAAI,SAAS,IAAI;AACjD,WAAO,KAAK,kBAAkB,IAAI,cAAc,UAAU,QAAQ;;;;;CAMxE,OAAe,kBACb,IACA,cACA,UACA,SAC2C;EAC3C,IAAI;AACJ,MAAI;AACF,UAAO,KAAK,MAAM,GAAG,SAAS,UAAU;UAClC;AACN,UAAO,EAAE;;AAGX,QAAM;GAAE,MAAM;GAAa,QAAQ,GAAG;GAAI,MAAM;GAAc;GAAM;AAEpE,MAAI;GACF,MAAM,SAAS,MAAM,QAAQ,YAAY,cAAc,KAAK;GAC5D,MAAM,YACJ,OAAO,WAAW,WAAW,SAAS,KAAK,UAAU,OAAO;AAE9D,SAAM;IAAE,MAAM;IAAe,QAAQ,GAAG;IAAI;IAAQ;AAEpD,YAAS,KAAK;IACZ,MAAM;IACN,SAAS;IACT,cAAc,GAAG;IAClB,CAAC;WACK,OAAO;GACd,MAAM,SACJ,iBAAiB,QAAQ,MAAM,UAAU;AAE3C,SAAM;IACJ,MAAM;IACN,QAAQ,GAAG;IACX,QAAQ;IACR,OAAO;IACR;AAED,YAAS,KAAK;IACZ,MAAM;IACN,SAAS,KAAK,UAAU,EAAE,OAAO,QAAQ,CAAC;IAC1C,cAAc,GAAG;IAClB,CAAC;;;CAIN,OAAe,iBACb,UACA,OACA,SAKA;EACA,MAAM,OAAgC;GACpC;GACA,QAAQ;GACR,YAAY,KAAK;GAClB;AAED,wBAAsB,MAAM,KAAK,iBAAiB;AAElD,MAAI,MAAM,SAAS,EACjB,MAAK,QAAQ;EAGf,IAAI;AACJ,MAAI;AACF,kBAAe,MAAM,KAAK,WAAW,MAAM,QAAQ,OAAO;WACnD,KAAK;AAEZ,SAAM;IAAE,MAAM;IAAU,QAAQ;IAAS,OAD7B,eAAe,QAAQ,IAAI,UAAU;IACI;AACrD,SAAM;;EAGR,MAAM,SAAS,aAAa,WAAW;EAEvC,MAAM,UAAU,IAAI,aAAa;EACjC,IAAI,SAAS;EACb,IAAI,WAAW;EACf,MAAM,sCAAsB,IAAI,KAQ7B;AAEH,MAAI;AACF,UAAO,MAAM;AACX,QAAI,QAAQ,QAAQ,QAAS;IAE7B,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,QAAI,KAAM;AAEV,cAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,MAAM,CAAC;IACjD,MAAM,QAAQ,OAAO,MAAM,KAAK;AAChC,aAAS,MAAM,KAAK,IAAI;AAExB,QAAI,OAAO,SAAS,KAAK,gBACvB,OAAM,IAAI,MACR,gEAAgE,KAAK,gBAAgB,qBACtF;AAGH,SAAK,MAAM,QAAQ,OAAO;AACxB,SAAI,KAAK,SAAS,KAAK,gBACrB,OAAM,IAAI,MACR,yDAAyD,KAAK,gBAAgB,qBAC/E;KAGH,MAAM,UAAU,KAAK,MAAM;AAC3B,SAAI,CAAC,QAAQ,WAAW,SAAS,CAAE;KACnC,MAAM,OAAO,QAAQ,MAAM,EAAE;AAC7B,SAAI,SAAS,SAAU;KAEvB,IAAI;AACJ,SAAI;AACF,eAAS,KAAK,MAAM,KAAK;cAClB,UAAU;AACjB,cAAQ,MACN,oDACA,EAAE,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,GAAG,KAAK,SAAS,MAAM,MAAM,MAAM,EAChE,SACD;AACD;;KAGF,MAAM,eAAe,mBAAmB,OAAO;AAC/C,SAAI,CAAC,SAAS,aAAa,CAAE;AAE7B,SAAI,OAAO,aAAa,YAAY,UAAU;MAC5C,MAAM,UAAU,aAAa;AAC7B,gCACE,2BACA,SAAS,QACT,SACA,KAAK,mBACN;AACD,kBAAY;AACZ,YAAM;OAAE,MAAM;OAA0B;OAAS;;KAGnD,MAAM,eAAe,aAAa;AAClC,SAAI,CAAC,MAAM,QAAQ,aAAa,CAAE;AAElC,UAAK,MAAM,MAAM,cAAc;AAC7B,UAAI,CAAC,yBAAyB,GAAG,CAAE;MACnC,MAAM,MAAM,GAAG;MACf,MAAM,WAAW,oBAAoB,IAAI,GAAG,MAAM;AAClD,UAAI,UAAU;AACZ,WAAI,GAAG,UAAU,WAAW;AAC1B,kCACE,uBACA,SAAS,UAAU,QACnB,GAAG,SAAS,WACZ,KAAK,sBACN;AACD,iBAAS,aAAa,GAAG,SAAS;;AAEpC,WAAI,OAAO,CAAC,SAAS,iBACnB,UAAS,mBAAmB;aAEzB;OACL,MAAM,UAAU,GAAG,UAAU,aAAa;AAC1C,WAAI,QAAQ,SAAS,KAAK,sBACxB,OAAM,IAAI,MACR,mEAAmE,KAAK,sBAAsB,qBAC/F;AAEH,2BAAoB,IAAI,GAAG,OAAO;QAChC,IAAI,GAAG,MAAM,QAAQ,GAAG;QACxB,MAAM,GAAG,UAAU,QAAQ;QAC3B,WAAW;QACX,GAAI,MAAM,EAAE,kBAAkB,KAAK,GAAG,EAAE;QACzC,CAAC;;;;;YAKF;AACR,OAAI;AACF,UAAM,OAAO,QAAQ;YACd,WAAW;AAClB,YAAQ,MACN,8DACA,UACD;;AAEH,OAAI;AACF,WAAO,aAAa;YACb,WAAW;AAClB,YAAQ,MACN,mEACA,UACD;;;EAIL,MAAM,YAA8B,MAAM,KACxC,oBAAoB,QAAQ,CAC7B,CAAC,KAAK,QAAQ;GACb,IAAI,GAAG;GACP,MAAM;GACN,UAAU;IAAE,MAAM,GAAG;IAAM,WAAW,GAAG,aAAa;IAAM;GAC5D,GAAI,GAAG,mBAAmB,EAAE,kBAAkB,GAAG,kBAAkB,GAAG,EAAE;GACzE,EAAE;AAEH,SAAO;GAAE,MAAM;GAAU;GAAW;;CAGtC,OAAe,iBACb,OACA,UACA,SACA,YAC2C;EAC3C,MAAM,gBAAgB,SACpB,WAAW,IAAI,KAAK,IAAI,KAAK,QAAQ,OAAO,KAAK;EAEnD,MAAM,eAAiC,MAAM,KAAK,GAAG,OAAO;GAC1D,IAAI,aAAa;GACjB,MAAM;GACN,UAAU;IACR,MAAM,aAAa,EAAE,KAAK;IAC1B,WAAW,KAAK,UAAU,EAAE,KAAK;IAClC;GACF,EAAE;AAEH,WAAS,KAAK;GACZ,MAAM;GACN,SAAS;GACT,YAAY;GACb,CAAC;AAEF,OAAK,IAAI,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;GAC5C,MAAM,KAAK,aAAa;GACxB,MAAM,eAAe,MAAM,IAAI,QAAQ,GAAG,SAAS;AACnD,UAAO,KAAK,kBAAkB,IAAI,cAAc,UAAU,QAAQ;;;;;;;;CAStE,AAAQ,cACN,UACA,YACiB;EACjB,MAAM,gBAAgB,SACpB,WAAW,IAAI,KAAK,IAAI,KAAK,QAAQ,OAAO,KAAK;AAEnD,SAAO,SAAS,KAAK,MAAM;GACzB,IAAI,UAAyB,EAAE;AAC/B,OACE,EAAE,SAAS,eACX,EAAE,aACF,EAAE,UAAU,SAAS,MACpB,CAAC,EAAE,WAAW,EAAE,QAAQ,MAAM,KAAK,IAEpC,WAAU;GAGZ,MAAM,MAAqB;IACzB,MAAM,EAAE;IACR;IACD;AAED,OAAI,EAAE,WACJ,KAAI,eAAe,EAAE;AAGvB,OAAI,EAAE,aAAa,EAAE,UAAU,SAAS,EACtC,KAAI,aAAa,EAAE,UAAU,KAAK,QAAQ;IACxC,IAAI,GAAG;IACP,MAAM;IACN,UAAU;KACR,MAAM,aAAa,GAAG,KAAK;KAC3B,WACE,OAAO,GAAG,SAAS,WACf,GAAG,OACH,KAAK,UAAU,GAAG,QAAQ,EAAE,CAAC;KACpC;IACD,GAAI,GAAG,mBACH,EAAE,kBAAkB,GAAG,kBAAkB,GACzC,EAAE;IACP,EAAE;AAGL,UAAO;IACP;;CAGJ,AAAQ,WACN,aACA,YACc;AACd,SAAO,YAAY,KAAK,SAAS;GAC/B,MAAM;GACN,UAAU;IACR,MAAM,WAAW,IAAI,IAAI,KAAK,IAAI,IAAI;IACtC,aAAa,IAAI;IACjB,YAAY,IAAI;IACjB;GACF,EAAE;;;;;;;;;;AAeP,SAAgB,mBACd,MACwC;CACxC,MAAM,UAAU,KAAK,MAAM;CAE3B,MAAM,aAAa,2BAA2B,QAAQ;AACtD,KAAI,WAAW,SAAS,EAAG,QAAO;CAElC,MAAM,WAAW,6BAA6B,QAAQ;AACtD,KAAI,SAAS,SAAS,EAAG,QAAO;AAEhC,QAAO,EAAE;;AAGX,SAAS,oBAAoB,OAK3B;AACA,KAAI,CAAC,SAAS,MAAM,CAAE,QAAO;AAC7B,QAAO,OAAO,MAAM,SAAS;;AAG/B,SAAS,2BACP,MACwC;CACxC,MAAM,QAAQ,0BAA0B,KAAK;AAC7C,KAAI,CAAC,MAAO,QAAO,EAAE;AAErB,KAAI;EACF,MAAM,SAAkB,KAAK,MAAM,MAAM;AACzC,MAAI,CAAC,MAAM,QAAQ,OAAO,CAAE,QAAO,EAAE;AAErC,SAAO,OAAO,OAAO,oBAAoB,CAAC,KAAK,UAAU;GACvD,MAAM,KAAK;GACX,MAAM,KAAK,cAAc,KAAK,aAAa,KAAK,QAAQ,EAAE;GAC3D,EAAE;SACG;AACN,SAAO,EAAE;;;AAIb,SAAS,6BACP,MACwC;AACxC,KAAI,KAAK,SAAS,kCAChB,QAAO,EAAE;CAGX,MAAM,UAAU;CAChB,MAAM,UAAkD,EAAE;AAE1D,MAAK,MAAM,SAAS,KAAK,SAAS,QAAQ,EAAE;EAC1C,MAAM,OAAO,MAAM;EACnB,MAAM,UAAU,MAAM;EAEtB,MAAM,OAAgC,EAAE;AAExC,OAAK,MAAM,YAAY,QAAQ,SADZ,6CACgC,EAAE;GACnD,MAAM,MAAM,SAAS;AAErB,QAAK,OADS,SAAS,MAAM,SAAS,MAAM,SAAS;;AAIvD,UAAQ,KAAK;GAAE;GAAM;GAAM,CAAC;;AAG9B,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"supervisor-api.d.ts","names":[],"sources":["../../src/agents/supervisor-api.ts"],"mappings":";;;;;;;;AAkGA;;;;;;UAAiB,mBAAA,SAA4B,aAAA;EAC3C,MAAA;IAAU,cAAA,IAAkB,OAAA;EAAA;AAAA;;;;;;;;;;;KAiBlB,cAAA;EACN,IAAA;EAAqB,WAAA;IAAe,EAAA;IAAY,WAAA;EAAA;AAAA;EAChD,IAAA;EAAqB,WAAA;IAAe,IAAA;IAAc,WAAA;EAAA;AAAA;EAElD,IAAA;EACA,mBAAA;IACE,sBAAA;IACA,WAAA;EAAA;AAAA;EAGF,IAAA;EAAa,GAAA;IAAO,IAAA;IAAc,WAAA;EAAA;AAAA;EAElC,IAAA;EACA,aAAA;IAAiB,IAAA;IAAc,WAAA;EAAA;AAAA;;;AA6ErC;;;;;;UAlEiB,oBAAA;EAAA,SACN,MAAA;EAAA,SACA,IAAA,EAAM,cAAA;AAAA;;;;;;;iBASD,gBAAA,CACd,KAAA,YACC,KAAA,IAAS,oBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAqDC,eAAA;;;;;IAKT,EAAA;IACA,WAAA;EAAA,MACE,oBAAA;;;;;IAQF,IAAA;IACA,WAAA;EAAA,MACE,oBAAA;;;;;IAQF,oBAAA;IACA,WAAA;EAAA,MACE,oBAAA;;;;;IAcF,IAAA;IACA,WAAA;EAAA,MACE,oBAAA;;;;;IAQF,IAAA;IACA,WAAA;EAAA,MACE,oBAAA;AAAA;;;;;;;cAgBO,wBAAA;;AAyHb;;;;UAlHiB,mBAAA;EACf,WAAA,GAAc,cAAA;AAAA;AAAA,UAgBC,2BAAA;EAiG4B;;;;EA5F3C,KAAA;EA8FQ;;;;;;;;;;;;EAjFR,eAAA,GAAkB,mBAAA;EA8GA;;;;;AAgcpB;;;;EApiBE,SAAA;AAAA;AAAA,UAGQ,+BAAA;EACR,UAAA,EAAY,UAAA;EACZ,KAAA;EACA,SAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA+DW,oBAAA,YAAgC,YAAA;EAAA,QACnC,UAAA;EAAA,QACA,KAAA;EAAA,QACA,SAAA;;;;;;;WAQC,iBAAA;;;;;;;;WASA,kBAAA;cAEG,OAAA,EAAS,+BAAA;EAMd,GAAA,CACL,KAAA,EAAO,UAAA,EACP,OAAA,EAAS,eAAA,GACR,cAAA,CAAe,UAAA;EAAA,QAiBH,cAAA;;;;;;;;;UA+NP,UAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgNY,iBAAA,CACpB,OAAA,EAAS,2BAAA,GACR,OAAA,CAAQ,YAAA"}
1
+ {"version":3,"file":"supervisor-api.d.ts","names":[],"sources":["../../src/agents/supervisor-api.ts"],"mappings":";;;;;;;;AAmGA;;;;;;UAAiB,mBAAA,SAA4B,aAAA;EAC3C,MAAA;IAAU,cAAA,IAAkB,OAAA;EAAA;AAAA;;;;;;;;;;;KAiBlB,cAAA;EACN,IAAA;EAAqB,WAAA;IAAe,EAAA;IAAY,WAAA;EAAA;AAAA;EAChD,IAAA;EAAqB,WAAA;IAAe,IAAA;IAAc,WAAA;EAAA;AAAA;EAElD,IAAA;EACA,mBAAA;IACE,sBAAA;IACA,WAAA;EAAA;AAAA;EAGF,IAAA;EAAa,GAAA;IAAO,IAAA;IAAc,WAAA;EAAA;AAAA;EAElC,IAAA;EACA,aAAA;IAAiB,IAAA;IAAc,WAAA;EAAA;AAAA;;;AA6ErC;;;;;;UAlEiB,oBAAA;EAAA,SACN,MAAA;EAAA,SACA,IAAA,EAAM,cAAA;AAAA;;;;;;;iBASD,gBAAA,CACd,KAAA,YACC,KAAA,IAAS,oBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAqDC,eAAA;;;;;IAKT,EAAA;IACA,WAAA;EAAA,MACE,oBAAA;;;;;IAQF,IAAA;IACA,WAAA;EAAA,MACE,oBAAA;;;;;IAQF,oBAAA;IACA,WAAA;EAAA,MACE,oBAAA;;;;;IAcF,IAAA;IACA,WAAA;EAAA,MACE,oBAAA;;;;;IAQF,IAAA;IACA,WAAA;EAAA,MACE,oBAAA;AAAA;;;;;;;cAgBO,wBAAA;;AAyHb;;;;UAlHiB,mBAAA;EACf,WAAA,GAAc,cAAA;AAAA;AAAA,UAgBC,2BAAA;EAiG4B;;;;EA5F3C,KAAA;EA8FQ;;;;;;;;;;;;EAjFR,eAAA,GAAkB,mBAAA;EA8GA;;;;;AAgcpB;;;;EApiBE,SAAA;AAAA;AAAA,UAGQ,+BAAA;EACR,UAAA,EAAY,UAAA;EACZ,KAAA;EACA,SAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA+DW,oBAAA,YAAgC,YAAA;EAAA,QACnC,UAAA;EAAA,QACA,KAAA;EAAA,QACA,SAAA;;;;;;;WAQC,iBAAA;;;;;;;;WASA,kBAAA;cAEG,OAAA,EAAS,+BAAA;EAMd,GAAA,CACL,KAAA,EAAO,UAAA,EACP,OAAA,EAAS,eAAA,GACR,cAAA,CAAe,UAAA;EAAA,QAiBH,cAAA;;;;;;;;;UA+NP,UAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgNY,iBAAA,CACpB,OAAA,EAAS,2BAAA,GACR,OAAA,CAAQ,YAAA"}
@@ -1 +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\";\nimport { createWorkspaceClient } from \"../workspace-client\";\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 // The wrapper's client provides everything `WorkspaceClientLike` needs\n // (`apiClient.request` + `config.ensureResolved`) but its\n // `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 = createWorkspaceClient() 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
+ {"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\";\n\nimport {\n type ApiClientLike,\n type StreamBody,\n streamPath,\n} from \"../connectors/serving/client\";\nimport { createLogger } from \"../logging/logger\";\nimport { readSseEvents } from \"../stream\";\nimport { createWorkspaceClient } from \"../workspace-client\";\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 // The wrapper's client provides everything `WorkspaceClientLike` needs\n // (`apiClient.request` + `config.ensureResolved`) but its\n // `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 = createWorkspaceClient() 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 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/app/index.ts"],"mappings":";UAMU,WAAA;EACR,KAAA,GAAQ,MAAA;EACR,OAAA,EAAS,MAAA;AAAA;AAAA,UAGD,aAAA;EACR,QAAA,CAAS,QAAA,UAAkB,GAAA,EAAK,WAAA,GAAc,OAAA;EAC9C,OAAA,CAAQ,OAAA,UAAiB,GAAA,EAAK,WAAA,GAAc,OAAA;AAAA;AAAA,UAGpC,WAAA;EACR,KAAA;EACA,QAAA;AAAA;AAAA,cAWW,UAAA;EAAA,iBACM,WAAA;EAAA,iBACA,eAAA;cAGf,UAAA,WAMA,cAAA;EAAA,IASE,UAAA,CAAA;EAAA,IAIA,cAAA,CAAA;EAxC+C;;;;;EAAA,QAiD3C,YAAA;EAlDsC;;;;;;;EAAA,QA6DtC,YAAA;EAzDA;;;EAAA,QAyEA,eAAA;EAvEA;AAWV;;;;;;;EA+FQ,WAAA,CACJ,QAAA,UACA,GAAA,GAAM,WAAA,EACN,aAAA,GAAgB,aAAA,GACf,OAAA,CAAQ,WAAA;EAoHO;;;;;;;;;;;EAAA,QArCJ,eAAA;EA9JV;;;;;;;;EAgME,cAAA,CACJ,QAAA,UACA,GAAA,GAAM,WAAA,EACN,aAAA,GAAgB,aAAA,GACf,OAAA;EAtHe;;;;;;;;;;EAoIZ,qBAAA,CACJ,QAAA,UACA,GAAA,GAAM,WAAA,EACN,aAAA,GAAgB,aAAA,GACf,OAAA;EAAA,QASK,eAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/app/index.ts"],"mappings":";UAOU,WAAA;EACR,KAAA,GAAQ,MAAA;EACR,OAAA,EAAS,MAAA;AAAA;AAAA,UAGD,aAAA;EACR,QAAA,CAAS,QAAA,UAAkB,GAAA,EAAK,WAAA,GAAc,OAAA;EAC9C,OAAA,CAAQ,OAAA,UAAiB,GAAA,EAAK,WAAA,GAAc,OAAA;AAAA;AAAA,UAGpC,WAAA;EACR,KAAA;EACA,QAAA;AAAA;AAAA,cAWW,UAAA;EAAA,iBACM,WAAA;EAAA,iBACA,eAAA;cAGf,UAAA,WAMA,cAAA;EAAA,IASE,UAAA,CAAA;EAAA,IAIA,cAAA,CAAA;EAxC+C;;;;;EAAA,QAiD3C,YAAA;EAlDsC;;;;;;;EAAA,QA6DtC,YAAA;EAzDA;;;EAAA,QAyEA,eAAA;EAvEA;AAWV;;;;;;;EA+FQ,WAAA,CACJ,QAAA,UACA,GAAA,GAAM,WAAA,EACN,aAAA,GAAgB,aAAA,GACf,OAAA,CAAQ,WAAA;EAoHO;;;;;;;;;;;EAAA,QArCJ,eAAA;EA9JV;;;;;;;;EAgME,cAAA,CACJ,QAAA,UACA,GAAA,GAAM,WAAA,EACN,aAAA,GAAgB,aAAA,GACf,OAAA;EAtHe;;;;;;;;;;EAoIZ,qBAAA,CACJ,QAAA,UACA,GAAA,GAAM,WAAA,EACN,aAAA,GAAgB,aAAA,GACf,OAAA;EAAA,QASK,eAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/app/index.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createLogger } from \"../logging/logger\";\n\nconst logger = createLogger(\"app\");\n\ninterface RequestLike {\n query?: Record<string, any>;\n headers: Record<string, string | string[] | undefined>;\n}\n\ninterface DevFileReader {\n readFile(filePath: string, req: RequestLike): Promise<string>;\n readdir(dirPath: string, req: RequestLike): Promise<string[]>;\n}\n\ninterface QueryResult {\n query: string;\n isAsUser: boolean;\n}\n\n/**\n * Abstraction for filesystem operations that works in both dev and production modes\n */\ninterface FileSystemAdapter {\n readdir(dirPath: string): Promise<string[]>;\n readFile(filePath: string): Promise<string>;\n}\n\nexport class AppManager {\n private readonly _queriesDir: string;\n private readonly _metricViewsDir: string;\n\n constructor(\n queriesDir: string = path.resolve(process.cwd(), \"config/queries\"),\n // Metric-view declarations live in a sibling `config/metric-views/`\n // directory (next to `config/queries/` and `config/agents/`), NOT inside\n // the queries folder. Default it as a sibling of `queriesDir` so a\n // single-arg `new AppManager(dir)` (and every test that overrides only the\n // queries dir) still resolves the metric-views dir consistently.\n metricViewsDir: string = path.resolve(\n path.dirname(queriesDir),\n \"metric-views\",\n ),\n ) {\n this._queriesDir = queriesDir;\n this._metricViewsDir = metricViewsDir;\n }\n\n get queriesDir(): string {\n return this._queriesDir;\n }\n\n get metricViewsDir(): string {\n return this._metricViewsDir;\n }\n\n /**\n * Whether `req` is a dev-tunnel (`?dev`) request. Internal `?dev` predicate\n * shared by {@link createFsAdapter} (tunnel-vs-`fs` branch) and\n * {@link isNotFoundError} (dev's message-match not-found classification).\n */\n private isDevRequest(req?: RequestLike): boolean {\n return req?.query?.dev !== undefined;\n }\n\n /**\n * Validates that a file path is within the given base directory. The\n * `baseDir` is passed explicitly (rather than pinned to `queriesDir`) so the\n * same traversal guard protects reads from any config directory —\n * `config/queries/` for `.sql` files and `config/metric-views/` for the\n * metric-view definitions.\n */\n private validatePath(fileName: string, baseDir: string): string | null {\n const filePath = path.join(baseDir, fileName);\n const resolvedPath = path.resolve(filePath);\n const resolvedBaseDir = path.resolve(baseDir);\n\n if (!resolvedPath.startsWith(resolvedBaseDir)) {\n logger.error(\"Invalid config path: path traversal detected\");\n return null;\n }\n\n return resolvedPath;\n }\n\n /**\n * Creates a filesystem adapter based on dev mode or production mode\n */\n private createFsAdapter(\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): FileSystemAdapter {\n const isDevMode = this.isDevRequest(req);\n\n if (isDevMode && devFileReader && req) {\n // Dev mode: use WebSocket tunnel to read from local filesystem\n return {\n readdir: async (dirPath: string) => {\n const relativePath = path.relative(process.cwd(), dirPath);\n return devFileReader.readdir(relativePath, req);\n },\n readFile: async (filePath: string) => {\n const relativePath = path.relative(process.cwd(), filePath);\n return devFileReader.readFile(relativePath, req);\n },\n };\n }\n\n // Production mode: use server filesystem\n return {\n readdir: (dirPath: string) => fs.readdir(dirPath),\n readFile: (filePath: string) => fs.readFile(filePath, \"utf8\"),\n };\n }\n\n /**\n * Retrieves a query file by key from the queries directory\n * In dev mode with a request context, reads from local filesystem via WebSocket\n * @param queryKey - The query file name (without extension)\n * @param req - Optional request object to detect dev mode\n * @param devFileReader - Optional DevFileReader instance to read files from local filesystem\n * @returns The query content and execution mode (as user or as service principal)\n */\n async getAppQuery(\n queryKey: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<QueryResult | null> {\n // Security: Sanitize query key to prevent path traversal\n if (!queryKey || !/^[a-zA-Z0-9_-]+$/.test(queryKey)) {\n logger.error(\n \"Invalid query key format: %s. Only alphanumeric characters, underscores, and hyphens are allowed.\",\n queryKey,\n );\n return null;\n }\n\n // Create filesystem adapter for dev or production mode\n const fsAdapter = this.createFsAdapter(req, devFileReader);\n\n // Priority order: .obo.sql first (as user), then .sql (as service principal)\n const oboFileName = `${queryKey}.obo.sql`;\n const defaultFileName = `${queryKey}.sql`;\n\n // List directory to find which query file exists\n let files: string[];\n try {\n files = await fsAdapter.readdir(this.queriesDir);\n } catch (error) {\n logger.error(\n `Failed to read queries directory: ${(error as Error).message}`,\n );\n return null;\n }\n\n // Determine which query file to use\n let queryFileName: string | null = null;\n let isAsUser = false;\n\n if (files.includes(oboFileName)) {\n queryFileName = oboFileName;\n isAsUser = true;\n\n // Warn if both variants exist\n if (files.includes(defaultFileName)) {\n logger.warn(\n `Both ${oboFileName} and ${defaultFileName} found for query ${queryKey}. Using ${oboFileName}.`,\n );\n }\n } else if (files.includes(defaultFileName)) {\n queryFileName = defaultFileName;\n isAsUser = false;\n }\n\n if (!queryFileName) {\n logger.error(`Query file not found: ${queryKey}`);\n return null;\n }\n\n // Validate and resolve the file path\n const resolvedPath = this.validatePath(queryFileName, this._queriesDir);\n if (!resolvedPath) {\n return null;\n }\n\n // Read the query file\n try {\n const query = await fsAdapter.readFile(resolvedPath);\n return { query, isAsUser };\n } catch (error) {\n logger.error(`Failed to read query file: ${(error as Error).message}`);\n return null;\n }\n }\n\n /**\n * Read a single config file from a given base directory, dev-tunnel-aware.\n * Shared core behind {@link readConfigFile} (queries dir) and\n * {@link readMetricViewsConfig} (metric-views dir).\n *\n * @param baseDir - The config directory the file must resolve within.\n * @param fileName - File name (or relative path) within `baseDir`.\n * @param req - Optional request object to detect dev mode.\n * @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.\n * @returns The raw file contents, or `null` when the file is absent / the path is rejected.\n */\n private async readFileFromDir(\n baseDir: string,\n fileName: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<string | null> {\n // Traversal guard: refuse to read outside the base directory.\n const resolvedPath = this.validatePath(fileName, baseDir);\n if (!resolvedPath) {\n return null;\n }\n\n const fsAdapter = this.createFsAdapter(req, devFileReader);\n\n try {\n return await fsAdapter.readFile(resolvedPath);\n } catch (error) {\n if (this.isNotFoundError(error, req)) {\n return null;\n }\n // Any other error (permission / IO / tunnel) is fatal: propagate so the\n // caller can distinguish it from a dormant (absent) config.\n throw error;\n }\n }\n\n /**\n * Read a single config file from the queries directory, dev-tunnel-aware.\n *\n * @param fileName - File name (or relative path) within the queries directory.\n * @param req - Optional request object to detect dev mode.\n * @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.\n * @returns The raw file contents, or `null` when the file is absent / the path is rejected.\n */\n async readConfigFile(\n fileName: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<string | null> {\n return this.readFileFromDir(this._queriesDir, fileName, req, devFileReader);\n }\n\n /**\n * Read a single config file from the metric-views directory\n * (`config/metric-views/`), dev-tunnel-aware. Same absent-file/traversal\n * semantics as {@link readConfigFile}, but rooted at {@link metricViewsDir}.\n *\n * @param fileName - File name (or relative path) within the metric-views directory.\n * @param req - Optional request object to detect dev mode.\n * @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.\n * @returns The raw file contents, or `null` when the file is absent / the path is rejected.\n */\n async readMetricViewsConfig(\n fileName: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<string | null> {\n return this.readFileFromDir(\n this._metricViewsDir,\n fileName,\n req,\n devFileReader,\n );\n }\n\n private isNotFoundError(error: unknown, req?: RequestLike): boolean {\n if ((error as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n return true;\n }\n if (this.isDevRequest(req)) {\n const message = (error as Error)?.message ?? \"\";\n return /ENOENT|no such file/i.test(message);\n }\n return false;\n }\n}\n\nexport type { DevFileReader, RequestLike };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/app/index.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { createLogger } from \"../logging/logger\";\n\nconst logger = createLogger(\"app\");\n\ninterface RequestLike {\n query?: Record<string, any>;\n headers: Record<string, string | string[] | undefined>;\n}\n\ninterface DevFileReader {\n readFile(filePath: string, req: RequestLike): Promise<string>;\n readdir(dirPath: string, req: RequestLike): Promise<string[]>;\n}\n\ninterface QueryResult {\n query: string;\n isAsUser: boolean;\n}\n\n/**\n * Abstraction for filesystem operations that works in both dev and production modes\n */\ninterface FileSystemAdapter {\n readdir(dirPath: string): Promise<string[]>;\n readFile(filePath: string): Promise<string>;\n}\n\nexport class AppManager {\n private readonly _queriesDir: string;\n private readonly _metricViewsDir: string;\n\n constructor(\n queriesDir: string = path.resolve(process.cwd(), \"config/queries\"),\n // Metric-view declarations live in a sibling `config/metric-views/`\n // directory (next to `config/queries/` and `config/agents/`), NOT inside\n // the queries folder. Default it as a sibling of `queriesDir` so a\n // single-arg `new AppManager(dir)` (and every test that overrides only the\n // queries dir) still resolves the metric-views dir consistently.\n metricViewsDir: string = path.resolve(\n path.dirname(queriesDir),\n \"metric-views\",\n ),\n ) {\n this._queriesDir = queriesDir;\n this._metricViewsDir = metricViewsDir;\n }\n\n get queriesDir(): string {\n return this._queriesDir;\n }\n\n get metricViewsDir(): string {\n return this._metricViewsDir;\n }\n\n /**\n * Whether `req` is a dev-tunnel (`?dev`) request. Internal `?dev` predicate\n * shared by {@link createFsAdapter} (tunnel-vs-`fs` branch) and\n * {@link isNotFoundError} (dev's message-match not-found classification).\n */\n private isDevRequest(req?: RequestLike): boolean {\n return req?.query?.dev !== undefined;\n }\n\n /**\n * Validates that a file path is within the given base directory. The\n * `baseDir` is passed explicitly (rather than pinned to `queriesDir`) so the\n * same traversal guard protects reads from any config directory —\n * `config/queries/` for `.sql` files and `config/metric-views/` for the\n * metric-view definitions.\n */\n private validatePath(fileName: string, baseDir: string): string | null {\n const filePath = path.join(baseDir, fileName);\n const resolvedPath = path.resolve(filePath);\n const resolvedBaseDir = path.resolve(baseDir);\n\n if (!resolvedPath.startsWith(resolvedBaseDir)) {\n logger.error(\"Invalid config path: path traversal detected\");\n return null;\n }\n\n return resolvedPath;\n }\n\n /**\n * Creates a filesystem adapter based on dev mode or production mode\n */\n private createFsAdapter(\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): FileSystemAdapter {\n const isDevMode = this.isDevRequest(req);\n\n if (isDevMode && devFileReader && req) {\n // Dev mode: use WebSocket tunnel to read from local filesystem\n return {\n readdir: async (dirPath: string) => {\n const relativePath = path.relative(process.cwd(), dirPath);\n return devFileReader.readdir(relativePath, req);\n },\n readFile: async (filePath: string) => {\n const relativePath = path.relative(process.cwd(), filePath);\n return devFileReader.readFile(relativePath, req);\n },\n };\n }\n\n // Production mode: use server filesystem\n return {\n readdir: (dirPath: string) => fs.readdir(dirPath),\n readFile: (filePath: string) => fs.readFile(filePath, \"utf8\"),\n };\n }\n\n /**\n * Retrieves a query file by key from the queries directory\n * In dev mode with a request context, reads from local filesystem via WebSocket\n * @param queryKey - The query file name (without extension)\n * @param req - Optional request object to detect dev mode\n * @param devFileReader - Optional DevFileReader instance to read files from local filesystem\n * @returns The query content and execution mode (as user or as service principal)\n */\n async getAppQuery(\n queryKey: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<QueryResult | null> {\n // Security: Sanitize query key to prevent path traversal\n if (!queryKey || !/^[a-zA-Z0-9_-]+$/.test(queryKey)) {\n logger.error(\n \"Invalid query key format: %s. Only alphanumeric characters, underscores, and hyphens are allowed.\",\n queryKey,\n );\n return null;\n }\n\n // Create filesystem adapter for dev or production mode\n const fsAdapter = this.createFsAdapter(req, devFileReader);\n\n // Priority order: .obo.sql first (as user), then .sql (as service principal)\n const oboFileName = `${queryKey}.obo.sql`;\n const defaultFileName = `${queryKey}.sql`;\n\n // List directory to find which query file exists\n let files: string[];\n try {\n files = await fsAdapter.readdir(this.queriesDir);\n } catch (error) {\n logger.error(\n `Failed to read queries directory: ${(error as Error).message}`,\n );\n return null;\n }\n\n // Determine which query file to use\n let queryFileName: string | null = null;\n let isAsUser = false;\n\n if (files.includes(oboFileName)) {\n queryFileName = oboFileName;\n isAsUser = true;\n\n // Warn if both variants exist\n if (files.includes(defaultFileName)) {\n logger.warn(\n `Both ${oboFileName} and ${defaultFileName} found for query ${queryKey}. Using ${oboFileName}.`,\n );\n }\n } else if (files.includes(defaultFileName)) {\n queryFileName = defaultFileName;\n isAsUser = false;\n }\n\n if (!queryFileName) {\n logger.error(`Query file not found: ${queryKey}`);\n return null;\n }\n\n // Validate and resolve the file path\n const resolvedPath = this.validatePath(queryFileName, this._queriesDir);\n if (!resolvedPath) {\n return null;\n }\n\n // Read the query file\n try {\n const query = await fsAdapter.readFile(resolvedPath);\n return { query, isAsUser };\n } catch (error) {\n logger.error(`Failed to read query file: ${(error as Error).message}`);\n return null;\n }\n }\n\n /**\n * Read a single config file from a given base directory, dev-tunnel-aware.\n * Shared core behind {@link readConfigFile} (queries dir) and\n * {@link readMetricViewsConfig} (metric-views dir).\n *\n * @param baseDir - The config directory the file must resolve within.\n * @param fileName - File name (or relative path) within `baseDir`.\n * @param req - Optional request object to detect dev mode.\n * @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.\n * @returns The raw file contents, or `null` when the file is absent / the path is rejected.\n */\n private async readFileFromDir(\n baseDir: string,\n fileName: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<string | null> {\n // Traversal guard: refuse to read outside the base directory.\n const resolvedPath = this.validatePath(fileName, baseDir);\n if (!resolvedPath) {\n return null;\n }\n\n const fsAdapter = this.createFsAdapter(req, devFileReader);\n\n try {\n return await fsAdapter.readFile(resolvedPath);\n } catch (error) {\n if (this.isNotFoundError(error, req)) {\n return null;\n }\n // Any other error (permission / IO / tunnel) is fatal: propagate so the\n // caller can distinguish it from a dormant (absent) config.\n throw error;\n }\n }\n\n /**\n * Read a single config file from the queries directory, dev-tunnel-aware.\n *\n * @param fileName - File name (or relative path) within the queries directory.\n * @param req - Optional request object to detect dev mode.\n * @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.\n * @returns The raw file contents, or `null` when the file is absent / the path is rejected.\n */\n async readConfigFile(\n fileName: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<string | null> {\n return this.readFileFromDir(this._queriesDir, fileName, req, devFileReader);\n }\n\n /**\n * Read a single config file from the metric-views directory\n * (`config/metric-views/`), dev-tunnel-aware. Same absent-file/traversal\n * semantics as {@link readConfigFile}, but rooted at {@link metricViewsDir}.\n *\n * @param fileName - File name (or relative path) within the metric-views directory.\n * @param req - Optional request object to detect dev mode.\n * @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.\n * @returns The raw file contents, or `null` when the file is absent / the path is rejected.\n */\n async readMetricViewsConfig(\n fileName: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<string | null> {\n return this.readFileFromDir(\n this._metricViewsDir,\n fileName,\n req,\n devFileReader,\n );\n }\n\n private isNotFoundError(error: unknown, req?: RequestLike): boolean {\n if ((error as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n return true;\n }\n if (this.isDevRequest(req)) {\n const message = (error as Error)?.message ?? \"\";\n return /ENOENT|no such file/i.test(message);\n }\n return false;\n }\n}\n\nexport type { DevFileReader, RequestLike };\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.61.1";
3
+ var version = "0.62.0";
4
4
 
5
5
  //#endregion
6
6
  export { name, version };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/cache/index.ts"],"mappings":";;;;;;;AAoDA;;;;;;;;;;;cAAa,YAAA;EAAA,wBACa,uBAAA;EAAA,wBACA,qBAAA;EAAA,iBACP,IAAA;EAAA,eACF,QAAA;EAAA,eACA,WAAA;EAAA,QAEP,OAAA;EAAA,QACA,MAAA;EAAA,QACA,gBAAA;EAAA,QACA,iBAAA;EAAA,QACA,kBAAA;EAAA,QAEA,SAAA;EAAA,QACA,gBAAA;EAAA,QAKD,WAAA,CAAA;EAlBiB;;;;;;EAAA,OA+CjB,eAAA,CAAA,GAAmB,YAAA;EAvClB;;;;;;;EAAA,OAyDK,WAAA,CACX,UAAA,GAAa,OAAA,CAAQ,WAAA,IACpB,OAAA,CAAQ,YAAA;EAFE;;;;;;;;;;;;EAAA,eA+BQ,MAAA;EA2EyB;;;;;;;;;;;;;;;;;;;;EAFxC,YAAA,GAAA,CACJ,GAAA,gCACA,EAAA,GAAK,YAAA,GAAe,WAAA,KAAgB,OAAA,CAAQ,CAAA,GAC5C,OAAA,UACA,OAAA;IAAY,GAAA;IAAc,YAAA,GAAe,WAAA;EAAA,IACxC,OAAA,CAAQ,CAAA;EAkRR;;;;;;;EAAA,QA7HK,iBAAA;EA6JgB;;;;;EAhGlB,GAAA,GAAA,CAAO,GAAA,WAAc,OAAA,CAAQ,CAAA;EA4H7B;;;;;;;EAAA,QA3GQ,QAAA;;UAYN,YAAA;;;;;;;;EA+BF,GAAA,GAAA,CACJ,GAAA,UACA,KAAA,EAAO,CAAA,EACP,OAAA;IAAY,GAAA;EAAA,IACX,OAAA;;;;;;EAaG,MAAA,CAAO,GAAA,WAAc,OAAA;;EAMrB,KAAA,CAAA,GAAS,OAAA;;;;;;EAaT,GAAA,CAAI,GAAA,WAAc,OAAA;;;;;;;EAaxB,WAAA,CAAY,KAAA,gCAAqC,OAAA;;EAO3C,KAAA,CAAA,GAAS,OAAA;;;;;EAQT,gBAAA,CAAA,GAAoB,OAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/cache/index.ts"],"mappings":";;;;;;;AAsDA;;;;;;;;;;;cAAa,YAAA;EAAA,wBACa,uBAAA;EAAA,wBACA,qBAAA;EAAA,iBACP,IAAA;EAAA,eACF,QAAA;EAAA,eACA,WAAA;EAAA,QAEP,OAAA;EAAA,QACA,MAAA;EAAA,QACA,gBAAA;EAAA,QACA,iBAAA;EAAA,QACA,kBAAA;EAAA,QAEA,SAAA;EAAA,QACA,gBAAA;EAAA,QAKD,WAAA,CAAA;EAlBiB;;;;;;EAAA,OA+CjB,eAAA,CAAA,GAAmB,YAAA;EAvClB;;;;;;;EAAA,OAyDK,WAAA,CACX,UAAA,GAAa,OAAA,CAAQ,WAAA,IACpB,OAAA,CAAQ,YAAA;EAFE;;;;;;;;;;;;EAAA,eA+BQ,MAAA;EA2EyB;;;;;;;;;;;;;;;;;;;;EAFxC,YAAA,GAAA,CACJ,GAAA,gCACA,EAAA,GAAK,YAAA,GAAe,WAAA,KAAgB,OAAA,CAAQ,CAAA,GAC5C,OAAA,UACA,OAAA;IAAY,GAAA;IAAc,YAAA,GAAe,WAAA;EAAA,IACxC,OAAA,CAAQ,CAAA;EAkRR;;;;;;;EAAA,QA7HK,iBAAA;EA6JgB;;;;;EAhGlB,GAAA,GAAA,CAAO,GAAA,WAAc,OAAA,CAAQ,CAAA;EA4H7B;;;;;;;EAAA,QA3GQ,QAAA;;UAYN,YAAA;;;;;;;;EA+BF,GAAA,GAAA,CACJ,GAAA,UACA,KAAA,EAAO,CAAA,EACP,OAAA;IAAY,GAAA;EAAA,IACX,OAAA;;;;;;EAaG,MAAA,CAAO,GAAA,WAAc,OAAA;;EAMrB,KAAA,CAAA,GAAS,OAAA;;;;;;EAaT,GAAA,CAAI,GAAA,WAAc,OAAA;;;;;;;EAaxB,WAAA,CAAY,KAAA,gCAAqC,OAAA;;EAO3C,KAAA,CAAA,GAAS,OAAA;;;;;EAQT,gBAAA,CAAA,GAAoB,OAAA;AAAA"}