@databricks/appkit 0.45.0 → 0.47.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.
- package/CLAUDE.md +1 -0
- package/dist/agents/databricks.d.ts +25 -1
- package/dist/agents/databricks.d.ts.map +1 -1
- package/dist/agents/databricks.js +20 -1
- package/dist/agents/databricks.js.map +1 -1
- package/dist/app/index.d.ts +49 -2
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +87 -10
- package/dist/app/index.js.map +1 -1
- package/dist/appkit/package.js +1 -1
- package/dist/beta.d.ts +2 -2
- package/dist/cli/commands/generate-types.js +9 -4
- package/dist/cli/commands/generate-types.js.map +1 -1
- package/dist/core/agent/load-agents.d.ts.map +1 -1
- package/dist/core/agent/load-agents.js +52 -0
- package/dist/core/agent/load-agents.js.map +1 -1
- package/dist/core/agent/types.d.ts +11 -0
- package/dist/core/agent/types.d.ts.map +1 -1
- package/dist/core/agent/types.js.map +1 -1
- package/dist/core/appkit.d.ts.map +1 -1
- package/dist/core/appkit.js +2 -0
- package/dist/core/appkit.js.map +1 -1
- package/dist/core/lifecycle-manager.js +183 -0
- package/dist/core/lifecycle-manager.js.map +1 -0
- package/dist/core/plugin-context.d.ts +19 -1
- package/dist/core/plugin-context.d.ts.map +1 -1
- package/dist/core/plugin-context.js +9 -3
- package/dist/core/plugin-context.js.map +1 -1
- package/dist/plugin/plugin.d.ts.map +1 -1
- package/dist/plugin/plugin.js +2 -1
- package/dist/plugin/plugin.js.map +1 -1
- package/dist/plugins/agents/agents.d.ts.map +1 -1
- package/dist/plugins/agents/agents.js +3 -3
- package/dist/plugins/agents/agents.js.map +1 -1
- package/dist/plugins/analytics/analytics.d.ts +16 -0
- package/dist/plugins/analytics/analytics.d.ts.map +1 -1
- package/dist/plugins/analytics/analytics.js +162 -1
- package/dist/plugins/analytics/analytics.js.map +1 -1
- package/dist/plugins/analytics/metric.js +7 -0
- package/dist/plugins/analytics/mv/cache.js +51 -0
- package/dist/plugins/analytics/mv/cache.js.map +1 -0
- package/dist/plugins/analytics/mv/constants.js +72 -0
- package/dist/plugins/analytics/mv/constants.js.map +1 -0
- package/dist/plugins/analytics/mv/formatters.js +151 -0
- package/dist/plugins/analytics/mv/formatters.js.map +1 -0
- package/dist/plugins/analytics/mv/index.js +6 -0
- package/dist/plugins/analytics/mv/registry.js +55 -0
- package/dist/plugins/analytics/mv/registry.js.map +1 -0
- package/dist/plugins/analytics/mv/schemas.js +178 -0
- package/dist/plugins/analytics/mv/schemas.js.map +1 -0
- package/dist/plugins/analytics/types.js.map +1 -1
- package/dist/plugins/lakebase/lakebase.d.ts +10 -2
- package/dist/plugins/lakebase/lakebase.d.ts.map +1 -1
- package/dist/plugins/lakebase/lakebase.js +18 -7
- package/dist/plugins/lakebase/lakebase.js.map +1 -1
- package/dist/plugins/server/index.d.ts +51 -1
- package/dist/plugins/server/index.d.ts.map +1 -1
- package/dist/plugins/server/index.js +110 -23
- package/dist/plugins/server/index.js.map +1 -1
- package/dist/registry/resource-registry.d.ts +9 -1
- package/dist/registry/resource-registry.d.ts.map +1 -1
- package/dist/registry/resource-registry.js +22 -5
- package/dist/registry/resource-registry.js.map +1 -1
- package/dist/schemas/metric-fqn.js +14 -0
- package/dist/schemas/metric-fqn.js.map +1 -0
- package/dist/shared/src/execute.d.ts +1 -3
- package/dist/shared/src/execute.d.ts.map +1 -1
- package/dist/shared/src/plugin.d.ts +16 -0
- package/dist/shared/src/plugin.d.ts.map +1 -1
- package/dist/shared/src/schemas/metric-fqn.js +78 -46
- package/dist/shared/src/schemas/metric-fqn.js.map +1 -1
- package/dist/shared/src/schemas/metric-source.js +90 -0
- package/dist/shared/src/schemas/metric-source.js.map +1 -0
- package/dist/stream/buffers.js +1 -1
- package/dist/stream/buffers.js.map +1 -1
- package/dist/stream/defaults.js +0 -2
- package/dist/stream/defaults.js.map +1 -1
- package/dist/stream/stream-manager.d.ts +2 -2
- package/dist/stream/stream-manager.d.ts.map +1 -1
- package/dist/stream/stream-manager.js +26 -24
- package/dist/stream/stream-manager.js.map +1 -1
- package/dist/stream/stream-registry.js +30 -23
- package/dist/stream/stream-registry.js.map +1 -1
- package/dist/stream/timers.js +17 -0
- package/dist/stream/timers.js.map +1 -0
- package/dist/stream/types.js.map +1 -1
- package/dist/telemetry/telemetry-manager.js +19 -13
- package/dist/telemetry/telemetry-manager.js.map +1 -1
- package/dist/type-generator/index.js +14 -7
- package/dist/type-generator/index.js.map +1 -1
- package/dist/type-generator/mv-registry/config.js +13 -31
- package/dist/type-generator/mv-registry/config.js.map +1 -1
- package/dist/type-generator/mv-registry/describe.js +1 -31
- package/dist/type-generator/mv-registry/describe.js.map +1 -1
- package/dist/type-generator/mv-registry/sync.js +1 -1
- package/dist/type-generator/mv-registry/sync.js.map +1 -1
- package/dist/type-generator/vite-plugin.d.ts +5 -1
- package/dist/type-generator/vite-plugin.d.ts.map +1 -1
- package/dist/type-generator/vite-plugin.js +21 -4
- package/dist/type-generator/vite-plugin.js.map +1 -1
- package/dist/utils/safe-handler.js +28 -0
- package/dist/utils/safe-handler.js.map +1 -0
- package/docs/api/appkit/Class.Plugin.md +2 -0
- package/docs/api/appkit/Class.ResourceRegistry.md +1 -1
- package/docs/api/appkit/Interface.AgentDefinition.md +11 -0
- package/docs/api/appkit/Interface.GenerationParams.md +58 -0
- package/docs/api/appkit/Interface.RegisteredAgent.md +11 -0
- package/docs/api/appkit.md +1 -0
- package/docs/development/type-generation.md +3 -3
- package/docs/plugins/analytics.md +172 -23
- package/llms.txt +1 -0
- package/package.json +1 -1
- package/sbom.cdx.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -125,6 +125,7 @@ npx @databricks/appkit docs <query>
|
|
|
125
125
|
- [Interface: FileResource](./docs/api/appkit/Interface.FileResource.md): Describes the file or directory being acted upon.
|
|
126
126
|
- [Interface: FunctionTool](./docs/api/appkit/Interface.FunctionTool.md): Properties
|
|
127
127
|
- [Interface: GenerateDatabaseCredentialRequest](./docs/api/appkit/Interface.GenerateDatabaseCredentialRequest.md): Request parameters for generating database OAuth credentials
|
|
128
|
+
- [Interface: GenerationParams](./docs/api/appkit/Interface.GenerationParams.md): Optional generation parameters forwarded to the OpenAI-compatible serving
|
|
128
129
|
- [Interface: IJobsConfig](./docs/api/appkit/Interface.IJobsConfig.md): Configuration for the Jobs plugin.
|
|
129
130
|
- [Interface: ITelemetry](./docs/api/appkit/Interface.ITelemetry.md): Plugin-facing interface for OpenTelemetry instrumentation.
|
|
130
131
|
- [Interface: JobAPI](./docs/api/appkit/Interface.JobAPI.md): User-facing API for a single configured job.
|
|
@@ -2,6 +2,24 @@ import { AgentAdapter, AgentEvent, AgentInput, AgentRunContext } from "../shared
|
|
|
2
2
|
import "../shared/src/index.js";
|
|
3
3
|
|
|
4
4
|
//#region src/agents/databricks.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Optional generation parameters forwarded to the OpenAI-compatible serving
|
|
7
|
+
* request body. Names match the serving API wire keys. Only keys that are set
|
|
8
|
+
* are sent — undefined values are omitted so the endpoint applies its own
|
|
9
|
+
* defaults. Ranges are not validated here; the serving endpoint validates.
|
|
10
|
+
*/
|
|
11
|
+
interface GenerationParams {
|
|
12
|
+
/** Sampling temperature. */
|
|
13
|
+
temperature?: number;
|
|
14
|
+
/** Nucleus sampling probability mass (`top_p`). */
|
|
15
|
+
top_p?: number;
|
|
16
|
+
/** Stop sequence(s) that end generation. */
|
|
17
|
+
stop?: string | string[];
|
|
18
|
+
/** Penalize tokens by frequency. */
|
|
19
|
+
frequency_penalty?: number;
|
|
20
|
+
/** Penalize tokens by prior presence. */
|
|
21
|
+
presence_penalty?: number;
|
|
22
|
+
}
|
|
5
23
|
/**
|
|
6
24
|
* Transport shim: given an OpenAI-compatible request body, returns the raw
|
|
7
25
|
* SSE byte stream from the serving endpoint. Injected at construction time so
|
|
@@ -19,6 +37,8 @@ interface RawFetchAdapterOptions {
|
|
|
19
37
|
authenticate: () => Promise<Record<string, string>>;
|
|
20
38
|
maxSteps?: number;
|
|
21
39
|
maxTokens?: number;
|
|
40
|
+
/** Optional generation params forwarded to the serving request body. */
|
|
41
|
+
generationParams?: GenerationParams;
|
|
22
42
|
/** Max length of one SSE line (including an incomplete tail in the buffer). */
|
|
23
43
|
maxSseLineChars?: number;
|
|
24
44
|
/** Max total length of assistant `delta.content` across the stream. */
|
|
@@ -36,6 +56,7 @@ interface StreamBodyAdapterOptions {
|
|
|
36
56
|
streamBody: StreamBody;
|
|
37
57
|
maxSteps?: number;
|
|
38
58
|
maxTokens?: number;
|
|
59
|
+
generationParams?: GenerationParams;
|
|
39
60
|
maxSseLineChars?: number;
|
|
40
61
|
maxStreamTextChars?: number;
|
|
41
62
|
maxToolArgumentsChars?: number;
|
|
@@ -58,6 +79,7 @@ interface ServingEndpointOptions {
|
|
|
58
79
|
endpointName: string;
|
|
59
80
|
maxSteps?: number;
|
|
60
81
|
maxTokens?: number;
|
|
82
|
+
generationParams?: GenerationParams;
|
|
61
83
|
maxSseLineChars?: number;
|
|
62
84
|
maxStreamTextChars?: number;
|
|
63
85
|
maxToolArgumentsChars?: number;
|
|
@@ -65,6 +87,7 @@ interface ServingEndpointOptions {
|
|
|
65
87
|
interface ModelServingOptions {
|
|
66
88
|
maxSteps?: number;
|
|
67
89
|
maxTokens?: number;
|
|
90
|
+
generationParams?: GenerationParams;
|
|
68
91
|
workspaceClient?: WorkspaceClientLike;
|
|
69
92
|
maxSseLineChars?: number;
|
|
70
93
|
maxStreamTextChars?: number;
|
|
@@ -117,6 +140,7 @@ declare class DatabricksAdapter implements AgentAdapter {
|
|
|
117
140
|
private streamBody;
|
|
118
141
|
private maxSteps;
|
|
119
142
|
private maxTokens;
|
|
143
|
+
private generationParams;
|
|
120
144
|
private maxSseLineChars;
|
|
121
145
|
private maxStreamTextChars;
|
|
122
146
|
private maxToolArgumentsChars;
|
|
@@ -176,5 +200,5 @@ declare function parseTextToolCalls(text: string): Array<{
|
|
|
176
200
|
args: unknown;
|
|
177
201
|
}>;
|
|
178
202
|
//#endregion
|
|
179
|
-
export { DatabricksAdapter, parseTextToolCalls };
|
|
203
|
+
export { DatabricksAdapter, GenerationParams, parseTextToolCalls };
|
|
180
204
|
//# sourceMappingURL=databricks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"databricks.d.ts","names":[],"sources":["../../src/agents/databricks.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"databricks.d.ts","names":[],"sources":["../../src/agents/databricks.ts"],"mappings":";;;;;;;AAmCA;;;UAAiB,gBAAA;EAEf;EAAA,WAAA;EAIA;EAFA,KAAA;EAMA;EAJA,IAAA;EAIgB;EAFhB,iBAAA;EAkEa;EAhEb,gBAAA;AAAA;;;;;;;KAgEG,UAAA,IACH,IAAA,EAAM,MAAA,mBACN,MAAA,GAAS,WAAA,KACN,OAAA,CAAQ,cAAA,CAAe,UAAA;;;;;;UAOlB,sBAAA;EACR,WAAA;EACA,YAAA,QAAoB,OAAA,CAAQ,MAAA;EAC5B,QAAA;EACA,SAAA;;EAEA,gBAAA,GAAmB,gBAAA;EAJC;EAMpB,eAAA;EAFmC;EAInC,kBAAA;EATA;EAWA,qBAAA;AAAA;;;;;;;UASQ,wBAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,KAGG,wBAAA,GACD,sBAAA,GACA,wBAAA;;;;;;;;UAeM,mBAAA;EACR,SAAA;IACE,OAAA,CAAQ,OAAA,EAAS,MAAA,oBAA0B,OAAA;EAAA;AAAA;AAAA,UAIrC,sBAAA;EACR,eAAA,EAAiB,mBAAA;EACjB,YAAA;EACA,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;AAAA,UAGQ,mBAAA;EACR,QAAA;EACA,SAAA;EACA,gBAAA,GAAmB,gBAAA;EACnB,eAAA,GAAkB,mBAAA;EAClB,eAAA;EACA,kBAAA;EACA,qBAAA;AAAA;;;;;;;;;;;AAVqB;;;;;;;;;;;;;;;AAkGvB;;;;;;;;;;;;;;;;;;cAAa,iBAAA,YAA6B,YAAA;EAAA,QAChC,UAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAAA,QACA,gBAAA;EAAA,QACA,eAAA;EAAA,QACA,kBAAA;EAAA,QACA,qBAAA;cAEI,OAAA,EAAS,wBAAA;EAAT;;;;;;;;EAAA,OAiDC,mBAAA,CACX,OAAA,EAAS,sBAAA,GACR,OAAA,CAAQ,iBAAA;EAqDT;;;;;;;;;;;;;;;;;AAiaJ;;;EAjaI,OAFW,gBAAA,CACX,YAAA,WACA,OAAA,GAAU,mBAAA,GACT,OAAA,CAAQ,iBAAA;EAiCJ,GAAA,CACL,KAAA,EAAO,UAAA,EACP,OAAA,EAAS,eAAA,GACR,cAAA,CAAe,UAAA;EA8XjB;EAAA,QAvUc,iBAAA;EAAA,QA8CA,gBAAA;EAAA,QAyKA,gBAAA;EAgHY;;;;;EAAA,QA5EnB,aAAA;EAAA,QAgDA,UAAA;AAAA;;;;;;;;iBA0BM,kBAAA,CACd,IAAA,WACC,KAAA;EAAQ,IAAA;EAAc,IAAA;AAAA"}
|
|
@@ -15,6 +15,20 @@ const RAW_FETCH_DEFAULT_TIMEOUT_MS = 12e4;
|
|
|
15
15
|
function isRecord(value) {
|
|
16
16
|
return typeof value === "object" && value !== null;
|
|
17
17
|
}
|
|
18
|
+
const GENERATION_PARAM_KEYS = [
|
|
19
|
+
"temperature",
|
|
20
|
+
"top_p",
|
|
21
|
+
"stop",
|
|
22
|
+
"frequency_penalty",
|
|
23
|
+
"presence_penalty"
|
|
24
|
+
];
|
|
25
|
+
/** Copy only the set generation params onto the request body. */
|
|
26
|
+
function applyGenerationParams(body, params) {
|
|
27
|
+
for (const key of GENERATION_PARAM_KEYS) {
|
|
28
|
+
const value = params[key];
|
|
29
|
+
if (value !== void 0) body[key] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
18
32
|
function extractLlamaToolJsonSlice(text) {
|
|
19
33
|
const start = text.indexOf("[{");
|
|
20
34
|
if (start < 0) return void 0;
|
|
@@ -88,12 +102,14 @@ var DatabricksAdapter = class DatabricksAdapter {
|
|
|
88
102
|
streamBody;
|
|
89
103
|
maxSteps;
|
|
90
104
|
maxTokens;
|
|
105
|
+
generationParams;
|
|
91
106
|
maxSseLineChars;
|
|
92
107
|
maxStreamTextChars;
|
|
93
108
|
maxToolArgumentsChars;
|
|
94
109
|
constructor(options) {
|
|
95
110
|
this.maxSteps = options.maxSteps ?? 10;
|
|
96
111
|
this.maxTokens = options.maxTokens ?? 4096;
|
|
112
|
+
this.generationParams = options.generationParams ?? {};
|
|
97
113
|
this.maxSseLineChars = options.maxSseLineChars ?? DEFAULT_MAX_SSE_LINE_CHARS;
|
|
98
114
|
this.maxStreamTextChars = options.maxStreamTextChars ?? DEFAULT_MAX_STREAM_TEXT_CHARS;
|
|
99
115
|
this.maxToolArgumentsChars = options.maxToolArgumentsChars ?? DEFAULT_MAX_TOOL_ARGUMENT_CHARS;
|
|
@@ -131,11 +147,12 @@ var DatabricksAdapter = class DatabricksAdapter {
|
|
|
131
147
|
* serving surface — no bespoke `fetch()` + `authenticate()` plumbing.
|
|
132
148
|
*/
|
|
133
149
|
static async fromServingEndpoint(options) {
|
|
134
|
-
const { workspaceClient, endpointName, maxSteps, maxTokens, maxSseLineChars, maxStreamTextChars, maxToolArgumentsChars } = options;
|
|
150
|
+
const { workspaceClient, endpointName, maxSteps, maxTokens, generationParams, maxSseLineChars, maxStreamTextChars, maxToolArgumentsChars } = options;
|
|
135
151
|
return new DatabricksAdapter({
|
|
136
152
|
streamBody: (body, signal) => stream(workspaceClient, endpointName, body, signal),
|
|
137
153
|
maxSteps,
|
|
138
154
|
maxTokens,
|
|
155
|
+
generationParams,
|
|
139
156
|
maxSseLineChars,
|
|
140
157
|
maxStreamTextChars,
|
|
141
158
|
maxToolArgumentsChars
|
|
@@ -171,6 +188,7 @@ var DatabricksAdapter = class DatabricksAdapter {
|
|
|
171
188
|
endpointName: resolvedEndpoint,
|
|
172
189
|
maxSteps: options?.maxSteps,
|
|
173
190
|
maxTokens: options?.maxTokens,
|
|
191
|
+
generationParams: options?.generationParams,
|
|
174
192
|
maxSseLineChars: options?.maxSseLineChars,
|
|
175
193
|
maxStreamTextChars: options?.maxStreamTextChars,
|
|
176
194
|
maxToolArgumentsChars: options?.maxToolArgumentsChars
|
|
@@ -262,6 +280,7 @@ var DatabricksAdapter = class DatabricksAdapter {
|
|
|
262
280
|
stream: true,
|
|
263
281
|
max_tokens: this.maxTokens
|
|
264
282
|
};
|
|
283
|
+
applyGenerationParams(body, this.generationParams);
|
|
265
284
|
if (tools.length > 0) body.tools = tools;
|
|
266
285
|
let responseBody;
|
|
267
286
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"databricks.js","names":["servingStream"],"sources":["../../src/agents/databricks.ts"],"sourcesContent":["import type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n AgentRunContext,\n AgentToolDefinition,\n} from \"shared\";\nimport { stream as servingStream } from \"../connectors/serving/client\";\nimport { APPKIT_USER_AGENT, getClientOptions } from \"../context/client-options\";\n\n/** Default cap for a single incomplete SSE line tail (DoS guard). */\nconst DEFAULT_MAX_SSE_LINE_CHARS = 1024 * 1024;\n\n/** Default cap for accumulated assistant text from `delta.content`. */\nconst DEFAULT_MAX_STREAM_TEXT_CHARS = 4 * 1024 * 1024;\n\n/** Default cap for accumulated JSON arguments per streamed tool call index. */\nconst DEFAULT_MAX_TOOL_ARGUMENT_CHARS = 2 * 1024 * 1024;\n\n/** Cap text length before running Python-style tool-call regex (ReDoS guard). */\nconst PYTHON_STYLE_TOOL_PARSE_MAX_INPUT = 64 * 1024;\n\n/** Fallback HTTP timeout when the raw fetch adapter path receives no AbortSignal from the runner. */\nconst RAW_FETCH_DEFAULT_TIMEOUT_MS = 120_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction extractLlamaToolJsonSlice(text: string): string | undefined {\n const start = text.indexOf(\"[{\");\n if (start < 0) return undefined;\n const endBracket = text.lastIndexOf(\"}]\");\n if (endBracket < start) return undefined;\n return text.slice(start, endBracket + 2);\n}\n\n/** OpenAI SSE payload: `{ choices: [{ delta }] }`. */\nfunction openAiChoicesDelta(parsed: unknown): unknown {\n if (!isRecord(parsed)) return undefined;\n const choices = parsed.choices;\n if (!Array.isArray(choices) || choices.length < 1) return undefined;\n const first = choices[0];\n if (!isRecord(first)) return undefined;\n return first.delta;\n}\n\nfunction isStreamingDeltaToolCall(value: unknown): value is DeltaToolCall {\n if (!isRecord(value)) return false;\n return typeof value.index === \"number\";\n}\n\nfunction throwIfExceedsStreamLimit(\n label: string,\n currentLength: number,\n chunk: string,\n max: number,\n): void {\n if (currentLength + chunk.length > max) {\n throw new Error(\n `DatabricksAdapter: ${label} exceeds configured limit (${max} UTF-16 code units)`,\n );\n }\n}\n\n/**\n * Transport shim: given an OpenAI-compatible request body, returns the raw\n * SSE byte stream from the serving endpoint. Injected at construction time so\n * callers can swap in the workspace SDK (factory paths), a bare `fetch`\n * (the raw constructor), or a test fake.\n */\ntype StreamBody = (\n body: Record<string, unknown>,\n signal?: AbortSignal,\n) => Promise<ReadableStream<Uint8Array>>;\n\n/**\n * Escape-hatch options: provide an `endpointUrl` + `authenticate()` and the\n * adapter uses a bare `fetch()` to call it. Useful for tests and for pointing\n * the adapter at non-workspace endpoints (reverse proxies, mocks).\n */\ninterface RawFetchAdapterOptions {\n endpointUrl: string;\n authenticate: () => Promise<Record<string, string>>;\n maxSteps?: number;\n maxTokens?: number;\n /** Max length of one SSE line (including an incomplete tail in the buffer). */\n maxSseLineChars?: number;\n /** Max total length of assistant `delta.content` across the stream. */\n maxStreamTextChars?: number;\n /** Max length of streamed `function.arguments` per tool call index. */\n maxToolArgumentsChars?: number;\n}\n\n/**\n * Preferred options: caller provides the transport function directly.\n * The `fromServingEndpoint` / `fromModelServing` factories use this to route\n * through `connectors/serving/stream`, which centralises URL encoding, auth\n * via the SDK's `apiClient.request`, and any future retries/telemetry.\n */\ninterface StreamBodyAdapterOptions {\n streamBody: StreamBody;\n maxSteps?: number;\n maxTokens?: number;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ntype DatabricksAdapterOptions =\n | RawFetchAdapterOptions\n | StreamBodyAdapterOptions;\n\nfunction isStreamBodyOptions(\n o: DatabricksAdapterOptions,\n): o is StreamBodyAdapterOptions {\n return \"streamBody\" in o;\n}\n\n/**\n * Duck-typed subset of the Databricks SDK `WorkspaceClient`. Callers of\n * `fromServingEndpoint` and `fromModelServing` pass a real `WorkspaceClient`,\n * but we only need the `apiClient.request` surface — so we declare the minimal\n * interface rather than importing the SDK type directly. This keeps the adapter\n * free of a hard compile-time dependency on `@databricks/sdk-experimental`.\n */\ninterface WorkspaceClientLike {\n apiClient: {\n request(options: Record<string, unknown>): Promise<unknown>;\n };\n}\n\ninterface ServingEndpointOptions {\n workspaceClient: WorkspaceClientLike;\n endpointName: string;\n maxSteps?: number;\n maxTokens?: number;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface ModelServingOptions {\n maxSteps?: number;\n maxTokens?: number;\n workspaceClient?: WorkspaceClientLike;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface OpenAIMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | null;\n tool_calls?: OpenAIToolCall[];\n tool_call_id?: string;\n}\n\ninterface OpenAIToolCall {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n /**\n * Opaque Vertex/Gemini \"thought signature\" blob the request must echo\n * back verbatim on the next turn. Vertex's OpenAI-compat proxy emits\n * this as `thoughtSignature` (camelCase) at the top level of the\n * tool_call delta (verified against `gemini-3.1-flash-lite-preview`),\n * and accepts the same spelling back on outbound. Non-Gemini endpoints\n * (Claude on Databricks, external OpenAI-compat models, Llama, etc.)\n * leave this undefined and the serializer omits the key.\n * See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/thought-signatures\n */\n thoughtSignature?: string;\n}\n\ninterface OpenAITool {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: unknown;\n };\n}\n\ninterface DeltaToolCall {\n index: number;\n id?: string;\n type?: string;\n function?: { name?: string; arguments?: string };\n /** See {@link OpenAIToolCall.thoughtSignature}. */\n thoughtSignature?: string;\n}\n\n/**\n * Adapter that talks directly to Databricks Model Serving `/invocations` endpoint.\n *\n * No dependency on the Vercel AI SDK or LangChain. Uses raw `fetch()` to POST\n * OpenAI-compatible payloads and parses the SSE stream itself. Calls\n * `authenticate()` per-request so tokens are always fresh.\n *\n * Handles both structured `tool_calls` responses and text-based tool call\n * fallback parsing for models that output tool calls as text.\n *\n * @example Using the factory (recommended)\n * ```ts\n * import { createApp, createAgent, agents } from \"@databricks/appkit\";\n * import { DatabricksAdapter } from \"@databricks/appkit/beta\";\n * import { WorkspaceClient } from \"@databricks/sdk-experimental\";\n *\n * const adapter = DatabricksAdapter.fromServingEndpoint({\n * workspaceClient: new WorkspaceClient({}),\n * endpointName: \"my-endpoint\",\n * });\n *\n * await createApp({\n * plugins: [\n * agents({\n * agents: {\n * assistant: createAgent({\n * instructions: \"You are a helpful assistant.\",\n * model: adapter,\n * }),\n * },\n * }),\n * ],\n * });\n * ```\n *\n * @example Using the raw constructor\n * ```ts\n * const adapter = new DatabricksAdapter({\n * endpointUrl: \"https://host/serving-endpoints/my-endpoint/invocations\",\n * authenticate: async () => ({ Authorization: `Bearer ${token}` }),\n * });\n * ```\n */\nexport class DatabricksAdapter implements AgentAdapter {\n private streamBody: StreamBody;\n private maxSteps: number;\n private maxTokens: number;\n private maxSseLineChars: number;\n private maxStreamTextChars: number;\n private maxToolArgumentsChars: number;\n\n constructor(options: DatabricksAdapterOptions) {\n this.maxSteps = options.maxSteps ?? 10;\n this.maxTokens = options.maxTokens ?? 4096;\n this.maxSseLineChars =\n options.maxSseLineChars ?? DEFAULT_MAX_SSE_LINE_CHARS;\n this.maxStreamTextChars =\n options.maxStreamTextChars ?? DEFAULT_MAX_STREAM_TEXT_CHARS;\n this.maxToolArgumentsChars =\n options.maxToolArgumentsChars ?? DEFAULT_MAX_TOOL_ARGUMENT_CHARS;\n\n if (isStreamBodyOptions(options)) {\n this.streamBody = options.streamBody;\n } else {\n const { endpointUrl, authenticate } = options;\n this.streamBody = async (body, signal) => {\n const fetchSignal =\n signal ?? AbortSignal.timeout(RAW_FETCH_DEFAULT_TIMEOUT_MS);\n const authHeaders = await authenticate();\n const response = await fetch(endpointUrl, {\n method: \"POST\",\n headers: {\n \"User-Agent\": APPKIT_USER_AGENT,\n \"Content-Type\": \"application/json\",\n ...authHeaders,\n },\n body: JSON.stringify(body),\n signal: fetchSignal,\n });\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n throw new Error(\n `Databricks API error (${response.status}): ${errorText}`,\n );\n }\n if (!response.body) throw new Error(\"No response body\");\n return response.body;\n };\n }\n }\n\n /**\n * Creates a DatabricksAdapter for a Databricks Model Serving endpoint.\n *\n * Routes through the shared `connectors/serving/stream` helper, which\n * delegates to the SDK's `apiClient.request({ raw: true })`. That gives the\n * adapter centralised URL encoding + authentication with the rest of the\n * serving surface — no bespoke `fetch()` + `authenticate()` plumbing.\n */\n static async fromServingEndpoint(\n options: ServingEndpointOptions,\n ): Promise<DatabricksAdapter> {\n const {\n workspaceClient,\n endpointName,\n maxSteps,\n maxTokens,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n } = options;\n return new DatabricksAdapter({\n streamBody: (body, signal) =>\n // Cast through the structural shape: the connector types\n // `workspaceClient` as the SDK's concrete `WorkspaceClient`, but we\n // only need `apiClient.request`.\n servingStream(\n workspaceClient as unknown as Parameters<typeof servingStream>[0],\n endpointName,\n body,\n signal,\n ),\n maxSteps,\n maxTokens,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n });\n }\n\n /**\n * Creates a DatabricksAdapter from a Model Serving endpoint name.\n * Auto-creates a WorkspaceClient internally. Reads the endpoint name\n * from the argument or the `DATABRICKS_SERVING_ENDPOINT_NAME` env var.\n *\n * @example\n * ```ts\n * // Reads endpoint from DATABRICKS_SERVING_ENDPOINT_NAME env var\n * const adapter = await DatabricksAdapter.fromModelServing();\n *\n * // Explicit endpoint\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\");\n *\n * // With options\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\", {\n * maxSteps: 5,\n * maxTokens: 2048,\n * });\n * ```\n */\n static async fromModelServing(\n endpointName?: string,\n options?: ModelServingOptions,\n ): Promise<DatabricksAdapter> {\n const resolvedEndpoint =\n endpointName ?? process.env.DATABRICKS_SERVING_ENDPOINT_NAME;\n\n if (!resolvedEndpoint) {\n throw new Error(\n \"No endpoint name provided and DATABRICKS_SERVING_ENDPOINT_NAME env var is not set. \" +\n \"Pass an endpoint name or set DATABRICKS_SERVING_ENDPOINT_NAME.\",\n );\n }\n\n let workspaceClient: WorkspaceClientLike | undefined =\n options?.workspaceClient;\n if (!workspaceClient) {\n const sdk = await import(\"@databricks/sdk-experimental\");\n workspaceClient = new sdk.WorkspaceClient(\n {},\n getClientOptions(),\n ) as unknown as WorkspaceClientLike;\n }\n\n return DatabricksAdapter.fromServingEndpoint({\n workspaceClient,\n endpointName: resolvedEndpoint,\n maxSteps: options?.maxSteps,\n maxTokens: options?.maxTokens,\n maxSseLineChars: options?.maxSseLineChars,\n maxStreamTextChars: options?.maxStreamTextChars,\n maxToolArgumentsChars: options?.maxToolArgumentsChars,\n });\n }\n\n async *run(\n input: AgentInput,\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n // Databricks API requires tool names to match [a-zA-Z0-9_-].\n // Our tool names use dots (e.g. \"analytics.query\"), so we swap dots\n // for double-underscores in the wire format and map back on receipt.\n const nameToWire = new Map<string, string>();\n const wireToName = new Map<string, string>();\n for (const tool of input.tools) {\n const wire = tool.name.replace(/\\./g, \"__\");\n if (wireToName.has(wire) && wireToName.get(wire) !== tool.name) {\n throw new Error(\n `Tool name collision: '${tool.name}' and '${wireToName.get(wire)}' both map to wire name '${wire}'`,\n );\n }\n nameToWire.set(tool.name, wire);\n wireToName.set(wire, tool.name);\n }\n\n const tools = this.buildTools(input.tools, nameToWire);\n const messages = this.buildMessages(input.messages, nameToWire);\n\n yield { type: \"status\", status: \"running\" };\n\n for (let step = 0; step < this.maxSteps; step++) {\n if (context.signal?.aborted) break;\n\n const { text, toolCalls } = yield* this.streamCompletion(\n messages,\n tools,\n context,\n );\n\n if (toolCalls.length === 0) {\n const parsed = parseTextToolCalls(text);\n if (parsed.length > 0) {\n yield* this.executeToolCalls(parsed, messages, context, nameToWire);\n continue;\n }\n break;\n }\n\n messages.push({\n role: \"assistant\",\n content: text || null,\n tool_calls: toolCalls,\n });\n\n for (const tc of toolCalls) {\n const wireName = tc.function.name;\n const originalName = wireToName.get(wireName) ?? wireName;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n }\n\n /** Parse wire arguments, emit tool_call / tool_result, append tool messages. */\n private async *executeSingleTool(\n tc: OpenAIToolCall,\n originalName: string,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n let args: unknown;\n try {\n args = JSON.parse(tc.function.arguments);\n } catch {\n args = {};\n }\n\n yield { type: \"tool_call\", callId: tc.id, name: originalName, args };\n\n try {\n const result = await context.executeTool(originalName, args);\n const resultStr =\n typeof result === \"string\" ? result : JSON.stringify(result);\n\n yield { type: \"tool_result\", callId: tc.id, result };\n\n messages.push({\n role: \"tool\",\n content: resultStr,\n tool_call_id: tc.id,\n });\n } catch (error) {\n const errMsg =\n error instanceof Error ? error.message : \"Tool execution failed\";\n\n yield {\n type: \"tool_result\",\n callId: tc.id,\n result: null,\n error: errMsg,\n };\n\n messages.push({\n role: \"tool\",\n content: JSON.stringify({ error: errMsg }),\n tool_call_id: tc.id,\n });\n }\n }\n\n private async *streamCompletion(\n messages: OpenAIMessage[],\n tools: OpenAITool[],\n context: AgentRunContext,\n ): AsyncGenerator<\n AgentEvent,\n { text: string; toolCalls: OpenAIToolCall[] },\n unknown\n > {\n const body: Record<string, unknown> = {\n messages,\n stream: true,\n max_tokens: this.maxTokens,\n };\n\n if (tools.length > 0) {\n body.tools = tools;\n }\n\n let responseBody: ReadableStream<Uint8Array>;\n try {\n responseBody = await this.streamBody(body, context.signal);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Stream request failed\";\n yield { type: \"status\", status: \"error\", error: msg };\n throw err;\n }\n\n const reader = responseBody.getReader();\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let fullText = \"\";\n const toolCallAccumulator = new Map<\n number,\n {\n id: string;\n name: string;\n arguments: string;\n thoughtSignature?: string;\n }\n >();\n\n try {\n while (true) {\n if (context.signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n if (buffer.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line buffer exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n for (const line of lines) {\n if (line.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n const trimmed = line.trim();\n if (!trimmed.startsWith(\"data: \")) continue;\n const data = trimmed.slice(6);\n if (data === \"[DONE]\") continue;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch (parseErr) {\n console.debug(\n \"[DatabricksAdapter] malformed SSE data line JSON\",\n { line: `${data.slice(0, 256)}${data.length > 256 ? \"…\" : \"\"}` },\n parseErr,\n );\n continue;\n }\n\n const deltaUnknown = openAiChoicesDelta(parsed);\n if (!isRecord(deltaUnknown)) continue;\n\n if (typeof deltaUnknown.content === \"string\") {\n const content = deltaUnknown.content;\n throwIfExceedsStreamLimit(\n \"streamed assistant text\",\n fullText.length,\n content,\n this.maxStreamTextChars,\n );\n fullText += content;\n yield { type: \"message_delta\" as const, content };\n }\n\n const toolCallsRaw = deltaUnknown.tool_calls;\n if (!Array.isArray(toolCallsRaw)) continue;\n\n for (const tc of toolCallsRaw) {\n if (!isStreamingDeltaToolCall(tc)) continue;\n const sig = tc.thoughtSignature;\n const existing = toolCallAccumulator.get(tc.index);\n if (existing) {\n if (tc.function?.arguments) {\n throwIfExceedsStreamLimit(\n \"tool call arguments\",\n existing.arguments.length,\n tc.function.arguments,\n this.maxToolArgumentsChars,\n );\n existing.arguments += tc.function.arguments;\n }\n if (sig && !existing.thoughtSignature) {\n existing.thoughtSignature = sig;\n }\n } else {\n const initial = tc.function?.arguments ?? \"\";\n if (initial.length > this.maxToolArgumentsChars) {\n throw new Error(\n `DatabricksAdapter: tool call arguments exceed configured limit (${this.maxToolArgumentsChars} UTF-16 code units)`,\n );\n }\n toolCallAccumulator.set(tc.index, {\n id: tc.id ?? `call_${tc.index}`,\n name: tc.function?.name ?? \"\",\n arguments: initial,\n ...(sig ? { thoughtSignature: sig } : {}),\n });\n }\n }\n }\n }\n } finally {\n try {\n await reader.cancel();\n } catch (cancelErr) {\n console.debug(\n \"[DatabricksAdapter] reader.cancel() failed during teardown\",\n cancelErr,\n );\n }\n try {\n reader.releaseLock();\n } catch (unlockErr) {\n console.debug(\n \"[DatabricksAdapter] reader.releaseLock() failed during teardown\",\n unlockErr,\n );\n }\n }\n\n const toolCalls: OpenAIToolCall[] = Array.from(\n toolCallAccumulator.values(),\n ).map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments || \"{}\" },\n ...(tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}),\n }));\n\n return { text: fullText, toolCalls };\n }\n\n private async *executeToolCalls(\n calls: Array<{ name: string; args: unknown }>,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n nameToWire: Map<string, string>,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n const toolCallObjs: OpenAIToolCall[] = calls.map((c, i) => ({\n id: `text_call_${i}`,\n type: \"function\" as const,\n function: {\n name: wireToolName(c.name),\n arguments: JSON.stringify(c.args),\n },\n }));\n\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: toolCallObjs,\n });\n\n for (let i = 0; i < toolCallObjs.length; i++) {\n const tc = toolCallObjs[i];\n const originalName = calls[i]?.name ?? tc.function.name;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n\n /**\n * Maps AppKit {@link AgentInput} messages into OpenAI-compatible wire messages.\n * Preserves multi-turn tool state (`toolCalls` → `tool_calls`, `toolCallId` →\n * `tool_call_id`) so resumed threads and hydrated history reach the model.\n */\n private buildMessages(\n messages: AgentInput[\"messages\"],\n nameToWire: Map<string, string>,\n ): OpenAIMessage[] {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n return messages.map((m) => {\n let content: string | null = m.content;\n if (\n m.role === \"assistant\" &&\n m.toolCalls &&\n m.toolCalls.length > 0 &&\n (!m.content || m.content.trim() === \"\")\n ) {\n content = null;\n }\n\n const out: OpenAIMessage = {\n role: m.role as OpenAIMessage[\"role\"],\n content,\n };\n\n if (m.toolCallId) {\n out.tool_call_id = m.toolCallId;\n }\n\n if (m.toolCalls && m.toolCalls.length > 0) {\n out.tool_calls = m.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: wireToolName(tc.name),\n arguments:\n typeof tc.args === \"string\"\n ? tc.args\n : JSON.stringify(tc.args ?? {}),\n },\n ...(tc.thoughtSignature\n ? { thoughtSignature: tc.thoughtSignature }\n : {}),\n }));\n }\n\n return out;\n });\n }\n\n private buildTools(\n definitions: AgentToolDefinition[],\n nameToWire: Map<string, string>,\n ): OpenAITool[] {\n return definitions.map((def) => ({\n type: \"function\" as const,\n function: {\n name: nameToWire.get(def.name) ?? def.name,\n description: def.description,\n parameters: def.parameters,\n },\n }));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Text-based tool call parsing (fallback)\n// ---------------------------------------------------------------------------\n\n/**\n * Parses text-based tool calls from model output.\n *\n * Handles two formats:\n * 1. Llama native: `[{\"name\": \"tool_name\", \"parameters\": {\"arg\": \"val\"}}]`\n * 2. Python-style: `[tool_name(arg1='val1', arg2='val2')]`\n */\nexport function parseTextToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const trimmed = text.trim();\n\n const jsonResult = tryParseLlamaJsonToolCalls(trimmed);\n if (jsonResult.length > 0) return jsonResult;\n\n const pyResult = tryParsePythonStyleToolCalls(trimmed);\n if (pyResult.length > 0) return pyResult;\n\n return [];\n}\n\nfunction isLlamaToolJsonItem(value: unknown): value is Record<\n string,\n unknown\n> & {\n name: string;\n} {\n if (!isRecord(value)) return false;\n return typeof value.name === \"string\";\n}\n\nfunction tryParseLlamaJsonToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const slice = extractLlamaToolJsonSlice(text);\n if (!slice) return [];\n\n try {\n const parsed: unknown = JSON.parse(slice);\n if (!Array.isArray(parsed)) return [];\n\n return parsed.filter(isLlamaToolJsonItem).map((item) => ({\n name: item.name,\n args: item.parameters ?? item.arguments ?? item.args ?? {},\n }));\n } catch {\n return [];\n }\n}\n\nfunction tryParsePythonStyleToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n if (text.length > PYTHON_STYLE_TOOL_PARSE_MAX_INPUT) {\n return [];\n }\n\n const pattern = /\\[?([a-zA-Z_][\\w.]*)\\(([^)]*)\\)\\]?/g;\n const results: Array<{ name: string; args: unknown }> = [];\n\n for (const match of text.matchAll(pattern)) {\n const name = match[1];\n const argsStr = match[2];\n\n const args: Record<string, unknown> = {};\n const argPattern = /(\\w+)\\s*=\\s*(?:'([^']*)'|\"([^\"]*)\"|(\\S+))/g;\n for (const argMatch of argsStr.matchAll(argPattern)) {\n const key = argMatch[1];\n const value = argMatch[2] ?? argMatch[3] ?? argMatch[4];\n args[key] = value;\n }\n\n results.push({ name, args });\n }\n\n return results;\n}\n"],"mappings":";;;;;AAWA,MAAM,6BAA6B,OAAO;;AAG1C,MAAM,gCAAgC,IAAI,OAAO;;AAGjD,MAAM,kCAAkC,IAAI,OAAO;;AAGnD,MAAM,oCAAoC,KAAK;;AAG/C,MAAM,+BAA+B;AAErC,SAAS,SAAS,OAAkD;AAClE,QAAO,OAAO,UAAU,YAAY,UAAU;;AAGhD,SAAS,0BAA0B,MAAkC;CACnE,MAAM,QAAQ,KAAK,QAAQ,KAAK;AAChC,KAAI,QAAQ,EAAG,QAAO;CACtB,MAAM,aAAa,KAAK,YAAY,KAAK;AACzC,KAAI,aAAa,MAAO,QAAO;AAC/B,QAAO,KAAK,MAAM,OAAO,aAAa,EAAE;;;AAI1C,SAAS,mBAAmB,QAA0B;AACpD,KAAI,CAAC,SAAS,OAAO,CAAE,QAAO;CAC9B,MAAM,UAAU,OAAO;AACvB,KAAI,CAAC,MAAM,QAAQ,QAAQ,IAAI,QAAQ,SAAS,EAAG,QAAO;CAC1D,MAAM,QAAQ,QAAQ;AACtB,KAAI,CAAC,SAAS,MAAM,CAAE,QAAO;AAC7B,QAAO,MAAM;;AAGf,SAAS,yBAAyB,OAAwC;AACxE,KAAI,CAAC,SAAS,MAAM,CAAE,QAAO;AAC7B,QAAO,OAAO,MAAM,UAAU;;AAGhC,SAAS,0BACP,OACA,eACA,OACA,KACM;AACN,KAAI,gBAAgB,MAAM,SAAS,IACjC,OAAM,IAAI,MACR,sBAAsB,MAAM,6BAA6B,IAAI,qBAC9D;;AAoDL,SAAS,oBACP,GAC+B;AAC/B,QAAO,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwHzB,IAAa,oBAAb,MAAa,kBAA0C;CACrD,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,SAAmC;AAC7C,OAAK,WAAW,QAAQ,YAAY;AACpC,OAAK,YAAY,QAAQ,aAAa;AACtC,OAAK,kBACH,QAAQ,mBAAmB;AAC7B,OAAK,qBACH,QAAQ,sBAAsB;AAChC,OAAK,wBACH,QAAQ,yBAAyB;AAEnC,MAAI,oBAAoB,QAAQ,CAC9B,MAAK,aAAa,QAAQ;OACrB;GACL,MAAM,EAAE,aAAa,iBAAiB;AACtC,QAAK,aAAa,OAAO,MAAM,WAAW;IACxC,MAAM,cACJ,UAAU,YAAY,QAAQ,6BAA6B;IAC7D,MAAM,cAAc,MAAM,cAAc;IACxC,MAAM,WAAW,MAAM,MAAM,aAAa;KACxC,QAAQ;KACR,SAAS;MACP,cAAc;MACd,gBAAgB;MAChB,GAAG;MACJ;KACD,MAAM,KAAK,UAAU,KAAK;KAC1B,QAAQ;KACT,CAAC;AACF,QAAI,CAAC,SAAS,IAAI;KAChB,MAAM,YAAY,MAAM,SAAS,MAAM,CAAC,YAAY,gBAAgB;AACpE,WAAM,IAAI,MACR,yBAAyB,SAAS,OAAO,KAAK,YAC/C;;AAEH,QAAI,CAAC,SAAS,KAAM,OAAM,IAAI,MAAM,mBAAmB;AACvD,WAAO,SAAS;;;;;;;;;;;;CAatB,aAAa,oBACX,SAC4B;EAC5B,MAAM,EACJ,iBACA,cACA,UACA,WACA,iBACA,oBACA,0BACE;AACJ,SAAO,IAAI,kBAAkB;GAC3B,aAAa,MAAM,WAIjBA,OACE,iBACA,cACA,MACA,OACD;GACH;GACA;GACA;GACA;GACA;GACD,CAAC;;;;;;;;;;;;;;;;;;;;;;CAuBJ,aAAa,iBACX,cACA,SAC4B;EAC5B,MAAM,mBACJ,gBAAgB,QAAQ,IAAI;AAE9B,MAAI,CAAC,iBACH,OAAM,IAAI,MACR,oJAED;EAGH,IAAI,kBACF,SAAS;AACX,MAAI,CAAC,gBAEH,mBAAkB,KADN,OAAM,OAAO,kCACC,gBACxB,EAAE,EACF,kBAAkB,CACnB;AAGH,SAAO,kBAAkB,oBAAoB;GAC3C;GACA,cAAc;GACd,UAAU,SAAS;GACnB,WAAW,SAAS;GACpB,iBAAiB,SAAS;GAC1B,oBAAoB,SAAS;GAC7B,uBAAuB,SAAS;GACjC,CAAC;;CAGJ,OAAO,IACL,OACA,SAC2C;EAI3C,MAAM,6BAAa,IAAI,KAAqB;EAC5C,MAAM,6BAAa,IAAI,KAAqB;AAC5C,OAAK,MAAM,QAAQ,MAAM,OAAO;GAC9B,MAAM,OAAO,KAAK,KAAK,QAAQ,OAAO,KAAK;AAC3C,OAAI,WAAW,IAAI,KAAK,IAAI,WAAW,IAAI,KAAK,KAAK,KAAK,KACxD,OAAM,IAAI,MACR,yBAAyB,KAAK,KAAK,SAAS,WAAW,IAAI,KAAK,CAAC,2BAA2B,KAAK,GAClG;AAEH,cAAW,IAAI,KAAK,MAAM,KAAK;AAC/B,cAAW,IAAI,MAAM,KAAK,KAAK;;EAGjC,MAAM,QAAQ,KAAK,WAAW,MAAM,OAAO,WAAW;EACtD,MAAM,WAAW,KAAK,cAAc,MAAM,UAAU,WAAW;AAE/D,QAAM;GAAE,MAAM;GAAU,QAAQ;GAAW;AAE3C,OAAK,IAAI,OAAO,GAAG,OAAO,KAAK,UAAU,QAAQ;AAC/C,OAAI,QAAQ,QAAQ,QAAS;GAE7B,MAAM,EAAE,MAAM,cAAc,OAAO,KAAK,iBACtC,UACA,OACA,QACD;AAED,OAAI,UAAU,WAAW,GAAG;IAC1B,MAAM,SAAS,mBAAmB,KAAK;AACvC,QAAI,OAAO,SAAS,GAAG;AACrB,YAAO,KAAK,iBAAiB,QAAQ,UAAU,SAAS,WAAW;AACnE;;AAEF;;AAGF,YAAS,KAAK;IACZ,MAAM;IACN,SAAS,QAAQ;IACjB,YAAY;IACb,CAAC;AAEF,QAAK,MAAM,MAAM,WAAW;IAC1B,MAAM,WAAW,GAAG,SAAS;IAC7B,MAAM,eAAe,WAAW,IAAI,SAAS,IAAI;AACjD,WAAO,KAAK,kBAAkB,IAAI,cAAc,UAAU,QAAQ;;;;;CAMxE,OAAe,kBACb,IACA,cACA,UACA,SAC2C;EAC3C,IAAI;AACJ,MAAI;AACF,UAAO,KAAK,MAAM,GAAG,SAAS,UAAU;UAClC;AACN,UAAO,EAAE;;AAGX,QAAM;GAAE,MAAM;GAAa,QAAQ,GAAG;GAAI,MAAM;GAAc;GAAM;AAEpE,MAAI;GACF,MAAM,SAAS,MAAM,QAAQ,YAAY,cAAc,KAAK;GAC5D,MAAM,YACJ,OAAO,WAAW,WAAW,SAAS,KAAK,UAAU,OAAO;AAE9D,SAAM;IAAE,MAAM;IAAe,QAAQ,GAAG;IAAI;IAAQ;AAEpD,YAAS,KAAK;IACZ,MAAM;IACN,SAAS;IACT,cAAc,GAAG;IAClB,CAAC;WACK,OAAO;GACd,MAAM,SACJ,iBAAiB,QAAQ,MAAM,UAAU;AAE3C,SAAM;IACJ,MAAM;IACN,QAAQ,GAAG;IACX,QAAQ;IACR,OAAO;IACR;AAED,YAAS,KAAK;IACZ,MAAM;IACN,SAAS,KAAK,UAAU,EAAE,OAAO,QAAQ,CAAC;IAC1C,cAAc,GAAG;IAClB,CAAC;;;CAIN,OAAe,iBACb,UACA,OACA,SAKA;EACA,MAAM,OAAgC;GACpC;GACA,QAAQ;GACR,YAAY,KAAK;GAClB;AAED,MAAI,MAAM,SAAS,EACjB,MAAK,QAAQ;EAGf,IAAI;AACJ,MAAI;AACF,kBAAe,MAAM,KAAK,WAAW,MAAM,QAAQ,OAAO;WACnD,KAAK;AAEZ,SAAM;IAAE,MAAM;IAAU,QAAQ;IAAS,OAD7B,eAAe,QAAQ,IAAI,UAAU;IACI;AACrD,SAAM;;EAGR,MAAM,SAAS,aAAa,WAAW;EAEvC,MAAM,UAAU,IAAI,aAAa;EACjC,IAAI,SAAS;EACb,IAAI,WAAW;EACf,MAAM,sCAAsB,IAAI,KAQ7B;AAEH,MAAI;AACF,UAAO,MAAM;AACX,QAAI,QAAQ,QAAQ,QAAS;IAE7B,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,QAAI,KAAM;AAEV,cAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,MAAM,CAAC;IACjD,MAAM,QAAQ,OAAO,MAAM,KAAK;AAChC,aAAS,MAAM,KAAK,IAAI;AAExB,QAAI,OAAO,SAAS,KAAK,gBACvB,OAAM,IAAI,MACR,gEAAgE,KAAK,gBAAgB,qBACtF;AAGH,SAAK,MAAM,QAAQ,OAAO;AACxB,SAAI,KAAK,SAAS,KAAK,gBACrB,OAAM,IAAI,MACR,yDAAyD,KAAK,gBAAgB,qBAC/E;KAGH,MAAM,UAAU,KAAK,MAAM;AAC3B,SAAI,CAAC,QAAQ,WAAW,SAAS,CAAE;KACnC,MAAM,OAAO,QAAQ,MAAM,EAAE;AAC7B,SAAI,SAAS,SAAU;KAEvB,IAAI;AACJ,SAAI;AACF,eAAS,KAAK,MAAM,KAAK;cAClB,UAAU;AACjB,cAAQ,MACN,oDACA,EAAE,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,GAAG,KAAK,SAAS,MAAM,MAAM,MAAM,EAChE,SACD;AACD;;KAGF,MAAM,eAAe,mBAAmB,OAAO;AAC/C,SAAI,CAAC,SAAS,aAAa,CAAE;AAE7B,SAAI,OAAO,aAAa,YAAY,UAAU;MAC5C,MAAM,UAAU,aAAa;AAC7B,gCACE,2BACA,SAAS,QACT,SACA,KAAK,mBACN;AACD,kBAAY;AACZ,YAAM;OAAE,MAAM;OAA0B;OAAS;;KAGnD,MAAM,eAAe,aAAa;AAClC,SAAI,CAAC,MAAM,QAAQ,aAAa,CAAE;AAElC,UAAK,MAAM,MAAM,cAAc;AAC7B,UAAI,CAAC,yBAAyB,GAAG,CAAE;MACnC,MAAM,MAAM,GAAG;MACf,MAAM,WAAW,oBAAoB,IAAI,GAAG,MAAM;AAClD,UAAI,UAAU;AACZ,WAAI,GAAG,UAAU,WAAW;AAC1B,kCACE,uBACA,SAAS,UAAU,QACnB,GAAG,SAAS,WACZ,KAAK,sBACN;AACD,iBAAS,aAAa,GAAG,SAAS;;AAEpC,WAAI,OAAO,CAAC,SAAS,iBACnB,UAAS,mBAAmB;aAEzB;OACL,MAAM,UAAU,GAAG,UAAU,aAAa;AAC1C,WAAI,QAAQ,SAAS,KAAK,sBACxB,OAAM,IAAI,MACR,mEAAmE,KAAK,sBAAsB,qBAC/F;AAEH,2BAAoB,IAAI,GAAG,OAAO;QAChC,IAAI,GAAG,MAAM,QAAQ,GAAG;QACxB,MAAM,GAAG,UAAU,QAAQ;QAC3B,WAAW;QACX,GAAI,MAAM,EAAE,kBAAkB,KAAK,GAAG,EAAE;QACzC,CAAC;;;;;YAKF;AACR,OAAI;AACF,UAAM,OAAO,QAAQ;YACd,WAAW;AAClB,YAAQ,MACN,8DACA,UACD;;AAEH,OAAI;AACF,WAAO,aAAa;YACb,WAAW;AAClB,YAAQ,MACN,mEACA,UACD;;;EAIL,MAAM,YAA8B,MAAM,KACxC,oBAAoB,QAAQ,CAC7B,CAAC,KAAK,QAAQ;GACb,IAAI,GAAG;GACP,MAAM;GACN,UAAU;IAAE,MAAM,GAAG;IAAM,WAAW,GAAG,aAAa;IAAM;GAC5D,GAAI,GAAG,mBAAmB,EAAE,kBAAkB,GAAG,kBAAkB,GAAG,EAAE;GACzE,EAAE;AAEH,SAAO;GAAE,MAAM;GAAU;GAAW;;CAGtC,OAAe,iBACb,OACA,UACA,SACA,YAC2C;EAC3C,MAAM,gBAAgB,SACpB,WAAW,IAAI,KAAK,IAAI,KAAK,QAAQ,OAAO,KAAK;EAEnD,MAAM,eAAiC,MAAM,KAAK,GAAG,OAAO;GAC1D,IAAI,aAAa;GACjB,MAAM;GACN,UAAU;IACR,MAAM,aAAa,EAAE,KAAK;IAC1B,WAAW,KAAK,UAAU,EAAE,KAAK;IAClC;GACF,EAAE;AAEH,WAAS,KAAK;GACZ,MAAM;GACN,SAAS;GACT,YAAY;GACb,CAAC;AAEF,OAAK,IAAI,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;GAC5C,MAAM,KAAK,aAAa;GACxB,MAAM,eAAe,MAAM,IAAI,QAAQ,GAAG,SAAS;AACnD,UAAO,KAAK,kBAAkB,IAAI,cAAc,UAAU,QAAQ;;;;;;;;CAStE,AAAQ,cACN,UACA,YACiB;EACjB,MAAM,gBAAgB,SACpB,WAAW,IAAI,KAAK,IAAI,KAAK,QAAQ,OAAO,KAAK;AAEnD,SAAO,SAAS,KAAK,MAAM;GACzB,IAAI,UAAyB,EAAE;AAC/B,OACE,EAAE,SAAS,eACX,EAAE,aACF,EAAE,UAAU,SAAS,MACpB,CAAC,EAAE,WAAW,EAAE,QAAQ,MAAM,KAAK,IAEpC,WAAU;GAGZ,MAAM,MAAqB;IACzB,MAAM,EAAE;IACR;IACD;AAED,OAAI,EAAE,WACJ,KAAI,eAAe,EAAE;AAGvB,OAAI,EAAE,aAAa,EAAE,UAAU,SAAS,EACtC,KAAI,aAAa,EAAE,UAAU,KAAK,QAAQ;IACxC,IAAI,GAAG;IACP,MAAM;IACN,UAAU;KACR,MAAM,aAAa,GAAG,KAAK;KAC3B,WACE,OAAO,GAAG,SAAS,WACf,GAAG,OACH,KAAK,UAAU,GAAG,QAAQ,EAAE,CAAC;KACpC;IACD,GAAI,GAAG,mBACH,EAAE,kBAAkB,GAAG,kBAAkB,GACzC,EAAE;IACP,EAAE;AAGL,UAAO;IACP;;CAGJ,AAAQ,WACN,aACA,YACc;AACd,SAAO,YAAY,KAAK,SAAS;GAC/B,MAAM;GACN,UAAU;IACR,MAAM,WAAW,IAAI,IAAI,KAAK,IAAI,IAAI;IACtC,aAAa,IAAI;IACjB,YAAY,IAAI;IACjB;GACF,EAAE;;;;;;;;;;AAeP,SAAgB,mBACd,MACwC;CACxC,MAAM,UAAU,KAAK,MAAM;CAE3B,MAAM,aAAa,2BAA2B,QAAQ;AACtD,KAAI,WAAW,SAAS,EAAG,QAAO;CAElC,MAAM,WAAW,6BAA6B,QAAQ;AACtD,KAAI,SAAS,SAAS,EAAG,QAAO;AAEhC,QAAO,EAAE;;AAGX,SAAS,oBAAoB,OAK3B;AACA,KAAI,CAAC,SAAS,MAAM,CAAE,QAAO;AAC7B,QAAO,OAAO,MAAM,SAAS;;AAG/B,SAAS,2BACP,MACwC;CACxC,MAAM,QAAQ,0BAA0B,KAAK;AAC7C,KAAI,CAAC,MAAO,QAAO,EAAE;AAErB,KAAI;EACF,MAAM,SAAkB,KAAK,MAAM,MAAM;AACzC,MAAI,CAAC,MAAM,QAAQ,OAAO,CAAE,QAAO,EAAE;AAErC,SAAO,OAAO,OAAO,oBAAoB,CAAC,KAAK,UAAU;GACvD,MAAM,KAAK;GACX,MAAM,KAAK,cAAc,KAAK,aAAa,KAAK,QAAQ,EAAE;GAC3D,EAAE;SACG;AACN,SAAO,EAAE;;;AAIb,SAAS,6BACP,MACwC;AACxC,KAAI,KAAK,SAAS,kCAChB,QAAO,EAAE;CAGX,MAAM,UAAU;CAChB,MAAM,UAAkD,EAAE;AAE1D,MAAK,MAAM,SAAS,KAAK,SAAS,QAAQ,EAAE;EAC1C,MAAM,OAAO,MAAM;EACnB,MAAM,UAAU,MAAM;EAEtB,MAAM,OAAgC,EAAE;AAExC,OAAK,MAAM,YAAY,QAAQ,SADZ,6CACgC,EAAE;GACnD,MAAM,MAAM,SAAS;AAErB,QAAK,OADS,SAAS,MAAM,SAAS,MAAM,SAAS;;AAIvD,UAAQ,KAAK;GAAE;GAAM;GAAM,CAAC;;AAG9B,QAAO"}
|
|
1
|
+
{"version":3,"file":"databricks.js","names":["servingStream"],"sources":["../../src/agents/databricks.ts"],"sourcesContent":["import type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n AgentRunContext,\n AgentToolDefinition,\n} from \"shared\";\nimport { stream as servingStream } from \"../connectors/serving/client\";\nimport { APPKIT_USER_AGENT, getClientOptions } from \"../context/client-options\";\n\n/** Default cap for a single incomplete SSE line tail (DoS guard). */\nconst DEFAULT_MAX_SSE_LINE_CHARS = 1024 * 1024;\n\n/** Default cap for accumulated assistant text from `delta.content`. */\nconst DEFAULT_MAX_STREAM_TEXT_CHARS = 4 * 1024 * 1024;\n\n/** Default cap for accumulated JSON arguments per streamed tool call index. */\nconst DEFAULT_MAX_TOOL_ARGUMENT_CHARS = 2 * 1024 * 1024;\n\n/** Cap text length before running Python-style tool-call regex (ReDoS guard). */\nconst PYTHON_STYLE_TOOL_PARSE_MAX_INPUT = 64 * 1024;\n\n/** Fallback HTTP timeout when the raw fetch adapter path receives no AbortSignal from the runner. */\nconst RAW_FETCH_DEFAULT_TIMEOUT_MS = 120_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Optional generation parameters forwarded to the OpenAI-compatible serving\n * request body. Names match the serving API wire keys. Only keys that are set\n * are sent — undefined values are omitted so the endpoint applies its own\n * defaults. Ranges are not validated here; the serving endpoint validates.\n */\nexport interface GenerationParams {\n /** Sampling temperature. */\n temperature?: number;\n /** Nucleus sampling probability mass (`top_p`). */\n top_p?: number;\n /** Stop sequence(s) that end generation. */\n stop?: string | string[];\n /** Penalize tokens by frequency. */\n frequency_penalty?: number;\n /** Penalize tokens by prior presence. */\n presence_penalty?: number;\n}\n\nconst GENERATION_PARAM_KEYS = [\n \"temperature\",\n \"top_p\",\n \"stop\",\n \"frequency_penalty\",\n \"presence_penalty\",\n] as const satisfies readonly (keyof GenerationParams)[];\n\n/** Copy only the set generation params onto the request body. */\nfunction applyGenerationParams(\n body: Record<string, unknown>,\n params: GenerationParams,\n): void {\n for (const key of GENERATION_PARAM_KEYS) {\n const value = params[key];\n if (value !== undefined) body[key] = value;\n }\n}\n\nfunction extractLlamaToolJsonSlice(text: string): string | undefined {\n const start = text.indexOf(\"[{\");\n if (start < 0) return undefined;\n const endBracket = text.lastIndexOf(\"}]\");\n if (endBracket < start) return undefined;\n return text.slice(start, endBracket + 2);\n}\n\n/** OpenAI SSE payload: `{ choices: [{ delta }] }`. */\nfunction openAiChoicesDelta(parsed: unknown): unknown {\n if (!isRecord(parsed)) return undefined;\n const choices = parsed.choices;\n if (!Array.isArray(choices) || choices.length < 1) return undefined;\n const first = choices[0];\n if (!isRecord(first)) return undefined;\n return first.delta;\n}\n\nfunction isStreamingDeltaToolCall(value: unknown): value is DeltaToolCall {\n if (!isRecord(value)) return false;\n return typeof value.index === \"number\";\n}\n\nfunction throwIfExceedsStreamLimit(\n label: string,\n currentLength: number,\n chunk: string,\n max: number,\n): void {\n if (currentLength + chunk.length > max) {\n throw new Error(\n `DatabricksAdapter: ${label} exceeds configured limit (${max} UTF-16 code units)`,\n );\n }\n}\n\n/**\n * Transport shim: given an OpenAI-compatible request body, returns the raw\n * SSE byte stream from the serving endpoint. Injected at construction time so\n * callers can swap in the workspace SDK (factory paths), a bare `fetch`\n * (the raw constructor), or a test fake.\n */\ntype StreamBody = (\n body: Record<string, unknown>,\n signal?: AbortSignal,\n) => Promise<ReadableStream<Uint8Array>>;\n\n/**\n * Escape-hatch options: provide an `endpointUrl` + `authenticate()` and the\n * adapter uses a bare `fetch()` to call it. Useful for tests and for pointing\n * the adapter at non-workspace endpoints (reverse proxies, mocks).\n */\ninterface RawFetchAdapterOptions {\n endpointUrl: string;\n authenticate: () => Promise<Record<string, string>>;\n maxSteps?: number;\n maxTokens?: number;\n /** Optional generation params forwarded to the serving request body. */\n generationParams?: GenerationParams;\n /** Max length of one SSE line (including an incomplete tail in the buffer). */\n maxSseLineChars?: number;\n /** Max total length of assistant `delta.content` across the stream. */\n maxStreamTextChars?: number;\n /** Max length of streamed `function.arguments` per tool call index. */\n maxToolArgumentsChars?: number;\n}\n\n/**\n * Preferred options: caller provides the transport function directly.\n * The `fromServingEndpoint` / `fromModelServing` factories use this to route\n * through `connectors/serving/stream`, which centralises URL encoding, auth\n * via the SDK's `apiClient.request`, and any future retries/telemetry.\n */\ninterface StreamBodyAdapterOptions {\n streamBody: StreamBody;\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ntype DatabricksAdapterOptions =\n | RawFetchAdapterOptions\n | StreamBodyAdapterOptions;\n\nfunction isStreamBodyOptions(\n o: DatabricksAdapterOptions,\n): o is StreamBodyAdapterOptions {\n return \"streamBody\" in o;\n}\n\n/**\n * Duck-typed subset of the Databricks SDK `WorkspaceClient`. Callers of\n * `fromServingEndpoint` and `fromModelServing` pass a real `WorkspaceClient`,\n * but we only need the `apiClient.request` surface — so we declare the minimal\n * interface rather than importing the SDK type directly. This keeps the adapter\n * free of a hard compile-time dependency on `@databricks/sdk-experimental`.\n */\ninterface WorkspaceClientLike {\n apiClient: {\n request(options: Record<string, unknown>): Promise<unknown>;\n };\n}\n\ninterface ServingEndpointOptions {\n workspaceClient: WorkspaceClientLike;\n endpointName: string;\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface ModelServingOptions {\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: GenerationParams;\n workspaceClient?: WorkspaceClientLike;\n maxSseLineChars?: number;\n maxStreamTextChars?: number;\n maxToolArgumentsChars?: number;\n}\n\ninterface OpenAIMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | null;\n tool_calls?: OpenAIToolCall[];\n tool_call_id?: string;\n}\n\ninterface OpenAIToolCall {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n /**\n * Opaque Vertex/Gemini \"thought signature\" blob the request must echo\n * back verbatim on the next turn. Vertex's OpenAI-compat proxy emits\n * this as `thoughtSignature` (camelCase) at the top level of the\n * tool_call delta (verified against `gemini-3.1-flash-lite-preview`),\n * and accepts the same spelling back on outbound. Non-Gemini endpoints\n * (Claude on Databricks, external OpenAI-compat models, Llama, etc.)\n * leave this undefined and the serializer omits the key.\n * See https://docs.cloud.google.com/vertex-ai/generative-ai/docs/thought-signatures\n */\n thoughtSignature?: string;\n}\n\ninterface OpenAITool {\n type: \"function\";\n function: {\n name: string;\n description: string;\n parameters: unknown;\n };\n}\n\ninterface DeltaToolCall {\n index: number;\n id?: string;\n type?: string;\n function?: { name?: string; arguments?: string };\n /** See {@link OpenAIToolCall.thoughtSignature}. */\n thoughtSignature?: string;\n}\n\n/**\n * Adapter that talks directly to Databricks Model Serving `/invocations` endpoint.\n *\n * No dependency on the Vercel AI SDK or LangChain. Uses raw `fetch()` to POST\n * OpenAI-compatible payloads and parses the SSE stream itself. Calls\n * `authenticate()` per-request so tokens are always fresh.\n *\n * Handles both structured `tool_calls` responses and text-based tool call\n * fallback parsing for models that output tool calls as text.\n *\n * @example Using the factory (recommended)\n * ```ts\n * import { createApp, createAgent, agents } from \"@databricks/appkit\";\n * import { DatabricksAdapter } from \"@databricks/appkit/beta\";\n * import { WorkspaceClient } from \"@databricks/sdk-experimental\";\n *\n * const adapter = DatabricksAdapter.fromServingEndpoint({\n * workspaceClient: new WorkspaceClient({}),\n * endpointName: \"my-endpoint\",\n * });\n *\n * await createApp({\n * plugins: [\n * agents({\n * agents: {\n * assistant: createAgent({\n * instructions: \"You are a helpful assistant.\",\n * model: adapter,\n * }),\n * },\n * }),\n * ],\n * });\n * ```\n *\n * @example Using the raw constructor\n * ```ts\n * const adapter = new DatabricksAdapter({\n * endpointUrl: \"https://host/serving-endpoints/my-endpoint/invocations\",\n * authenticate: async () => ({ Authorization: `Bearer ${token}` }),\n * });\n * ```\n */\nexport class DatabricksAdapter implements AgentAdapter {\n private streamBody: StreamBody;\n private maxSteps: number;\n private maxTokens: number;\n private generationParams: GenerationParams;\n private maxSseLineChars: number;\n private maxStreamTextChars: number;\n private maxToolArgumentsChars: number;\n\n constructor(options: DatabricksAdapterOptions) {\n this.maxSteps = options.maxSteps ?? 10;\n this.maxTokens = options.maxTokens ?? 4096;\n this.generationParams = options.generationParams ?? {};\n this.maxSseLineChars =\n options.maxSseLineChars ?? DEFAULT_MAX_SSE_LINE_CHARS;\n this.maxStreamTextChars =\n options.maxStreamTextChars ?? DEFAULT_MAX_STREAM_TEXT_CHARS;\n this.maxToolArgumentsChars =\n options.maxToolArgumentsChars ?? DEFAULT_MAX_TOOL_ARGUMENT_CHARS;\n\n if (isStreamBodyOptions(options)) {\n this.streamBody = options.streamBody;\n } else {\n const { endpointUrl, authenticate } = options;\n this.streamBody = async (body, signal) => {\n const fetchSignal =\n signal ?? AbortSignal.timeout(RAW_FETCH_DEFAULT_TIMEOUT_MS);\n const authHeaders = await authenticate();\n const response = await fetch(endpointUrl, {\n method: \"POST\",\n headers: {\n \"User-Agent\": APPKIT_USER_AGENT,\n \"Content-Type\": \"application/json\",\n ...authHeaders,\n },\n body: JSON.stringify(body),\n signal: fetchSignal,\n });\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n throw new Error(\n `Databricks API error (${response.status}): ${errorText}`,\n );\n }\n if (!response.body) throw new Error(\"No response body\");\n return response.body;\n };\n }\n }\n\n /**\n * Creates a DatabricksAdapter for a Databricks Model Serving endpoint.\n *\n * Routes through the shared `connectors/serving/stream` helper, which\n * delegates to the SDK's `apiClient.request({ raw: true })`. That gives the\n * adapter centralised URL encoding + authentication with the rest of the\n * serving surface — no bespoke `fetch()` + `authenticate()` plumbing.\n */\n static async fromServingEndpoint(\n options: ServingEndpointOptions,\n ): Promise<DatabricksAdapter> {\n const {\n workspaceClient,\n endpointName,\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n } = options;\n return new DatabricksAdapter({\n streamBody: (body, signal) =>\n // Cast through the structural shape: the connector types\n // `workspaceClient` as the SDK's concrete `WorkspaceClient`, but we\n // only need `apiClient.request`.\n servingStream(\n workspaceClient as unknown as Parameters<typeof servingStream>[0],\n endpointName,\n body,\n signal,\n ),\n maxSteps,\n maxTokens,\n generationParams,\n maxSseLineChars,\n maxStreamTextChars,\n maxToolArgumentsChars,\n });\n }\n\n /**\n * Creates a DatabricksAdapter from a Model Serving endpoint name.\n * Auto-creates a WorkspaceClient internally. Reads the endpoint name\n * from the argument or the `DATABRICKS_SERVING_ENDPOINT_NAME` env var.\n *\n * @example\n * ```ts\n * // Reads endpoint from DATABRICKS_SERVING_ENDPOINT_NAME env var\n * const adapter = await DatabricksAdapter.fromModelServing();\n *\n * // Explicit endpoint\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\");\n *\n * // With options\n * const adapter = await DatabricksAdapter.fromModelServing(\"my-endpoint\", {\n * maxSteps: 5,\n * maxTokens: 2048,\n * });\n * ```\n */\n static async fromModelServing(\n endpointName?: string,\n options?: ModelServingOptions,\n ): Promise<DatabricksAdapter> {\n const resolvedEndpoint =\n endpointName ?? process.env.DATABRICKS_SERVING_ENDPOINT_NAME;\n\n if (!resolvedEndpoint) {\n throw new Error(\n \"No endpoint name provided and DATABRICKS_SERVING_ENDPOINT_NAME env var is not set. \" +\n \"Pass an endpoint name or set DATABRICKS_SERVING_ENDPOINT_NAME.\",\n );\n }\n\n let workspaceClient: WorkspaceClientLike | undefined =\n options?.workspaceClient;\n if (!workspaceClient) {\n const sdk = await import(\"@databricks/sdk-experimental\");\n workspaceClient = new sdk.WorkspaceClient(\n {},\n getClientOptions(),\n ) as unknown as WorkspaceClientLike;\n }\n\n return DatabricksAdapter.fromServingEndpoint({\n workspaceClient,\n endpointName: resolvedEndpoint,\n maxSteps: options?.maxSteps,\n maxTokens: options?.maxTokens,\n generationParams: options?.generationParams,\n maxSseLineChars: options?.maxSseLineChars,\n maxStreamTextChars: options?.maxStreamTextChars,\n maxToolArgumentsChars: options?.maxToolArgumentsChars,\n });\n }\n\n async *run(\n input: AgentInput,\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n // Databricks API requires tool names to match [a-zA-Z0-9_-].\n // Our tool names use dots (e.g. \"analytics.query\"), so we swap dots\n // for double-underscores in the wire format and map back on receipt.\n const nameToWire = new Map<string, string>();\n const wireToName = new Map<string, string>();\n for (const tool of input.tools) {\n const wire = tool.name.replace(/\\./g, \"__\");\n if (wireToName.has(wire) && wireToName.get(wire) !== tool.name) {\n throw new Error(\n `Tool name collision: '${tool.name}' and '${wireToName.get(wire)}' both map to wire name '${wire}'`,\n );\n }\n nameToWire.set(tool.name, wire);\n wireToName.set(wire, tool.name);\n }\n\n const tools = this.buildTools(input.tools, nameToWire);\n const messages = this.buildMessages(input.messages, nameToWire);\n\n yield { type: \"status\", status: \"running\" };\n\n for (let step = 0; step < this.maxSteps; step++) {\n if (context.signal?.aborted) break;\n\n const { text, toolCalls } = yield* this.streamCompletion(\n messages,\n tools,\n context,\n );\n\n if (toolCalls.length === 0) {\n const parsed = parseTextToolCalls(text);\n if (parsed.length > 0) {\n yield* this.executeToolCalls(parsed, messages, context, nameToWire);\n continue;\n }\n break;\n }\n\n messages.push({\n role: \"assistant\",\n content: text || null,\n tool_calls: toolCalls,\n });\n\n for (const tc of toolCalls) {\n const wireName = tc.function.name;\n const originalName = wireToName.get(wireName) ?? wireName;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n }\n\n /** Parse wire arguments, emit tool_call / tool_result, append tool messages. */\n private async *executeSingleTool(\n tc: OpenAIToolCall,\n originalName: string,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n let args: unknown;\n try {\n args = JSON.parse(tc.function.arguments);\n } catch {\n args = {};\n }\n\n yield { type: \"tool_call\", callId: tc.id, name: originalName, args };\n\n try {\n const result = await context.executeTool(originalName, args);\n const resultStr =\n typeof result === \"string\" ? result : JSON.stringify(result);\n\n yield { type: \"tool_result\", callId: tc.id, result };\n\n messages.push({\n role: \"tool\",\n content: resultStr,\n tool_call_id: tc.id,\n });\n } catch (error) {\n const errMsg =\n error instanceof Error ? error.message : \"Tool execution failed\";\n\n yield {\n type: \"tool_result\",\n callId: tc.id,\n result: null,\n error: errMsg,\n };\n\n messages.push({\n role: \"tool\",\n content: JSON.stringify({ error: errMsg }),\n tool_call_id: tc.id,\n });\n }\n }\n\n private async *streamCompletion(\n messages: OpenAIMessage[],\n tools: OpenAITool[],\n context: AgentRunContext,\n ): AsyncGenerator<\n AgentEvent,\n { text: string; toolCalls: OpenAIToolCall[] },\n unknown\n > {\n const body: Record<string, unknown> = {\n messages,\n stream: true,\n max_tokens: this.maxTokens,\n };\n\n applyGenerationParams(body, this.generationParams);\n\n if (tools.length > 0) {\n body.tools = tools;\n }\n\n let responseBody: ReadableStream<Uint8Array>;\n try {\n responseBody = await this.streamBody(body, context.signal);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Stream request failed\";\n yield { type: \"status\", status: \"error\", error: msg };\n throw err;\n }\n\n const reader = responseBody.getReader();\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let fullText = \"\";\n const toolCallAccumulator = new Map<\n number,\n {\n id: string;\n name: string;\n arguments: string;\n thoughtSignature?: string;\n }\n >();\n\n try {\n while (true) {\n if (context.signal?.aborted) break;\n\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n if (buffer.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line buffer exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n for (const line of lines) {\n if (line.length > this.maxSseLineChars) {\n throw new Error(\n `DatabricksAdapter: SSE line exceeds configured limit (${this.maxSseLineChars} UTF-16 code units)`,\n );\n }\n\n const trimmed = line.trim();\n if (!trimmed.startsWith(\"data: \")) continue;\n const data = trimmed.slice(6);\n if (data === \"[DONE]\") continue;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch (parseErr) {\n console.debug(\n \"[DatabricksAdapter] malformed SSE data line JSON\",\n { line: `${data.slice(0, 256)}${data.length > 256 ? \"…\" : \"\"}` },\n parseErr,\n );\n continue;\n }\n\n const deltaUnknown = openAiChoicesDelta(parsed);\n if (!isRecord(deltaUnknown)) continue;\n\n if (typeof deltaUnknown.content === \"string\") {\n const content = deltaUnknown.content;\n throwIfExceedsStreamLimit(\n \"streamed assistant text\",\n fullText.length,\n content,\n this.maxStreamTextChars,\n );\n fullText += content;\n yield { type: \"message_delta\" as const, content };\n }\n\n const toolCallsRaw = deltaUnknown.tool_calls;\n if (!Array.isArray(toolCallsRaw)) continue;\n\n for (const tc of toolCallsRaw) {\n if (!isStreamingDeltaToolCall(tc)) continue;\n const sig = tc.thoughtSignature;\n const existing = toolCallAccumulator.get(tc.index);\n if (existing) {\n if (tc.function?.arguments) {\n throwIfExceedsStreamLimit(\n \"tool call arguments\",\n existing.arguments.length,\n tc.function.arguments,\n this.maxToolArgumentsChars,\n );\n existing.arguments += tc.function.arguments;\n }\n if (sig && !existing.thoughtSignature) {\n existing.thoughtSignature = sig;\n }\n } else {\n const initial = tc.function?.arguments ?? \"\";\n if (initial.length > this.maxToolArgumentsChars) {\n throw new Error(\n `DatabricksAdapter: tool call arguments exceed configured limit (${this.maxToolArgumentsChars} UTF-16 code units)`,\n );\n }\n toolCallAccumulator.set(tc.index, {\n id: tc.id ?? `call_${tc.index}`,\n name: tc.function?.name ?? \"\",\n arguments: initial,\n ...(sig ? { thoughtSignature: sig } : {}),\n });\n }\n }\n }\n }\n } finally {\n try {\n await reader.cancel();\n } catch (cancelErr) {\n console.debug(\n \"[DatabricksAdapter] reader.cancel() failed during teardown\",\n cancelErr,\n );\n }\n try {\n reader.releaseLock();\n } catch (unlockErr) {\n console.debug(\n \"[DatabricksAdapter] reader.releaseLock() failed during teardown\",\n unlockErr,\n );\n }\n }\n\n const toolCalls: OpenAIToolCall[] = Array.from(\n toolCallAccumulator.values(),\n ).map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments || \"{}\" },\n ...(tc.thoughtSignature ? { thoughtSignature: tc.thoughtSignature } : {}),\n }));\n\n return { text: fullText, toolCalls };\n }\n\n private async *executeToolCalls(\n calls: Array<{ name: string; args: unknown }>,\n messages: OpenAIMessage[],\n context: AgentRunContext,\n nameToWire: Map<string, string>,\n ): AsyncGenerator<AgentEvent, void, unknown> {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n const toolCallObjs: OpenAIToolCall[] = calls.map((c, i) => ({\n id: `text_call_${i}`,\n type: \"function\" as const,\n function: {\n name: wireToolName(c.name),\n arguments: JSON.stringify(c.args),\n },\n }));\n\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: toolCallObjs,\n });\n\n for (let i = 0; i < toolCallObjs.length; i++) {\n const tc = toolCallObjs[i];\n const originalName = calls[i]?.name ?? tc.function.name;\n yield* this.executeSingleTool(tc, originalName, messages, context);\n }\n }\n\n /**\n * Maps AppKit {@link AgentInput} messages into OpenAI-compatible wire messages.\n * Preserves multi-turn tool state (`toolCalls` → `tool_calls`, `toolCallId` →\n * `tool_call_id`) so resumed threads and hydrated history reach the model.\n */\n private buildMessages(\n messages: AgentInput[\"messages\"],\n nameToWire: Map<string, string>,\n ): OpenAIMessage[] {\n const wireToolName = (name: string) =>\n nameToWire.get(name) ?? name.replace(/\\./g, \"__\");\n\n return messages.map((m) => {\n let content: string | null = m.content;\n if (\n m.role === \"assistant\" &&\n m.toolCalls &&\n m.toolCalls.length > 0 &&\n (!m.content || m.content.trim() === \"\")\n ) {\n content = null;\n }\n\n const out: OpenAIMessage = {\n role: m.role as OpenAIMessage[\"role\"],\n content,\n };\n\n if (m.toolCallId) {\n out.tool_call_id = m.toolCallId;\n }\n\n if (m.toolCalls && m.toolCalls.length > 0) {\n out.tool_calls = m.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: wireToolName(tc.name),\n arguments:\n typeof tc.args === \"string\"\n ? tc.args\n : JSON.stringify(tc.args ?? {}),\n },\n ...(tc.thoughtSignature\n ? { thoughtSignature: tc.thoughtSignature }\n : {}),\n }));\n }\n\n return out;\n });\n }\n\n private buildTools(\n definitions: AgentToolDefinition[],\n nameToWire: Map<string, string>,\n ): OpenAITool[] {\n return definitions.map((def) => ({\n type: \"function\" as const,\n function: {\n name: nameToWire.get(def.name) ?? def.name,\n description: def.description,\n parameters: def.parameters,\n },\n }));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Text-based tool call parsing (fallback)\n// ---------------------------------------------------------------------------\n\n/**\n * Parses text-based tool calls from model output.\n *\n * Handles two formats:\n * 1. Llama native: `[{\"name\": \"tool_name\", \"parameters\": {\"arg\": \"val\"}}]`\n * 2. Python-style: `[tool_name(arg1='val1', arg2='val2')]`\n */\nexport function parseTextToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const trimmed = text.trim();\n\n const jsonResult = tryParseLlamaJsonToolCalls(trimmed);\n if (jsonResult.length > 0) return jsonResult;\n\n const pyResult = tryParsePythonStyleToolCalls(trimmed);\n if (pyResult.length > 0) return pyResult;\n\n return [];\n}\n\nfunction isLlamaToolJsonItem(value: unknown): value is Record<\n string,\n unknown\n> & {\n name: string;\n} {\n if (!isRecord(value)) return false;\n return typeof value.name === \"string\";\n}\n\nfunction tryParseLlamaJsonToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n const slice = extractLlamaToolJsonSlice(text);\n if (!slice) return [];\n\n try {\n const parsed: unknown = JSON.parse(slice);\n if (!Array.isArray(parsed)) return [];\n\n return parsed.filter(isLlamaToolJsonItem).map((item) => ({\n name: item.name,\n args: item.parameters ?? item.arguments ?? item.args ?? {},\n }));\n } catch {\n return [];\n }\n}\n\nfunction tryParsePythonStyleToolCalls(\n text: string,\n): Array<{ name: string; args: unknown }> {\n if (text.length > PYTHON_STYLE_TOOL_PARSE_MAX_INPUT) {\n return [];\n }\n\n const pattern = /\\[?([a-zA-Z_][\\w.]*)\\(([^)]*)\\)\\]?/g;\n const results: Array<{ name: string; args: unknown }> = [];\n\n for (const match of text.matchAll(pattern)) {\n const name = match[1];\n const argsStr = match[2];\n\n const args: Record<string, unknown> = {};\n const argPattern = /(\\w+)\\s*=\\s*(?:'([^']*)'|\"([^\"]*)\"|(\\S+))/g;\n for (const argMatch of argsStr.matchAll(argPattern)) {\n const key = argMatch[1];\n const value = argMatch[2] ?? argMatch[3] ?? argMatch[4];\n args[key] = value;\n }\n\n results.push({ name, args });\n }\n\n return results;\n}\n"],"mappings":"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"}
|
package/dist/app/index.d.ts
CHANGED
|
@@ -12,9 +12,23 @@ interface QueryResult {
|
|
|
12
12
|
isAsUser: boolean;
|
|
13
13
|
}
|
|
14
14
|
declare class AppManager {
|
|
15
|
-
private readonly
|
|
15
|
+
private readonly _queriesDir;
|
|
16
|
+
private readonly _metricViewsDir;
|
|
17
|
+
constructor(queriesDir?: string, metricViewsDir?: string);
|
|
18
|
+
get queriesDir(): string;
|
|
19
|
+
get metricViewsDir(): string;
|
|
16
20
|
/**
|
|
17
|
-
*
|
|
21
|
+
* Whether `req` is a dev-tunnel (`?dev`) request. Internal `?dev` predicate
|
|
22
|
+
* shared by {@link createFsAdapter} (tunnel-vs-`fs` branch) and
|
|
23
|
+
* {@link isNotFoundError} (dev's message-match not-found classification).
|
|
24
|
+
*/
|
|
25
|
+
private isDevRequest;
|
|
26
|
+
/**
|
|
27
|
+
* Validates that a file path is within the given base directory. The
|
|
28
|
+
* `baseDir` is passed explicitly (rather than pinned to `queriesDir`) so the
|
|
29
|
+
* same traversal guard protects reads from any config directory —
|
|
30
|
+
* `config/queries/` for `.sql` files and `config/metric-views/` for the
|
|
31
|
+
* metric-view definitions.
|
|
18
32
|
*/
|
|
19
33
|
private validatePath;
|
|
20
34
|
/**
|
|
@@ -30,6 +44,39 @@ declare class AppManager {
|
|
|
30
44
|
* @returns The query content and execution mode (as user or as service principal)
|
|
31
45
|
*/
|
|
32
46
|
getAppQuery(queryKey: string, req?: RequestLike, devFileReader?: DevFileReader): Promise<QueryResult | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Read a single config file from a given base directory, dev-tunnel-aware.
|
|
49
|
+
* Shared core behind {@link readConfigFile} (queries dir) and
|
|
50
|
+
* {@link readMetricViewsConfig} (metric-views dir).
|
|
51
|
+
*
|
|
52
|
+
* @param baseDir - The config directory the file must resolve within.
|
|
53
|
+
* @param fileName - File name (or relative path) within `baseDir`.
|
|
54
|
+
* @param req - Optional request object to detect dev mode.
|
|
55
|
+
* @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.
|
|
56
|
+
* @returns The raw file contents, or `null` when the file is absent / the path is rejected.
|
|
57
|
+
*/
|
|
58
|
+
private readFileFromDir;
|
|
59
|
+
/**
|
|
60
|
+
* Read a single config file from the queries directory, dev-tunnel-aware.
|
|
61
|
+
*
|
|
62
|
+
* @param fileName - File name (or relative path) within the queries directory.
|
|
63
|
+
* @param req - Optional request object to detect dev mode.
|
|
64
|
+
* @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.
|
|
65
|
+
* @returns The raw file contents, or `null` when the file is absent / the path is rejected.
|
|
66
|
+
*/
|
|
67
|
+
readConfigFile(fileName: string, req?: RequestLike, devFileReader?: DevFileReader): Promise<string | null>;
|
|
68
|
+
/**
|
|
69
|
+
* Read a single config file from the metric-views directory
|
|
70
|
+
* (`config/metric-views/`), dev-tunnel-aware. Same absent-file/traversal
|
|
71
|
+
* semantics as {@link readConfigFile}, but rooted at {@link metricViewsDir}.
|
|
72
|
+
*
|
|
73
|
+
* @param fileName - File name (or relative path) within the metric-views directory.
|
|
74
|
+
* @param req - Optional request object to detect dev mode.
|
|
75
|
+
* @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.
|
|
76
|
+
* @returns The raw file contents, or `null` when the file is absent / the path is rejected.
|
|
77
|
+
*/
|
|
78
|
+
readMetricViewsConfig(fileName: string, req?: RequestLike, devFileReader?: DevFileReader): Promise<string | null>;
|
|
79
|
+
private isNotFoundError;
|
|
33
80
|
}
|
|
34
81
|
//#endregion
|
|
35
82
|
export { AppManager };
|
package/dist/app/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/app/index.ts"],"mappings":";UAMU,WAAA;EACR,KAAA,GAAQ,MAAA;EACR,OAAA,EAAS,MAAA;AAAA;AAAA,UAGD,aAAA;EACR,QAAA,CAAS,QAAA,UAAkB,GAAA,EAAK,WAAA,GAAc,OAAA;EAC9C,OAAA,CAAQ,OAAA,UAAiB,GAAA,EAAK,WAAA,GAAc,OAAA;AAAA;AAAA,UAGpC,WAAA;EACR,KAAA;EACA,QAAA;AAAA;AAAA,cAWW,UAAA;EAAA,iBACM,UAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/app/index.ts"],"mappings":";UAMU,WAAA;EACR,KAAA,GAAQ,MAAA;EACR,OAAA,EAAS,MAAA;AAAA;AAAA,UAGD,aAAA;EACR,QAAA,CAAS,QAAA,UAAkB,GAAA,EAAK,WAAA,GAAc,OAAA;EAC9C,OAAA,CAAQ,OAAA,UAAiB,GAAA,EAAK,WAAA,GAAc,OAAA;AAAA;AAAA,UAGpC,WAAA;EACR,KAAA;EACA,QAAA;AAAA;AAAA,cAWW,UAAA;EAAA,iBACM,WAAA;EAAA,iBACA,eAAA;cAGf,UAAA,WAMA,cAAA;EAAA,IASE,UAAA,CAAA;EAAA,IAIA,cAAA,CAAA;EAxC+C;;;;;EAAA,QAiD3C,YAAA;EAlDsC;;;;;;;EAAA,QA6DtC,YAAA;EAzDA;;;EAAA,QAyEA,eAAA;EAvEA;AAWV;;;;;;;EA+FQ,WAAA,CACJ,QAAA,UACA,GAAA,GAAM,WAAA,EACN,aAAA,GAAgB,aAAA,GACf,OAAA,CAAQ,WAAA;EAoHO;;;;;;;;;;;EAAA,QArCJ,eAAA;EA9JV;;;;;;;;EAgME,cAAA,CACJ,QAAA,UACA,GAAA,GAAM,WAAA,EACN,aAAA,GAAgB,aAAA,GACf,OAAA;EAtHe;;;;;;;;;;EAoIZ,qBAAA,CACJ,QAAA,UACA,GAAA,GAAM,WAAA,EACN,aAAA,GAAgB,aAAA,GACf,OAAA;EAAA,QASK,eAAA;AAAA"}
|
package/dist/app/index.js
CHANGED
|
@@ -5,16 +5,39 @@ import path from "node:path";
|
|
|
5
5
|
//#region src/app/index.ts
|
|
6
6
|
const logger = createLogger("app");
|
|
7
7
|
var AppManager = class {
|
|
8
|
-
|
|
8
|
+
_queriesDir;
|
|
9
|
+
_metricViewsDir;
|
|
10
|
+
constructor(queriesDir = path.resolve(process.cwd(), "config/queries"), metricViewsDir = path.resolve(path.dirname(queriesDir), "metric-views")) {
|
|
11
|
+
this._queriesDir = queriesDir;
|
|
12
|
+
this._metricViewsDir = metricViewsDir;
|
|
13
|
+
}
|
|
14
|
+
get queriesDir() {
|
|
15
|
+
return this._queriesDir;
|
|
16
|
+
}
|
|
17
|
+
get metricViewsDir() {
|
|
18
|
+
return this._metricViewsDir;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Whether `req` is a dev-tunnel (`?dev`) request. Internal `?dev` predicate
|
|
22
|
+
* shared by {@link createFsAdapter} (tunnel-vs-`fs` branch) and
|
|
23
|
+
* {@link isNotFoundError} (dev's message-match not-found classification).
|
|
24
|
+
*/
|
|
25
|
+
isDevRequest(req) {
|
|
26
|
+
return req?.query?.dev !== void 0;
|
|
27
|
+
}
|
|
9
28
|
/**
|
|
10
|
-
* Validates that a file path is within the
|
|
29
|
+
* Validates that a file path is within the given base directory. The
|
|
30
|
+
* `baseDir` is passed explicitly (rather than pinned to `queriesDir`) so the
|
|
31
|
+
* same traversal guard protects reads from any config directory —
|
|
32
|
+
* `config/queries/` for `.sql` files and `config/metric-views/` for the
|
|
33
|
+
* metric-view definitions.
|
|
11
34
|
*/
|
|
12
|
-
validatePath(fileName) {
|
|
13
|
-
const
|
|
14
|
-
const resolvedPath = path.resolve(
|
|
15
|
-
const
|
|
16
|
-
if (!resolvedPath.startsWith(
|
|
17
|
-
logger.error("Invalid
|
|
35
|
+
validatePath(fileName, baseDir) {
|
|
36
|
+
const filePath = path.join(baseDir, fileName);
|
|
37
|
+
const resolvedPath = path.resolve(filePath);
|
|
38
|
+
const resolvedBaseDir = path.resolve(baseDir);
|
|
39
|
+
if (!resolvedPath.startsWith(resolvedBaseDir)) {
|
|
40
|
+
logger.error("Invalid config path: path traversal detected");
|
|
18
41
|
return null;
|
|
19
42
|
}
|
|
20
43
|
return resolvedPath;
|
|
@@ -23,7 +46,7 @@ var AppManager = class {
|
|
|
23
46
|
* Creates a filesystem adapter based on dev mode or production mode
|
|
24
47
|
*/
|
|
25
48
|
createFsAdapter(req, devFileReader) {
|
|
26
|
-
if (req
|
|
49
|
+
if (this.isDevRequest(req) && devFileReader && req) return {
|
|
27
50
|
readdir: async (dirPath) => {
|
|
28
51
|
const relativePath = path.relative(process.cwd(), dirPath);
|
|
29
52
|
return devFileReader.readdir(relativePath, req);
|
|
@@ -75,7 +98,7 @@ var AppManager = class {
|
|
|
75
98
|
logger.error(`Query file not found: ${queryKey}`);
|
|
76
99
|
return null;
|
|
77
100
|
}
|
|
78
|
-
const resolvedPath = this.validatePath(queryFileName);
|
|
101
|
+
const resolvedPath = this.validatePath(queryFileName, this._queriesDir);
|
|
79
102
|
if (!resolvedPath) return null;
|
|
80
103
|
try {
|
|
81
104
|
return {
|
|
@@ -87,6 +110,60 @@ var AppManager = class {
|
|
|
87
110
|
return null;
|
|
88
111
|
}
|
|
89
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Read a single config file from a given base directory, dev-tunnel-aware.
|
|
115
|
+
* Shared core behind {@link readConfigFile} (queries dir) and
|
|
116
|
+
* {@link readMetricViewsConfig} (metric-views dir).
|
|
117
|
+
*
|
|
118
|
+
* @param baseDir - The config directory the file must resolve within.
|
|
119
|
+
* @param fileName - File name (or relative path) within `baseDir`.
|
|
120
|
+
* @param req - Optional request object to detect dev mode.
|
|
121
|
+
* @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.
|
|
122
|
+
* @returns The raw file contents, or `null` when the file is absent / the path is rejected.
|
|
123
|
+
*/
|
|
124
|
+
async readFileFromDir(baseDir, fileName, req, devFileReader) {
|
|
125
|
+
const resolvedPath = this.validatePath(fileName, baseDir);
|
|
126
|
+
if (!resolvedPath) return null;
|
|
127
|
+
const fsAdapter = this.createFsAdapter(req, devFileReader);
|
|
128
|
+
try {
|
|
129
|
+
return await fsAdapter.readFile(resolvedPath);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (this.isNotFoundError(error, req)) return null;
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Read a single config file from the queries directory, dev-tunnel-aware.
|
|
137
|
+
*
|
|
138
|
+
* @param fileName - File name (or relative path) within the queries directory.
|
|
139
|
+
* @param req - Optional request object to detect dev mode.
|
|
140
|
+
* @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.
|
|
141
|
+
* @returns The raw file contents, or `null` when the file is absent / the path is rejected.
|
|
142
|
+
*/
|
|
143
|
+
async readConfigFile(fileName, req, devFileReader) {
|
|
144
|
+
return this.readFileFromDir(this._queriesDir, fileName, req, devFileReader);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Read a single config file from the metric-views directory
|
|
148
|
+
* (`config/metric-views/`), dev-tunnel-aware. Same absent-file/traversal
|
|
149
|
+
* semantics as {@link readConfigFile}, but rooted at {@link metricViewsDir}.
|
|
150
|
+
*
|
|
151
|
+
* @param fileName - File name (or relative path) within the metric-views directory.
|
|
152
|
+
* @param req - Optional request object to detect dev mode.
|
|
153
|
+
* @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.
|
|
154
|
+
* @returns The raw file contents, or `null` when the file is absent / the path is rejected.
|
|
155
|
+
*/
|
|
156
|
+
async readMetricViewsConfig(fileName, req, devFileReader) {
|
|
157
|
+
return this.readFileFromDir(this._metricViewsDir, fileName, req, devFileReader);
|
|
158
|
+
}
|
|
159
|
+
isNotFoundError(error, req) {
|
|
160
|
+
if (error?.code === "ENOENT") return true;
|
|
161
|
+
if (this.isDevRequest(req)) {
|
|
162
|
+
const message = error?.message ?? "";
|
|
163
|
+
return /ENOENT|no such file/i.test(message);
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
90
167
|
};
|
|
91
168
|
|
|
92
169
|
//#endregion
|
package/dist/app/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/app/index.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createLogger } from \"../logging/logger\";\n\nconst logger = createLogger(\"app\");\n\ninterface RequestLike {\n query?: Record<string, any>;\n headers: Record<string, string | string[] | undefined>;\n}\n\ninterface DevFileReader {\n readFile(filePath: string, req: RequestLike): Promise<string>;\n readdir(dirPath: string, req: RequestLike): Promise<string[]>;\n}\n\ninterface QueryResult {\n query: string;\n isAsUser: boolean;\n}\n\n/**\n * Abstraction for filesystem operations that works in both dev and production modes\n */\ninterface FileSystemAdapter {\n readdir(dirPath: string): Promise<string[]>;\n readFile(filePath: string): Promise<string>;\n}\n\nexport class AppManager {\n private readonly queriesDir = path.resolve(process.cwd(), \"config/queries\");\n\n /**\n * Validates that a file path is within the queries directory\n */\n private validatePath(fileName: string): string | null {\n const queryFilePath = path.join(this.queriesDir, fileName);\n const resolvedPath = path.resolve(queryFilePath);\n const resolvedQueriesDir = path.resolve(this.queriesDir);\n\n if (!resolvedPath.startsWith(resolvedQueriesDir)) {\n logger.error(\"Invalid query path: path traversal detected\");\n return null;\n }\n\n return resolvedPath;\n }\n\n /**\n * Creates a filesystem adapter based on dev mode or production mode\n */\n private createFsAdapter(\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): FileSystemAdapter {\n const isDevMode = req?.query?.dev !== undefined;\n\n if (isDevMode && devFileReader && req) {\n // Dev mode: use WebSocket tunnel to read from local filesystem\n return {\n readdir: async (dirPath: string) => {\n const relativePath = path.relative(process.cwd(), dirPath);\n return devFileReader.readdir(relativePath, req);\n },\n readFile: async (filePath: string) => {\n const relativePath = path.relative(process.cwd(), filePath);\n return devFileReader.readFile(relativePath, req);\n },\n };\n }\n\n // Production mode: use server filesystem\n return {\n readdir: (dirPath: string) => fs.readdir(dirPath),\n readFile: (filePath: string) => fs.readFile(filePath, \"utf8\"),\n };\n }\n\n /**\n * Retrieves a query file by key from the queries directory\n * In dev mode with a request context, reads from local filesystem via WebSocket\n * @param queryKey - The query file name (without extension)\n * @param req - Optional request object to detect dev mode\n * @param devFileReader - Optional DevFileReader instance to read files from local filesystem\n * @returns The query content and execution mode (as user or as service principal)\n */\n async getAppQuery(\n queryKey: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<QueryResult | null> {\n // Security: Sanitize query key to prevent path traversal\n if (!queryKey || !/^[a-zA-Z0-9_-]+$/.test(queryKey)) {\n logger.error(\n \"Invalid query key format: %s. Only alphanumeric characters, underscores, and hyphens are allowed.\",\n queryKey,\n );\n return null;\n }\n\n // Create filesystem adapter for dev or production mode\n const fsAdapter = this.createFsAdapter(req, devFileReader);\n\n // Priority order: .obo.sql first (as user), then .sql (as service principal)\n const oboFileName = `${queryKey}.obo.sql`;\n const defaultFileName = `${queryKey}.sql`;\n\n // List directory to find which query file exists\n let files: string[];\n try {\n files = await fsAdapter.readdir(this.queriesDir);\n } catch (error) {\n logger.error(\n `Failed to read queries directory: ${(error as Error).message}`,\n );\n return null;\n }\n\n // Determine which query file to use\n let queryFileName: string | null = null;\n let isAsUser = false;\n\n if (files.includes(oboFileName)) {\n queryFileName = oboFileName;\n isAsUser = true;\n\n // Warn if both variants exist\n if (files.includes(defaultFileName)) {\n logger.warn(\n `Both ${oboFileName} and ${defaultFileName} found for query ${queryKey}. Using ${oboFileName}.`,\n );\n }\n } else if (files.includes(defaultFileName)) {\n queryFileName = defaultFileName;\n isAsUser = false;\n }\n\n if (!queryFileName) {\n logger.error(`Query file not found: ${queryKey}`);\n return null;\n }\n\n // Validate and resolve the file path\n const resolvedPath = this.validatePath(queryFileName);\n if (!resolvedPath) {\n return null;\n }\n\n // Read the query file\n try {\n const query = await fsAdapter.readFile(resolvedPath);\n return { query, isAsUser };\n } catch (error) {\n logger.error(`Failed to read query file: ${(error as Error).message}`);\n return null;\n }\n }\n}\n\nexport type { DevFileReader };\n"],"mappings":";;;;;AAIA,MAAM,SAAS,aAAa,MAAM;AAyBlC,IAAa,aAAb,MAAwB;CACtB,AAAiB,aAAa,KAAK,QAAQ,QAAQ,KAAK,EAAE,iBAAiB;;;;CAK3E,AAAQ,aAAa,UAAiC;EACpD,MAAM,gBAAgB,KAAK,KAAK,KAAK,YAAY,SAAS;EAC1D,MAAM,eAAe,KAAK,QAAQ,cAAc;EAChD,MAAM,qBAAqB,KAAK,QAAQ,KAAK,WAAW;AAExD,MAAI,CAAC,aAAa,WAAW,mBAAmB,EAAE;AAChD,UAAO,MAAM,8CAA8C;AAC3D,UAAO;;AAGT,SAAO;;;;;CAMT,AAAQ,gBACN,KACA,eACmB;AAGnB,MAFkB,KAAK,OAAO,QAAQ,UAErB,iBAAiB,IAEhC,QAAO;GACL,SAAS,OAAO,YAAoB;IAClC,MAAM,eAAe,KAAK,SAAS,QAAQ,KAAK,EAAE,QAAQ;AAC1D,WAAO,cAAc,QAAQ,cAAc,IAAI;;GAEjD,UAAU,OAAO,aAAqB;IACpC,MAAM,eAAe,KAAK,SAAS,QAAQ,KAAK,EAAE,SAAS;AAC3D,WAAO,cAAc,SAAS,cAAc,IAAI;;GAEnD;AAIH,SAAO;GACL,UAAU,YAAoB,GAAG,QAAQ,QAAQ;GACjD,WAAW,aAAqB,GAAG,SAAS,UAAU,OAAO;GAC9D;;;;;;;;;;CAWH,MAAM,YACJ,UACA,KACA,eAC6B;AAE7B,MAAI,CAAC,YAAY,CAAC,mBAAmB,KAAK,SAAS,EAAE;AACnD,UAAO,MACL,qGACA,SACD;AACD,UAAO;;EAIT,MAAM,YAAY,KAAK,gBAAgB,KAAK,cAAc;EAG1D,MAAM,cAAc,GAAG,SAAS;EAChC,MAAM,kBAAkB,GAAG,SAAS;EAGpC,IAAI;AACJ,MAAI;AACF,WAAQ,MAAM,UAAU,QAAQ,KAAK,WAAW;WACzC,OAAO;AACd,UAAO,MACL,qCAAsC,MAAgB,UACvD;AACD,UAAO;;EAIT,IAAI,gBAA+B;EACnC,IAAI,WAAW;AAEf,MAAI,MAAM,SAAS,YAAY,EAAE;AAC/B,mBAAgB;AAChB,cAAW;AAGX,OAAI,MAAM,SAAS,gBAAgB,CACjC,QAAO,KACL,QAAQ,YAAY,OAAO,gBAAgB,mBAAmB,SAAS,UAAU,YAAY,GAC9F;aAEM,MAAM,SAAS,gBAAgB,EAAE;AAC1C,mBAAgB;AAChB,cAAW;;AAGb,MAAI,CAAC,eAAe;AAClB,UAAO,MAAM,yBAAyB,WAAW;AACjD,UAAO;;EAIT,MAAM,eAAe,KAAK,aAAa,cAAc;AACrD,MAAI,CAAC,aACH,QAAO;AAIT,MAAI;AAEF,UAAO;IAAE,OADK,MAAM,UAAU,SAAS,aAAa;IACpC;IAAU;WACnB,OAAO;AACd,UAAO,MAAM,8BAA+B,MAAgB,UAAU;AACtE,UAAO"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/app/index.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createLogger } from \"../logging/logger\";\n\nconst logger = createLogger(\"app\");\n\ninterface RequestLike {\n query?: Record<string, any>;\n headers: Record<string, string | string[] | undefined>;\n}\n\ninterface DevFileReader {\n readFile(filePath: string, req: RequestLike): Promise<string>;\n readdir(dirPath: string, req: RequestLike): Promise<string[]>;\n}\n\ninterface QueryResult {\n query: string;\n isAsUser: boolean;\n}\n\n/**\n * Abstraction for filesystem operations that works in both dev and production modes\n */\ninterface FileSystemAdapter {\n readdir(dirPath: string): Promise<string[]>;\n readFile(filePath: string): Promise<string>;\n}\n\nexport class AppManager {\n private readonly _queriesDir: string;\n private readonly _metricViewsDir: string;\n\n constructor(\n queriesDir: string = path.resolve(process.cwd(), \"config/queries\"),\n // Metric-view declarations live in a sibling `config/metric-views/`\n // directory (next to `config/queries/` and `config/agents/`), NOT inside\n // the queries folder. Default it as a sibling of `queriesDir` so a\n // single-arg `new AppManager(dir)` (and every test that overrides only the\n // queries dir) still resolves the metric-views dir consistently.\n metricViewsDir: string = path.resolve(\n path.dirname(queriesDir),\n \"metric-views\",\n ),\n ) {\n this._queriesDir = queriesDir;\n this._metricViewsDir = metricViewsDir;\n }\n\n get queriesDir(): string {\n return this._queriesDir;\n }\n\n get metricViewsDir(): string {\n return this._metricViewsDir;\n }\n\n /**\n * Whether `req` is a dev-tunnel (`?dev`) request. Internal `?dev` predicate\n * shared by {@link createFsAdapter} (tunnel-vs-`fs` branch) and\n * {@link isNotFoundError} (dev's message-match not-found classification).\n */\n private isDevRequest(req?: RequestLike): boolean {\n return req?.query?.dev !== undefined;\n }\n\n /**\n * Validates that a file path is within the given base directory. The\n * `baseDir` is passed explicitly (rather than pinned to `queriesDir`) so the\n * same traversal guard protects reads from any config directory —\n * `config/queries/` for `.sql` files and `config/metric-views/` for the\n * metric-view definitions.\n */\n private validatePath(fileName: string, baseDir: string): string | null {\n const filePath = path.join(baseDir, fileName);\n const resolvedPath = path.resolve(filePath);\n const resolvedBaseDir = path.resolve(baseDir);\n\n if (!resolvedPath.startsWith(resolvedBaseDir)) {\n logger.error(\"Invalid config path: path traversal detected\");\n return null;\n }\n\n return resolvedPath;\n }\n\n /**\n * Creates a filesystem adapter based on dev mode or production mode\n */\n private createFsAdapter(\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): FileSystemAdapter {\n const isDevMode = this.isDevRequest(req);\n\n if (isDevMode && devFileReader && req) {\n // Dev mode: use WebSocket tunnel to read from local filesystem\n return {\n readdir: async (dirPath: string) => {\n const relativePath = path.relative(process.cwd(), dirPath);\n return devFileReader.readdir(relativePath, req);\n },\n readFile: async (filePath: string) => {\n const relativePath = path.relative(process.cwd(), filePath);\n return devFileReader.readFile(relativePath, req);\n },\n };\n }\n\n // Production mode: use server filesystem\n return {\n readdir: (dirPath: string) => fs.readdir(dirPath),\n readFile: (filePath: string) => fs.readFile(filePath, \"utf8\"),\n };\n }\n\n /**\n * Retrieves a query file by key from the queries directory\n * In dev mode with a request context, reads from local filesystem via WebSocket\n * @param queryKey - The query file name (without extension)\n * @param req - Optional request object to detect dev mode\n * @param devFileReader - Optional DevFileReader instance to read files from local filesystem\n * @returns The query content and execution mode (as user or as service principal)\n */\n async getAppQuery(\n queryKey: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<QueryResult | null> {\n // Security: Sanitize query key to prevent path traversal\n if (!queryKey || !/^[a-zA-Z0-9_-]+$/.test(queryKey)) {\n logger.error(\n \"Invalid query key format: %s. Only alphanumeric characters, underscores, and hyphens are allowed.\",\n queryKey,\n );\n return null;\n }\n\n // Create filesystem adapter for dev or production mode\n const fsAdapter = this.createFsAdapter(req, devFileReader);\n\n // Priority order: .obo.sql first (as user), then .sql (as service principal)\n const oboFileName = `${queryKey}.obo.sql`;\n const defaultFileName = `${queryKey}.sql`;\n\n // List directory to find which query file exists\n let files: string[];\n try {\n files = await fsAdapter.readdir(this.queriesDir);\n } catch (error) {\n logger.error(\n `Failed to read queries directory: ${(error as Error).message}`,\n );\n return null;\n }\n\n // Determine which query file to use\n let queryFileName: string | null = null;\n let isAsUser = false;\n\n if (files.includes(oboFileName)) {\n queryFileName = oboFileName;\n isAsUser = true;\n\n // Warn if both variants exist\n if (files.includes(defaultFileName)) {\n logger.warn(\n `Both ${oboFileName} and ${defaultFileName} found for query ${queryKey}. Using ${oboFileName}.`,\n );\n }\n } else if (files.includes(defaultFileName)) {\n queryFileName = defaultFileName;\n isAsUser = false;\n }\n\n if (!queryFileName) {\n logger.error(`Query file not found: ${queryKey}`);\n return null;\n }\n\n // Validate and resolve the file path\n const resolvedPath = this.validatePath(queryFileName, this._queriesDir);\n if (!resolvedPath) {\n return null;\n }\n\n // Read the query file\n try {\n const query = await fsAdapter.readFile(resolvedPath);\n return { query, isAsUser };\n } catch (error) {\n logger.error(`Failed to read query file: ${(error as Error).message}`);\n return null;\n }\n }\n\n /**\n * Read a single config file from a given base directory, dev-tunnel-aware.\n * Shared core behind {@link readConfigFile} (queries dir) and\n * {@link readMetricViewsConfig} (metric-views dir).\n *\n * @param baseDir - The config directory the file must resolve within.\n * @param fileName - File name (or relative path) within `baseDir`.\n * @param req - Optional request object to detect dev mode.\n * @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.\n * @returns The raw file contents, or `null` when the file is absent / the path is rejected.\n */\n private async readFileFromDir(\n baseDir: string,\n fileName: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<string | null> {\n // Traversal guard: refuse to read outside the base directory.\n const resolvedPath = this.validatePath(fileName, baseDir);\n if (!resolvedPath) {\n return null;\n }\n\n const fsAdapter = this.createFsAdapter(req, devFileReader);\n\n try {\n return await fsAdapter.readFile(resolvedPath);\n } catch (error) {\n if (this.isNotFoundError(error, req)) {\n return null;\n }\n // Any other error (permission / IO / tunnel) is fatal: propagate so the\n // caller can distinguish it from a dormant (absent) config.\n throw error;\n }\n }\n\n /**\n * Read a single config file from the queries directory, dev-tunnel-aware.\n *\n * @param fileName - File name (or relative path) within the queries directory.\n * @param req - Optional request object to detect dev mode.\n * @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.\n * @returns The raw file contents, or `null` when the file is absent / the path is rejected.\n */\n async readConfigFile(\n fileName: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<string | null> {\n return this.readFileFromDir(this._queriesDir, fileName, req, devFileReader);\n }\n\n /**\n * Read a single config file from the metric-views directory\n * (`config/metric-views/`), dev-tunnel-aware. Same absent-file/traversal\n * semantics as {@link readConfigFile}, but rooted at {@link metricViewsDir}.\n *\n * @param fileName - File name (or relative path) within the metric-views directory.\n * @param req - Optional request object to detect dev mode.\n * @param devFileReader - Optional DevFileReader to read via the WebSocket tunnel.\n * @returns The raw file contents, or `null` when the file is absent / the path is rejected.\n */\n async readMetricViewsConfig(\n fileName: string,\n req?: RequestLike,\n devFileReader?: DevFileReader,\n ): Promise<string | null> {\n return this.readFileFromDir(\n this._metricViewsDir,\n fileName,\n req,\n devFileReader,\n );\n }\n\n private isNotFoundError(error: unknown, req?: RequestLike): boolean {\n if ((error as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n return true;\n }\n if (this.isDevRequest(req)) {\n const message = (error as Error)?.message ?? \"\";\n return /ENOENT|no such file/i.test(message);\n }\n return false;\n }\n}\n\nexport type { DevFileReader, RequestLike };\n"],"mappings":"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"}
|
package/dist/appkit/package.js
CHANGED
package/dist/beta.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AgentAdapter, AgentEvent, AgentInput, AgentRunContext, AgentToolDefinition, Message, Thread, ThreadStore, ToolAnnotations, ToolProvider } from "./shared/src/agent.js";
|
|
2
2
|
import "./shared/src/index.js";
|
|
3
|
-
import { DatabricksAdapter, parseTextToolCalls } from "./agents/databricks.js";
|
|
3
|
+
import { DatabricksAdapter, GenerationParams, parseTextToolCalls } from "./agents/databricks.js";
|
|
4
4
|
import { AppKitMcpClient, McpConnectAllResult } from "./connectors/mcp/client.js";
|
|
5
5
|
import { FunctionTool, functionToolToDefinition, isFunctionTool } from "./core/agent/tools/function-tool.js";
|
|
6
6
|
import { HostedTool, isHostedTool, mcpServer, resolveHostedTools } from "./core/agent/tools/hosted-tools.js";
|
|
@@ -14,4 +14,4 @@ import { agentIdFromMarkdownPath, loadAgentFromFile, loadAgentsFromDir } from ".
|
|
|
14
14
|
import { agents } from "./plugins/agents/agents.js";
|
|
15
15
|
import "./plugins/agents/index.js";
|
|
16
16
|
import "./plugins/beta-exports.generated.js";
|
|
17
|
-
export { type AgentAdapter, type AgentDefinition, type AgentEvent, type AgentInput, type AgentRunContext, type AgentTool, type AgentToolDefinition, type AgentTools, type AgentToolsFn, type AgentsPluginConfig, AppKitMcpClient, type AutoInheritToolsConfig, type BaseSystemPromptOption, DatabricksAdapter, type FunctionTool, type HostedTool, type McpConnectAllResult, type Message, type PluginToolkitProvider, type Plugins, type PromptContext, type RegisteredAgent, type ResolvedToolEntry, type RunAgentInput, type RunAgentResult, type Thread, type ThreadStore, type ToolAnnotations, type ToolConfig, type ToolEntry, type ToolProvider, type ToolRegistry, type ToolkitEntry, type ToolkitOptions, agentIdFromMarkdownPath, agents, createAgent, defineTool, executeFromRegistry, functionToolToDefinition, isFunctionTool, isHostedTool, isToolkitEntry, loadAgentFromFile, loadAgentsFromDir, mcpServer, parseTextToolCalls, resolveHostedTools, runAgent, tool, toolsFromRegistry };
|
|
17
|
+
export { type AgentAdapter, type AgentDefinition, type AgentEvent, type AgentInput, type AgentRunContext, type AgentTool, type AgentToolDefinition, type AgentTools, type AgentToolsFn, type AgentsPluginConfig, AppKitMcpClient, type AutoInheritToolsConfig, type BaseSystemPromptOption, DatabricksAdapter, type FunctionTool, type GenerationParams, type HostedTool, type McpConnectAllResult, type Message, type PluginToolkitProvider, type Plugins, type PromptContext, type RegisteredAgent, type ResolvedToolEntry, type RunAgentInput, type RunAgentResult, type Thread, type ThreadStore, type ToolAnnotations, type ToolConfig, type ToolEntry, type ToolProvider, type ToolRegistry, type ToolkitEntry, type ToolkitOptions, agentIdFromMarkdownPath, agents, createAgent, defineTool, executeFromRegistry, functionToolToDefinition, isFunctionTool, isHostedTool, isToolkitEntry, loadAgentFromFile, loadAgentsFromDir, mcpServer, parseTextToolCalls, resolveHostedTools, runAgent, tool, toolsFromRegistry };
|