@databricks/appkit 0.31.0 → 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 (152) 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 +12 -1
  135. package/dist/plugins/server/index.d.ts.map +1 -1
  136. package/dist/plugins/server/index.js +39 -5
  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/shared/src/agent.d.ts +63 -1
  143. package/dist/shared/src/agent.d.ts.map +1 -1
  144. package/dist/shared/src/index.d.ts +1 -1
  145. package/dist/shared/src/plugin.d.ts +8 -0
  146. package/dist/shared/src/plugin.d.ts.map +1 -1
  147. package/docs/api/appkit/Class.Plugin.md +65 -23
  148. package/docs/api/appkit/Function.createApp.md +10 -8
  149. package/docs/privacy.md +41 -0
  150. package/llms.txt +1 -0
  151. package/package.json +4 -2
  152. package/sbom.cdx.json +1 -1
@@ -0,0 +1,256 @@
1
+ //#region src/core/agent/tools/sql-policy.ts
2
+ /**
3
+ * Conservative SQL classifier used by agent-facing query tools to enforce
4
+ * `readOnly: true` annotations at execution time.
5
+ *
6
+ * Why a hand-rolled tokenizer rather than `node-sql-parser` or `pgsql-parser`:
7
+ *
8
+ * - `node-sql-parser`'s Hive/Spark dialect coverage rejects common Databricks
9
+ * SQL patterns (three-part `catalog.schema.table` names, `SHOW TABLES IN`,
10
+ * `DESCRIBE EXTENDED`, `EXPLAIN`) that must be allowed by a read-only
11
+ * classifier. Its PostgreSQL grammar rejects `SHOW`/`DESCRIBE` too.
12
+ * - `pgsql-parser` (libpg_query) is a native binding and fails to install
13
+ * cleanly on every Databricks App runtime we care about.
14
+ *
15
+ * We don't need to fully parse SQL — we only need to decide whether every
16
+ * statement in the batch starts with a read-only keyword. A small tokenizer
17
+ * that correctly strips strings, identifiers, and comments is enough and
18
+ * costs no extra dependencies.
19
+ *
20
+ * What this classifier guarantees (when it returns `readOnly: true`):
21
+ *
22
+ * - Every semicolon-separated statement outside a string, identifier, or
23
+ * comment begins with `SELECT`, `WITH`, `SHOW`, `EXPLAIN`, `DESCRIBE`, or
24
+ * `DESC`.
25
+ * - `SELECT 1; DROP TABLE x` is rejected (stacked write detected).
26
+ * - `SELECT 'value; DROP TABLE x'` passes (literal inside a string).
27
+ * - `-- DROP TABLE x\nSELECT 1` passes (comment stripped).
28
+ * - `SELECT 1 <block-comment ; DROP block-comment>` passes (comment stripped).
29
+ *
30
+ * What this classifier does NOT guarantee:
31
+ *
32
+ * - A `SELECT` statement may still have side effects via function calls
33
+ * (`SELECT pg_advisory_lock(...)`, `SELECT lo_import('/etc/passwd')`, CTEs
34
+ * with DML in Postgres 9.1+). Callers that need stronger guarantees should
35
+ * combine this check with a runtime mechanism: for PostgreSQL, execute the
36
+ * statement inside a dedicated client's `BEGIN READ ONLY … ROLLBACK`
37
+ * transaction (see `LakebasePlugin.runReadOnlyStatement`). A batched
38
+ * `pool.query("BEGIN READ ONLY; <stmt>; ROLLBACK")` cannot be used because
39
+ * the Postgres Extended Query protocol rejects multi-statement prepared
40
+ * queries, which silently breaks parameterized SQL.
41
+ */
42
+ const READ_ONLY_KEYWORDS = new Set([
43
+ "SELECT",
44
+ "WITH",
45
+ "SHOW",
46
+ "EXPLAIN",
47
+ "DESCRIBE",
48
+ "DESC"
49
+ ]);
50
+ /**
51
+ * Classify a SQL string as read-only or not. See module docstring for the
52
+ * precise guarantee this offers.
53
+ */
54
+ function classifyReadOnly(sql) {
55
+ const strip = stripCommentsAndQuoted(sql);
56
+ if (strip.unterminated) return {
57
+ readOnly: false,
58
+ reason: `SQL has an unterminated ${strip.unterminated} literal`
59
+ };
60
+ const statements = splitStatements(strip.cleaned);
61
+ if (statements.length === 0) return {
62
+ readOnly: false,
63
+ reason: "SQL is empty or contains only comments"
64
+ };
65
+ for (let i = 0; i < statements.length; i++) {
66
+ const stmt = statements[i];
67
+ const firstWord = firstKeyword(stmt);
68
+ if (!firstWord) return {
69
+ readOnly: false,
70
+ reason: `statement ${i + 1} of ${statements.length} is empty`
71
+ };
72
+ if (!READ_ONLY_KEYWORDS.has(firstWord.toUpperCase())) return {
73
+ readOnly: false,
74
+ reason: `statement starts with '${firstWord}'; only SELECT, WITH, SHOW, EXPLAIN, DESCRIBE, DESC are allowed in read-only mode`
75
+ };
76
+ }
77
+ return {
78
+ readOnly: true,
79
+ statements: statements.length
80
+ };
81
+ }
82
+ /**
83
+ * Assert `sql` is read-only or throw {@link ReadOnlySqlViolation}. Suitable
84
+ * for calling from agent-tool handlers where the thrown string surfaces back
85
+ * to the LLM as the tool's error output.
86
+ */
87
+ function assertReadOnlySql(sql) {
88
+ const result = classifyReadOnly(sql);
89
+ if (!result.readOnly) throw new ReadOnlySqlViolation(result.reason);
90
+ }
91
+ var ReadOnlySqlViolation = class extends Error {
92
+ constructor(reason) {
93
+ super(`SQL read-only policy violation: ${reason}`);
94
+ this.name = "ReadOnlySqlViolation";
95
+ }
96
+ };
97
+ function stripCommentsAndQuoted(sql) {
98
+ const out = [];
99
+ let i = 0;
100
+ const n = sql.length;
101
+ let unterminated = null;
102
+ while (i < n) {
103
+ const ch = sql[i];
104
+ const next = i + 1 < n ? sql[i + 1] : "";
105
+ if (ch === "-" && next === "-") {
106
+ out.push(" ");
107
+ i += 2;
108
+ while (i < n && sql[i] !== "\n") {
109
+ out.push(" ");
110
+ i++;
111
+ }
112
+ continue;
113
+ }
114
+ if (ch === "/" && next === "*") {
115
+ out.push(" ");
116
+ i += 2;
117
+ let depth = 1;
118
+ while (i < n && depth > 0) {
119
+ if (sql[i] === "/" && sql[i + 1] === "*") {
120
+ out.push(" ");
121
+ i += 2;
122
+ depth++;
123
+ continue;
124
+ }
125
+ if (sql[i] === "*" && sql[i + 1] === "/") {
126
+ out.push(" ");
127
+ i += 2;
128
+ depth--;
129
+ continue;
130
+ }
131
+ out.push(sql[i] === "\n" ? "\n" : " ");
132
+ i++;
133
+ }
134
+ if (depth > 0) unterminated = "block comment";
135
+ continue;
136
+ }
137
+ if (ch === "'" || ch === "E" && next === "'" || ch === "e" && next === "'") {
138
+ if (ch === "E" || ch === "e") {
139
+ out.push(" ");
140
+ i++;
141
+ }
142
+ out.push(" ");
143
+ i++;
144
+ let closed = false;
145
+ while (i < n) {
146
+ if (sql[i] === "'" && sql[i + 1] === "'") {
147
+ out.push(" ");
148
+ i += 2;
149
+ continue;
150
+ }
151
+ if (sql[i] === "\\" && sql[i + 1]) {
152
+ out.push(" ");
153
+ i += 2;
154
+ continue;
155
+ }
156
+ if (sql[i] === "'") {
157
+ out.push(" ");
158
+ i++;
159
+ closed = true;
160
+ break;
161
+ }
162
+ out.push(sql[i] === "\n" ? "\n" : " ");
163
+ i++;
164
+ }
165
+ if (!closed) unterminated = "string";
166
+ continue;
167
+ }
168
+ if (ch === "\"") {
169
+ out.push(" ");
170
+ i++;
171
+ let closed = false;
172
+ while (i < n) {
173
+ if (sql[i] === "\"" && sql[i + 1] === "\"") {
174
+ out.push(" ");
175
+ i += 2;
176
+ continue;
177
+ }
178
+ if (sql[i] === "\"") {
179
+ out.push(" ");
180
+ i++;
181
+ closed = true;
182
+ break;
183
+ }
184
+ out.push(sql[i] === "\n" ? "\n" : " ");
185
+ i++;
186
+ }
187
+ if (!closed) unterminated = "identifier";
188
+ continue;
189
+ }
190
+ if (ch === "`") {
191
+ out.push(" ");
192
+ i++;
193
+ let closed = false;
194
+ while (i < n) {
195
+ if (sql[i] === "`" && sql[i + 1] === "`") {
196
+ out.push(" ");
197
+ i += 2;
198
+ continue;
199
+ }
200
+ if (sql[i] === "`") {
201
+ out.push(" ");
202
+ i++;
203
+ closed = true;
204
+ break;
205
+ }
206
+ out.push(sql[i] === "\n" ? "\n" : " ");
207
+ i++;
208
+ }
209
+ if (!closed) unterminated = "identifier";
210
+ continue;
211
+ }
212
+ if (ch === "$") {
213
+ const tagMatch = sql.slice(i).match(/^\$([A-Za-z_][A-Za-z0-9_]*)?\$/);
214
+ if (tagMatch) {
215
+ const tag = tagMatch[0];
216
+ out.push(" ".repeat(tag.length));
217
+ i += tag.length;
218
+ const closeIdx = sql.indexOf(tag, i);
219
+ if (closeIdx === -1) {
220
+ while (i < n) {
221
+ out.push(sql[i] === "\n" ? "\n" : " ");
222
+ i++;
223
+ }
224
+ unterminated = "dollar-quoted string";
225
+ } else {
226
+ while (i < closeIdx) {
227
+ out.push(sql[i] === "\n" ? "\n" : " ");
228
+ i++;
229
+ }
230
+ out.push(" ".repeat(tag.length));
231
+ i += tag.length;
232
+ }
233
+ continue;
234
+ }
235
+ }
236
+ out.push(ch);
237
+ i++;
238
+ }
239
+ return {
240
+ cleaned: out.join(""),
241
+ unterminated
242
+ };
243
+ }
244
+ /** Split on unquoted `;`, trim, drop empty segments. */
245
+ function splitStatements(cleanedSql) {
246
+ return cleanedSql.split(";").map((s) => s.trim()).filter((s) => s.length > 0);
247
+ }
248
+ /** Return the first bareword keyword of a statement, or null if empty. */
249
+ function firstKeyword(stmt) {
250
+ const match = stmt.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)/);
251
+ return match ? match[1] : null;
252
+ }
253
+
254
+ //#endregion
255
+ export { assertReadOnlySql };
256
+ //# sourceMappingURL=sql-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-policy.js","names":[],"sources":["../../../../src/core/agent/tools/sql-policy.ts"],"sourcesContent":["/**\n * Conservative SQL classifier used by agent-facing query tools to enforce\n * `readOnly: true` annotations at execution time.\n *\n * Why a hand-rolled tokenizer rather than `node-sql-parser` or `pgsql-parser`:\n *\n * - `node-sql-parser`'s Hive/Spark dialect coverage rejects common Databricks\n * SQL patterns (three-part `catalog.schema.table` names, `SHOW TABLES IN`,\n * `DESCRIBE EXTENDED`, `EXPLAIN`) that must be allowed by a read-only\n * classifier. Its PostgreSQL grammar rejects `SHOW`/`DESCRIBE` too.\n * - `pgsql-parser` (libpg_query) is a native binding and fails to install\n * cleanly on every Databricks App runtime we care about.\n *\n * We don't need to fully parse SQL — we only need to decide whether every\n * statement in the batch starts with a read-only keyword. A small tokenizer\n * that correctly strips strings, identifiers, and comments is enough and\n * costs no extra dependencies.\n *\n * What this classifier guarantees (when it returns `readOnly: true`):\n *\n * - Every semicolon-separated statement outside a string, identifier, or\n * comment begins with `SELECT`, `WITH`, `SHOW`, `EXPLAIN`, `DESCRIBE`, or\n * `DESC`.\n * - `SELECT 1; DROP TABLE x` is rejected (stacked write detected).\n * - `SELECT 'value; DROP TABLE x'` passes (literal inside a string).\n * - `-- DROP TABLE x\\nSELECT 1` passes (comment stripped).\n * - `SELECT 1 <block-comment ; DROP block-comment>` passes (comment stripped).\n *\n * What this classifier does NOT guarantee:\n *\n * - A `SELECT` statement may still have side effects via function calls\n * (`SELECT pg_advisory_lock(...)`, `SELECT lo_import('/etc/passwd')`, CTEs\n * with DML in Postgres 9.1+). Callers that need stronger guarantees should\n * combine this check with a runtime mechanism: for PostgreSQL, execute the\n * statement inside a dedicated client's `BEGIN READ ONLY … ROLLBACK`\n * transaction (see `LakebasePlugin.runReadOnlyStatement`). A batched\n * `pool.query(\"BEGIN READ ONLY; <stmt>; ROLLBACK\")` cannot be used because\n * the Postgres Extended Query protocol rejects multi-statement prepared\n * queries, which silently breaks parameterized SQL.\n */\n\nconst READ_ONLY_KEYWORDS = new Set([\n \"SELECT\",\n \"WITH\",\n \"SHOW\",\n \"EXPLAIN\",\n \"DESCRIBE\",\n \"DESC\",\n]);\n\ntype SqlReadOnlyResult =\n | { readOnly: true; statements: number }\n | { readOnly: false; reason: string };\n\n/**\n * Classify a SQL string as read-only or not. See module docstring for the\n * precise guarantee this offers.\n */\nexport function classifyReadOnly(sql: string): SqlReadOnlyResult {\n const strip = stripCommentsAndQuoted(sql);\n if (strip.unterminated) {\n return {\n readOnly: false,\n reason: `SQL has an unterminated ${strip.unterminated} literal`,\n };\n }\n const statements = splitStatements(strip.cleaned);\n\n if (statements.length === 0) {\n return {\n readOnly: false,\n reason: \"SQL is empty or contains only comments\",\n };\n }\n\n for (let i = 0; i < statements.length; i++) {\n const stmt = statements[i];\n const firstWord = firstKeyword(stmt);\n if (!firstWord) {\n return {\n readOnly: false,\n reason: `statement ${i + 1} of ${statements.length} is empty`,\n };\n }\n if (!READ_ONLY_KEYWORDS.has(firstWord.toUpperCase())) {\n return {\n readOnly: false,\n reason: `statement starts with '${firstWord}'; only SELECT, WITH, SHOW, EXPLAIN, DESCRIBE, DESC are allowed in read-only mode`,\n };\n }\n }\n\n return { readOnly: true, statements: statements.length };\n}\n\n/**\n * Assert `sql` is read-only or throw {@link ReadOnlySqlViolation}. Suitable\n * for calling from agent-tool handlers where the thrown string surfaces back\n * to the LLM as the tool's error output.\n */\nexport function assertReadOnlySql(sql: string): void {\n const result = classifyReadOnly(sql);\n if (!result.readOnly) {\n throw new ReadOnlySqlViolation(result.reason);\n }\n}\n\nexport class ReadOnlySqlViolation extends Error {\n constructor(reason: string) {\n super(`SQL read-only policy violation: ${reason}`);\n this.name = \"ReadOnlySqlViolation\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tokenizer helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Walk `sql` character-by-character and replace every string literal,\n * identifier quote, and comment body with a single space of equivalent\n * length. Leaves structural tokens (semicolons, whitespace, identifiers,\n * operators) in place.\n *\n * Handles:\n * - `-- line comments` through end-of-line\n * - SQL block comments (slash-star ... star-slash) with correct nesting (PostgreSQL)\n * - `'single-quoted strings'` with `''` escape\n * - `\"double-quoted identifiers\"` with `\"\"` escape (ANSI)\n * - `` `backtick identifiers` `` (Databricks)\n * - `$tag$dollar quoted$tag$` strings (PostgreSQL)\n * - `E'escape-style'` strings (PostgreSQL)\n */\ntype StripResult = {\n cleaned: string;\n /** Non-null if tokenization ended inside an unterminated literal or comment. */\n unterminated:\n | null\n | \"string\"\n | \"identifier\"\n | \"block comment\"\n | \"dollar-quoted string\";\n};\n\nfunction stripCommentsAndQuoted(sql: string): StripResult {\n const out: string[] = [];\n let i = 0;\n const n = sql.length;\n let unterminated: StripResult[\"unterminated\"] = null;\n\n while (i < n) {\n const ch = sql[i];\n const next = i + 1 < n ? sql[i + 1] : \"\";\n\n if (ch === \"-\" && next === \"-\") {\n out.push(\" \");\n i += 2;\n while (i < n && sql[i] !== \"\\n\") {\n out.push(\" \");\n i++;\n }\n continue;\n }\n\n if (ch === \"/\" && next === \"*\") {\n out.push(\" \");\n i += 2;\n let depth = 1;\n while (i < n && depth > 0) {\n if (sql[i] === \"/\" && sql[i + 1] === \"*\") {\n out.push(\" \");\n i += 2;\n depth++;\n continue;\n }\n if (sql[i] === \"*\" && sql[i + 1] === \"/\") {\n out.push(\" \");\n i += 2;\n depth--;\n continue;\n }\n out.push(sql[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n if (depth > 0) {\n unterminated = \"block comment\";\n }\n continue;\n }\n\n if (\n ch === \"'\" ||\n (ch === \"E\" && next === \"'\") ||\n (ch === \"e\" && next === \"'\")\n ) {\n if (ch === \"E\" || ch === \"e\") {\n out.push(\" \");\n i++;\n }\n out.push(\" \");\n i++;\n let closed = false;\n while (i < n) {\n if (sql[i] === \"'\" && sql[i + 1] === \"'\") {\n out.push(\" \");\n i += 2;\n continue;\n }\n if (sql[i] === \"\\\\\" && sql[i + 1]) {\n out.push(\" \");\n i += 2;\n continue;\n }\n if (sql[i] === \"'\") {\n out.push(\" \");\n i++;\n closed = true;\n break;\n }\n out.push(sql[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n if (!closed) unterminated = \"string\";\n continue;\n }\n\n if (ch === '\"') {\n out.push(\" \");\n i++;\n let closed = false;\n while (i < n) {\n if (sql[i] === '\"' && sql[i + 1] === '\"') {\n out.push(\" \");\n i += 2;\n continue;\n }\n if (sql[i] === '\"') {\n out.push(\" \");\n i++;\n closed = true;\n break;\n }\n out.push(sql[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n if (!closed) unterminated = \"identifier\";\n continue;\n }\n\n if (ch === \"`\") {\n out.push(\" \");\n i++;\n let closed = false;\n while (i < n) {\n if (sql[i] === \"`\" && sql[i + 1] === \"`\") {\n out.push(\" \");\n i += 2;\n continue;\n }\n if (sql[i] === \"`\") {\n out.push(\" \");\n i++;\n closed = true;\n break;\n }\n out.push(sql[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n if (!closed) unterminated = \"identifier\";\n continue;\n }\n\n if (ch === \"$\") {\n const tagMatch = sql.slice(i).match(/^\\$([A-Za-z_][A-Za-z0-9_]*)?\\$/);\n if (tagMatch) {\n const tag = tagMatch[0];\n out.push(\" \".repeat(tag.length));\n i += tag.length;\n const closeIdx = sql.indexOf(tag, i);\n if (closeIdx === -1) {\n while (i < n) {\n out.push(sql[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n unterminated = \"dollar-quoted string\";\n } else {\n while (i < closeIdx) {\n out.push(sql[i] === \"\\n\" ? \"\\n\" : \" \");\n i++;\n }\n out.push(\" \".repeat(tag.length));\n i += tag.length;\n }\n continue;\n }\n }\n\n out.push(ch);\n i++;\n }\n\n return { cleaned: out.join(\"\"), unterminated };\n}\n\n/** Split on unquoted `;`, trim, drop empty segments. */\nfunction splitStatements(cleanedSql: string): string[] {\n return cleanedSql\n .split(\";\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n}\n\n/** Return the first bareword keyword of a statement, or null if empty. */\nfunction firstKeyword(stmt: string): string | null {\n const match = stmt.match(/^\\s*([A-Za-z_][A-Za-z0-9_]*)/);\n return match ? match[1] : null;\n}\n"],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { ToolAnnotations } from "../../../shared/src/agent.js";
2
+ import "../../../shared/src/index.js";
3
+ import { FunctionTool } from "./function-tool.js";
4
+ import { z } from "zod";
5
+
6
+ //#region src/core/agent/tools/tool.d.ts
7
+ interface ToolConfig<S extends z.ZodType> {
8
+ name: string;
9
+ description?: string;
10
+ schema: S;
11
+ /**
12
+ * Behavioural hints forwarded to the resolved tool definition. Prefer
13
+ * `effect` (`"read" | "write" | "update" | "destructive"`) — any mutating
14
+ * value forces the agents-plugin approval gate before `execute()` runs
15
+ * and the client's approval card will colour itself accordingly. Legacy
16
+ * `destructive: true` still gates. Dropped silently before the fix that
17
+ * added this field.
18
+ */
19
+ annotations?: ToolAnnotations;
20
+ execute: (args: z.infer<S>) => Promise<string> | string;
21
+ }
22
+ /**
23
+ * Factory for defining function tools with Zod schemas.
24
+ *
25
+ * - Generates JSON Schema (for the LLM) from the Zod schema via `z.toJSONSchema()`.
26
+ * - Infers the `execute` argument type from the schema.
27
+ * - Validates tool call arguments at runtime. On validation failure, returns
28
+ * a formatted error string to the LLM instead of throwing, so the model
29
+ * can self-correct on its next turn.
30
+ */
31
+ declare function tool<S extends z.ZodType>(config: ToolConfig<S>): FunctionTool;
32
+ //#endregion
33
+ export { ToolConfig, tool };
34
+ //# sourceMappingURL=tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.d.ts","names":[],"sources":["../../../../src/core/agent/tools/tool.ts"],"mappings":";;;;;;UAKiB,UAAA,WAAqB,CAAA,CAAE,OAAA;EACtC,IAAA;EACA,WAAA;EACA,MAAA,EAAQ,CAAA;EAHiB;;;;;;;;EAYzB,WAAA,GAAc,eAAA;EACd,OAAA,GAAU,IAAA,EAAM,CAAA,CAAE,KAAA,CAAM,CAAA,MAAO,OAAA;AAAA;;;;;;;;;;iBAYjB,IAAA,WAAe,CAAA,CAAE,OAAA,CAAA,CAAS,MAAA,EAAQ,UAAA,CAAW,CAAA,IAAK,YAAA"}
@@ -0,0 +1,41 @@
1
+ import { toToolJSONSchema } from "./json-schema.js";
2
+
3
+ //#region src/core/agent/tools/tool.ts
4
+ /**
5
+ * Factory for defining function tools with Zod schemas.
6
+ *
7
+ * - Generates JSON Schema (for the LLM) from the Zod schema via `z.toJSONSchema()`.
8
+ * - Infers the `execute` argument type from the schema.
9
+ * - Validates tool call arguments at runtime. On validation failure, returns
10
+ * a formatted error string to the LLM instead of throwing, so the model
11
+ * can self-correct on its next turn.
12
+ */
13
+ function tool(config) {
14
+ const parameters = toToolJSONSchema(config.schema);
15
+ return {
16
+ type: "function",
17
+ name: config.name,
18
+ description: config.description ?? config.name,
19
+ parameters,
20
+ ...config.annotations ? { annotations: config.annotations } : {},
21
+ execute: async (args) => {
22
+ const parsed = config.schema.safeParse(args);
23
+ if (!parsed.success) return formatZodError(parsed.error, config.name);
24
+ return config.execute(parsed.data);
25
+ }
26
+ };
27
+ }
28
+ /**
29
+ * Formats a Zod validation error into an LLM-friendly string.
30
+ *
31
+ * Example: `Invalid arguments for get_weather: city: Invalid input: expected string, received undefined`
32
+ */
33
+ function formatZodError(error, toolName) {
34
+ return `Invalid arguments for ${toolName}: ${error.issues.map((issue) => {
35
+ return `${issue.path.length > 0 ? issue.path.join(".") : "(root)"}: ${issue.message}`;
36
+ }).join("; ")}`;
37
+ }
38
+
39
+ //#endregion
40
+ export { formatZodError, tool };
41
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.js","names":[],"sources":["../../../../src/core/agent/tools/tool.ts"],"sourcesContent":["import type { ToolAnnotations } from \"shared\";\nimport type { z } from \"zod\";\nimport type { FunctionTool } from \"./function-tool\";\nimport { toToolJSONSchema } from \"./json-schema\";\n\nexport interface ToolConfig<S extends z.ZodType> {\n name: string;\n description?: string;\n schema: S;\n /**\n * Behavioural hints forwarded to the resolved tool definition. Prefer\n * `effect` (`\"read\" | \"write\" | \"update\" | \"destructive\"`) — any mutating\n * value forces the agents-plugin approval gate before `execute()` runs\n * and the client's approval card will colour itself accordingly. Legacy\n * `destructive: true` still gates. Dropped silently before the fix that\n * added this field.\n */\n annotations?: ToolAnnotations;\n execute: (args: z.infer<S>) => Promise<string> | string;\n}\n\n/**\n * Factory for defining function tools with Zod schemas.\n *\n * - Generates JSON Schema (for the LLM) from the Zod schema via `z.toJSONSchema()`.\n * - Infers the `execute` argument type from the schema.\n * - Validates tool call arguments at runtime. On validation failure, returns\n * a formatted error string to the LLM instead of throwing, so the model\n * can self-correct on its next turn.\n */\nexport function tool<S extends z.ZodType>(config: ToolConfig<S>): FunctionTool {\n const parameters = toToolJSONSchema(config.schema) as unknown as Record<\n string,\n unknown\n >;\n\n return {\n type: \"function\",\n name: config.name,\n description: config.description ?? config.name,\n parameters,\n ...(config.annotations ? { annotations: config.annotations } : {}),\n execute: async (args: Record<string, unknown>) => {\n const parsed = config.schema.safeParse(args);\n if (!parsed.success) {\n return formatZodError(parsed.error, config.name);\n }\n return config.execute(parsed.data as z.infer<S>);\n },\n };\n}\n\n/**\n * Formats a Zod validation error into an LLM-friendly string.\n *\n * Example: `Invalid arguments for get_weather: city: Invalid input: expected string, received undefined`\n */\nexport function formatZodError(error: z.ZodError, toolName: string): string {\n const parts = error.issues.map((issue) => {\n const field = issue.path.length > 0 ? issue.path.join(\".\") : \"(root)\";\n return `${field}: ${issue.message}`;\n });\n return `Invalid arguments for ${toolName}: ${parts.join(\"; \")}`;\n}\n"],"mappings":";;;;;;;;;;;;AA8BA,SAAgB,KAA0B,QAAqC;CAC7E,MAAM,aAAa,iBAAiB,OAAO,OAAO;AAKlD,QAAO;EACL,MAAM;EACN,MAAM,OAAO;EACb,aAAa,OAAO,eAAe,OAAO;EAC1C;EACA,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,aAAa,GAAG,EAAE;EACjE,SAAS,OAAO,SAAkC;GAChD,MAAM,SAAS,OAAO,OAAO,UAAU,KAAK;AAC5C,OAAI,CAAC,OAAO,QACV,QAAO,eAAe,OAAO,OAAO,OAAO,KAAK;AAElD,UAAO,OAAO,QAAQ,OAAO,KAAmB;;EAEnD;;;;;;;AAQH,SAAgB,eAAe,OAAmB,UAA0B;AAK1E,QAAO,yBAAyB,SAAS,IAJ3B,MAAM,OAAO,KAAK,UAAU;AAExC,SAAO,GADO,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,IAAI,GAAG,SAC7C,IAAI,MAAM;GAC1B,CACiD,KAAK,KAAK"}
@@ -0,0 +1,214 @@
1
+ import { AgentAdapter, AgentToolDefinition, ThreadStore, ToolAnnotations } from "../../shared/src/agent.js";
2
+ import { BasePluginConfig } from "../../shared/src/plugin.js";
3
+ import "../../shared/src/index.js";
4
+ import { McpHostPolicyConfig } from "../../connectors/mcp/host-policy.js";
5
+ import "../../connectors/mcp/index.js";
6
+ import { FunctionTool } from "./tools/function-tool.js";
7
+ import { HostedTool } from "./tools/hosted-tools.js";
8
+
9
+ //#region src/core/agent/types.d.ts
10
+ /**
11
+ * A tool reference produced by a plugin's `.toolkit()` call. The agents plugin
12
+ * recognizes the `__toolkitRef` brand and dispatches tool invocations through
13
+ * `PluginContext.executeTool(req, pluginName, localName, ...)`, preserving
14
+ * OBO (asUser) and telemetry spans.
15
+ */
16
+ interface ToolkitEntry {
17
+ readonly __toolkitRef: true;
18
+ pluginName: string;
19
+ localName: string;
20
+ def: AgentToolDefinition;
21
+ annotations?: ToolAnnotations;
22
+ /**
23
+ * Whether this tool is eligible for `autoInheritTools` spreading. Mirrors
24
+ * {@link ToolEntry.autoInheritable} from the source registry so the agents
25
+ * plugin can filter auto-inherited tools without re-walking the provider's
26
+ * internal registry.
27
+ */
28
+ autoInheritable?: boolean;
29
+ }
30
+ /**
31
+ * Any tool an agent can invoke: inline function tools (`tool()`), hosted MCP
32
+ * tools (`mcpServer()` / raw hosted), or toolkit references from plugins
33
+ * (`analytics().toolkit()`).
34
+ */
35
+ type AgentTool = FunctionTool | HostedTool | ToolkitEntry;
36
+ interface ToolkitOptions {
37
+ /** Key prefix to prepend to each tool's local name. Defaults to `${pluginName}.`. */
38
+ prefix?: string;
39
+ /** Only include tools whose local name matches one of these. */
40
+ only?: string[];
41
+ /** Exclude tools whose local name matches one of these. */
42
+ except?: string[];
43
+ /** Remap specific local names to different keys (applied after prefix). */
44
+ rename?: Record<string, string>;
45
+ }
46
+ /**
47
+ * Context passed to `baseSystemPrompt` callbacks.
48
+ */
49
+ interface PromptContext {
50
+ agentName: string;
51
+ pluginNames: string[];
52
+ toolNames: string[];
53
+ }
54
+ type BaseSystemPromptOption = false | string | ((ctx: PromptContext) => string);
55
+ interface AgentDefinition {
56
+ /** Filled in from the enclosing key when used in `agents: { foo: def }`. */
57
+ name?: string;
58
+ /** System prompt body. For markdown-loaded agents this is the file body. */
59
+ instructions: string;
60
+ /**
61
+ * Model adapter (or endpoint-name string sugar for
62
+ * `DatabricksAdapter.fromServingEndpoint({ endpointName })`). Optional —
63
+ * falls back to the plugin's `defaultModel`.
64
+ */
65
+ model?: AgentAdapter | Promise<AgentAdapter> | string;
66
+ /** Per-agent tool record. Key is the LLM-visible tool-call name. */
67
+ tools?: Record<string, AgentTool>;
68
+ /** Sub-agents, exposed as `agent-<key>` tools on this agent. */
69
+ agents?: Record<string, AgentDefinition>;
70
+ /** Override the plugin's baseSystemPrompt for this agent only. */
71
+ baseSystemPrompt?: BaseSystemPromptOption;
72
+ maxSteps?: number;
73
+ maxTokens?: number;
74
+ /**
75
+ * When true, the thread used for a chat request against this agent is
76
+ * deleted from `ThreadStore` after the stream completes (success or
77
+ * failure). Use for stateless one-shot agents — e.g. autocomplete, where
78
+ * each request is independent and retaining history would both poison
79
+ * future calls and accumulate unbounded state in the default
80
+ * `InMemoryThreadStore`. Defaults to `false`.
81
+ */
82
+ ephemeral?: boolean;
83
+ }
84
+ /**
85
+ * Auto-inherit configuration. When enabled for a given agent origin, agents
86
+ * with no explicit `tools:` declaration receive every registered ToolProvider
87
+ * plugin tool whose author marked `autoInheritable: true`. Tools without that
88
+ * flag — destructive, state-mutating, or privilege-sensitive — never spread
89
+ * automatically and must be wired via `tools:`, `toolkits:`, or `fromPlugin`.
90
+ *
91
+ * Defaults are `false` for both origins (safe-by-default): developers must
92
+ * consciously opt an origin in to any auto-inherit behaviour.
93
+ */
94
+ interface AutoInheritToolsConfig {
95
+ /** Default for agents loaded from markdown files. Default: `false`. */
96
+ file?: boolean;
97
+ /** Default for code-defined agents (via `agents: { foo: createAgent(...) }`). Default: `false`. */
98
+ code?: boolean;
99
+ }
100
+ interface AgentsPluginConfig extends BasePluginConfig {
101
+ /** Directory of agent packages (`<id>/agent.md` each). Default `./config/agents`. Set to `false` to disable. */
102
+ dir?: string | false;
103
+ /** Code-defined agents, merged with file-loaded ones (code wins on key collision). */
104
+ agents?: Record<string, AgentDefinition>;
105
+ /** Agent used when clients don't specify one. Defaults to the first-registered agent or the file with `default: true` frontmatter. */
106
+ defaultAgent?: string;
107
+ /** Default model for agents that don't specify their own (in code or frontmatter). */
108
+ defaultModel?: AgentAdapter | Promise<AgentAdapter> | string;
109
+ /** Ambient tool library. Keys may be referenced by markdown frontmatter via `tools: [key1, key2]`. */
110
+ tools?: Record<string, AgentTool>;
111
+ /** Whether to auto-inherit every ToolProvider plugin's toolkit. Accepts a boolean shorthand. */
112
+ autoInheritTools?: boolean | AutoInheritToolsConfig;
113
+ /** Persistent thread store. Default: in-memory. */
114
+ threadStore?: ThreadStore;
115
+ /** Customize or disable the AppKit base system prompt. */
116
+ baseSystemPrompt?: BaseSystemPromptOption;
117
+ /**
118
+ * MCP server host policy. By default only same-origin Databricks workspace
119
+ * URLs may be used as MCP endpoints; custom hosts must be explicitly
120
+ * allowlisted here. Workspace credentials (SP / OBO) are never forwarded
121
+ * to non-workspace hosts.
122
+ */
123
+ mcp?: McpHostPolicyConfig;
124
+ /**
125
+ * Human-in-the-loop approval gate for destructive tool calls. When enabled
126
+ * (the default), the agents plugin emits an `appkit.approval_pending` SSE
127
+ * event before executing any tool annotated `destructive: true` and waits
128
+ * for a `POST /chat/approve` decision from the same user who initiated the
129
+ * stream. A missing decision after `timeoutMs` auto-denies the call.
130
+ */
131
+ approval?: {
132
+ /** Require human approval for tools annotated `destructive: true`. Default: `true`. */requireForDestructive?: boolean; /** Milliseconds to wait before auto-denying. Default: 60_000. */
133
+ timeoutMs?: number;
134
+ };
135
+ /**
136
+ * Runtime resource limits applied during agent execution. Defaults are
137
+ * tuned to protect a single-instance deployment from a misbehaving user or
138
+ * a runaway prompt injection; tighten or relax as appropriate for the
139
+ * deployment's scale and trust model. Request-body caps (chat message
140
+ * size, invocations input size / length) are enforced statically by the
141
+ * Zod schemas and are not configurable here.
142
+ */
143
+ limits?: {
144
+ /**
145
+ * Max concurrent chat streams a single user may have open. Subsequent
146
+ * `POST /chat` requests from that user while at-limit are rejected with
147
+ * HTTP 429. Default: `5`.
148
+ */
149
+ maxConcurrentStreamsPerUser?: number;
150
+ /**
151
+ * Max tool invocations per agent run (across the full tool-call graph,
152
+ * including sub-agent invocations). A run that exceeds the budget is
153
+ * aborted with a terminal error event. Default: `50`.
154
+ */
155
+ maxToolCalls?: number;
156
+ /**
157
+ * Max sub-agent recursion depth. Protects against a prompt-injected
158
+ * agent that delegates to a sub-agent which in turn delegates back to
159
+ * itself (directly or transitively). Default: `3`.
160
+ */
161
+ maxSubAgentDepth?: number;
162
+ /**
163
+ * Per-call timeout for tools dispatched through `PluginContext`
164
+ * (toolkit-routed tools — analytics SQL warehouse queries, Genie
165
+ * messages, Lakebase queries). Independent of `maxToolCalls`: the
166
+ * budget caps how many tools fire per run, this caps how long any
167
+ * single tool call may run. The signal handed to plugin tool
168
+ * implementations combines this timeout with the parent stream's
169
+ * abort signal via `AbortSignal.any`. Function and MCP tools have
170
+ * their own timeouts in their respective adapters and ignore this
171
+ * setting. Default: `300_000` (5 minutes) — generous enough for cold
172
+ * SQL Warehouse round-trips and long Genie conversations.
173
+ */
174
+ toolCallTimeoutMs?: number;
175
+ };
176
+ }
177
+ /** Internal tool-index entry after a tool record has been resolved to a dispatchable form. */
178
+ type ResolvedToolEntry = {
179
+ source: "toolkit";
180
+ pluginName: string;
181
+ localName: string;
182
+ def: AgentToolDefinition;
183
+ } | {
184
+ source: "function";
185
+ functionTool: FunctionTool;
186
+ def: AgentToolDefinition;
187
+ } | {
188
+ source: "mcp";
189
+ mcpToolName: string;
190
+ def: AgentToolDefinition;
191
+ } | {
192
+ source: "subagent";
193
+ agentName: string;
194
+ def: AgentToolDefinition;
195
+ };
196
+ interface RegisteredAgent {
197
+ name: string;
198
+ instructions: string;
199
+ adapter: AgentAdapter;
200
+ toolIndex: Map<string, ResolvedToolEntry>;
201
+ baseSystemPrompt?: BaseSystemPromptOption;
202
+ maxSteps?: number;
203
+ maxTokens?: number;
204
+ /** Mirrors `AgentDefinition.ephemeral` — skip thread persistence. */
205
+ ephemeral?: boolean;
206
+ }
207
+ /**
208
+ * Type guard for `ToolkitEntry` — used by the agents plugin to differentiate
209
+ * toolkit references from inline tools in a mixed `tools` record.
210
+ */
211
+ declare function isToolkitEntry(value: unknown): value is ToolkitEntry;
212
+ //#endregion
213
+ export { AgentDefinition, AgentTool, AgentsPluginConfig, AutoInheritToolsConfig, BaseSystemPromptOption, PromptContext, RegisteredAgent, ResolvedToolEntry, ToolkitEntry, ToolkitOptions, isToolkitEntry };
214
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/agent/types.ts"],"mappings":";;;;;;;;;;;;;;AAiBA;UAAiB,YAAA;EAAA,SACN,YAAA;EACT,UAAA;EACA,SAAA;EACA,GAAA,EAAK,mBAAA;EACL,WAAA,GAAc,eAAA;EADd;;;;;;EAQA,eAAA;AAAA;;;;;;KAQU,SAAA,GAAY,YAAA,GAAe,UAAA,GAAa,YAAA;AAAA,UAEnC,cAAA;EAFO;EAItB,MAAA;EAJkD;EAMlD,IAAA;EAN8D;EAQ9D,MAAA;EAN6B;EAQ7B,MAAA,GAAS,MAAA;AAAA;;;;UAMM,aAAA;EACf,SAAA;EACA,WAAA;EACA,SAAA;AAAA;AAAA,KAGU,sBAAA,sBAGN,GAAA,EAAK,aAAA;AAAA,UAEM,eAAA;EAXa;EAa5B,IAAA;EAXA;EAaA,YAAA;EAZS;;AAGX;;;EAeE,KAAA,GAAQ,YAAA,GAAe,OAAA,CAAQ,YAAA;EAZT;EActB,KAAA,GAAQ,MAAA,SAAe,SAAA;EAZO;EAc9B,MAAA,GAAS,MAAA,SAAe,eAAA;EAJhB;EAMR,gBAAA,GAAmB,sBAAA;EACnB,QAAA;EACA,SAAA;EANQ;;;;;;;;EAeR,SAAA;AAAA;;;;;;;;;;;UAae,sBAAA;EAtBf;EAwBA,IAAA;EAfS;EAiBT,IAAA;AAAA;AAAA,UAGe,kBAAA,SAA2B,gBAAA;;EAE1C,GAAA;EALI;EAOJ,MAAA,GAAS,MAAA,SAAe,eAAA;EAJU;EAMlC,YAAA;EAFwB;EAIxB,YAAA,GAAe,YAAA,GAAe,OAAA,CAAQ,YAAA;EAAvB;EAEf,KAAA,GAAQ,MAAA,SAAe,SAAA;EAFO;EAI9B,gBAAA,aAA6B,sBAAA;EAFrB;EAIR,WAAA,GAAc,WAAA;EAAA;EAEd,gBAAA,GAAmB,sBAAA;EAOb;;;;;;EAAN,GAAA,GAAM,mBAAA;EAnBG;;;;;;;EA2BT,QAAA;IArBQ,uFAuBN,qBAAA,YArBF;IAuBE,SAAA;EAAA;EArBY;;;;;;;;EA+Bd,MAAA;IAME;;;;;IAAA,2BAAA;IA8BQ;;;;;IAxBR,YAAA;IAuCO;;;;;IAjCP,gBAAA;IAsBE;;;;;;;;;;;;IATF,iBAAA;EAAA;AAAA;;KAKQ,iBAAA;EAEN,MAAA;EACA,UAAA;EACA,SAAA;EACA,GAAA,EAAK,mBAAA;AAAA;EAGL,MAAA;EACA,YAAA,EAAc,YAAA;EACd,GAAA,EAAK,mBAAA;AAAA;EAGL,MAAA;EACA,WAAA;EACA,GAAA,EAAK,mBAAA;AAAA;EAGL,MAAA;EACA,SAAA;EACA,GAAA,EAAK,mBAAA;AAAA;AAAA,UAGM,eAAA;EACf,IAAA;EACA,YAAA;EACA,OAAA,EAAS,YAAA;EACT,SAAA,EAAW,GAAA,SAAY,iBAAA;EACvB,gBAAA,GAAmB,sBAAA;EACnB,QAAA;EACA,SAAA;EAS4B;EAP5B,SAAA;AAAA;;;;;iBAOc,cAAA,CAAe,KAAA,YAAiB,KAAA,IAAS,YAAA"}
@@ -0,0 +1,12 @@
1
+ //#region src/core/agent/types.ts
2
+ /**
3
+ * Type guard for `ToolkitEntry` — used by the agents plugin to differentiate
4
+ * toolkit references from inline tools in a mixed `tools` record.
5
+ */
6
+ function isToolkitEntry(value) {
7
+ return typeof value === "object" && value !== null && value.__toolkitRef === true;
8
+ }
9
+
10
+ //#endregion
11
+ export { isToolkitEntry };
12
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../../src/core/agent/types.ts"],"sourcesContent":["import type {\n AgentAdapter,\n AgentToolDefinition,\n BasePluginConfig,\n ThreadStore,\n ToolAnnotations,\n} from \"shared\";\nimport type { McpHostPolicyConfig } from \"../../connectors/mcp\";\nimport type { FunctionTool } from \"./tools/function-tool\";\nimport type { HostedTool } from \"./tools/hosted-tools\";\n\n/**\n * A tool reference produced by a plugin's `.toolkit()` call. The agents plugin\n * recognizes the `__toolkitRef` brand and dispatches tool invocations through\n * `PluginContext.executeTool(req, pluginName, localName, ...)`, preserving\n * OBO (asUser) and telemetry spans.\n */\nexport interface ToolkitEntry {\n readonly __toolkitRef: true;\n pluginName: string;\n localName: string;\n def: AgentToolDefinition;\n annotations?: ToolAnnotations;\n /**\n * Whether this tool is eligible for `autoInheritTools` spreading. Mirrors\n * {@link ToolEntry.autoInheritable} from the source registry so the agents\n * plugin can filter auto-inherited tools without re-walking the provider's\n * internal registry.\n */\n autoInheritable?: boolean;\n}\n\n/**\n * Any tool an agent can invoke: inline function tools (`tool()`), hosted MCP\n * tools (`mcpServer()` / raw hosted), or toolkit references from plugins\n * (`analytics().toolkit()`).\n */\nexport type AgentTool = FunctionTool | HostedTool | ToolkitEntry;\n\nexport interface ToolkitOptions {\n /** Key prefix to prepend to each tool's local name. Defaults to `${pluginName}.`. */\n prefix?: string;\n /** Only include tools whose local name matches one of these. */\n only?: string[];\n /** Exclude tools whose local name matches one of these. */\n except?: string[];\n /** Remap specific local names to different keys (applied after prefix). */\n rename?: Record<string, string>;\n}\n\n/**\n * Context passed to `baseSystemPrompt` callbacks.\n */\nexport interface PromptContext {\n agentName: string;\n pluginNames: string[];\n toolNames: string[];\n}\n\nexport type BaseSystemPromptOption =\n | false\n | string\n | ((ctx: PromptContext) => string);\n\nexport interface AgentDefinition {\n /** Filled in from the enclosing key when used in `agents: { foo: def }`. */\n name?: string;\n /** System prompt body. For markdown-loaded agents this is the file body. */\n instructions: string;\n /**\n * Model adapter (or endpoint-name string sugar for\n * `DatabricksAdapter.fromServingEndpoint({ endpointName })`). Optional —\n * falls back to the plugin's `defaultModel`.\n */\n model?: AgentAdapter | Promise<AgentAdapter> | string;\n /** Per-agent tool record. Key is the LLM-visible tool-call name. */\n tools?: Record<string, AgentTool>;\n /** Sub-agents, exposed as `agent-<key>` tools on this agent. */\n agents?: Record<string, AgentDefinition>;\n /** Override the plugin's baseSystemPrompt for this agent only. */\n baseSystemPrompt?: BaseSystemPromptOption;\n maxSteps?: number;\n maxTokens?: number;\n /**\n * When true, the thread used for a chat request against this agent is\n * deleted from `ThreadStore` after the stream completes (success or\n * failure). Use for stateless one-shot agents — e.g. autocomplete, where\n * each request is independent and retaining history would both poison\n * future calls and accumulate unbounded state in the default\n * `InMemoryThreadStore`. Defaults to `false`.\n */\n ephemeral?: boolean;\n}\n\n/**\n * Auto-inherit configuration. When enabled for a given agent origin, agents\n * with no explicit `tools:` declaration receive every registered ToolProvider\n * plugin tool whose author marked `autoInheritable: true`. Tools without that\n * flag — destructive, state-mutating, or privilege-sensitive — never spread\n * automatically and must be wired via `tools:`, `toolkits:`, or `fromPlugin`.\n *\n * Defaults are `false` for both origins (safe-by-default): developers must\n * consciously opt an origin in to any auto-inherit behaviour.\n */\nexport interface AutoInheritToolsConfig {\n /** Default for agents loaded from markdown files. Default: `false`. */\n file?: boolean;\n /** Default for code-defined agents (via `agents: { foo: createAgent(...) }`). Default: `false`. */\n code?: boolean;\n}\n\nexport interface AgentsPluginConfig extends BasePluginConfig {\n /** Directory of agent packages (`<id>/agent.md` each). Default `./config/agents`. Set to `false` to disable. */\n dir?: string | false;\n /** Code-defined agents, merged with file-loaded ones (code wins on key collision). */\n agents?: Record<string, AgentDefinition>;\n /** Agent used when clients don't specify one. Defaults to the first-registered agent or the file with `default: true` frontmatter. */\n defaultAgent?: string;\n /** Default model for agents that don't specify their own (in code or frontmatter). */\n defaultModel?: AgentAdapter | Promise<AgentAdapter> | string;\n /** Ambient tool library. Keys may be referenced by markdown frontmatter via `tools: [key1, key2]`. */\n tools?: Record<string, AgentTool>;\n /** Whether to auto-inherit every ToolProvider plugin's toolkit. Accepts a boolean shorthand. */\n autoInheritTools?: boolean | AutoInheritToolsConfig;\n /** Persistent thread store. Default: in-memory. */\n threadStore?: ThreadStore;\n /** Customize or disable the AppKit base system prompt. */\n baseSystemPrompt?: BaseSystemPromptOption;\n /**\n * MCP server host policy. By default only same-origin Databricks workspace\n * URLs may be used as MCP endpoints; custom hosts must be explicitly\n * allowlisted here. Workspace credentials (SP / OBO) are never forwarded\n * to non-workspace hosts.\n */\n mcp?: McpHostPolicyConfig;\n /**\n * Human-in-the-loop approval gate for destructive tool calls. When enabled\n * (the default), the agents plugin emits an `appkit.approval_pending` SSE\n * event before executing any tool annotated `destructive: true` and waits\n * for a `POST /chat/approve` decision from the same user who initiated the\n * stream. A missing decision after `timeoutMs` auto-denies the call.\n */\n approval?: {\n /** Require human approval for tools annotated `destructive: true`. Default: `true`. */\n requireForDestructive?: boolean;\n /** Milliseconds to wait before auto-denying. Default: 60_000. */\n timeoutMs?: number;\n };\n /**\n * Runtime resource limits applied during agent execution. Defaults are\n * tuned to protect a single-instance deployment from a misbehaving user or\n * a runaway prompt injection; tighten or relax as appropriate for the\n * deployment's scale and trust model. Request-body caps (chat message\n * size, invocations input size / length) are enforced statically by the\n * Zod schemas and are not configurable here.\n */\n limits?: {\n /**\n * Max concurrent chat streams a single user may have open. Subsequent\n * `POST /chat` requests from that user while at-limit are rejected with\n * HTTP 429. Default: `5`.\n */\n maxConcurrentStreamsPerUser?: number;\n /**\n * Max tool invocations per agent run (across the full tool-call graph,\n * including sub-agent invocations). A run that exceeds the budget is\n * aborted with a terminal error event. Default: `50`.\n */\n maxToolCalls?: number;\n /**\n * Max sub-agent recursion depth. Protects against a prompt-injected\n * agent that delegates to a sub-agent which in turn delegates back to\n * itself (directly or transitively). Default: `3`.\n */\n maxSubAgentDepth?: number;\n /**\n * Per-call timeout for tools dispatched through `PluginContext`\n * (toolkit-routed tools — analytics SQL warehouse queries, Genie\n * messages, Lakebase queries). Independent of `maxToolCalls`: the\n * budget caps how many tools fire per run, this caps how long any\n * single tool call may run. The signal handed to plugin tool\n * implementations combines this timeout with the parent stream's\n * abort signal via `AbortSignal.any`. Function and MCP tools have\n * their own timeouts in their respective adapters and ignore this\n * setting. Default: `300_000` (5 minutes) — generous enough for cold\n * SQL Warehouse round-trips and long Genie conversations.\n */\n toolCallTimeoutMs?: number;\n };\n}\n\n/** Internal tool-index entry after a tool record has been resolved to a dispatchable form. */\nexport type ResolvedToolEntry =\n | {\n source: \"toolkit\";\n pluginName: string;\n localName: string;\n def: AgentToolDefinition;\n }\n | {\n source: \"function\";\n functionTool: FunctionTool;\n def: AgentToolDefinition;\n }\n | {\n source: \"mcp\";\n mcpToolName: string;\n def: AgentToolDefinition;\n }\n | {\n source: \"subagent\";\n agentName: string;\n def: AgentToolDefinition;\n };\n\nexport interface RegisteredAgent {\n name: string;\n instructions: string;\n adapter: AgentAdapter;\n toolIndex: Map<string, ResolvedToolEntry>;\n baseSystemPrompt?: BaseSystemPromptOption;\n maxSteps?: number;\n maxTokens?: number;\n /** Mirrors `AgentDefinition.ephemeral` — skip thread persistence. */\n ephemeral?: boolean;\n}\n\n/**\n * Type guard for `ToolkitEntry` — used by the agents plugin to differentiate\n * toolkit references from inline tools in a mixed `tools` record.\n */\nexport function isToolkitEntry(value: unknown): value is ToolkitEntry {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as { __toolkitRef?: unknown }).__toolkitRef === true\n );\n}\n"],"mappings":";;;;;AAuOA,SAAgB,eAAe,OAAuC;AACpE,QACE,OAAO,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB"}
@@ -48,6 +48,7 @@ declare function createApp<T extends PluginData<PluginConstructor, unknown, stri
48
48
  cache?: CacheConfig;
49
49
  client?: WorkspaceClient;
50
50
  onPluginsReady?: (appkit: PluginMap<T>) => void | Promise<void>;
51
+ disableInternalTelemetry?: boolean;
51
52
  }): Promise<PluginMap<T>>;
52
53
  //#endregion
53
54
  export { createApp };
@@ -1 +1 @@
1
- {"version":3,"file":"appkit.d.ts","names":[],"sources":["../../src/core/appkit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgRsB,SAAA,WACV,UAAA,CAAW,iBAAA,qBAAA,CAErB,MAAA;EACE,OAAA,GAAU,CAAA;EACV,SAAA,GAAY,eAAA;EACZ,KAAA,GAAQ,WAAA;EACR,MAAA,GAAS,eAAA;EACT,cAAA,IAAkB,MAAA,EAAQ,SAAA,CAAU,CAAA,aAAc,OAAA;AAAA,IAEnD,OAAA,CAAQ,SAAA,CAAU,CAAA"}
1
+ {"version":3,"file":"appkit.d.ts","names":[],"sources":["../../src/core/appkit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2TsB,SAAA,WACV,UAAA,CAAW,iBAAA,qBAAA,CAErB,MAAA;EACE,OAAA,GAAU,CAAA;EACV,SAAA,GAAY,eAAA;EACZ,KAAA,GAAQ,WAAA;EACR,MAAA,GAAS,eAAA;EACT,cAAA,IAAkB,MAAA,EAAQ,SAAA,CAAU,CAAA,aAAc,OAAA;EAClD,wBAAA;AAAA,IAED,OAAA,CAAQ,SAAA,CAAU,CAAA"}