@databricks/appkit 0.30.1 → 0.32.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 (154) hide show
  1. package/CLAUDE.md +1 -0
  2. package/NOTICE.md +1 -0
  3. package/dist/appkit/package.js +1 -1
  4. package/dist/beta.d.ts +14 -1
  5. package/dist/beta.js +12 -1
  6. package/dist/connectors/index.js +3 -0
  7. package/dist/connectors/mcp/client.d.ts +60 -0
  8. package/dist/connectors/mcp/client.d.ts.map +1 -0
  9. package/dist/connectors/mcp/client.js +197 -0
  10. package/dist/connectors/mcp/client.js.map +1 -0
  11. package/dist/connectors/mcp/host-policy.d.ts +51 -0
  12. package/dist/connectors/mcp/host-policy.d.ts.map +1 -0
  13. package/dist/connectors/mcp/host-policy.js +168 -0
  14. package/dist/connectors/mcp/host-policy.js.map +1 -0
  15. package/dist/connectors/mcp/index.d.ts +3 -0
  16. package/dist/connectors/mcp/index.js +4 -0
  17. package/dist/connectors/mcp/types.d.ts +16 -0
  18. package/dist/connectors/mcp/types.d.ts.map +1 -0
  19. package/dist/context/index.js +1 -1
  20. package/dist/core/agent/build-toolkit.d.ts +2 -0
  21. package/dist/core/agent/build-toolkit.js +50 -0
  22. package/dist/core/agent/build-toolkit.js.map +1 -0
  23. package/dist/core/agent/consume-adapter-stream.js +33 -0
  24. package/dist/core/agent/consume-adapter-stream.js.map +1 -0
  25. package/dist/core/agent/create-agent.d.ts +27 -0
  26. package/dist/core/agent/create-agent.d.ts.map +1 -0
  27. package/dist/core/agent/create-agent.js +50 -0
  28. package/dist/core/agent/create-agent.js.map +1 -0
  29. package/dist/core/agent/load-agents.d.ts +67 -0
  30. package/dist/core/agent/load-agents.d.ts.map +1 -0
  31. package/dist/core/agent/load-agents.js +228 -0
  32. package/dist/core/agent/load-agents.js.map +1 -0
  33. package/dist/core/agent/normalize-result.js +39 -0
  34. package/dist/core/agent/normalize-result.js.map +1 -0
  35. package/dist/core/agent/run-agent.d.ts +34 -0
  36. package/dist/core/agent/run-agent.d.ts.map +1 -0
  37. package/dist/core/agent/run-agent.js +146 -0
  38. package/dist/core/agent/run-agent.js.map +1 -0
  39. package/dist/core/agent/system-prompt.js +38 -0
  40. package/dist/core/agent/system-prompt.js.map +1 -0
  41. package/dist/core/agent/tools/define-tool.d.ts +54 -0
  42. package/dist/core/agent/tools/define-tool.d.ts.map +1 -0
  43. package/dist/core/agent/tools/define-tool.js +50 -0
  44. package/dist/core/agent/tools/define-tool.js.map +1 -0
  45. package/dist/core/agent/tools/function-tool.d.ts +27 -0
  46. package/dist/core/agent/tools/function-tool.d.ts.map +1 -0
  47. package/dist/core/agent/tools/function-tool.js +21 -0
  48. package/dist/core/agent/tools/function-tool.js.map +1 -0
  49. package/dist/core/agent/tools/hosted-tools.d.ts +47 -0
  50. package/dist/core/agent/tools/hosted-tools.d.ts.map +1 -0
  51. package/dist/core/agent/tools/hosted-tools.js +67 -0
  52. package/dist/core/agent/tools/hosted-tools.js.map +1 -0
  53. package/dist/core/agent/tools/index.d.ts +5 -0
  54. package/dist/core/agent/tools/index.js +7 -0
  55. package/dist/core/agent/tools/json-schema.js +24 -0
  56. package/dist/core/agent/tools/json-schema.js.map +1 -0
  57. package/dist/core/agent/tools/sql-policy.js +256 -0
  58. package/dist/core/agent/tools/sql-policy.js.map +1 -0
  59. package/dist/core/agent/tools/tool.d.ts +34 -0
  60. package/dist/core/agent/tools/tool.d.ts.map +1 -0
  61. package/dist/core/agent/tools/tool.js +41 -0
  62. package/dist/core/agent/tools/tool.js.map +1 -0
  63. package/dist/core/agent/types.d.ts +214 -0
  64. package/dist/core/agent/types.d.ts.map +1 -0
  65. package/dist/core/agent/types.js +12 -0
  66. package/dist/core/agent/types.js.map +1 -0
  67. package/dist/core/appkit.d.ts +1 -0
  68. package/dist/core/appkit.d.ts.map +1 -1
  69. package/dist/core/appkit.js +31 -4
  70. package/dist/core/appkit.js.map +1 -1
  71. package/dist/core/plugin-context.d.ts +133 -0
  72. package/dist/core/plugin-context.d.ts.map +1 -0
  73. package/dist/core/plugin-context.js +220 -0
  74. package/dist/core/plugin-context.js.map +1 -0
  75. package/dist/index.d.ts +11 -11
  76. package/dist/internal-telemetry/appkit-log.js +19 -0
  77. package/dist/internal-telemetry/appkit-log.js.map +1 -0
  78. package/dist/internal-telemetry/config.js +15 -0
  79. package/dist/internal-telemetry/config.js.map +1 -0
  80. package/dist/internal-telemetry/index.js +4 -0
  81. package/dist/internal-telemetry/reporter.js +132 -0
  82. package/dist/internal-telemetry/reporter.js.map +1 -0
  83. package/dist/plugin/index.d.ts +1 -1
  84. package/dist/plugin/plugin.d.ts +18 -3
  85. package/dist/plugin/plugin.d.ts.map +1 -1
  86. package/dist/plugin/plugin.js +26 -2
  87. package/dist/plugin/plugin.js.map +1 -1
  88. package/dist/plugin/to-plugin.d.ts +15 -4
  89. package/dist/plugin/to-plugin.d.ts.map +1 -1
  90. package/dist/plugin/to-plugin.js +14 -4
  91. package/dist/plugin/to-plugin.js.map +1 -1
  92. package/dist/plugins/agents/agents.d.ts +4 -0
  93. package/dist/plugins/agents/agents.js +882 -0
  94. package/dist/plugins/agents/agents.js.map +1 -0
  95. package/dist/plugins/agents/defaults.js +13 -0
  96. package/dist/plugins/agents/defaults.js.map +1 -0
  97. package/dist/plugins/agents/event-channel.js +64 -0
  98. package/dist/plugins/agents/event-channel.js.map +1 -0
  99. package/dist/plugins/agents/event-translator.js +224 -0
  100. package/dist/plugins/agents/event-translator.js.map +1 -0
  101. package/dist/plugins/agents/index.d.ts +4 -0
  102. package/dist/plugins/agents/index.js +6 -0
  103. package/dist/plugins/agents/manifest.js +27 -0
  104. package/dist/plugins/agents/manifest.js.map +1 -0
  105. package/dist/plugins/agents/schemas.js +51 -0
  106. package/dist/plugins/agents/schemas.js.map +1 -0
  107. package/dist/plugins/agents/thread-store.js +58 -0
  108. package/dist/plugins/agents/thread-store.js.map +1 -0
  109. package/dist/plugins/agents/tool-approval-gate.js +75 -0
  110. package/dist/plugins/agents/tool-approval-gate.js.map +1 -0
  111. package/dist/plugins/analytics/analytics.d.ts +17 -2
  112. package/dist/plugins/analytics/analytics.d.ts.map +1 -1
  113. package/dist/plugins/analytics/analytics.js +33 -0
  114. package/dist/plugins/analytics/analytics.js.map +1 -1
  115. package/dist/plugins/files/plugin.d.ts +22 -3
  116. package/dist/plugins/files/plugin.d.ts.map +1 -1
  117. package/dist/plugins/files/plugin.js +102 -2
  118. package/dist/plugins/files/plugin.js.map +1 -1
  119. package/dist/plugins/genie/genie.d.ts +15 -2
  120. package/dist/plugins/genie/genie.d.ts.map +1 -1
  121. package/dist/plugins/genie/genie.js +45 -0
  122. package/dist/plugins/genie/genie.js.map +1 -1
  123. package/dist/plugins/jobs/plugin.d.ts +2 -1
  124. package/dist/plugins/jobs/plugin.d.ts.map +1 -1
  125. package/dist/plugins/jobs/plugin.js +1 -1
  126. package/dist/plugins/lakebase/index.d.ts +2 -2
  127. package/dist/plugins/lakebase/index.js +1 -1
  128. package/dist/plugins/lakebase/lakebase.d.ts +33 -4
  129. package/dist/plugins/lakebase/lakebase.d.ts.map +1 -1
  130. package/dist/plugins/lakebase/lakebase.js +77 -5
  131. package/dist/plugins/lakebase/lakebase.js.map +1 -1
  132. package/dist/plugins/lakebase/types.d.ts +38 -1
  133. package/dist/plugins/lakebase/types.d.ts.map +1 -1
  134. package/dist/plugins/server/index.d.ts +20 -1
  135. package/dist/plugins/server/index.d.ts.map +1 -1
  136. package/dist/plugins/server/index.js +66 -7
  137. package/dist/plugins/server/index.js.map +1 -1
  138. package/dist/plugins/server/types.d.ts +0 -3
  139. package/dist/plugins/server/types.d.ts.map +1 -1
  140. package/dist/plugins/serving/serving.d.ts +2 -1
  141. package/dist/plugins/serving/serving.d.ts.map +1 -1
  142. package/dist/registry/manifest-loader.d.ts +2 -2
  143. package/dist/registry/manifest-loader.d.ts.map +1 -1
  144. package/dist/shared/src/agent.d.ts +63 -1
  145. package/dist/shared/src/agent.d.ts.map +1 -1
  146. package/dist/shared/src/index.d.ts +1 -1
  147. package/dist/shared/src/plugin.d.ts +8 -0
  148. package/dist/shared/src/plugin.d.ts.map +1 -1
  149. package/docs/api/appkit/Class.Plugin.md +65 -23
  150. package/docs/api/appkit/Function.createApp.md +10 -8
  151. package/docs/privacy.md +41 -0
  152. package/llms.txt +1 -0
  153. package/package.json +5 -2
  154. package/sbom.cdx.json +1 -1
@@ -0,0 +1,58 @@
1
+ import { randomUUID } from "node:crypto";
2
+
3
+ //#region src/plugins/agents/thread-store.ts
4
+ /**
5
+ * In-memory thread store backed by a nested Map.
6
+ *
7
+ * Outer key: userId, inner key: threadId. Thread history is retained for the
8
+ * lifetime of the process with no eviction, caps, or TTL — a chatty user will
9
+ * grow the in-memory footprint monotonically, and the server loses every
10
+ * thread on restart. **This implementation is intended for local development
11
+ * and single-process demos only.**
12
+ *
13
+ * For any real deployment, pass a persistent `ThreadStore` to `agents({ ... })`
14
+ * (e.g. a Lakebase- or Postgres-backed implementation). A bounded
15
+ * `InMemoryThreadStore` with eviction policies is tracked as a follow-up.
16
+ */
17
+ var InMemoryThreadStore = class {
18
+ store = /* @__PURE__ */ new Map();
19
+ async create(userId) {
20
+ const now = /* @__PURE__ */ new Date();
21
+ const thread = {
22
+ id: randomUUID(),
23
+ userId,
24
+ messages: [],
25
+ createdAt: now,
26
+ updatedAt: now
27
+ };
28
+ this.userMap(userId).set(thread.id, thread);
29
+ return thread;
30
+ }
31
+ async get(threadId, userId) {
32
+ return this.userMap(userId).get(threadId) ?? null;
33
+ }
34
+ async list(userId) {
35
+ return Array.from(this.userMap(userId).values()).sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
36
+ }
37
+ async addMessage(threadId, userId, message) {
38
+ const thread = this.userMap(userId).get(threadId);
39
+ if (!thread) throw new Error(`Thread ${threadId} not found`);
40
+ thread.messages.push(message);
41
+ thread.updatedAt = /* @__PURE__ */ new Date();
42
+ }
43
+ async delete(threadId, userId) {
44
+ return this.userMap(userId).delete(threadId);
45
+ }
46
+ userMap(userId) {
47
+ let map = this.store.get(userId);
48
+ if (!map) {
49
+ map = /* @__PURE__ */ new Map();
50
+ this.store.set(userId, map);
51
+ }
52
+ return map;
53
+ }
54
+ };
55
+
56
+ //#endregion
57
+ export { InMemoryThreadStore };
58
+ //# sourceMappingURL=thread-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-store.js","names":[],"sources":["../../../src/plugins/agents/thread-store.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { Message, Thread, ThreadStore } from \"shared\";\n\n/**\n * In-memory thread store backed by a nested Map.\n *\n * Outer key: userId, inner key: threadId. Thread history is retained for the\n * lifetime of the process with no eviction, caps, or TTL — a chatty user will\n * grow the in-memory footprint monotonically, and the server loses every\n * thread on restart. **This implementation is intended for local development\n * and single-process demos only.**\n *\n * For any real deployment, pass a persistent `ThreadStore` to `agents({ ... })`\n * (e.g. a Lakebase- or Postgres-backed implementation). A bounded\n * `InMemoryThreadStore` with eviction policies is tracked as a follow-up.\n */\nexport class InMemoryThreadStore implements ThreadStore {\n private store = new Map<string, Map<string, Thread>>();\n\n async create(userId: string): Promise<Thread> {\n const now = new Date();\n const thread: Thread = {\n id: randomUUID(),\n userId,\n messages: [],\n createdAt: now,\n updatedAt: now,\n };\n this.userMap(userId).set(thread.id, thread);\n return thread;\n }\n\n async get(threadId: string, userId: string): Promise<Thread | null> {\n return this.userMap(userId).get(threadId) ?? null;\n }\n\n async list(userId: string): Promise<Thread[]> {\n return Array.from(this.userMap(userId).values()).sort(\n (a, b) => b.updatedAt.getTime() - a.updatedAt.getTime(),\n );\n }\n\n async addMessage(\n threadId: string,\n userId: string,\n message: Message,\n ): Promise<void> {\n const thread = this.userMap(userId).get(threadId);\n if (!thread) throw new Error(`Thread ${threadId} not found`);\n thread.messages.push(message);\n thread.updatedAt = new Date();\n }\n\n async delete(threadId: string, userId: string): Promise<boolean> {\n return this.userMap(userId).delete(threadId);\n }\n\n private userMap(userId: string): Map<string, Thread> {\n let map = this.store.get(userId);\n if (!map) {\n map = new Map();\n this.store.set(userId, map);\n }\n return map;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,IAAa,sBAAb,MAAwD;CACtD,AAAQ,wBAAQ,IAAI,KAAkC;CAEtD,MAAM,OAAO,QAAiC;EAC5C,MAAM,sBAAM,IAAI,MAAM;EACtB,MAAM,SAAiB;GACrB,IAAI,YAAY;GAChB;GACA,UAAU,EAAE;GACZ,WAAW;GACX,WAAW;GACZ;AACD,OAAK,QAAQ,OAAO,CAAC,IAAI,OAAO,IAAI,OAAO;AAC3C,SAAO;;CAGT,MAAM,IAAI,UAAkB,QAAwC;AAClE,SAAO,KAAK,QAAQ,OAAO,CAAC,IAAI,SAAS,IAAI;;CAG/C,MAAM,KAAK,QAAmC;AAC5C,SAAO,MAAM,KAAK,KAAK,QAAQ,OAAO,CAAC,QAAQ,CAAC,CAAC,MAC9C,GAAG,MAAM,EAAE,UAAU,SAAS,GAAG,EAAE,UAAU,SAAS,CACxD;;CAGH,MAAM,WACJ,UACA,QACA,SACe;EACf,MAAM,SAAS,KAAK,QAAQ,OAAO,CAAC,IAAI,SAAS;AACjD,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,UAAU,SAAS,YAAY;AAC5D,SAAO,SAAS,KAAK,QAAQ;AAC7B,SAAO,4BAAY,IAAI,MAAM;;CAG/B,MAAM,OAAO,UAAkB,QAAkC;AAC/D,SAAO,KAAK,QAAQ,OAAO,CAAC,OAAO,SAAS;;CAG9C,AAAQ,QAAQ,QAAqC;EACnD,IAAI,MAAM,KAAK,MAAM,IAAI,OAAO;AAChC,MAAI,CAAC,KAAK;AACR,yBAAM,IAAI,KAAK;AACf,QAAK,MAAM,IAAI,QAAQ,IAAI;;AAE7B,SAAO"}
@@ -0,0 +1,75 @@
1
+ //#region src/plugins/agents/tool-approval-gate.ts
2
+ var ToolApprovalGate = class {
3
+ pending = /* @__PURE__ */ new Map();
4
+ /**
5
+ * Register a pending approval and return a promise that resolves with the
6
+ * user's decision or with `"deny"` when the timeout elapses. The returned
7
+ * promise never rejects.
8
+ */
9
+ wait(args) {
10
+ const { approvalId, streamId, userId, timeoutMs } = args;
11
+ return new Promise((resolve) => {
12
+ const timeout = setTimeout(() => {
13
+ if (this.pending.delete(approvalId)) resolve("deny");
14
+ }, timeoutMs);
15
+ this.pending.set(approvalId, {
16
+ resolve,
17
+ userId,
18
+ streamId,
19
+ timeout
20
+ });
21
+ });
22
+ }
23
+ /**
24
+ * Settle an approval with a user decision. Returns:
25
+ * - `{ ok: true }` if the pending record existed, the userId matched, and
26
+ * the promise was resolved.
27
+ * - `{ ok: false, reason: "unknown" }` if no pending record matches the id.
28
+ * - `{ ok: false, reason: "forbidden" }` if the userId does not match the
29
+ * user who initiated the stream.
30
+ */
31
+ submit(args) {
32
+ const { approvalId, userId, decision } = args;
33
+ const p = this.pending.get(approvalId);
34
+ if (!p) return {
35
+ ok: false,
36
+ reason: "unknown"
37
+ };
38
+ if (p.userId !== userId) return {
39
+ ok: false,
40
+ reason: "forbidden"
41
+ };
42
+ clearTimeout(p.timeout);
43
+ this.pending.delete(approvalId);
44
+ p.resolve(decision);
45
+ return { ok: true };
46
+ }
47
+ /**
48
+ * Cancel all pending gates for a specific stream (e.g., when the user
49
+ * cancels the stream). Each gate resolves with `"deny"` so the adapter
50
+ * unwinds cleanly.
51
+ */
52
+ abortStream(streamId) {
53
+ for (const [id, p] of this.pending) if (p.streamId === streamId) {
54
+ clearTimeout(p.timeout);
55
+ this.pending.delete(id);
56
+ p.resolve("deny");
57
+ }
58
+ }
59
+ /** Cancel every pending gate. Used at plugin shutdown. */
60
+ abortAll() {
61
+ for (const [id, p] of this.pending) {
62
+ clearTimeout(p.timeout);
63
+ this.pending.delete(id);
64
+ p.resolve("deny");
65
+ }
66
+ }
67
+ /** Number of pending approvals (test/diagnostic helper). */
68
+ get size() {
69
+ return this.pending.size;
70
+ }
71
+ };
72
+
73
+ //#endregion
74
+ export { ToolApprovalGate };
75
+ //# sourceMappingURL=tool-approval-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-approval-gate.js","names":[],"sources":["../../../src/plugins/agents/tool-approval-gate.ts"],"sourcesContent":["/**\n * Server-side state for the human-in-the-loop approval gate on\n * `destructive: true` agent tool calls.\n *\n * Lifecycle:\n *\n * 1. `wait(...)` is called from inside `executeTool` when a destructive tool\n * is about to execute. A `Pending` record is registered and a timer is\n * scheduled for auto-deny. The returned promise is what blocks the\n * adapter until the decision arrives.\n * 2. The client receives an `appkit.approval_pending` SSE event carrying the\n * `approvalId` + `streamId` and posts a decision to `POST /chat/approve`.\n * The route calls {@link ToolApprovalGate.submit} which resolves the\n * pending promise and clears the timer.\n * 3. If no submit arrives within `timeoutMs`, the timer fires and the\n * promise resolves with `\"deny\"`.\n *\n * Security invariants:\n *\n * - `submit` verifies that the decider's user id matches the user who\n * initiated the stream (set by `wait`). Mismatches are rejected without\n * resolving the pending promise — this prevents a second user from\n * approving (or denying) another user's destructive action.\n * - `abort(streamId)` cancels every pending gate for a stream and denies\n * each one. Used when the enclosing stream is cancelled or the plugin is\n * shutting down.\n */\ntype ApprovalDecision = \"approve\" | \"deny\";\n\ninterface Pending {\n resolve: (decision: ApprovalDecision) => void;\n userId: string;\n streamId: string;\n timeout: ReturnType<typeof setTimeout>;\n}\n\ntype ApprovalSubmitResult =\n | { ok: true }\n | { ok: false; reason: \"unknown\" | \"forbidden\" };\n\nexport class ToolApprovalGate {\n private pending = new Map<string, Pending>();\n\n /**\n * Register a pending approval and return a promise that resolves with the\n * user's decision or with `\"deny\"` when the timeout elapses. The returned\n * promise never rejects.\n */\n wait(args: {\n approvalId: string;\n streamId: string;\n userId: string;\n timeoutMs: number;\n }): Promise<ApprovalDecision> {\n const { approvalId, streamId, userId, timeoutMs } = args;\n return new Promise<ApprovalDecision>((resolve) => {\n const timeout = setTimeout(() => {\n if (this.pending.delete(approvalId)) {\n resolve(\"deny\");\n }\n }, timeoutMs);\n this.pending.set(approvalId, {\n resolve,\n userId,\n streamId,\n timeout,\n });\n });\n }\n\n /**\n * Settle an approval with a user decision. Returns:\n * - `{ ok: true }` if the pending record existed, the userId matched, and\n * the promise was resolved.\n * - `{ ok: false, reason: \"unknown\" }` if no pending record matches the id.\n * - `{ ok: false, reason: \"forbidden\" }` if the userId does not match the\n * user who initiated the stream.\n */\n submit(args: {\n approvalId: string;\n userId: string;\n decision: ApprovalDecision;\n }): ApprovalSubmitResult {\n const { approvalId, userId, decision } = args;\n const p = this.pending.get(approvalId);\n if (!p) return { ok: false, reason: \"unknown\" };\n if (p.userId !== userId) return { ok: false, reason: \"forbidden\" };\n clearTimeout(p.timeout);\n this.pending.delete(approvalId);\n p.resolve(decision);\n return { ok: true };\n }\n\n /**\n * Cancel all pending gates for a specific stream (e.g., when the user\n * cancels the stream). Each gate resolves with `\"deny\"` so the adapter\n * unwinds cleanly.\n */\n abortStream(streamId: string): void {\n for (const [id, p] of this.pending) {\n if (p.streamId === streamId) {\n clearTimeout(p.timeout);\n this.pending.delete(id);\n p.resolve(\"deny\");\n }\n }\n }\n\n /** Cancel every pending gate. Used at plugin shutdown. */\n abortAll(): void {\n for (const [id, p] of this.pending) {\n clearTimeout(p.timeout);\n this.pending.delete(id);\n p.resolve(\"deny\");\n }\n }\n\n /** Number of pending approvals (test/diagnostic helper). */\n get size(): number {\n return this.pending.size;\n }\n}\n"],"mappings":";AAwCA,IAAa,mBAAb,MAA8B;CAC5B,AAAQ,0BAAU,IAAI,KAAsB;;;;;;CAO5C,KAAK,MAKyB;EAC5B,MAAM,EAAE,YAAY,UAAU,QAAQ,cAAc;AACpD,SAAO,IAAI,SAA2B,YAAY;GAChD,MAAM,UAAU,iBAAiB;AAC/B,QAAI,KAAK,QAAQ,OAAO,WAAW,CACjC,SAAQ,OAAO;MAEhB,UAAU;AACb,QAAK,QAAQ,IAAI,YAAY;IAC3B;IACA;IACA;IACA;IACD,CAAC;IACF;;;;;;;;;;CAWJ,OAAO,MAIkB;EACvB,MAAM,EAAE,YAAY,QAAQ,aAAa;EACzC,MAAM,IAAI,KAAK,QAAQ,IAAI,WAAW;AACtC,MAAI,CAAC,EAAG,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAW;AAC/C,MAAI,EAAE,WAAW,OAAQ,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAa;AAClE,eAAa,EAAE,QAAQ;AACvB,OAAK,QAAQ,OAAO,WAAW;AAC/B,IAAE,QAAQ,SAAS;AACnB,SAAO,EAAE,IAAI,MAAM;;;;;;;CAQrB,YAAY,UAAwB;AAClC,OAAK,MAAM,CAAC,IAAI,MAAM,KAAK,QACzB,KAAI,EAAE,aAAa,UAAU;AAC3B,gBAAa,EAAE,QAAQ;AACvB,QAAK,QAAQ,OAAO,GAAG;AACvB,KAAE,QAAQ,OAAO;;;;CAMvB,WAAiB;AACf,OAAK,MAAM,CAAC,IAAI,MAAM,KAAK,SAAS;AAClC,gBAAa,EAAE,QAAQ;AACvB,QAAK,QAAQ,OAAO,GAAG;AACvB,KAAE,QAAQ,OAAO;;;;CAKrB,IAAI,OAAe;AACjB,SAAO,KAAK,QAAQ"}
@@ -1,16 +1,20 @@
1
+ import { AgentToolDefinition, ToolProvider } from "../../shared/src/agent.js";
1
2
  import { IAppRouter, ToPlugin } from "../../shared/src/plugin.js";
2
3
  import { SQLTypeMarker } from "../../shared/src/sql/types.js";
3
4
  import "../../shared/src/index.js";
5
+ import { ToolkitEntry, ToolkitOptions } from "../../core/agent/types.js";
4
6
  import { Plugin } from "../../plugin/plugin.js";
7
+ import { NamedPluginFactory } from "../../plugin/to-plugin.js";
5
8
  import "../../plugin/index.js";
6
9
  import { PluginManifest } from "../../registry/types.js";
7
10
  import "../../registry/index.js";
11
+ import "../agents/index.js";
8
12
  import { IAnalyticsConfig } from "./types.js";
9
13
  import { WorkspaceClient } from "@databricks/sdk-experimental";
10
14
  import express from "express";
11
15
 
12
16
  //#region src/plugins/analytics/analytics.d.ts
13
- declare class AnalyticsPlugin extends Plugin {
17
+ declare class AnalyticsPlugin extends Plugin implements ToolProvider {
14
18
  /** Plugin manifest declaring metadata and resource requirements */
15
19
  static manifest: PluginManifest<"analytics">;
16
20
  protected static description: string;
@@ -50,6 +54,17 @@ declare class AnalyticsPlugin extends Plugin {
50
54
  */
51
55
  protected getArrowData(workspaceClient: WorkspaceClient, jobId: string, signal?: AbortSignal): Promise<ReturnType<typeof this.SQLClient.getArrowData>>;
52
56
  shutdown(): Promise<void>;
57
+ private tools;
58
+ getAgentTools(): AgentToolDefinition[];
59
+ executeAgentTool(name: string, args: unknown, signal?: AbortSignal): Promise<unknown>;
60
+ /**
61
+ * Returns the plugin's tools as a keyed record of `ToolkitEntry` markers.
62
+ * Called by the agents plugin (via `resolveToolkitFromProvider`) to spread
63
+ * a filtered, renamed view of the plugin's tools into an agent's tool
64
+ * index. Most callers should go through `fromPlugin(analytics, opts)` at
65
+ * module scope instead of reaching for this directly.
66
+ */
67
+ toolkit(opts?: ToolkitOptions): Record<string, ToolkitEntry>;
53
68
  /**
54
69
  * Returns the public exports for the analytics plugin.
55
70
  * Note: `asUser()` is automatically added by AppKit.
@@ -64,7 +79,7 @@ declare class AnalyticsPlugin extends Plugin {
64
79
  /**
65
80
  * @internal
66
81
  */
67
- declare const analytics: ToPlugin<typeof AnalyticsPlugin, IAnalyticsConfig, "analytics">;
82
+ declare const analytics: ToPlugin<typeof AnalyticsPlugin, IAnalyticsConfig, "analytics"> & NamedPluginFactory<"analytics">;
68
83
  //#endregion
69
84
  export { AnalyticsPlugin, analytics };
70
85
  //# sourceMappingURL=analytics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","names":[],"sources":["../../../src/plugins/analytics/analytics.ts"],"mappings":";;;;;;;;;;;;cAwBa,eAAA,SAAwB,MAAA;;SAE5B,QAAA,EAAuB,cAAA;EAAA,iBAEb,WAAA;EAAA,UACC,MAAA,EAAQ,gBAAA;EAAA,QAGlB,SAAA;EAAA,QACA,cAAA;cAEI,MAAA,EAAQ,gBAAA;EAWpB,YAAA,CAAa,MAAA,EAAQ,UAAA;EApBS;;;;EAgDxB,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EADI;;;;EAuCD,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EA8G2B;;;;;;;;;;;;;;;EAFxB,KAAA,CACJ,KAAA,UACA,UAAA,GAAa,MAAA,SAAe,aAAA,sBAC5B,gBAAA,GAAmB,MAAA,eACnB,MAAA,GAAS,WAAA,GACR,OAAA;EA/MsC;;;EAAA,UAuOzB,YAAA,CACd,eAAA,EAAiB,eAAA,EACjB,KAAA,UACA,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,UAAA,aAAuB,SAAA,CAAU,YAAA;EAItC,QAAA,CAAA,GAAY,OAAA;EA3OD;;;;EAmPjB,OAAA,CAAA;;;;2BA5Ce,UAAA,GACA,MAAA,SAAe,aAAA,sBAAiC,gBAAA,GAC1C,MAAA,eAAmB,MAAA,GAC7B,WAAA,KACR,OAAA;EAAA;AAAA;;;;cAqDQ,SAAA,EAAS,QAAA,QAAA,eAAA,EAAA,gBAAA"}
1
+ {"version":3,"file":"analytics.d.ts","names":[],"sources":["../../../src/plugins/analytics/analytics.ts"],"mappings":";;;;;;;;;;;;;;;;cAkCa,eAAA,SAAwB,MAAA,YAAkB,YAAA;;SAE9C,QAAA,EAAuB,cAAA;EAAA,iBAEb,WAAA;EAAA,UACC,MAAA,EAAQ,gBAAA;EAAA,QAGlB,SAAA;EAAA,QACA,cAAA;cAEI,MAAA,EAAQ,gBAAA;EAWpB,YAAA,CAAa,MAAA,EAAQ,UAAA;;AAtBvB;;;EAkDQ,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAhDuB;;;;EAsFpB,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAzCA;;;;;;;;;;;;;;;EAqJG,KAAA,CACJ,KAAA,UACA,UAAA,GAAa,MAAA,SAAe,aAAA,sBAC5B,gBAAA,GAAmB,MAAA,eACnB,MAAA,GAAS,WAAA,GACR,OAAA;EAmEA;;;EAAA,UA3Ca,YAAA,CACd,eAAA,EAAiB,eAAA,EACjB,KAAA,UACA,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,UAAA,aAAuB,SAAA,CAAU,YAAA;EAItC,QAAA,CAAA,GAAY,OAAA;EAAA,QAIV,KAAA;EAuBR,aAAA,CAAA,GAAiB,mBAAA;EAIX,gBAAA,CACJ,IAAA,UACA,IAAA,WACA,MAAA,GAAS,WAAA,GACR,OAAA;EAnEA;;;;;;;EA8EH,OAAA,CAAQ,IAAA,GAXE,cAAA,GAWoD,MAAA,SAAA,YAAA;EA3RhC;;;;EAmS9B,OAAA,CAAA;IA5RQ;;;2BAkMO,UAAA,GACA,MAAA,SAAe,aAAA,sBAAiC,gBAAA,GAC1C,MAAA,eAAmB,MAAA,GAC7B,WAAA,KACR,OAAA;EAAA;AAAA;;;;cAmGQ,SAAA,EAAS,QAAA,QAAA,eAAA,EAAA,gBAAA,iBAAA,kBAAA"}
@@ -6,9 +6,13 @@ import { toPlugin } from "../../plugin/to-plugin.js";
6
6
  import "../../plugin/index.js";
7
7
  import { SQLWarehouseConnector } from "../../connectors/sql-warehouse/client.js";
8
8
  import "../../connectors/index.js";
9
+ import { buildToolkitEntries } from "../../core/agent/build-toolkit.js";
10
+ import { defineTool, executeFromRegistry, toolsFromRegistry } from "../../core/agent/tools/define-tool.js";
11
+ import { assertReadOnlySql } from "../../core/agent/tools/sql-policy.js";
9
12
  import { queryDefaults } from "./defaults.js";
10
13
  import manifest_default from "./manifest.js";
11
14
  import { QueryProcessor } from "./query.js";
15
+ import { z } from "zod";
12
16
 
13
17
  //#region src/plugins/analytics/analytics.ts
14
18
  init_context();
@@ -165,6 +169,35 @@ var AnalyticsPlugin = class extends Plugin {
165
169
  async shutdown() {
166
170
  this.streamManager.abortAll();
167
171
  }
172
+ tools = { query: defineTool({
173
+ description: "Execute a read-only SQL query against the Databricks SQL warehouse. Only SELECT, WITH, SHOW, EXPLAIN, and DESCRIBE statements are accepted; writes are rejected. Returns the query results as JSON.",
174
+ schema: z.object({ query: z.string().describe("The SQL query to execute. Must be a SELECT, WITH, SHOW, EXPLAIN, or DESCRIBE statement.") }),
175
+ annotations: {
176
+ readOnly: true,
177
+ requiresUserContext: true
178
+ },
179
+ autoInheritable: true,
180
+ handler: (args, signal) => {
181
+ assertReadOnlySql(args.query);
182
+ return this.query(args.query, void 0, void 0, signal);
183
+ }
184
+ }) };
185
+ getAgentTools() {
186
+ return toolsFromRegistry(this.tools);
187
+ }
188
+ async executeAgentTool(name, args, signal) {
189
+ return executeFromRegistry(this.tools, name, args, signal);
190
+ }
191
+ /**
192
+ * Returns the plugin's tools as a keyed record of `ToolkitEntry` markers.
193
+ * Called by the agents plugin (via `resolveToolkitFromProvider`) to spread
194
+ * a filtered, renamed view of the plugin's tools into an agent's tool
195
+ * index. Most callers should go through `fromPlugin(analytics, opts)` at
196
+ * module scope instead of reaching for this directly.
197
+ */
198
+ toolkit(opts) {
199
+ return buildToolkitEntries(this.name, this.tools, opts);
200
+ }
168
201
  /**
169
202
  * Returns the public exports for the analytics plugin.
170
203
  * Note: `asUser()` is automatically added by AppKit.
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.js","names":["manifest"],"sources":["../../../src/plugins/analytics/analytics.ts"],"sourcesContent":["import type { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport type express from \"express\";\nimport type {\n IAppRouter,\n PluginExecuteConfig,\n SQLTypeMarker,\n StreamExecutionSettings,\n} from \"shared\";\nimport { SQLWarehouseConnector } from \"../../connectors\";\nimport { getWarehouseId, getWorkspaceClient } from \"../../context\";\nimport { createLogger } from \"../../logging/logger\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest } from \"../../registry\";\nimport { queryDefaults } from \"./defaults\";\nimport manifest from \"./manifest.json\";\nimport { QueryProcessor } from \"./query\";\nimport type {\n AnalyticsQueryResponse,\n IAnalyticsConfig,\n IAnalyticsQueryRequest,\n} from \"./types\";\n\nconst logger = createLogger(\"analytics\");\n\nexport class AnalyticsPlugin extends Plugin {\n /** Plugin manifest declaring metadata and resource requirements */\n static manifest = manifest as PluginManifest<\"analytics\">;\n\n protected static description = \"Analytics plugin for data analysis\";\n protected declare config: IAnalyticsConfig;\n\n // analytics services\n private SQLClient: SQLWarehouseConnector;\n private queryProcessor: QueryProcessor;\n\n constructor(config: IAnalyticsConfig) {\n super(config);\n this.config = config;\n this.queryProcessor = new QueryProcessor();\n\n this.SQLClient = new SQLWarehouseConnector({\n timeout: config.timeout,\n telemetry: config.telemetry,\n });\n }\n\n injectRoutes(router: IAppRouter) {\n // Arrow data downloads always run as service principal and bypass the\n // interceptor chain (execute/executeStream). The original query execution\n // handles OBO via executeStream(); this endpoint fetches pre-computed\n // results by job ID.\n this.route(router, {\n name: \"arrow\",\n method: \"get\",\n path: \"/arrow-result/:jobId\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleArrowRoute(req, res);\n },\n });\n\n this.route<AnalyticsQueryResponse>(router, {\n name: \"query\",\n method: \"post\",\n path: \"/query/:query_key\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleQueryRoute(req, res);\n },\n });\n }\n\n /**\n * Handle Arrow data download requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleArrowRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n try {\n const { jobId } = req.params;\n const workspaceClient = getWorkspaceClient();\n\n logger.debug(\"Processing Arrow job request for jobId=%s\", jobId);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"getArrowData\").setContext(\"analytics\", {\n job_id: jobId,\n plugin: this.name,\n });\n\n const result = await this.getArrowData(workspaceClient, jobId);\n\n res.setHeader(\"Content-Type\", \"application/octet-stream\");\n res.setHeader(\"Content-Length\", result.data.length.toString());\n res.setHeader(\"Cache-Control\", \"public, max-age=3600\");\n\n logger.debug(\n \"Sending Arrow buffer: %d bytes for job %s\",\n result.data.length,\n jobId,\n );\n res.send(Buffer.from(result.data));\n } catch (error) {\n logger.error(\"Arrow job error: %O\", error);\n res.status(404).json({\n error: error instanceof Error ? error.message : \"Arrow job not found\",\n plugin: this.name,\n });\n }\n }\n\n /**\n * Handle SQL query execution requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleQueryRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { query_key } = req.params;\n const { parameters, format = \"JSON\" } = req.body as IAnalyticsQueryRequest;\n\n // Request-scoped logging with WideEvent tracking\n logger.debug(req, \"Executing query: %s (format=%s)\", query_key, format);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"executeQuery\").setContext(\"analytics\", {\n query_key,\n format,\n parameter_count: parameters ? Object.keys(parameters).length : 0,\n plugin: this.name,\n });\n\n if (!query_key) {\n res.status(400).json({ error: \"query_key is required\" });\n return;\n }\n\n const queryResult = await this.app.getAppQuery(\n query_key,\n req,\n this.devFileReader,\n );\n\n if (!queryResult) {\n res.status(404).json({ error: \"Query not found\" });\n return;\n }\n\n const { query, isAsUser } = queryResult;\n\n // get execution context - user-scoped if .obo.sql, otherwise service principal\n const executor = isAsUser ? this.asUser(req) : this;\n const executorKey = isAsUser ? this.resolveUserId(req) : \"global\";\n\n const queryParameters =\n format === \"ARROW\"\n ? {\n formatParameters: {\n disposition: \"EXTERNAL_LINKS\",\n format: \"ARROW_STREAM\",\n },\n type: \"arrow\",\n }\n : {\n type: \"result\",\n };\n\n const hashedQuery = this.queryProcessor.hashQuery(query);\n\n const defaultConfig: PluginExecuteConfig = {\n ...queryDefaults,\n cache: {\n ...queryDefaults.cache,\n cacheKey: [\n \"analytics:query\",\n query_key,\n JSON.stringify(parameters),\n JSON.stringify(format),\n hashedQuery,\n executorKey,\n ],\n },\n };\n\n const streamExecutionSettings: StreamExecutionSettings = {\n default: defaultConfig,\n };\n\n await executor.executeStream(\n res,\n async (signal) => {\n const processedParams = await this.queryProcessor.processQueryParams(\n query,\n parameters,\n );\n\n const result = await executor.query(\n query,\n processedParams,\n queryParameters.formatParameters,\n signal,\n );\n\n return { type: queryParameters.type, ...result };\n },\n streamExecutionSettings,\n executorKey,\n );\n }\n\n /**\n * Execute a SQL query using the current execution context.\n *\n * When called directly: uses service principal credentials.\n * When called via asUser(req).query(...): uses user's credentials.\n *\n * @example\n * ```typescript\n * // Service principal execution\n * const result = await analytics.query(\"SELECT * FROM table\")\n *\n * // User context execution (in route handler)\n * const result = await this.asUser(req).query(\"SELECT * FROM table\")\n * ```\n */\n async query(\n query: string,\n parameters?: Record<string, SQLTypeMarker | null | undefined>,\n formatParameters?: Record<string, any>,\n signal?: AbortSignal,\n ): Promise<any> {\n const workspaceClient = getWorkspaceClient();\n const warehouseId = await getWarehouseId();\n\n const { statement, parameters: sqlParameters } =\n this.queryProcessor.convertToSQLParameters(query, parameters);\n\n const response = await this.SQLClient.executeStatement(\n workspaceClient,\n {\n statement,\n warehouse_id: warehouseId,\n parameters: sqlParameters,\n ...formatParameters,\n },\n signal,\n );\n\n return response.result;\n }\n\n /**\n * Get Arrow-formatted data for a completed query job.\n */\n protected async getArrowData(\n workspaceClient: WorkspaceClient,\n jobId: string,\n signal?: AbortSignal,\n ): Promise<ReturnType<typeof this.SQLClient.getArrowData>> {\n return await this.SQLClient.getArrowData(workspaceClient, jobId, signal);\n }\n\n async shutdown(): Promise<void> {\n this.streamManager.abortAll();\n }\n\n /**\n * Returns the public exports for the analytics plugin.\n * Note: `asUser()` is automatically added by AppKit.\n */\n exports() {\n return {\n /**\n * Execute a SQL query using service principal credentials.\n */\n query: this.query,\n };\n }\n}\n\n/**\n * @internal\n */\nexport const analytics = toPlugin(AnalyticsPlugin);\n"],"mappings":";;;;;;;;;;;;;cASmE;AAanE,MAAM,SAAS,aAAa,YAAY;AAExC,IAAa,kBAAb,cAAqC,OAAO;;CAE1C,OAAO,WAAWA;CAElB,OAAiB,cAAc;CAI/B,AAAQ;CACR,AAAQ;CAER,YAAY,QAA0B;AACpC,QAAM,OAAO;AACb,OAAK,SAAS;AACd,OAAK,iBAAiB,IAAI,gBAAgB;AAE1C,OAAK,YAAY,IAAI,sBAAsB;GACzC,SAAS,OAAO;GAChB,WAAW,OAAO;GACnB,CAAC;;CAGJ,aAAa,QAAoB;AAK/B,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;AAC9D,UAAM,KAAK,kBAAkB,KAAK,IAAI;;GAEzC,CAAC;AAEF,OAAK,MAA8B,QAAQ;GACzC,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;AAC9D,UAAM,KAAK,kBAAkB,KAAK,IAAI;;GAEzC,CAAC;;;;;;CAOJ,MAAM,kBACJ,KACA,KACe;AACf,MAAI;GACF,MAAM,EAAE,UAAU,IAAI;GACtB,MAAM,kBAAkB,oBAAoB;AAE5C,UAAO,MAAM,6CAA6C,MAAM;AAGhE,GADc,OAAO,MAAM,IAAI,EACxB,aAAa,aAAa,eAAe,CAAC,WAAW,aAAa;IACvE,QAAQ;IACR,QAAQ,KAAK;IACd,CAAC;GAEF,MAAM,SAAS,MAAM,KAAK,aAAa,iBAAiB,MAAM;AAE9D,OAAI,UAAU,gBAAgB,2BAA2B;AACzD,OAAI,UAAU,kBAAkB,OAAO,KAAK,OAAO,UAAU,CAAC;AAC9D,OAAI,UAAU,iBAAiB,uBAAuB;AAEtD,UAAO,MACL,6CACA,OAAO,KAAK,QACZ,MACD;AACD,OAAI,KAAK,OAAO,KAAK,OAAO,KAAK,CAAC;WAC3B,OAAO;AACd,UAAO,MAAM,uBAAuB,MAAM;AAC1C,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO,iBAAiB,QAAQ,MAAM,UAAU;IAChD,QAAQ,KAAK;IACd,CAAC;;;;;;;CAQN,MAAM,kBACJ,KACA,KACe;EACf,MAAM,EAAE,cAAc,IAAI;EAC1B,MAAM,EAAE,YAAY,SAAS,WAAW,IAAI;AAG5C,SAAO,MAAM,KAAK,mCAAmC,WAAW,OAAO;AAGvE,EADc,OAAO,MAAM,IAAI,EACxB,aAAa,aAAa,eAAe,CAAC,WAAW,aAAa;GACvE;GACA;GACA,iBAAiB,aAAa,OAAO,KAAK,WAAW,CAAC,SAAS;GAC/D,QAAQ,KAAK;GACd,CAAC;AAEF,MAAI,CAAC,WAAW;AACd,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,yBAAyB,CAAC;AACxD;;EAGF,MAAM,cAAc,MAAM,KAAK,IAAI,YACjC,WACA,KACA,KAAK,cACN;AAED,MAAI,CAAC,aAAa;AAChB,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAClD;;EAGF,MAAM,EAAE,OAAO,aAAa;EAG5B,MAAM,WAAW,WAAW,KAAK,OAAO,IAAI,GAAG;EAC/C,MAAM,cAAc,WAAW,KAAK,cAAc,IAAI,GAAG;EAEzD,MAAM,kBACJ,WAAW,UACP;GACE,kBAAkB;IAChB,aAAa;IACb,QAAQ;IACT;GACD,MAAM;GACP,GACD,EACE,MAAM,UACP;EAEP,MAAM,cAAc,KAAK,eAAe,UAAU,MAAM;EAiBxD,MAAM,0BAAmD,EACvD,SAhByC;GACzC,GAAG;GACH,OAAO;IACL,GAAG,cAAc;IACjB,UAAU;KACR;KACA;KACA,KAAK,UAAU,WAAW;KAC1B,KAAK,UAAU,OAAO;KACtB;KACA;KACD;IACF;GACF,EAIA;AAED,QAAM,SAAS,cACb,KACA,OAAO,WAAW;GAChB,MAAM,kBAAkB,MAAM,KAAK,eAAe,mBAChD,OACA,WACD;GAED,MAAM,SAAS,MAAM,SAAS,MAC5B,OACA,iBACA,gBAAgB,kBAChB,OACD;AAED,UAAO;IAAE,MAAM,gBAAgB;IAAM,GAAG;IAAQ;KAElD,yBACA,YACD;;;;;;;;;;;;;;;;;CAkBH,MAAM,MACJ,OACA,YACA,kBACA,QACc;EACd,MAAM,kBAAkB,oBAAoB;EAC5C,MAAM,cAAc,MAAM,gBAAgB;EAE1C,MAAM,EAAE,WAAW,YAAY,kBAC7B,KAAK,eAAe,uBAAuB,OAAO,WAAW;AAa/D,UAXiB,MAAM,KAAK,UAAU,iBACpC,iBACA;GACE;GACA,cAAc;GACd,YAAY;GACZ,GAAG;GACJ,EACD,OACD,EAEe;;;;;CAMlB,MAAgB,aACd,iBACA,OACA,QACyD;AACzD,SAAO,MAAM,KAAK,UAAU,aAAa,iBAAiB,OAAO,OAAO;;CAG1E,MAAM,WAA0B;AAC9B,OAAK,cAAc,UAAU;;;;;;CAO/B,UAAU;AACR,SAAO,EAIL,OAAO,KAAK,OACb;;;;;;AAOL,MAAa,YAAY,SAAS,gBAAgB"}
1
+ {"version":3,"file":"analytics.js","names":["manifest"],"sources":["../../../src/plugins/analytics/analytics.ts"],"sourcesContent":["import type { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport type express from \"express\";\nimport type {\n AgentToolDefinition,\n IAppRouter,\n PluginExecuteConfig,\n SQLTypeMarker,\n StreamExecutionSettings,\n ToolProvider,\n} from \"shared\";\nimport { z } from \"zod\";\nimport { SQLWarehouseConnector } from \"../../connectors\";\nimport { getWarehouseId, getWorkspaceClient } from \"../../context\";\nimport { buildToolkitEntries } from \"../../core/agent/build-toolkit\";\nimport {\n defineTool,\n executeFromRegistry,\n toolsFromRegistry,\n} from \"../../core/agent/tools/define-tool\";\nimport { assertReadOnlySql } from \"../../core/agent/tools/sql-policy\";\nimport { createLogger } from \"../../logging/logger\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest } from \"../../registry\";\nimport { queryDefaults } from \"./defaults\";\nimport manifest from \"./manifest.json\";\nimport { QueryProcessor } from \"./query\";\nimport type {\n AnalyticsQueryResponse,\n IAnalyticsConfig,\n IAnalyticsQueryRequest,\n} from \"./types\";\n\nconst logger = createLogger(\"analytics\");\n\nexport class AnalyticsPlugin extends Plugin implements ToolProvider {\n /** Plugin manifest declaring metadata and resource requirements */\n static manifest = manifest as PluginManifest<\"analytics\">;\n\n protected static description = \"Analytics plugin for data analysis\";\n protected declare config: IAnalyticsConfig;\n\n // analytics services\n private SQLClient: SQLWarehouseConnector;\n private queryProcessor: QueryProcessor;\n\n constructor(config: IAnalyticsConfig) {\n super(config);\n this.config = config;\n this.queryProcessor = new QueryProcessor();\n\n this.SQLClient = new SQLWarehouseConnector({\n timeout: config.timeout,\n telemetry: config.telemetry,\n });\n }\n\n injectRoutes(router: IAppRouter) {\n // Arrow data downloads always run as service principal and bypass the\n // interceptor chain (execute/executeStream). The original query execution\n // handles OBO via executeStream(); this endpoint fetches pre-computed\n // results by job ID.\n this.route(router, {\n name: \"arrow\",\n method: \"get\",\n path: \"/arrow-result/:jobId\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleArrowRoute(req, res);\n },\n });\n\n this.route<AnalyticsQueryResponse>(router, {\n name: \"query\",\n method: \"post\",\n path: \"/query/:query_key\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleQueryRoute(req, res);\n },\n });\n }\n\n /**\n * Handle Arrow data download requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleArrowRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n try {\n const { jobId } = req.params;\n const workspaceClient = getWorkspaceClient();\n\n logger.debug(\"Processing Arrow job request for jobId=%s\", jobId);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"getArrowData\").setContext(\"analytics\", {\n job_id: jobId,\n plugin: this.name,\n });\n\n const result = await this.getArrowData(workspaceClient, jobId);\n\n res.setHeader(\"Content-Type\", \"application/octet-stream\");\n res.setHeader(\"Content-Length\", result.data.length.toString());\n res.setHeader(\"Cache-Control\", \"public, max-age=3600\");\n\n logger.debug(\n \"Sending Arrow buffer: %d bytes for job %s\",\n result.data.length,\n jobId,\n );\n res.send(Buffer.from(result.data));\n } catch (error) {\n logger.error(\"Arrow job error: %O\", error);\n res.status(404).json({\n error: error instanceof Error ? error.message : \"Arrow job not found\",\n plugin: this.name,\n });\n }\n }\n\n /**\n * Handle SQL query execution requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleQueryRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { query_key } = req.params;\n const { parameters, format = \"JSON\" } = req.body as IAnalyticsQueryRequest;\n\n // Request-scoped logging with WideEvent tracking\n logger.debug(req, \"Executing query: %s (format=%s)\", query_key, format);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"executeQuery\").setContext(\"analytics\", {\n query_key,\n format,\n parameter_count: parameters ? Object.keys(parameters).length : 0,\n plugin: this.name,\n });\n\n if (!query_key) {\n res.status(400).json({ error: \"query_key is required\" });\n return;\n }\n\n const queryResult = await this.app.getAppQuery(\n query_key,\n req,\n this.devFileReader,\n );\n\n if (!queryResult) {\n res.status(404).json({ error: \"Query not found\" });\n return;\n }\n\n const { query, isAsUser } = queryResult;\n\n // get execution context - user-scoped if .obo.sql, otherwise service principal\n const executor = isAsUser ? this.asUser(req) : this;\n const executorKey = isAsUser ? this.resolveUserId(req) : \"global\";\n\n const queryParameters =\n format === \"ARROW\"\n ? {\n formatParameters: {\n disposition: \"EXTERNAL_LINKS\",\n format: \"ARROW_STREAM\",\n },\n type: \"arrow\",\n }\n : {\n type: \"result\",\n };\n\n const hashedQuery = this.queryProcessor.hashQuery(query);\n\n const defaultConfig: PluginExecuteConfig = {\n ...queryDefaults,\n cache: {\n ...queryDefaults.cache,\n cacheKey: [\n \"analytics:query\",\n query_key,\n JSON.stringify(parameters),\n JSON.stringify(format),\n hashedQuery,\n executorKey,\n ],\n },\n };\n\n const streamExecutionSettings: StreamExecutionSettings = {\n default: defaultConfig,\n };\n\n await executor.executeStream(\n res,\n async (signal) => {\n const processedParams = await this.queryProcessor.processQueryParams(\n query,\n parameters,\n );\n\n const result = await executor.query(\n query,\n processedParams,\n queryParameters.formatParameters,\n signal,\n );\n\n return { type: queryParameters.type, ...result };\n },\n streamExecutionSettings,\n executorKey,\n );\n }\n\n /**\n * Execute a SQL query using the current execution context.\n *\n * When called directly: uses service principal credentials.\n * When called via asUser(req).query(...): uses user's credentials.\n *\n * @example\n * ```typescript\n * // Service principal execution\n * const result = await analytics.query(\"SELECT * FROM table\")\n *\n * // User context execution (in route handler)\n * const result = await this.asUser(req).query(\"SELECT * FROM table\")\n * ```\n */\n async query(\n query: string,\n parameters?: Record<string, SQLTypeMarker | null | undefined>,\n formatParameters?: Record<string, any>,\n signal?: AbortSignal,\n ): Promise<any> {\n const workspaceClient = getWorkspaceClient();\n const warehouseId = await getWarehouseId();\n\n const { statement, parameters: sqlParameters } =\n this.queryProcessor.convertToSQLParameters(query, parameters);\n\n const response = await this.SQLClient.executeStatement(\n workspaceClient,\n {\n statement,\n warehouse_id: warehouseId,\n parameters: sqlParameters,\n ...formatParameters,\n },\n signal,\n );\n\n return response.result;\n }\n\n /**\n * Get Arrow-formatted data for a completed query job.\n */\n protected async getArrowData(\n workspaceClient: WorkspaceClient,\n jobId: string,\n signal?: AbortSignal,\n ): Promise<ReturnType<typeof this.SQLClient.getArrowData>> {\n return await this.SQLClient.getArrowData(workspaceClient, jobId, signal);\n }\n\n async shutdown(): Promise<void> {\n this.streamManager.abortAll();\n }\n\n private tools = {\n query: defineTool({\n description:\n \"Execute a read-only SQL query against the Databricks SQL warehouse. Only SELECT, WITH, SHOW, EXPLAIN, and DESCRIBE statements are accepted; writes are rejected. Returns the query results as JSON.\",\n schema: z.object({\n query: z\n .string()\n .describe(\n \"The SQL query to execute. Must be a SELECT, WITH, SHOW, EXPLAIN, or DESCRIBE statement.\",\n ),\n }),\n annotations: {\n readOnly: true,\n requiresUserContext: true,\n },\n autoInheritable: true,\n handler: (args, signal) => {\n assertReadOnlySql(args.query);\n return this.query(args.query, undefined, undefined, signal);\n },\n }),\n };\n\n getAgentTools(): AgentToolDefinition[] {\n return toolsFromRegistry(this.tools);\n }\n\n async executeAgentTool(\n name: string,\n args: unknown,\n signal?: AbortSignal,\n ): Promise<unknown> {\n return executeFromRegistry(this.tools, name, args, signal);\n }\n\n /**\n * Returns the plugin's tools as a keyed record of `ToolkitEntry` markers.\n * Called by the agents plugin (via `resolveToolkitFromProvider`) to spread\n * a filtered, renamed view of the plugin's tools into an agent's tool\n * index. Most callers should go through `fromPlugin(analytics, opts)` at\n * module scope instead of reaching for this directly.\n */\n toolkit(opts?: import(\"../../core/agent/types\").ToolkitOptions) {\n return buildToolkitEntries(this.name, this.tools, opts);\n }\n\n /**\n * Returns the public exports for the analytics plugin.\n * Note: `asUser()` is automatically added by AppKit.\n */\n exports() {\n return {\n /**\n * Execute a SQL query using service principal credentials.\n */\n query: this.query,\n };\n }\n}\n\n/**\n * @internal\n */\nexport const analytics = toPlugin(AnalyticsPlugin);\n"],"mappings":";;;;;;;;;;;;;;;;;cAYmE;AAoBnE,MAAM,SAAS,aAAa,YAAY;AAExC,IAAa,kBAAb,cAAqC,OAA+B;;CAElE,OAAO,WAAWA;CAElB,OAAiB,cAAc;CAI/B,AAAQ;CACR,AAAQ;CAER,YAAY,QAA0B;AACpC,QAAM,OAAO;AACb,OAAK,SAAS;AACd,OAAK,iBAAiB,IAAI,gBAAgB;AAE1C,OAAK,YAAY,IAAI,sBAAsB;GACzC,SAAS,OAAO;GAChB,WAAW,OAAO;GACnB,CAAC;;CAGJ,aAAa,QAAoB;AAK/B,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;AAC9D,UAAM,KAAK,kBAAkB,KAAK,IAAI;;GAEzC,CAAC;AAEF,OAAK,MAA8B,QAAQ;GACzC,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;AAC9D,UAAM,KAAK,kBAAkB,KAAK,IAAI;;GAEzC,CAAC;;;;;;CAOJ,MAAM,kBACJ,KACA,KACe;AACf,MAAI;GACF,MAAM,EAAE,UAAU,IAAI;GACtB,MAAM,kBAAkB,oBAAoB;AAE5C,UAAO,MAAM,6CAA6C,MAAM;AAGhE,GADc,OAAO,MAAM,IAAI,EACxB,aAAa,aAAa,eAAe,CAAC,WAAW,aAAa;IACvE,QAAQ;IACR,QAAQ,KAAK;IACd,CAAC;GAEF,MAAM,SAAS,MAAM,KAAK,aAAa,iBAAiB,MAAM;AAE9D,OAAI,UAAU,gBAAgB,2BAA2B;AACzD,OAAI,UAAU,kBAAkB,OAAO,KAAK,OAAO,UAAU,CAAC;AAC9D,OAAI,UAAU,iBAAiB,uBAAuB;AAEtD,UAAO,MACL,6CACA,OAAO,KAAK,QACZ,MACD;AACD,OAAI,KAAK,OAAO,KAAK,OAAO,KAAK,CAAC;WAC3B,OAAO;AACd,UAAO,MAAM,uBAAuB,MAAM;AAC1C,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO,iBAAiB,QAAQ,MAAM,UAAU;IAChD,QAAQ,KAAK;IACd,CAAC;;;;;;;CAQN,MAAM,kBACJ,KACA,KACe;EACf,MAAM,EAAE,cAAc,IAAI;EAC1B,MAAM,EAAE,YAAY,SAAS,WAAW,IAAI;AAG5C,SAAO,MAAM,KAAK,mCAAmC,WAAW,OAAO;AAGvE,EADc,OAAO,MAAM,IAAI,EACxB,aAAa,aAAa,eAAe,CAAC,WAAW,aAAa;GACvE;GACA;GACA,iBAAiB,aAAa,OAAO,KAAK,WAAW,CAAC,SAAS;GAC/D,QAAQ,KAAK;GACd,CAAC;AAEF,MAAI,CAAC,WAAW;AACd,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,yBAAyB,CAAC;AACxD;;EAGF,MAAM,cAAc,MAAM,KAAK,IAAI,YACjC,WACA,KACA,KAAK,cACN;AAED,MAAI,CAAC,aAAa;AAChB,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAClD;;EAGF,MAAM,EAAE,OAAO,aAAa;EAG5B,MAAM,WAAW,WAAW,KAAK,OAAO,IAAI,GAAG;EAC/C,MAAM,cAAc,WAAW,KAAK,cAAc,IAAI,GAAG;EAEzD,MAAM,kBACJ,WAAW,UACP;GACE,kBAAkB;IAChB,aAAa;IACb,QAAQ;IACT;GACD,MAAM;GACP,GACD,EACE,MAAM,UACP;EAEP,MAAM,cAAc,KAAK,eAAe,UAAU,MAAM;EAiBxD,MAAM,0BAAmD,EACvD,SAhByC;GACzC,GAAG;GACH,OAAO;IACL,GAAG,cAAc;IACjB,UAAU;KACR;KACA;KACA,KAAK,UAAU,WAAW;KAC1B,KAAK,UAAU,OAAO;KACtB;KACA;KACD;IACF;GACF,EAIA;AAED,QAAM,SAAS,cACb,KACA,OAAO,WAAW;GAChB,MAAM,kBAAkB,MAAM,KAAK,eAAe,mBAChD,OACA,WACD;GAED,MAAM,SAAS,MAAM,SAAS,MAC5B,OACA,iBACA,gBAAgB,kBAChB,OACD;AAED,UAAO;IAAE,MAAM,gBAAgB;IAAM,GAAG;IAAQ;KAElD,yBACA,YACD;;;;;;;;;;;;;;;;;CAkBH,MAAM,MACJ,OACA,YACA,kBACA,QACc;EACd,MAAM,kBAAkB,oBAAoB;EAC5C,MAAM,cAAc,MAAM,gBAAgB;EAE1C,MAAM,EAAE,WAAW,YAAY,kBAC7B,KAAK,eAAe,uBAAuB,OAAO,WAAW;AAa/D,UAXiB,MAAM,KAAK,UAAU,iBACpC,iBACA;GACE;GACA,cAAc;GACd,YAAY;GACZ,GAAG;GACJ,EACD,OACD,EAEe;;;;;CAMlB,MAAgB,aACd,iBACA,OACA,QACyD;AACzD,SAAO,MAAM,KAAK,UAAU,aAAa,iBAAiB,OAAO,OAAO;;CAG1E,MAAM,WAA0B;AAC9B,OAAK,cAAc,UAAU;;CAG/B,AAAQ,QAAQ,EACd,OAAO,WAAW;EAChB,aACE;EACF,QAAQ,EAAE,OAAO,EACf,OAAO,EACJ,QAAQ,CACR,SACC,0FACD,EACJ,CAAC;EACF,aAAa;GACX,UAAU;GACV,qBAAqB;GACtB;EACD,iBAAiB;EACjB,UAAU,MAAM,WAAW;AACzB,qBAAkB,KAAK,MAAM;AAC7B,UAAO,KAAK,MAAM,KAAK,OAAO,QAAW,QAAW,OAAO;;EAE9D,CAAC,EACH;CAED,gBAAuC;AACrC,SAAO,kBAAkB,KAAK,MAAM;;CAGtC,MAAM,iBACJ,MACA,MACA,QACkB;AAClB,SAAO,oBAAoB,KAAK,OAAO,MAAM,MAAM,OAAO;;;;;;;;;CAU5D,QAAQ,MAAwD;AAC9D,SAAO,oBAAoB,KAAK,MAAM,KAAK,OAAO,KAAK;;;;;;CAOzD,UAAU;AACR,SAAO,EAIL,OAAO,KAAK,OACb;;;;;;AAOL,MAAa,YAAY,SAAS,gBAAgB"}
@@ -1,14 +1,18 @@
1
+ import { AgentToolDefinition, ToolProvider } from "../../shared/src/agent.js";
1
2
  import { IAppRouter, ToPlugin } from "../../shared/src/plugin.js";
2
3
  import "../../shared/src/index.js";
4
+ import { ToolkitEntry, ToolkitOptions } from "../../core/agent/types.js";
3
5
  import { Plugin } from "../../plugin/plugin.js";
6
+ import { NamedPluginFactory } from "../../plugin/to-plugin.js";
4
7
  import "../../plugin/index.js";
5
- import { FilePolicy, FilePolicyUser } from "./policy.js";
6
8
  import { PluginManifest, ResourceRequirement } from "../../registry/types.js";
7
9
  import "../../registry/index.js";
10
+ import "../agents/index.js";
11
+ import { FilePolicy, FilePolicyUser } from "./policy.js";
8
12
  import { FilesExport, IFilesConfig, VolumeAPI, VolumeConfig } from "./types.js";
9
13
 
10
14
  //#region src/plugins/files/plugin.d.ts
11
- declare class FilesPlugin extends Plugin {
15
+ declare class FilesPlugin extends Plugin implements ToolProvider {
12
16
  name: string;
13
17
  /** Plugin manifest declaring metadata and resource requirements. */
14
18
  static manifest: PluginManifest;
@@ -17,6 +21,7 @@ declare class FilesPlugin extends Plugin {
17
21
  private volumeConnectors;
18
22
  private volumeConfigs;
19
23
  private volumeKeys;
24
+ private tools;
20
25
  /**
21
26
  * Scans `process.env` for `DATABRICKS_VOLUME_*` keys and merges them with
22
27
  * any explicitly configured volumes. Explicit config wins for per-volume
@@ -98,9 +103,23 @@ declare class FilesPlugin extends Plugin {
98
103
  protected createVolumeAPI(volumeKey: string, user: FilePolicyUser, options?: {
99
104
  bypassPolicy?: boolean;
100
105
  }): VolumeAPI;
106
+ /**
107
+ * Builds the agent-tool registry entries for a single volume. One set of
108
+ * tools per configured volume, keyed by `${volumeKey}.${method}`.
109
+ *
110
+ * Each handler resolves the caller's identity from the current execution
111
+ * context (OBO user when the agent run is wrapped in `asUser(req)`, service
112
+ * principal otherwise in local dev) and dispatches through
113
+ * `createVolumeAPI(volumeKey, user)` so the volume's policy is enforced
114
+ * uniformly for agent and HTTP callers.
115
+ */
116
+ private _defineVolumeTools;
101
117
  private inflightWrites;
102
118
  private trackWrite;
103
119
  shutdown(): Promise<void>;
120
+ getAgentTools(): AgentToolDefinition[];
121
+ executeAgentTool(name: string, args: unknown, signal?: AbortSignal): Promise<unknown>;
122
+ toolkit(opts?: ToolkitOptions): Record<string, ToolkitEntry>;
104
123
  /**
105
124
  * Returns the programmatic API for the Files plugin.
106
125
  * Callable with a volume key to get a volume-scoped handle.
@@ -123,7 +142,7 @@ declare class FilesPlugin extends Plugin {
123
142
  /**
124
143
  * @internal
125
144
  */
126
- declare const files: ToPlugin<typeof FilesPlugin, IFilesConfig, string> & {
145
+ declare const files: ToPlugin<typeof FilesPlugin, IFilesConfig, string> & NamedPluginFactory<string> & {
127
146
  policy: {
128
147
  readonly all: (...policies: FilePolicy[]) => FilePolicy;
129
148
  readonly any: (...policies: FilePolicy[]) => FilePolicy;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../../src/plugins/files/plugin.ts"],"mappings":";;;;;;;;;;cA2Ca,WAAA,SAAoB,MAAA;EAC/B,IAAA;;SAGO,QAAA,EAAuB,cAAA;EAAA,iBACb,WAAA;EAAA,UACC,MAAA,EAAQ,YAAA;EAAA,QAElB,gBAAA;EAAA,QACA,aAAA;EAAA,QACA,UAAA;EAJkB;;;;;EAAA,OAWnB,eAAA,CAAgB,MAAA,EAAQ,YAAA,GAAe,MAAA,SAAe,YAAA;EAuIzC;;;;EAAA,OAhHb,uBAAA,CAAwB,MAAA,EAAQ,YAAA,GAAe,mBAAA;EAs9B3C;;;;EAAA,QAh8BH,YAAA;EA9DuB;;;;EAAA,QAiFjB,YAAA;EA3EI;;;;;EAAA,QA4GJ,cAAA;cAsCF,MAAA,EAAQ,YAAA;EA8CpB,YAAA,CAAa,MAAA,EAAQ,UAAA;EArLyB;;;;EAAA,QAmTtC,cAAA;EA5R8C;;;;EAAA,QAmT9C,YAAA;EAAA,QAQA,aAAA;EA3MI;;;;;;;;;EAAA,QA+NJ,oBAAA;EAAA,QAgBA,eAAA;EAAA,QAqCA,gBAAA;EAAA,QAOM,WAAA;EAAA,QA8BA,WAAA;EAAA,QAmCA,eAAA;EAAA,QAWA,UAAA;EAqIA;;;;;EAAA,QArHA,UAAA;EAAA,QAiFA,aAAA;EAAA,QAoCA,eAAA;EAAA,QAoCA,cAAA;EAAA,QAoCA,aAAA;EAAA,QAgHA,YAAA;EAAA,QA0CA,aAAA;EA4GN;;;;;;;;;;EAAA,UAxDE,eAAA,CACR,SAAA,UACA,IAAA,EAAM,cAAA,EACN,OAAA;IAAY,YAAA;EAAA,IACX,SAAA;EAAA,QAoDK,cAAA;EAAA,QAEA,UAAA;EAOF,QAAA,CAAA,GAAY,OAAA;EA6EF;;;;;;;;;;;;;;;;EA1ChB,OAAA,CAAA,GAAW,WAAA;EAkCX,YAAA,CAAA,GAAgB,MAAA;AAAA;;;;cAQL,KAAA,EAAK,QAAA,QAAA,WAAA,EAAA,YAAA;;gCAAA,UAAA"}
1
+ {"version":3,"file":"plugin.d.ts","names":[],"sources":["../../../src/plugins/files/plugin.ts"],"mappings":";;;;;;;;;;;;;;cA4Da,WAAA,SAAoB,MAAA,YAAkB,YAAA;EACjD,IAAA;;SAGO,QAAA,EAAuB,cAAA;EAAA,iBACb,WAAA;EAAA,UACC,MAAA,EAAQ,YAAA;EAAA,QAElB,gBAAA;EAAA,QACA,aAAA;EAAA,QACA,UAAA;EAAA,QACA,KAAA;EAXe;;;;;EAAA,OAkBhB,eAAA,CAAgB,MAAA,EAAQ,YAAA,GAAe,MAAA,SAAe,YAAA;EAAf;;;;EAAA,OAuBvC,uBAAA,CAAwB,MAAA,EAAQ,YAAA,GAAe,mBAAA;EAo3B9C;;;;EAAA,QA91BA,YAAA;EA6gCL;;;;EAAA,QA1/BW,YAAA;EAojCE;;;;;EAAA,QAnhCF,cAAA;cAsCF,MAAA,EAAQ,YAAA;EA8CpB,YAAA,CAAa,MAAA,EAAQ,UAAA;EAnMd;;;;EAAA,QAiUC,cAAA;EA7TA;;;;EAAA,QAoVA,YAAA;EAAA,QAQA,aAAA;EAlVe;;;;;;;;;EAAA,QAsWf,oBAAA;EAAA,QAgBA,eAAA;EAAA,QAqCA,gBAAA;EAAA,QAOM,WAAA;EAAA,QA8BA,WAAA;EAAA,QAmCA,eAAA;EAAA,QAWA,UAAA;EA3LN;;;;;EAAA,QA2MM,UAAA;EAAA,QAiFA,aAAA;EAAA,QAoCA,eAAA;EAAA,QAoCA,cAAA;EAAA,QAoCA,aAAA;EAAA,QAgHA,YAAA;EAAA,QA0CA,aAAA;EAlOA;;;;;;;;;;EAAA,UAsRJ,eAAA,CACR,SAAA,UACA,IAAA,EAAM,cAAA,EACN,OAAA;IAAY,YAAA;EAAA,IACX,SAAA;EAyIK;;;;;;;;;;EAAA,QA3EA,kBAAA;EAAA,QA2EA,cAAA;EAAA,QAEA,UAAA;EAOF,QAAA,CAAA,GAAY,OAAA;EAmBlB,aAAA,CAAA,GAAiB,mBAAA;EAIX,gBAAA,CACJ,IAAA,UACA,IAAA,WACA,MAAA,GAAS,WAAA,GACR,OAAA;EAIH,OAAA,CAAQ,IAAA,GAJE,cAAA,GAIoD,MAAA,SAAA,YAAA;EAoB9D;;;;;;AA0CF;;;;;;;;;;EA1CE,OAAA,CAAA,GAAW,WAAA;EAkCX,YAAA,CAAA,GAAgB,MAAA;AAAA;;;;cAQL,KAAA,EAAK,QAAA,QAAA,WAAA,EAAA,YAAA,YAAA,kBAAA;;gCAAA,UAAA"}
@@ -1,7 +1,8 @@
1
1
  import { createLogger } from "../../logging/logger.js";
2
2
  import { AuthenticationError } from "../../errors/authentication.js";
3
3
  import { init_errors } from "../../errors/index.js";
4
- import { getCurrentUserId, getWorkspaceClient } from "../../context/execution-context.js";
4
+ import { init_user_context, isUserContext } from "../../context/user-context.js";
5
+ import { getCurrentUserId, getExecutionContext, getWorkspaceClient } from "../../context/execution-context.js";
5
6
  import { init_context } from "../../context/index.js";
6
7
  import { ResourceType } from "../../registry/types.generated.js";
7
8
  import "../../registry/index.js";
@@ -12,15 +13,19 @@ import { PolicyDeniedError, policy } from "./policy.js";
12
13
  import { contentTypeFromPath, isSafeInlineContentType, validateCustomContentTypes } from "../../connectors/files/defaults.js";
13
14
  import { FilesConnector } from "../../connectors/files/client.js";
14
15
  import "../../connectors/files/index.js";
16
+ import { buildToolkitEntries } from "../../core/agent/build-toolkit.js";
17
+ import { defineTool, executeFromRegistry, toolsFromRegistry } from "../../core/agent/tools/define-tool.js";
15
18
  import { FILES_DOWNLOAD_DEFAULTS, FILES_MAX_UPLOAD_SIZE, FILES_READ_DEFAULTS, FILES_WRITE_DEFAULTS } from "./defaults.js";
16
19
  import { parentDirectory, sanitizeFilename } from "./helpers.js";
17
20
  import manifest_default from "./manifest.js";
18
21
  import { ApiError } from "@databricks/sdk-experimental";
22
+ import { z } from "zod";
19
23
  import { STATUS_CODES } from "node:http";
20
24
  import { Readable } from "node:stream";
21
25
 
22
26
  //#region src/plugins/files/plugin.ts
23
27
  init_context();
28
+ init_user_context();
24
29
  init_errors();
25
30
  const logger = createLogger("files");
26
31
  var FilesPlugin = class FilesPlugin extends Plugin {
@@ -31,6 +36,7 @@ var FilesPlugin = class FilesPlugin extends Plugin {
31
36
  volumeConnectors = {};
32
37
  volumeConfigs = {};
33
38
  volumeKeys = [];
39
+ tools = {};
34
40
  /**
35
41
  * Scans `process.env` for `DATABRICKS_VOLUME_*` keys and merges them with
36
42
  * any explicitly configured volumes. Explicit config wins for per-volume
@@ -162,8 +168,9 @@ var FilesPlugin = class FilesPlugin extends Plugin {
162
168
  telemetry: config.telemetry,
163
169
  customContentTypes: mergedConfig.customContentTypes
164
170
  });
171
+ Object.assign(this.tools, this._defineVolumeTools(key));
172
+ if (!volumeCfg.policy) logger.warn("Volume \"%s\" has no explicit policy — defaulting to publicRead(). Set a policy in files({ volumes: { %s: { policy: ... } } }) to silence this warning.", key, key);
165
173
  }
166
- for (const key of this.volumeKeys) if (!volumes[key].policy) logger.warn("Volume \"%s\" has no explicit policy — defaulting to publicRead(). Set a policy in files({ volumes: { %s: { policy: ... } } }) to silence this warning.", key, key);
167
174
  }
168
175
  injectRoutes(router) {
169
176
  this.route(router, {
@@ -707,6 +714,90 @@ var FilesPlugin = class FilesPlugin extends Plugin {
707
714
  }
708
715
  };
709
716
  }
717
+ /**
718
+ * Builds the agent-tool registry entries for a single volume. One set of
719
+ * tools per configured volume, keyed by `${volumeKey}.${method}`.
720
+ *
721
+ * Each handler resolves the caller's identity from the current execution
722
+ * context (OBO user when the agent run is wrapped in `asUser(req)`, service
723
+ * principal otherwise in local dev) and dispatches through
724
+ * `createVolumeAPI(volumeKey, user)` so the volume's policy is enforced
725
+ * uniformly for agent and HTTP callers.
726
+ */
727
+ _defineVolumeTools(volumeKey) {
728
+ const buildUser = () => {
729
+ const ctx = getExecutionContext();
730
+ return isUserContext(ctx) ? { id: ctx.userId } : {
731
+ id: ctx.serviceUserId,
732
+ isServicePrincipal: true
733
+ };
734
+ };
735
+ const api = () => this.createVolumeAPI(volumeKey, buildUser());
736
+ return {
737
+ [`${volumeKey}.list`]: defineTool({
738
+ description: `List files and directories in the "${volumeKey}" volume`,
739
+ schema: z.object({ path: z.string().optional().describe("Directory path to list (optional, defaults to root)") }),
740
+ annotations: {
741
+ readOnly: true,
742
+ requiresUserContext: true
743
+ },
744
+ autoInheritable: true,
745
+ handler: (args) => api().list(args.path)
746
+ }),
747
+ [`${volumeKey}.read`]: defineTool({
748
+ description: `Read a text file from the "${volumeKey}" volume`,
749
+ schema: z.object({ path: z.string().describe("File path to read") }),
750
+ annotations: {
751
+ readOnly: true,
752
+ requiresUserContext: true
753
+ },
754
+ autoInheritable: true,
755
+ handler: (args) => api().read(args.path)
756
+ }),
757
+ [`${volumeKey}.exists`]: defineTool({
758
+ description: `Check if a file or directory exists in the "${volumeKey}" volume`,
759
+ schema: z.object({ path: z.string().describe("Path to check") }),
760
+ annotations: {
761
+ readOnly: true,
762
+ requiresUserContext: true
763
+ },
764
+ autoInheritable: true,
765
+ handler: (args) => api().exists(args.path)
766
+ }),
767
+ [`${volumeKey}.metadata`]: defineTool({
768
+ description: `Get metadata (size, type, last modified) for a file in the "${volumeKey}" volume`,
769
+ schema: z.object({ path: z.string().describe("File path") }),
770
+ annotations: {
771
+ readOnly: true,
772
+ requiresUserContext: true
773
+ },
774
+ autoInheritable: true,
775
+ handler: (args) => api().metadata(args.path)
776
+ }),
777
+ [`${volumeKey}.upload`]: defineTool({
778
+ description: `Upload a text file to the "${volumeKey}" volume`,
779
+ schema: z.object({
780
+ path: z.string().describe("Destination file path"),
781
+ contents: z.string().describe("File contents as a string"),
782
+ overwrite: z.boolean().optional().describe("Whether to overwrite existing file")
783
+ }),
784
+ annotations: {
785
+ destructive: true,
786
+ requiresUserContext: true
787
+ },
788
+ handler: (args) => api().upload(args.path, args.contents, { overwrite: args.overwrite })
789
+ }),
790
+ [`${volumeKey}.delete`]: defineTool({
791
+ description: `Delete a file from the "${volumeKey}" volume`,
792
+ schema: z.object({ path: z.string().describe("File path to delete") }),
793
+ annotations: {
794
+ destructive: true,
795
+ requiresUserContext: true
796
+ },
797
+ handler: (args) => api().delete(args.path)
798
+ })
799
+ };
800
+ }
710
801
  inflightWrites = 0;
711
802
  trackWrite(fn) {
712
803
  this.inflightWrites++;
@@ -723,6 +814,15 @@ var FilesPlugin = class FilesPlugin extends Plugin {
723
814
  if (this.inflightWrites > 0) logger.warn("Shutdown deadline reached with %d in-flight write(s) still pending.", this.inflightWrites);
724
815
  this.streamManager.abortAll();
725
816
  }
817
+ getAgentTools() {
818
+ return toolsFromRegistry(this.tools);
819
+ }
820
+ async executeAgentTool(name, args, signal) {
821
+ return executeFromRegistry(this.tools, name, args, signal);
822
+ }
823
+ toolkit(opts) {
824
+ return buildToolkitEntries(this.name, this.tools, opts);
825
+ }
726
826
  /**
727
827
  * Returns the programmatic API for the Files plugin.
728
828
  * Callable with a volume key to get a volume-scoped handle.