@databricks/appkit 0.70.0 → 0.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/CLAUDE.md +46 -0
  2. package/NOTICE.md +2 -2
  3. package/dist/appkit/package.js +1 -1
  4. package/dist/beta.d.ts +13 -1
  5. package/dist/beta.js +13 -1
  6. package/dist/cli/commands/agent/eval.js +112 -0
  7. package/dist/cli/commands/agent/eval.js.map +1 -0
  8. package/dist/cli/commands/agent/index.js +18 -0
  9. package/dist/cli/commands/agent/index.js.map +1 -0
  10. package/dist/cli/index.js +2 -0
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/connectors/index.js +2 -0
  13. package/dist/connectors/mlflow/auth.d.ts +18 -0
  14. package/dist/connectors/mlflow/auth.d.ts.map +1 -0
  15. package/dist/connectors/mlflow/auth.js +50 -0
  16. package/dist/connectors/mlflow/auth.js.map +1 -0
  17. package/dist/connectors/mlflow/client.d.ts +51 -0
  18. package/dist/connectors/mlflow/client.d.ts.map +1 -0
  19. package/dist/connectors/mlflow/client.js +93 -0
  20. package/dist/connectors/mlflow/client.js.map +1 -0
  21. package/dist/core/appkit.d.ts.map +1 -1
  22. package/dist/core/appkit.js +1 -0
  23. package/dist/core/appkit.js.map +1 -1
  24. package/dist/evals/define-eval.d.ts +26 -0
  25. package/dist/evals/define-eval.d.ts.map +1 -0
  26. package/dist/evals/define-eval.js +28 -0
  27. package/dist/evals/define-eval.js.map +1 -0
  28. package/dist/evals/discover.d.ts +20 -0
  29. package/dist/evals/discover.d.ts.map +1 -0
  30. package/dist/evals/discover.js +49 -0
  31. package/dist/evals/discover.js.map +1 -0
  32. package/dist/evals/http-driver.d.ts +33 -0
  33. package/dist/evals/http-driver.d.ts.map +1 -0
  34. package/dist/evals/http-driver.js +118 -0
  35. package/dist/evals/http-driver.js.map +1 -0
  36. package/dist/evals/index.js +13 -0
  37. package/dist/evals/judge.d.ts +26 -0
  38. package/dist/evals/judge.d.ts.map +1 -0
  39. package/dist/evals/judge.js +77 -0
  40. package/dist/evals/judge.js.map +1 -0
  41. package/dist/evals/matchers.d.ts +12 -0
  42. package/dist/evals/matchers.d.ts.map +1 -0
  43. package/dist/evals/matchers.js +26 -0
  44. package/dist/evals/matchers.js.map +1 -0
  45. package/dist/evals/mlflow-report.d.ts +36 -0
  46. package/dist/evals/mlflow-report.d.ts.map +1 -0
  47. package/dist/evals/mlflow-report.js +161 -0
  48. package/dist/evals/mlflow-report.js.map +1 -0
  49. package/dist/evals/mlflow-run.d.ts +11 -0
  50. package/dist/evals/mlflow-run.d.ts.map +1 -0
  51. package/dist/evals/mlflow-run.js +101 -0
  52. package/dist/evals/mlflow-run.js.map +1 -0
  53. package/dist/evals/pool.js +24 -0
  54. package/dist/evals/pool.js.map +1 -0
  55. package/dist/evals/report.d.ts +25 -0
  56. package/dist/evals/report.d.ts.map +1 -0
  57. package/dist/evals/report.js +57 -0
  58. package/dist/evals/report.js.map +1 -0
  59. package/dist/evals/run-eval.d.ts +20 -0
  60. package/dist/evals/run-eval.d.ts.map +1 -0
  61. package/dist/evals/run-eval.js +144 -0
  62. package/dist/evals/run-eval.js.map +1 -0
  63. package/dist/evals/run-evals.d.ts +85 -0
  64. package/dist/evals/run-evals.d.ts.map +1 -0
  65. package/dist/evals/run-evals.js +178 -0
  66. package/dist/evals/run-evals.js.map +1 -0
  67. package/dist/evals/types.d.ts +127 -0
  68. package/dist/evals/types.d.ts.map +1 -0
  69. package/dist/plugins/agents/agents.d.ts.map +1 -1
  70. package/dist/plugins/agents/agents.js +5 -2
  71. package/dist/plugins/agents/agents.js.map +1 -1
  72. package/dist/plugins/agents/mlflow.js +267 -11
  73. package/dist/plugins/agents/mlflow.js.map +1 -1
  74. package/dist/plugins/server/index.js +2 -2
  75. package/dist/plugins/server/index.js.map +1 -1
  76. package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js +3 -3
  77. package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
  78. package/dist/plugins/server/static-server.js +3 -3
  79. package/dist/plugins/server/static-server.js.map +1 -1
  80. package/dist/plugins/server/utils.js +3 -3
  81. package/dist/plugins/server/utils.js.map +1 -1
  82. package/dist/plugins/server/vite-dev-server.js +4 -4
  83. package/dist/plugins/server/vite-dev-server.js.map +1 -1
  84. package/dist/shared/src/schemas/manifest.d.ts +33 -33
  85. package/dist/telemetry/index.js +2 -2
  86. package/dist/telemetry/telemetry-manager.d.ts +2 -1
  87. package/dist/telemetry/telemetry-manager.js +114 -26
  88. package/dist/telemetry/telemetry-manager.js.map +1 -1
  89. package/dist/type-generator/database/generate.js +3 -3
  90. package/dist/type-generator/database/generate.js.map +1 -1
  91. package/dist/type-generator/errors.js +45 -11
  92. package/dist/type-generator/errors.js.map +1 -1
  93. package/dist/type-generator/migration.js +2 -2
  94. package/dist/type-generator/migration.js.map +1 -1
  95. package/dist/type-generator/mv-registry/sync.js +3 -3
  96. package/dist/type-generator/mv-registry/sync.js.map +1 -1
  97. package/dist/type-generator/mv-registry/types.d.ts +5 -2
  98. package/dist/type-generator/mv-registry/types.d.ts.map +1 -1
  99. package/dist/type-generator/query-registry.js +3 -3
  100. package/dist/type-generator/query-registry.js.map +1 -1
  101. package/dist/type-generator/serving/server-file-extractor.js +3 -3
  102. package/dist/type-generator/serving/server-file-extractor.js.map +1 -1
  103. package/docs/api/appkit/Class.MlflowClient.md +103 -0
  104. package/docs/api/appkit/Function.buildAssessments.md +16 -0
  105. package/docs/api/appkit/Function.configureJudge.md +18 -0
  106. package/docs/api/appkit/Function.createHttpDriver.md +18 -0
  107. package/docs/api/appkit/Function.defineEval.md +35 -0
  108. package/docs/api/appkit/Function.discoverEvalFiles.md +18 -0
  109. package/docs/api/appkit/Function.equals.md +18 -0
  110. package/docs/api/appkit/Function.evalGlyph.md +18 -0
  111. package/docs/api/appkit/Function.formatEvalDetail.md +18 -0
  112. package/docs/api/appkit/Function.formatEvalHeadline.md +18 -0
  113. package/docs/api/appkit/Function.formatEvalResults.md +18 -0
  114. package/docs/api/appkit/Function.formatSummaryLine.md +18 -0
  115. package/docs/api/appkit/Function.includes.md +18 -0
  116. package/docs/api/appkit/Function.isJudgeConfigured.md +10 -0
  117. package/docs/api/appkit/Function.matches.md +18 -0
  118. package/docs/api/appkit/Function.normalizeHost.md +18 -0
  119. package/docs/api/appkit/Function.reportToMlflow.md +23 -0
  120. package/docs/api/appkit/Function.resolveDatabricksAuth.md +16 -0
  121. package/docs/api/appkit/Function.runEval.md +19 -0
  122. package/docs/api/appkit/Function.runEvalsInDir.md +18 -0
  123. package/docs/api/appkit/Function.summarize.md +16 -0
  124. package/docs/api/appkit/Interface.AssertionHandle.md +54 -0
  125. package/docs/api/appkit/Interface.AssertionResult.md +48 -0
  126. package/docs/api/appkit/Interface.Assessment.md +83 -0
  127. package/docs/api/appkit/Interface.CustomJudgeSpec.md +30 -0
  128. package/docs/api/appkit/Interface.DatabricksAuth.md +21 -0
  129. package/docs/api/appkit/Interface.DiscoveredEval.md +36 -0
  130. package/docs/api/appkit/Interface.DriveResult.md +58 -0
  131. package/docs/api/appkit/Interface.EvalDefinition.md +46 -0
  132. package/docs/api/appkit/Interface.EvalDriver.md +22 -0
  133. package/docs/api/appkit/Interface.EvalResult.md +83 -0
  134. package/docs/api/appkit/Interface.EvalRunSummary.md +46 -0
  135. package/docs/api/appkit/Interface.EvalSummary.md +48 -0
  136. package/docs/api/appkit/Interface.HttpDriverOptions.md +67 -0
  137. package/docs/api/appkit/Interface.JudgeConfig.md +34 -0
  138. package/docs/api/appkit/Interface.JudgeScore.md +21 -0
  139. package/docs/api/appkit/Interface.MatchResult.md +34 -0
  140. package/docs/api/appkit/Interface.PostResult.md +30 -0
  141. package/docs/api/appkit/Interface.ReportOutcome.md +53 -0
  142. package/docs/api/appkit/Interface.ResolveDatabricksAuthOptions.md +34 -0
  143. package/docs/api/appkit/Interface.RunEvalOptions.md +34 -0
  144. package/docs/api/appkit/Interface.RunEvalsOptions.md +192 -0
  145. package/docs/api/appkit/Interface.TestContext.md +208 -0
  146. package/docs/api/appkit/TypeAlias.EvalProgress.md +26 -0
  147. package/docs/api/appkit/TypeAlias.Matcher.md +18 -0
  148. package/docs/api/appkit/TypeAlias.Severity.md +8 -0
  149. package/docs/api/appkit.md +63 -17
  150. package/llms.txt +46 -0
  151. package/package.json +5 -4
  152. package/sbom.cdx.json +1 -1
@@ -0,0 +1,51 @@
1
+ //#region src/connectors/mlflow/client.d.ts
2
+ /** Shared client for talking to the Databricks/MLflow REST API. */
3
+ /** Ensure the host has a scheme (Databricks env often lacks `https://`). */
4
+ declare function normalizeHost(raw: string): string;
5
+ /** Structured result for a best-effort POST that must not throw. */
6
+ interface PostResult {
7
+ ok: boolean;
8
+ status?: number;
9
+ error?: string;
10
+ }
11
+ /**
12
+ * A thin client over the Databricks workspace REST API, owning the host + bearer
13
+ * token so callers (eval-run creation, assessment writes, the judge's serving
14
+ * endpoint) don't each re-derive URLs or re-attach auth. The host is normalized
15
+ * once at construction.
16
+ */
17
+ declare class MlflowClient {
18
+ /** Normalized workspace base URL (scheme guaranteed, no trailing slash). */
19
+ readonly baseUrl: string;
20
+ private readonly token;
21
+ constructor(host: string, token: string);
22
+ private headers;
23
+ /**
24
+ * POST JSON to an MLflow REST endpoint. Returns the parsed JSON body, or
25
+ * throws with the status + response text so callers can surface a precise
26
+ * error. Use for calls whose failure should abort (e.g. `runs/create`).
27
+ *
28
+ * The thrown message embeds up to 500 chars of the upstream response body to
29
+ * aid debugging. That is fine for the dev-facing eval CLI, but do NOT relay
30
+ * it into an end-user HTTP response if this client is reused in a request
31
+ * handler — the body can carry workspace-internal detail.
32
+ */
33
+ post<T = unknown>(path: string, body: unknown): Promise<T>;
34
+ /**
35
+ * POST JSON without throwing: returns `{ ok, status, error }` so best-effort
36
+ * writes (e.g. per-trace assessments) can be collected and reported without
37
+ * aborting the run.
38
+ *
39
+ * `error` embeds up to 500 chars of the upstream body — same caveat as
40
+ * {@link post}: fine to log for the dev CLI, don't relay it to end users.
41
+ */
42
+ postResult(path: string, body: unknown): Promise<PostResult>;
43
+ /**
44
+ * OpenAI-compatible base URL for Databricks Model Serving, used as the judge's
45
+ * `OPENAI_BASE_URL`. Same workspace host + token as the MLflow REST calls.
46
+ */
47
+ servingEndpointsUrl(): string;
48
+ }
49
+ //#endregion
50
+ export { MlflowClient, PostResult, normalizeHost };
51
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","names":[],"sources":["../../../src/connectors/mlflow/client.ts"],"mappings":";;AAGA;AAAA,iBAAgB,aAAA,CAAc,GAAA;;UAMb,UAAA;EACf,EAAA;EACA,MAAA;EACA,KAAA;AAAA;;;;;;;cASW,YAAA;EAAY;EAAA,SAEd,OAAA;EAAA,iBACQ,KAAA;cAEL,IAAA,UAAc,KAAA;EAAA,QAKlB,OAAA;EAuCuC;;;;;;;;;;EAtBzC,IAAA,aAAA,CAAkB,IAAA,UAAc,IAAA,YAAgB,OAAA,CAAQ,CAAA;EAAtC;;;;;;;;EAsBlB,UAAA,CAAW,IAAA,UAAc,IAAA,YAAgB,OAAA,CAAQ,UAAA;EAwBvD;;;;EAAA,mBAAA,CAAA;AAAA"}
@@ -0,0 +1,93 @@
1
+ //#region src/connectors/mlflow/client.ts
2
+ /** Shared client for talking to the Databricks/MLflow REST API. */
3
+ /** Ensure the host has a scheme (Databricks env often lacks `https://`). */
4
+ function normalizeHost(raw) {
5
+ const h = raw.trim().replace(/\/+$/, "");
6
+ return /^https?:\/\//i.test(h) ? h : `https://${h}`;
7
+ }
8
+ /**
9
+ * A thin client over the Databricks workspace REST API, owning the host + bearer
10
+ * token so callers (eval-run creation, assessment writes, the judge's serving
11
+ * endpoint) don't each re-derive URLs or re-attach auth. The host is normalized
12
+ * once at construction.
13
+ */
14
+ var MlflowClient = class {
15
+ /** Normalized workspace base URL (scheme guaranteed, no trailing slash). */
16
+ baseUrl;
17
+ token;
18
+ constructor(host, token) {
19
+ this.baseUrl = normalizeHost(host);
20
+ this.token = token;
21
+ }
22
+ headers() {
23
+ return {
24
+ "content-type": "application/json",
25
+ authorization: `Bearer ${this.token}`
26
+ };
27
+ }
28
+ /**
29
+ * POST JSON to an MLflow REST endpoint. Returns the parsed JSON body, or
30
+ * throws with the status + response text so callers can surface a precise
31
+ * error. Use for calls whose failure should abort (e.g. `runs/create`).
32
+ *
33
+ * The thrown message embeds up to 500 chars of the upstream response body to
34
+ * aid debugging. That is fine for the dev-facing eval CLI, but do NOT relay
35
+ * it into an end-user HTTP response if this client is reused in a request
36
+ * handler — the body can carry workspace-internal detail.
37
+ */
38
+ async post(path, body) {
39
+ const res = await fetch(`${this.baseUrl}${path}`, {
40
+ method: "POST",
41
+ headers: this.headers(),
42
+ body: JSON.stringify(body)
43
+ });
44
+ if (!res.ok) {
45
+ const text = await res.text().catch(() => "");
46
+ throw new Error(`${path} -> ${res.status} ${text.slice(0, 500)}`);
47
+ }
48
+ const text = await res.text();
49
+ return text ? JSON.parse(text) : {};
50
+ }
51
+ /**
52
+ * POST JSON without throwing: returns `{ ok, status, error }` so best-effort
53
+ * writes (e.g. per-trace assessments) can be collected and reported without
54
+ * aborting the run.
55
+ *
56
+ * `error` embeds up to 500 chars of the upstream body — same caveat as
57
+ * {@link post}: fine to log for the dev CLI, don't relay it to end users.
58
+ */
59
+ async postResult(path, body) {
60
+ try {
61
+ const res = await fetch(`${this.baseUrl}${path}`, {
62
+ method: "POST",
63
+ headers: this.headers(),
64
+ body: JSON.stringify(body)
65
+ });
66
+ if (!res.ok) {
67
+ const text = await res.text().catch(() => "");
68
+ return {
69
+ ok: false,
70
+ status: res.status,
71
+ error: text.slice(0, 500)
72
+ };
73
+ }
74
+ return { ok: true };
75
+ } catch (err) {
76
+ return {
77
+ ok: false,
78
+ error: err instanceof Error ? err.message : String(err)
79
+ };
80
+ }
81
+ }
82
+ /**
83
+ * OpenAI-compatible base URL for Databricks Model Serving, used as the judge's
84
+ * `OPENAI_BASE_URL`. Same workspace host + token as the MLflow REST calls.
85
+ */
86
+ servingEndpointsUrl() {
87
+ return `${this.baseUrl}/serving-endpoints`;
88
+ }
89
+ };
90
+
91
+ //#endregion
92
+ export { MlflowClient, normalizeHost };
93
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":[],"sources":["../../../src/connectors/mlflow/client.ts"],"sourcesContent":["/** Shared client for talking to the Databricks/MLflow REST API. */\n\n/** Ensure the host has a scheme (Databricks env often lacks `https://`). */\nexport function normalizeHost(raw: string): string {\n const h = raw.trim().replace(/\\/+$/, \"\");\n return /^https?:\\/\\//i.test(h) ? h : `https://${h}`;\n}\n\n/** Structured result for a best-effort POST that must not throw. */\nexport interface PostResult {\n ok: boolean;\n status?: number;\n error?: string;\n}\n\n/**\n * A thin client over the Databricks workspace REST API, owning the host + bearer\n * token so callers (eval-run creation, assessment writes, the judge's serving\n * endpoint) don't each re-derive URLs or re-attach auth. The host is normalized\n * once at construction.\n */\nexport class MlflowClient {\n /** Normalized workspace base URL (scheme guaranteed, no trailing slash). */\n readonly baseUrl: string;\n private readonly token: string;\n\n constructor(host: string, token: string) {\n this.baseUrl = normalizeHost(host);\n this.token = token;\n }\n\n private headers(): Record<string, string> {\n return {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${this.token}`,\n };\n }\n\n /**\n * POST JSON to an MLflow REST endpoint. Returns the parsed JSON body, or\n * throws with the status + response text so callers can surface a precise\n * error. Use for calls whose failure should abort (e.g. `runs/create`).\n *\n * The thrown message embeds up to 500 chars of the upstream response body to\n * aid debugging. That is fine for the dev-facing eval CLI, but do NOT relay\n * it into an end-user HTTP response if this client is reused in a request\n * handler — the body can carry workspace-internal detail.\n */\n async post<T = unknown>(path: string, body: unknown): Promise<T> {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: \"POST\",\n headers: this.headers(),\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`${path} -> ${res.status} ${text.slice(0, 500)}`);\n }\n const text = await res.text();\n return (text ? JSON.parse(text) : {}) as T;\n }\n\n /**\n * POST JSON without throwing: returns `{ ok, status, error }` so best-effort\n * writes (e.g. per-trace assessments) can be collected and reported without\n * aborting the run.\n *\n * `error` embeds up to 500 chars of the upstream body — same caveat as\n * {@link post}: fine to log for the dev CLI, don't relay it to end users.\n */\n async postResult(path: string, body: unknown): Promise<PostResult> {\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: \"POST\",\n headers: this.headers(),\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n return { ok: false, status: res.status, error: text.slice(0, 500) };\n }\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n /**\n * OpenAI-compatible base URL for Databricks Model Serving, used as the judge's\n * `OPENAI_BASE_URL`. Same workspace host + token as the MLflow REST calls.\n */\n servingEndpointsUrl(): string {\n return `${this.baseUrl}/serving-endpoints`;\n }\n}\n"],"mappings":";;;AAGA,SAAgB,cAAc,KAAqB;CACjD,MAAM,IAAI,IAAI,MAAM,CAAC,QAAQ,QAAQ,GAAG;AACxC,QAAO,gBAAgB,KAAK,EAAE,GAAG,IAAI,WAAW;;;;;;;;AAgBlD,IAAa,eAAb,MAA0B;;CAExB,AAAS;CACT,AAAiB;CAEjB,YAAY,MAAc,OAAe;AACvC,OAAK,UAAU,cAAc,KAAK;AAClC,OAAK,QAAQ;;CAGf,AAAQ,UAAkC;AACxC,SAAO;GACL,gBAAgB;GAChB,eAAe,UAAU,KAAK;GAC/B;;;;;;;;;;;;CAaH,MAAM,KAAkB,MAAc,MAA2B;EAC/D,MAAM,MAAM,MAAM,MAAM,GAAG,KAAK,UAAU,QAAQ;GAChD,QAAQ;GACR,SAAS,KAAK,SAAS;GACvB,MAAM,KAAK,UAAU,KAAK;GAC3B,CAAC;AACF,MAAI,CAAC,IAAI,IAAI;GACX,MAAM,OAAO,MAAM,IAAI,MAAM,CAAC,YAAY,GAAG;AAC7C,SAAM,IAAI,MAAM,GAAG,KAAK,MAAM,IAAI,OAAO,GAAG,KAAK,MAAM,GAAG,IAAI,GAAG;;EAEnE,MAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,SAAQ,OAAO,KAAK,MAAM,KAAK,GAAG,EAAE;;;;;;;;;;CAWtC,MAAM,WAAW,MAAc,MAAoC;AACjE,MAAI;GACF,MAAM,MAAM,MAAM,MAAM,GAAG,KAAK,UAAU,QAAQ;IAChD,QAAQ;IACR,SAAS,KAAK,SAAS;IACvB,MAAM,KAAK,UAAU,KAAK;IAC3B,CAAC;AACF,OAAI,CAAC,IAAI,IAAI;IACX,MAAM,OAAO,MAAM,IAAI,MAAM,CAAC,YAAY,GAAG;AAC7C,WAAO;KAAE,IAAI;KAAO,QAAQ,IAAI;KAAQ,OAAO,KAAK,MAAM,GAAG,IAAI;KAAE;;AAErE,UAAO,EAAE,IAAI,MAAM;WACZ,KAAK;AACZ,UAAO;IACL,IAAI;IACJ,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;IACxD;;;;;;;CAQL,sBAA8B;AAC5B,SAAO,GAAG,KAAK,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"appkit.d.ts","names":[],"sources":["../../src/core/appkit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwXsB,SAAA,WACV,UAAA,CAAW,iBAAA,qBAAA,CAErB,MAAA;EACE,OAAA,GAAU,CAAA;EACV,SAAA,GAAY,eAAA;EACZ,KAAA,GAAQ,WAAA;EACR,MAAA,GAAS,eAAA;EACT,cAAA,IAAkB,MAAA,EAAQ,SAAA,CAAU,CAAA,aAAc,OAAA;EAClD,wBAAA;AAAA,IAED,OAAA,CAAQ,SAAA,CAAU,CAAA"}
1
+ {"version":3,"file":"appkit.d.ts","names":[],"sources":["../../src/core/appkit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8XsB,SAAA,WACV,UAAA,CAAW,iBAAA,qBAAA,CAErB,MAAA;EACE,OAAA,GAAU,CAAA;EACV,SAAA,GAAY,eAAA;EACZ,KAAA,GAAQ,WAAA;EACR,MAAA,GAAS,eAAA;EACT,cAAA,IAAkB,MAAA,EAAQ,SAAA,CAAU,CAAA,aAAc,OAAA;EAClD,wBAAA;AAAA,IAED,OAAA,CAAQ,SAAA,CAAU,CAAA"}
@@ -110,6 +110,7 @@ var AppKit = class AppKit {
110
110
  registry.enforceValidation();
111
111
  const instance = new AppKit({ plugins: AppKit.preparePlugins(rawPlugins) });
112
112
  await Promise.all(instance.#setupPromises);
113
+ TelemetryManager.start();
113
114
  await instance.#context.emitLifecycle("setup:complete");
114
115
  const handle = instance;
115
116
  if (config.onPluginsReady) {
@@ -1 +1 @@
1
- {"version":3,"file":"appkit.js","names":["#context","#pluginInstances","#setupPromises","productVersion"],"sources":["../../src/core/appkit.ts"],"sourcesContent":["import type {\n BasePlugin,\n CacheConfig,\n InputPluginMap,\n OptionalConfigPluginDef,\n PluginConstructor,\n PluginData,\n PluginMap,\n} from \"shared\";\n\nimport { version as productVersion } from \"../../package.json\";\nimport { CacheManager } from \"../cache\";\nimport { ServiceContext } from \"../context\";\nimport {\n isInternalTelemetryEnabled,\n TelemetryReporter,\n} from \"../internal-telemetry\";\nimport { createLogger } from \"../logging/logger\";\nimport { isPlainObject } from \"../plugin/plugin\";\nimport { uiVariants } from \"../plugins/ui-variants\";\nimport { ResourceRegistry, ResourceType } from \"../registry\";\nimport type { TelemetryConfig } from \"../telemetry\";\nimport { TelemetryManager } from \"../telemetry\";\nimport type { WorkspaceClient } from \"../workspace-client\";\nimport { LifecycleManager } from \"./lifecycle-manager\";\nimport { isToolProvider, PluginContext } from \"./plugin-context\";\n\nconst logger = createLogger(\"appkit\");\n\nexport class AppKit<TPlugins extends InputPluginMap> {\n #pluginInstances: Record<string, BasePlugin> = {};\n #setupPromises: Promise<void>[] = [];\n #context: PluginContext;\n\n private constructor(config: { plugins: TPlugins }) {\n const { plugins, ...globalConfig } = config;\n\n this.#context = new PluginContext();\n\n const pluginEntries = Object.entries(plugins);\n\n const corePlugins = pluginEntries.filter(([_, p]) => {\n return (p?.plugin?.phase ?? \"normal\") === \"core\";\n });\n const normalPlugins = pluginEntries.filter(\n ([_, p]) => (p?.plugin?.phase ?? \"normal\") === \"normal\",\n );\n const deferredPlugins = pluginEntries.filter(\n ([_, p]) => (p?.plugin?.phase ?? \"normal\") === \"deferred\",\n );\n\n for (const [name, pluginData] of corePlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData, {\n context: this.#context,\n });\n }\n }\n\n for (const [name, pluginData] of normalPlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData, {\n context: this.#context,\n });\n }\n }\n\n for (const [name, pluginData] of deferredPlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData, {\n context: this.#context,\n });\n }\n }\n }\n\n private createAndRegisterPlugin<T extends PluginConstructor>(\n config: Omit<{ plugins: TPlugins }, \"plugins\">,\n name: string,\n pluginData: OptionalConfigPluginDef<T>,\n extraData?: Record<string, unknown>,\n ) {\n const { plugin: Plugin, config: pluginConfig } = pluginData;\n const baseConfig = {\n ...config,\n ...Plugin.DEFAULT_CONFIG,\n ...pluginConfig,\n name,\n ...extraData,\n };\n const pluginInstance = new Plugin(baseConfig);\n\n if (typeof pluginInstance.attachContext === \"function\") {\n pluginInstance.attachContext({\n context: this.#context,\n telemetryConfig: baseConfig.telemetry,\n });\n }\n\n this.#pluginInstances[name] = pluginInstance;\n\n this.#context.registerPlugin(name, pluginInstance);\n if (isToolProvider(pluginInstance)) {\n this.#context.registerToolProvider(name, pluginInstance);\n }\n\n this.#setupPromises.push(pluginInstance.setup());\n\n const self = this;\n\n // The manifest `name` is camelCase, so it doubles as the public handle key\n // (`appkit.aiSearch`). The kebab HTTP route is derived separately.\n Object.defineProperty(this, name, {\n get() {\n const plugin = self.#pluginInstances[name];\n return self.wrapWithAsUser(plugin);\n },\n enumerable: true,\n });\n }\n\n /**\n * Binds all function properties in an exports object to the given context.\n * Recurses into plain objects to handle nested APIs (e.g., volume APIs).\n */\n private bindExportMethods(\n exports: Record<string, unknown>,\n context: BasePlugin,\n ) {\n for (const key in exports) {\n if (!Object.hasOwn(exports, key)) continue;\n const val = exports[key];\n if (typeof val === \"function\") {\n exports[key] = (val as (...args: unknown[]) => unknown).bind(context);\n } else if (isPlainObject(val)) {\n this.bindExportMethods(val as Record<string, unknown>, context);\n }\n }\n }\n\n /**\n * Wraps a plugin's exports with an `asUser` method that returns\n * a user-scoped version of the exports.\n *\n * When `exports()` returns a callable (function), it is returned as-is\n * since the plugin manages its own `asUser` per-call (e.g. files plugin).\n * When it returns a plain object, the standard `asUser` wrapper is added.\n *\n * The OBO-side wrapping lives inside `Plugin.asUser` — calling\n * `plugin.asUser(req).exports()` returns exports whose functions already\n * run inside the user's AsyncLocalStorage scope. AppKit only adapts the\n * shape; it does not own the user-context concept.\n */\n private wrapWithAsUser<T extends BasePlugin>(plugin: T) {\n // If plugin doesn't implement exports(), return empty object\n const pluginExports = plugin.exports?.() ?? {};\n\n // If exports is a function, the plugin manages its own asUser pattern\n if (typeof pluginExports === \"function\") {\n return pluginExports;\n }\n\n const objExports = pluginExports as Record<string, unknown>;\n this.bindExportMethods(objExports, plugin);\n\n // If plugin doesn't support asUser (no asUser method), return exports as-is\n if (typeof (plugin as any).asUser !== \"function\") {\n return objExports;\n }\n\n return {\n ...objExports,\n /**\n * Execute operations using the user's identity from the request.\n * Returns user-scoped exports where all methods execute with the\n * user's Databricks credentials instead of the service principal.\n */\n asUser: (req: import(\"express\").Request) =>\n (plugin as any).asUser(req).exports() as Record<string, unknown>,\n };\n }\n\n static async _createApp<\n T extends PluginData<PluginConstructor, unknown, string>[],\n >(\n config: {\n plugins?: T;\n telemetry?: TelemetryConfig;\n cache?: CacheConfig;\n client?: WorkspaceClient;\n onPluginsReady?: (appkit: PluginMap<T>) => void | Promise<void>;\n disableInternalTelemetry?: boolean;\n } = {},\n ): Promise<PluginMap<T>> {\n // Initialize core services\n TelemetryManager.initialize(config?.telemetry);\n await CacheManager.getInstance(config?.cache);\n\n const withDefaults = AppKit.withDefaultPlugins(config.plugins as T);\n const rawPlugins = AppKit.filterDevOnlyPlugins(withDefaults);\n\n // Collect manifest resources via registry\n const registry = new ResourceRegistry();\n registry.collectResources(rawPlugins);\n\n // Derive ServiceContext needs from what manifests declared\n const needsWarehouse = registry\n .getRequired()\n .some((r) => r.type === ResourceType.SQL_WAREHOUSE);\n await ServiceContext.initialize(\n { warehouseId: needsWarehouse },\n config?.client,\n );\n\n // Validate env vars\n registry.enforceValidation();\n\n const preparedPlugins = AppKit.preparePlugins(rawPlugins);\n const mergedConfig = {\n plugins: preparedPlugins,\n };\n\n const instance = new AppKit(mergedConfig);\n\n await Promise.all(instance.#setupPromises);\n await instance.#context.emitLifecycle(\"setup:complete\");\n\n const handle = instance as unknown as PluginMap<T>;\n\n if (config.onPluginsReady) {\n logger.debug(\"Running onPluginsReady hook\");\n await config.onPluginsReady(handle);\n logger.debug(\"onPluginsReady hook completed\");\n }\n\n if (isInternalTelemetryEnabled(config)) {\n AppKit.bootstrapInternalTelemetry();\n }\n\n const serverPlugin = instance.#pluginInstances.server;\n if (serverPlugin && typeof (serverPlugin as any).start === \"function\") {\n await (serverPlugin as any).start();\n }\n\n // Core owns graceful shutdown: install the signal handlers once every\n // plugin has started. Applies uniformly whether or not a server plugin\n // is present — server-less apps still get their telemetry flushed and\n // plugin shutdown() hooks run.\n new LifecycleManager(instance.#context).installSignalHandlers();\n\n return handle;\n }\n\n private static bootstrapInternalTelemetry(): void {\n const serviceCtx = ServiceContext.get();\n const reporter = TelemetryReporter.initialize({\n workspaceId: serviceCtx.workspaceId,\n client: serviceCtx.client,\n appId: process.env.DATABRICKS_CLIENT_ID || \"\",\n appkitVersion: productVersion,\n });\n reporter.start();\n reporter.sendStartup().catch(() => {});\n }\n\n /**\n * Injects framework-owned default plugins the app author shouldn't have to\n * register by hand. Runs before {@link filterDevOnlyPlugins}, so a default\n * that is itself `devOnly` (like `ui-variants`) is present in dev and stripped\n * in prod through the exact same guard as any user plugin — never alive in a\n * deployed app.\n *\n * Currently injects the dev-only `ui-variants` recorder that backs the\n * `<Variants>` UI picker, so neither the developer nor their coding agent has\n * to remember to add it. If the app already registered it explicitly, that\n * entry wins and no duplicate is added.\n */\n private static withDefaultPlugins<\n T extends PluginData<PluginConstructor, unknown, string>[],\n >(plugins: T): T {\n const list = (plugins ?? []) as PluginData<\n PluginConstructor,\n unknown,\n string\n >[];\n\n const defaults: PluginData<PluginConstructor, unknown, string>[] = [\n uiVariants(),\n ];\n\n const missing = defaults.filter(\n (def) => !list.some((p) => p?.name === def.name),\n );\n\n return [...list, ...missing] as T;\n }\n\n /**\n * Drops plugins whose manifest declares `devOnly: true` unless\n * `NODE_ENV === \"development\"`. Runs before resource collection so a skipped\n * plugin is never constructed, never injects routes, and has its resource\n * requirements ignored — the framework, not the app author, enforces that\n * dev-only tooling can never run in a deployed app.\n *\n * This is the primary guard; plugins that expose a mutating dev endpoint\n * should still keep an in-handler `NODE_ENV` check as fail-safe defense\n * against being mounted through a path that bypasses this filter.\n */\n private static filterDevOnlyPlugins<\n T extends PluginData<PluginConstructor, unknown, string>[],\n >(plugins: T): T {\n if (!plugins || process.env.NODE_ENV === \"development\") return plugins;\n\n return plugins.filter((pluginData) => {\n const isDevOnly = pluginData?.plugin?.manifest?.devOnly === true;\n if (isDevOnly) {\n logger.debug(\n \"Skipping dev-only plugin %s (NODE_ENV=%s)\",\n pluginData?.name ?? \"unknown\",\n process.env.NODE_ENV ?? \"<unset>\",\n );\n }\n return !isDevOnly;\n }) as T;\n }\n\n private static preparePlugins(\n plugins: PluginData<PluginConstructor, unknown, string>[],\n ) {\n const result: InputPluginMap = {};\n for (const currentPlugin of plugins) {\n result[currentPlugin.name] = {\n plugin: currentPlugin.plugin,\n config: currentPlugin.config as Record<string, unknown>,\n };\n }\n return result;\n }\n}\n\n/**\n * Bootstraps AppKit with the provided configuration.\n *\n * Initializes telemetry, cache, and service context, then registers plugins\n * in phase order (core, normal, deferred) and awaits their setup.\n * If a `onPluginsReady` callback is provided it runs after plugin setup but\n * before the server starts, giving you access to the full appkit handle\n * for registering custom routes or performing async setup.\n * The returned object maps each plugin name to its `exports()` API,\n * with an `asUser(req)` method for user-scoped execution.\n *\n * @returns A `PluginMap` keyed by plugin name with typed exports\n *\n * @example Minimal server\n * ```ts\n * import { createApp, server } from \"@databricks/appkit\";\n *\n * await createApp({\n * plugins: [server()],\n * });\n * ```\n *\n * @example Server with custom routes via onPluginsReady\n * ```ts\n * import { createApp, server, analytics } from \"@databricks/appkit\";\n *\n * await createApp({\n * plugins: [server(), analytics({})],\n * onPluginsReady(appkit) {\n * appkit.server.extend((app) => {\n * app.get(\"/custom\", (_req, res) => res.json({ ok: true }));\n * });\n * },\n * });\n * ```\n */\nexport async function createApp<\n T extends PluginData<PluginConstructor, unknown, string>[],\n>(\n config: {\n plugins?: T;\n telemetry?: TelemetryConfig;\n cache?: CacheConfig;\n client?: WorkspaceClient;\n onPluginsReady?: (appkit: PluginMap<T>) => void | Promise<void>;\n disableInternalTelemetry?: boolean;\n } = {},\n): Promise<PluginMap<T>> {\n return AppKit._createApp(config);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"appkit.js","names":["#context","#pluginInstances","#setupPromises","productVersion"],"sources":["../../src/core/appkit.ts"],"sourcesContent":["import type {\n BasePlugin,\n CacheConfig,\n InputPluginMap,\n OptionalConfigPluginDef,\n PluginConstructor,\n PluginData,\n PluginMap,\n} from \"shared\";\n\nimport { version as productVersion } from \"../../package.json\";\nimport { CacheManager } from \"../cache\";\nimport { ServiceContext } from \"../context\";\nimport {\n isInternalTelemetryEnabled,\n TelemetryReporter,\n} from \"../internal-telemetry\";\nimport { createLogger } from \"../logging/logger\";\nimport { isPlainObject } from \"../plugin/plugin\";\nimport { uiVariants } from \"../plugins/ui-variants\";\nimport { ResourceRegistry, ResourceType } from \"../registry\";\nimport type { TelemetryConfig } from \"../telemetry\";\nimport { TelemetryManager } from \"../telemetry\";\nimport type { WorkspaceClient } from \"../workspace-client\";\nimport { LifecycleManager } from \"./lifecycle-manager\";\nimport { isToolProvider, PluginContext } from \"./plugin-context\";\n\nconst logger = createLogger(\"appkit\");\n\nexport class AppKit<TPlugins extends InputPluginMap> {\n #pluginInstances: Record<string, BasePlugin> = {};\n #setupPromises: Promise<void>[] = [];\n #context: PluginContext;\n\n private constructor(config: { plugins: TPlugins }) {\n const { plugins, ...globalConfig } = config;\n\n this.#context = new PluginContext();\n\n const pluginEntries = Object.entries(plugins);\n\n const corePlugins = pluginEntries.filter(([_, p]) => {\n return (p?.plugin?.phase ?? \"normal\") === \"core\";\n });\n const normalPlugins = pluginEntries.filter(\n ([_, p]) => (p?.plugin?.phase ?? \"normal\") === \"normal\",\n );\n const deferredPlugins = pluginEntries.filter(\n ([_, p]) => (p?.plugin?.phase ?? \"normal\") === \"deferred\",\n );\n\n for (const [name, pluginData] of corePlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData, {\n context: this.#context,\n });\n }\n }\n\n for (const [name, pluginData] of normalPlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData, {\n context: this.#context,\n });\n }\n }\n\n for (const [name, pluginData] of deferredPlugins) {\n if (pluginData) {\n this.createAndRegisterPlugin(globalConfig, name, pluginData, {\n context: this.#context,\n });\n }\n }\n }\n\n private createAndRegisterPlugin<T extends PluginConstructor>(\n config: Omit<{ plugins: TPlugins }, \"plugins\">,\n name: string,\n pluginData: OptionalConfigPluginDef<T>,\n extraData?: Record<string, unknown>,\n ) {\n const { plugin: Plugin, config: pluginConfig } = pluginData;\n const baseConfig = {\n ...config,\n ...Plugin.DEFAULT_CONFIG,\n ...pluginConfig,\n name,\n ...extraData,\n };\n const pluginInstance = new Plugin(baseConfig);\n\n if (typeof pluginInstance.attachContext === \"function\") {\n pluginInstance.attachContext({\n context: this.#context,\n telemetryConfig: baseConfig.telemetry,\n });\n }\n\n this.#pluginInstances[name] = pluginInstance;\n\n this.#context.registerPlugin(name, pluginInstance);\n if (isToolProvider(pluginInstance)) {\n this.#context.registerToolProvider(name, pluginInstance);\n }\n\n this.#setupPromises.push(pluginInstance.setup());\n\n const self = this;\n\n // The manifest `name` is camelCase, so it doubles as the public handle key\n // (`appkit.aiSearch`). The kebab HTTP route is derived separately.\n Object.defineProperty(this, name, {\n get() {\n const plugin = self.#pluginInstances[name];\n return self.wrapWithAsUser(plugin);\n },\n enumerable: true,\n });\n }\n\n /**\n * Binds all function properties in an exports object to the given context.\n * Recurses into plain objects to handle nested APIs (e.g., volume APIs).\n */\n private bindExportMethods(\n exports: Record<string, unknown>,\n context: BasePlugin,\n ) {\n for (const key in exports) {\n if (!Object.hasOwn(exports, key)) continue;\n const val = exports[key];\n if (typeof val === \"function\") {\n exports[key] = (val as (...args: unknown[]) => unknown).bind(context);\n } else if (isPlainObject(val)) {\n this.bindExportMethods(val as Record<string, unknown>, context);\n }\n }\n }\n\n /**\n * Wraps a plugin's exports with an `asUser` method that returns\n * a user-scoped version of the exports.\n *\n * When `exports()` returns a callable (function), it is returned as-is\n * since the plugin manages its own `asUser` per-call (e.g. files plugin).\n * When it returns a plain object, the standard `asUser` wrapper is added.\n *\n * The OBO-side wrapping lives inside `Plugin.asUser` — calling\n * `plugin.asUser(req).exports()` returns exports whose functions already\n * run inside the user's AsyncLocalStorage scope. AppKit only adapts the\n * shape; it does not own the user-context concept.\n */\n private wrapWithAsUser<T extends BasePlugin>(plugin: T) {\n // If plugin doesn't implement exports(), return empty object\n const pluginExports = plugin.exports?.() ?? {};\n\n // If exports is a function, the plugin manages its own asUser pattern\n if (typeof pluginExports === \"function\") {\n return pluginExports;\n }\n\n const objExports = pluginExports as Record<string, unknown>;\n this.bindExportMethods(objExports, plugin);\n\n // If plugin doesn't support asUser (no asUser method), return exports as-is\n if (typeof (plugin as any).asUser !== \"function\") {\n return objExports;\n }\n\n return {\n ...objExports,\n /**\n * Execute operations using the user's identity from the request.\n * Returns user-scoped exports where all methods execute with the\n * user's Databricks credentials instead of the service principal.\n */\n asUser: (req: import(\"express\").Request) =>\n (plugin as any).asUser(req).exports() as Record<string, unknown>,\n };\n }\n\n static async _createApp<\n T extends PluginData<PluginConstructor, unknown, string>[],\n >(\n config: {\n plugins?: T;\n telemetry?: TelemetryConfig;\n cache?: CacheConfig;\n client?: WorkspaceClient;\n onPluginsReady?: (appkit: PluginMap<T>) => void | Promise<void>;\n disableInternalTelemetry?: boolean;\n } = {},\n ): Promise<PluginMap<T>> {\n // Initialize core services\n TelemetryManager.initialize(config?.telemetry);\n await CacheManager.getInstance(config?.cache);\n\n const withDefaults = AppKit.withDefaultPlugins(config.plugins as T);\n const rawPlugins = AppKit.filterDevOnlyPlugins(withDefaults);\n\n // Collect manifest resources via registry\n const registry = new ResourceRegistry();\n registry.collectResources(rawPlugins);\n\n // Derive ServiceContext needs from what manifests declared\n const needsWarehouse = registry\n .getRequired()\n .some((r) => r.type === ResourceType.SQL_WAREHOUSE);\n await ServiceContext.initialize(\n { warehouseId: needsWarehouse },\n config?.client,\n );\n\n // Validate env vars\n registry.enforceValidation();\n\n const preparedPlugins = AppKit.preparePlugins(rawPlugins);\n const mergedConfig = {\n plugins: preparedPlugins,\n };\n\n const instance = new AppKit(mergedConfig);\n\n await Promise.all(instance.#setupPromises);\n\n // Build the global tracer provider now that every plugin's setup() has run\n // and contributed any span processors. Deferred to here so a single provider\n // carries all processors (OTLP + plugin-contributed); see TelemetryManager.\n TelemetryManager.start();\n\n await instance.#context.emitLifecycle(\"setup:complete\");\n\n const handle = instance as unknown as PluginMap<T>;\n\n if (config.onPluginsReady) {\n logger.debug(\"Running onPluginsReady hook\");\n await config.onPluginsReady(handle);\n logger.debug(\"onPluginsReady hook completed\");\n }\n\n if (isInternalTelemetryEnabled(config)) {\n AppKit.bootstrapInternalTelemetry();\n }\n\n const serverPlugin = instance.#pluginInstances.server;\n if (serverPlugin && typeof (serverPlugin as any).start === \"function\") {\n await (serverPlugin as any).start();\n }\n\n // Core owns graceful shutdown: install the signal handlers once every\n // plugin has started. Applies uniformly whether or not a server plugin\n // is present — server-less apps still get their telemetry flushed and\n // plugin shutdown() hooks run.\n new LifecycleManager(instance.#context).installSignalHandlers();\n\n return handle;\n }\n\n private static bootstrapInternalTelemetry(): void {\n const serviceCtx = ServiceContext.get();\n const reporter = TelemetryReporter.initialize({\n workspaceId: serviceCtx.workspaceId,\n client: serviceCtx.client,\n appId: process.env.DATABRICKS_CLIENT_ID || \"\",\n appkitVersion: productVersion,\n });\n reporter.start();\n reporter.sendStartup().catch(() => {});\n }\n\n /**\n * Injects framework-owned default plugins the app author shouldn't have to\n * register by hand. Runs before {@link filterDevOnlyPlugins}, so a default\n * that is itself `devOnly` (like `ui-variants`) is present in dev and stripped\n * in prod through the exact same guard as any user plugin — never alive in a\n * deployed app.\n *\n * Currently injects the dev-only `ui-variants` recorder that backs the\n * `<Variants>` UI picker, so neither the developer nor their coding agent has\n * to remember to add it. If the app already registered it explicitly, that\n * entry wins and no duplicate is added.\n */\n private static withDefaultPlugins<\n T extends PluginData<PluginConstructor, unknown, string>[],\n >(plugins: T): T {\n const list = (plugins ?? []) as PluginData<\n PluginConstructor,\n unknown,\n string\n >[];\n\n const defaults: PluginData<PluginConstructor, unknown, string>[] = [\n uiVariants(),\n ];\n\n const missing = defaults.filter(\n (def) => !list.some((p) => p?.name === def.name),\n );\n\n return [...list, ...missing] as T;\n }\n\n /**\n * Drops plugins whose manifest declares `devOnly: true` unless\n * `NODE_ENV === \"development\"`. Runs before resource collection so a skipped\n * plugin is never constructed, never injects routes, and has its resource\n * requirements ignored — the framework, not the app author, enforces that\n * dev-only tooling can never run in a deployed app.\n *\n * This is the primary guard; plugins that expose a mutating dev endpoint\n * should still keep an in-handler `NODE_ENV` check as fail-safe defense\n * against being mounted through a path that bypasses this filter.\n */\n private static filterDevOnlyPlugins<\n T extends PluginData<PluginConstructor, unknown, string>[],\n >(plugins: T): T {\n if (!plugins || process.env.NODE_ENV === \"development\") return plugins;\n\n return plugins.filter((pluginData) => {\n const isDevOnly = pluginData?.plugin?.manifest?.devOnly === true;\n if (isDevOnly) {\n logger.debug(\n \"Skipping dev-only plugin %s (NODE_ENV=%s)\",\n pluginData?.name ?? \"unknown\",\n process.env.NODE_ENV ?? \"<unset>\",\n );\n }\n return !isDevOnly;\n }) as T;\n }\n\n private static preparePlugins(\n plugins: PluginData<PluginConstructor, unknown, string>[],\n ) {\n const result: InputPluginMap = {};\n for (const currentPlugin of plugins) {\n result[currentPlugin.name] = {\n plugin: currentPlugin.plugin,\n config: currentPlugin.config as Record<string, unknown>,\n };\n }\n return result;\n }\n}\n\n/**\n * Bootstraps AppKit with the provided configuration.\n *\n * Initializes telemetry, cache, and service context, then registers plugins\n * in phase order (core, normal, deferred) and awaits their setup.\n * If a `onPluginsReady` callback is provided it runs after plugin setup but\n * before the server starts, giving you access to the full appkit handle\n * for registering custom routes or performing async setup.\n * The returned object maps each plugin name to its `exports()` API,\n * with an `asUser(req)` method for user-scoped execution.\n *\n * @returns A `PluginMap` keyed by plugin name with typed exports\n *\n * @example Minimal server\n * ```ts\n * import { createApp, server } from \"@databricks/appkit\";\n *\n * await createApp({\n * plugins: [server()],\n * });\n * ```\n *\n * @example Server with custom routes via onPluginsReady\n * ```ts\n * import { createApp, server, analytics } from \"@databricks/appkit\";\n *\n * await createApp({\n * plugins: [server(), analytics({})],\n * onPluginsReady(appkit) {\n * appkit.server.extend((app) => {\n * app.get(\"/custom\", (_req, res) => res.json({ ok: true }));\n * });\n * },\n * });\n * ```\n */\nexport async function createApp<\n T extends PluginData<PluginConstructor, unknown, string>[],\n>(\n config: {\n plugins?: T;\n telemetry?: TelemetryConfig;\n cache?: CacheConfig;\n client?: WorkspaceClient;\n onPluginsReady?: (appkit: PluginMap<T>) => void | Promise<void>;\n disableInternalTelemetry?: boolean;\n } = {},\n): Promise<PluginMap<T>> {\n return AppKit._createApp(config);\n}\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { EvalDefinition } from "./types.js";
2
+
3
+ //#region src/evals/define-eval.d.ts
4
+ /**
5
+ * Define an agent eval. Default-export the result from a
6
+ * `server/agents/<id>/evals/*.eval.ts` file.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { defineEval, includes } from "@databricks/appkit/beta";
11
+ *
12
+ * export default defineEval({
13
+ * description: "Weather agent basic coverage",
14
+ * async test(t) {
15
+ * await t.send("What's the weather in Brooklyn?");
16
+ * t.succeeded();
17
+ * t.calledTool("get_weather");
18
+ * t.check(t.reply, includes("Sunny"));
19
+ * },
20
+ * });
21
+ * ```
22
+ */
23
+ declare function defineEval(def: EvalDefinition): EvalDefinition;
24
+ //#endregion
25
+ export { defineEval };
26
+ //# sourceMappingURL=define-eval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-eval.d.ts","names":[],"sources":["../../src/evals/define-eval.ts"],"mappings":";;;;;AAqBA;;;;;;;;;;;;;;;;;iBAAgB,UAAA,CAAW,GAAA,EAAK,cAAA,GAAiB,cAAA"}
@@ -0,0 +1,28 @@
1
+ //#region src/evals/define-eval.ts
2
+ /**
3
+ * Define an agent eval. Default-export the result from a
4
+ * `server/agents/<id>/evals/*.eval.ts` file.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { defineEval, includes } from "@databricks/appkit/beta";
9
+ *
10
+ * export default defineEval({
11
+ * description: "Weather agent basic coverage",
12
+ * async test(t) {
13
+ * await t.send("What's the weather in Brooklyn?");
14
+ * t.succeeded();
15
+ * t.calledTool("get_weather");
16
+ * t.check(t.reply, includes("Sunny"));
17
+ * },
18
+ * });
19
+ * ```
20
+ */
21
+ function defineEval(def) {
22
+ if (typeof def.test !== "function") throw new Error("defineEval: `test` must be a function");
23
+ return def;
24
+ }
25
+
26
+ //#endregion
27
+ export { defineEval };
28
+ //# sourceMappingURL=define-eval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-eval.js","names":[],"sources":["../../src/evals/define-eval.ts"],"sourcesContent":["import type { EvalDefinition } from \"./types\";\n\n/**\n * Define an agent eval. Default-export the result from a\n * `server/agents/<id>/evals/*.eval.ts` file.\n *\n * @example\n * ```ts\n * import { defineEval, includes } from \"@databricks/appkit/beta\";\n *\n * export default defineEval({\n * description: \"Weather agent basic coverage\",\n * async test(t) {\n * await t.send(\"What's the weather in Brooklyn?\");\n * t.succeeded();\n * t.calledTool(\"get_weather\");\n * t.check(t.reply, includes(\"Sunny\"));\n * },\n * });\n * ```\n */\nexport function defineEval(def: EvalDefinition): EvalDefinition {\n if (typeof def.test !== \"function\") {\n throw new Error(\"defineEval: `test` must be a function\");\n }\n return def;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,WAAW,KAAqC;AAC9D,KAAI,OAAO,IAAI,SAAS,WACtB,OAAM,IAAI,MAAM,wCAAwC;AAE1D,QAAO"}
@@ -0,0 +1,20 @@
1
+ //#region src/evals/discover.d.ts
2
+ /** An eval file found under `server/agents/<agent>/evals/`. */
3
+ interface DiscoveredEval {
4
+ /** Absolute path to the `*.eval.ts` file. */
5
+ file: string;
6
+ /** Id relative to the agent's evals dir, without `.eval.ts` (e.g. `weather/basic`). */
7
+ id: string;
8
+ /** The agent id (the `server/agents/<agent>` directory name). */
9
+ agent: string;
10
+ }
11
+ /**
12
+ * Discover evals under `<rootDir>/server/agents/<agent>/evals/` — co-located
13
+ * with each agent's `agent.{md,ts}` (same folder-per-agent layout the agents
14
+ * plugin discovers). The agent id is the folder name; the eval id is the file
15
+ * path relative to that evals dir with `.eval.ts` stripped. Sorted + stable.
16
+ */
17
+ declare function discoverEvalFiles(rootDir: string): DiscoveredEval[];
18
+ //#endregion
19
+ export { DiscoveredEval, discoverEvalFiles };
20
+ //# sourceMappingURL=discover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.d.ts","names":[],"sources":["../../src/evals/discover.ts"],"mappings":";;UAOiB,cAAA;EAAc;EAE7B,IAAA;EAF6B;EAI7B,EAAA;EAAA;EAEA,KAAA;AAAA;;AAoBF;;;;;iBAAgB,iBAAA,CAAkB,OAAA,WAAkB,cAAA"}
@@ -0,0 +1,49 @@
1
+ import { agentDirNames } from "../core/agent/agent-dirs.js";
2
+ import { CODE_AGENTS_SOURCE_DIR } from "../core/agent/load-code-agents.js";
3
+ import path from "node:path";
4
+ import { readdirSync } from "node:fs";
5
+
6
+ //#region src/evals/discover.ts
7
+ /** Recursively collect `*.eval.ts` files under `dir`. Empty when `dir` is absent. */
8
+ function evalFilesIn(dir) {
9
+ try {
10
+ return readdirSync(dir, {
11
+ recursive: true,
12
+ withFileTypes: true
13
+ }).filter((e) => e.isFile() && e.name.endsWith(".eval.ts")).map((e) => path.join(e.parentPath, e.name));
14
+ } catch {
15
+ return [];
16
+ }
17
+ }
18
+ /**
19
+ * Discover evals under `<rootDir>/server/agents/<agent>/evals/` — co-located
20
+ * with each agent's `agent.{md,ts}` (same folder-per-agent layout the agents
21
+ * plugin discovers). The agent id is the folder name; the eval id is the file
22
+ * path relative to that evals dir with `.eval.ts` stripped. Sorted + stable.
23
+ */
24
+ function discoverEvalFiles(rootDir) {
25
+ const agentsDir = path.join(rootDir, CODE_AGENTS_SOURCE_DIR);
26
+ const out = [];
27
+ let entries;
28
+ try {
29
+ entries = readdirSync(agentsDir, { withFileTypes: true });
30
+ } catch {
31
+ return out;
32
+ }
33
+ for (const agent of agentDirNames(entries)) {
34
+ const evalsDir = path.join(agentsDir, agent, "evals");
35
+ for (const file of evalFilesIn(evalsDir)) {
36
+ const id = path.relative(evalsDir, file).replace(/\.eval\.ts$/, "").split(path.sep).join("/");
37
+ out.push({
38
+ file,
39
+ id,
40
+ agent
41
+ });
42
+ }
43
+ }
44
+ return out.sort((a, b) => a.agent.localeCompare(b.agent) || a.id.localeCompare(b.id));
45
+ }
46
+
47
+ //#endregion
48
+ export { discoverEvalFiles };
49
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","names":[],"sources":["../../src/evals/discover.ts"],"sourcesContent":["import { type Dirent, readdirSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport { agentDirNames } from \"../core/agent/agent-dirs\";\nimport { CODE_AGENTS_SOURCE_DIR } from \"../core/agent/load-code-agents\";\n\n/** An eval file found under `server/agents/<agent>/evals/`. */\nexport interface DiscoveredEval {\n /** Absolute path to the `*.eval.ts` file. */\n file: string;\n /** Id relative to the agent's evals dir, without `.eval.ts` (e.g. `weather/basic`). */\n id: string;\n /** The agent id (the `server/agents/<agent>` directory name). */\n agent: string;\n}\n\n/** Recursively collect `*.eval.ts` files under `dir`. Empty when `dir` is absent. */\nfunction evalFilesIn(dir: string): string[] {\n try {\n return readdirSync(dir, { recursive: true, withFileTypes: true })\n .filter((e) => e.isFile() && e.name.endsWith(\".eval.ts\"))\n .map((e) => path.join(e.parentPath, e.name));\n } catch {\n return [];\n }\n}\n\n/**\n * Discover evals under `<rootDir>/server/agents/<agent>/evals/` — co-located\n * with each agent's `agent.{md,ts}` (same folder-per-agent layout the agents\n * plugin discovers). The agent id is the folder name; the eval id is the file\n * path relative to that evals dir with `.eval.ts` stripped. Sorted + stable.\n */\nexport function discoverEvalFiles(rootDir: string): DiscoveredEval[] {\n const agentsDir = path.join(rootDir, CODE_AGENTS_SOURCE_DIR);\n const out: DiscoveredEval[] = [];\n\n let entries: Dirent[];\n try {\n entries = readdirSync(agentsDir, { withFileTypes: true });\n } catch {\n return out;\n }\n\n for (const agent of agentDirNames(entries)) {\n const evalsDir = path.join(agentsDir, agent, \"evals\");\n for (const file of evalFilesIn(evalsDir)) {\n const id = path\n .relative(evalsDir, file)\n .replace(/\\.eval\\.ts$/, \"\")\n .split(path.sep)\n .join(\"/\");\n out.push({ file, id, agent });\n }\n }\n\n return out.sort(\n (a, b) => a.agent.localeCompare(b.agent) || a.id.localeCompare(b.id),\n );\n}\n"],"mappings":";;;;;;;AAiBA,SAAS,YAAY,KAAuB;AAC1C,KAAI;AACF,SAAO,YAAY,KAAK;GAAE,WAAW;GAAM,eAAe;GAAM,CAAC,CAC9D,QAAQ,MAAM,EAAE,QAAQ,IAAI,EAAE,KAAK,SAAS,WAAW,CAAC,CACxD,KAAK,MAAM,KAAK,KAAK,EAAE,YAAY,EAAE,KAAK,CAAC;SACxC;AACN,SAAO,EAAE;;;;;;;;;AAUb,SAAgB,kBAAkB,SAAmC;CACnE,MAAM,YAAY,KAAK,KAAK,SAAS,uBAAuB;CAC5D,MAAM,MAAwB,EAAE;CAEhC,IAAI;AACJ,KAAI;AACF,YAAU,YAAY,WAAW,EAAE,eAAe,MAAM,CAAC;SACnD;AACN,SAAO;;AAGT,MAAK,MAAM,SAAS,cAAc,QAAQ,EAAE;EAC1C,MAAM,WAAW,KAAK,KAAK,WAAW,OAAO,QAAQ;AACrD,OAAK,MAAM,QAAQ,YAAY,SAAS,EAAE;GACxC,MAAM,KAAK,KACR,SAAS,UAAU,KAAK,CACxB,QAAQ,eAAe,GAAG,CAC1B,MAAM,KAAK,IAAI,CACf,KAAK,IAAI;AACZ,OAAI,KAAK;IAAE;IAAM;IAAI;IAAO,CAAC;;;AAIjC,QAAO,IAAI,MACR,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,IAAI,EAAE,GAAG,cAAc,EAAE,GAAG,CACrE"}
@@ -0,0 +1,33 @@
1
+ import { EvalDriver } from "./types.js";
2
+
3
+ //#region src/evals/http-driver.d.ts
4
+ interface HttpDriverOptions {
5
+ /** Base URL of the running app, e.g. `http://localhost:3000`. */
6
+ baseUrl: string;
7
+ /** Agent alias to target. Omit to use the app's default agent. */
8
+ agent?: string;
9
+ /** Extra request headers (e.g. auth for a deployed app). */
10
+ headers?: Record<string, string>;
11
+ /** Chat endpoint path. Defaults to `/api/agents/chat`. */
12
+ path?: string;
13
+ /** MLflow run id to link each turn's trace to (for evaluation runs). */
14
+ mlflowRunId?: string;
15
+ /**
16
+ * Max wall-clock time for a single turn before it is abandoned as a failed
17
+ * turn (`succeeded: false`). Without this a hung agent — a blocked tool, a
18
+ * stalled model — never ends the SSE stream (heartbeats keep it alive), so
19
+ * the read loop spins forever and wedges the whole sequential suite.
20
+ * Defaults to 120s.
21
+ */
22
+ timeoutMs?: number;
23
+ }
24
+ /**
25
+ * Drives an agent by POSTing to a running app's chat endpoint and parsing the
26
+ * SSE response. Keeps the thread id across `send`s so multi-turn evals share a
27
+ * conversation. Agent/stream errors surface as `succeeded: false` rather than
28
+ * throwing, so `t.succeeded()` can assert on them.
29
+ */
30
+ declare function createHttpDriver(options: HttpDriverOptions): EvalDriver;
31
+ //#endregion
32
+ export { HttpDriverOptions, createHttpDriver };
33
+ //# sourceMappingURL=http-driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-driver.d.ts","names":[],"sources":["../../src/evals/http-driver.ts"],"mappings":";;;UAGiB,iBAAA;;EAEf,OAAA;EAFgC;EAIhC,KAAA;EAEgB;EAAhB,OAAA,GAAU,MAAA;EAFV;EAIA,IAAA;EAFU;EAIV,WAAA;EAAA;;;;AA6GF;;;EApGE,SAAA;AAAA;;;;;;;iBAoGc,gBAAA,CAAiB,OAAA,EAAS,iBAAA,GAAoB,UAAA"}
@@ -0,0 +1,118 @@
1
+ import { readSseEvents } from "../stream/sse-reader.js";
2
+
3
+ //#region src/evals/http-driver.ts
4
+ /** Record a `function_call` output item once per call id (deduped). */
5
+ function recordToolCall(item, state) {
6
+ if (item?.type !== "function_call" || !item.name) return;
7
+ const key = item.call_id ?? item.name;
8
+ if (state.seen.has(key)) return;
9
+ state.seen.add(key);
10
+ state.toolCalls.push(item.name);
11
+ }
12
+ /** Apply an `appkit.metadata` event's thread/trace ids. */
13
+ function applyMetadata(data, state, setThread) {
14
+ if (data?.threadId) setThread(data.threadId);
15
+ if (data?.mlflowTraceId) state.traceId = data.mlflowTraceId;
16
+ }
17
+ /** Parse a single Responses-API SSE `data:` payload into the running totals. */
18
+ function applyEvent(event, state, setThread) {
19
+ const type = event.type;
20
+ if (type === "response.output_text.delta") {
21
+ if (typeof event.delta === "string") state.reply += event.delta;
22
+ return;
23
+ }
24
+ if (type === "response.output_item.added" || type === "response.output_item.done") {
25
+ const item = event.item;
26
+ recordToolCall(item, state);
27
+ if (type === "response.output_item.done" && item.type === "message") {
28
+ const text = (item.content ?? []).map((c) => c.text ?? "").join("");
29
+ if (text) state.reply = text;
30
+ }
31
+ return;
32
+ }
33
+ if (type === "error" || type === "response.failed") {
34
+ state.ok = false;
35
+ return;
36
+ }
37
+ if (type === "appkit.metadata") applyMetadata(event.data, state, setThread);
38
+ }
39
+ /** Build the chat request payload, including only the optional fields that are set. */
40
+ function buildRequestBody(message, options, threadId) {
41
+ return JSON.stringify({
42
+ message,
43
+ ...options.agent ? { agent: options.agent } : {},
44
+ ...threadId ? { threadId } : {},
45
+ ...options.mlflowRunId ? { mlflowRunId: options.mlflowRunId } : {}
46
+ });
47
+ }
48
+ /**
49
+ * Drives an agent by POSTing to a running app's chat endpoint and parsing the
50
+ * SSE response. Keeps the thread id across `send`s so multi-turn evals share a
51
+ * conversation. Agent/stream errors surface as `succeeded: false` rather than
52
+ * throwing, so `t.succeeded()` can assert on them.
53
+ */
54
+ function createHttpDriver(options) {
55
+ const chatPath = options.path ?? "/api/agents/chat";
56
+ const timeoutMs = options.timeoutMs ?? 12e4;
57
+ let threadId;
58
+ return { async send(message) {
59
+ const signal = AbortSignal.timeout(timeoutMs);
60
+ let res;
61
+ try {
62
+ res = await fetch(`${options.baseUrl}${chatPath}`, {
63
+ method: "POST",
64
+ headers: {
65
+ "content-type": "application/json",
66
+ ...options.headers
67
+ },
68
+ body: buildRequestBody(message, options, threadId),
69
+ redirect: "manual",
70
+ signal
71
+ });
72
+ } catch {
73
+ return {
74
+ reply: "",
75
+ toolCalls: [],
76
+ succeeded: false
77
+ };
78
+ }
79
+ if (!res.ok || !res.body) return {
80
+ reply: "",
81
+ toolCalls: [],
82
+ succeeded: false,
83
+ sessionId: threadId
84
+ };
85
+ const state = {
86
+ reply: "",
87
+ toolCalls: [],
88
+ seen: /* @__PURE__ */ new Set(),
89
+ ok: true
90
+ };
91
+ const setThread = (id) => {
92
+ threadId = id;
93
+ };
94
+ try {
95
+ for await (const { event, data } of readSseEvents(res.body, signal)) {
96
+ if (data === "[DONE]") continue;
97
+ if (event === "error") state.ok = false;
98
+ try {
99
+ applyEvent(JSON.parse(data), state, setThread);
100
+ } catch {}
101
+ }
102
+ } catch {
103
+ state.ok = false;
104
+ }
105
+ if (signal.aborted) state.ok = false;
106
+ return {
107
+ reply: state.reply,
108
+ toolCalls: state.toolCalls,
109
+ succeeded: state.ok,
110
+ sessionId: threadId,
111
+ traceId: state.traceId
112
+ };
113
+ } };
114
+ }
115
+
116
+ //#endregion
117
+ export { createHttpDriver };
118
+ //# sourceMappingURL=http-driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-driver.js","names":[],"sources":["../../src/evals/http-driver.ts"],"sourcesContent":["import { readSseEvents } from \"../stream/sse-reader\";\nimport type { DriveResult, EvalDriver } from \"./types\";\n\nexport interface HttpDriverOptions {\n /** Base URL of the running app, e.g. `http://localhost:3000`. */\n baseUrl: string;\n /** Agent alias to target. Omit to use the app's default agent. */\n agent?: string;\n /** Extra request headers (e.g. auth for a deployed app). */\n headers?: Record<string, string>;\n /** Chat endpoint path. Defaults to `/api/agents/chat`. */\n path?: string;\n /** MLflow run id to link each turn's trace to (for evaluation runs). */\n mlflowRunId?: string;\n /**\n * Max wall-clock time for a single turn before it is abandoned as a failed\n * turn (`succeeded: false`). Without this a hung agent — a blocked tool, a\n * stalled model — never ends the SSE stream (heartbeats keep it alive), so\n * the read loop spins forever and wedges the whole sequential suite.\n * Defaults to 120s.\n */\n // ponytail: total per-turn cap; switch to an idle timeout if long legit turns get killed.\n timeoutMs?: number;\n}\n\n/** Mutable running totals accumulated while draining one turn's SSE stream. */\ninterface DriveState {\n reply: string;\n toolCalls: string[];\n seen: Set<string>;\n ok: boolean;\n traceId?: string;\n}\n\n/** Record a `function_call` output item once per call id (deduped). */\nfunction recordToolCall(\n item: { type?: string; name?: string; call_id?: string } | undefined,\n state: DriveState,\n): void {\n if (item?.type !== \"function_call\" || !item.name) return;\n const key = item.call_id ?? item.name;\n if (state.seen.has(key)) return;\n state.seen.add(key);\n state.toolCalls.push(item.name);\n}\n\n/** Apply an `appkit.metadata` event's thread/trace ids. */\nfunction applyMetadata(\n data: { threadId?: string; mlflowTraceId?: string } | undefined,\n state: DriveState,\n setThread: (id: string) => void,\n): void {\n if (data?.threadId) setThread(data.threadId);\n if (data?.mlflowTraceId) state.traceId = data.mlflowTraceId;\n}\n\n/** Parse a single Responses-API SSE `data:` payload into the running totals. */\nfunction applyEvent(\n event: Record<string, unknown>,\n state: DriveState,\n setThread: (id: string) => void,\n): void {\n const type = event.type;\n if (type === \"response.output_text.delta\") {\n if (typeof event.delta === \"string\") state.reply += event.delta;\n return;\n }\n if (\n type === \"response.output_item.added\" ||\n type === \"response.output_item.done\"\n ) {\n const item = event.item as {\n type?: string;\n name?: string;\n call_id?: string;\n content?: Array<{ text?: string }>;\n };\n recordToolCall(item, state);\n // A terminal `message` item carries the full reply text and *replaces*\n // the accumulated deltas — the server emits no delta for a full message\n // (event-translator `handleFullMessage`), so a non-streaming adapter's\n // whole reply arrives only here. Guard on non-empty so a streaming turn's\n // trailing done event can't clobber the deltas with \"\".\n if (type === \"response.output_item.done\" && item.type === \"message\") {\n const text = (item.content ?? []).map((c) => c.text ?? \"\").join(\"\");\n if (text) state.reply = text;\n }\n return;\n }\n if (type === \"error\" || type === \"response.failed\") {\n state.ok = false;\n return;\n }\n if (type === \"appkit.metadata\") {\n applyMetadata(\n event.data as { threadId?: string; mlflowTraceId?: string },\n state,\n setThread,\n );\n }\n}\n\n/** Build the chat request payload, including only the optional fields that are set. */\nfunction buildRequestBody(\n message: string,\n options: HttpDriverOptions,\n threadId: string | undefined,\n): string {\n return JSON.stringify({\n message,\n ...(options.agent ? { agent: options.agent } : {}),\n ...(threadId ? { threadId } : {}),\n ...(options.mlflowRunId ? { mlflowRunId: options.mlflowRunId } : {}),\n });\n}\n\n/**\n * Drives an agent by POSTing to a running app's chat endpoint and parsing the\n * SSE response. Keeps the thread id across `send`s so multi-turn evals share a\n * conversation. Agent/stream errors surface as `succeeded: false` rather than\n * throwing, so `t.succeeded()` can assert on them.\n */\nexport function createHttpDriver(options: HttpDriverOptions): EvalDriver {\n const chatPath = options.path ?? \"/api/agents/chat\";\n const timeoutMs = options.timeoutMs ?? 120_000;\n let threadId: string | undefined;\n\n return {\n async send(message: string): Promise<DriveResult> {\n // Bounds connect + the entire read below. Passed to both the fetch and\n // the SSE reader: on expiry the reader is cancelled and the turn fails.\n const signal = AbortSignal.timeout(timeoutMs);\n let res: Response;\n try {\n res = await fetch(`${options.baseUrl}${chatPath}`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...options.headers },\n body: buildRequestBody(message, options, threadId),\n // A chat endpoint never needs a redirect; following one would replay\n // custom auth headers (`options.headers`) to the redirect target.\n // A 3xx becomes an opaque response (res.ok === false), handled as a\n // failed turn by the !res.ok guard below.\n redirect: \"manual\",\n signal,\n });\n } catch {\n return { reply: \"\", toolCalls: [], succeeded: false };\n }\n\n if (!res.ok || !res.body) {\n return {\n reply: \"\",\n toolCalls: [],\n succeeded: false,\n sessionId: threadId,\n };\n }\n\n const state: DriveState = {\n reply: \"\",\n toolCalls: [],\n seen: new Set<string>(),\n ok: true,\n };\n const setThread = (id: string) => {\n threadId = id;\n };\n try {\n for await (const { event, data } of readSseEvents(res.body, signal)) {\n if (data === \"[DONE]\") continue;\n // A stream-level error frame (a thrown exception in the generator)\n // is framed as `event: error` with a payload that carries no `type`,\n // so the SSE event name — not the payload — is the real signal.\n if (event === \"error\") state.ok = false;\n try {\n applyEvent(\n JSON.parse(data) as Record<string, unknown>,\n state,\n setThread,\n );\n } catch {\n // skip malformed event payloads\n }\n }\n } catch {\n // mid-stream transport error or DoS-guard throw: a broken turn.\n state.ok = false;\n }\n // A timeout ends the iterator cleanly (reader cancel) rather than\n // throwing, so mark an aborted turn failed explicitly.\n if (signal.aborted) state.ok = false;\n\n return {\n reply: state.reply,\n toolCalls: state.toolCalls,\n succeeded: state.ok,\n sessionId: threadId,\n traceId: state.traceId,\n };\n },\n };\n}\n"],"mappings":";;;;AAmCA,SAAS,eACP,MACA,OACM;AACN,KAAI,MAAM,SAAS,mBAAmB,CAAC,KAAK,KAAM;CAClD,MAAM,MAAM,KAAK,WAAW,KAAK;AACjC,KAAI,MAAM,KAAK,IAAI,IAAI,CAAE;AACzB,OAAM,KAAK,IAAI,IAAI;AACnB,OAAM,UAAU,KAAK,KAAK,KAAK;;;AAIjC,SAAS,cACP,MACA,OACA,WACM;AACN,KAAI,MAAM,SAAU,WAAU,KAAK,SAAS;AAC5C,KAAI,MAAM,cAAe,OAAM,UAAU,KAAK;;;AAIhD,SAAS,WACP,OACA,OACA,WACM;CACN,MAAM,OAAO,MAAM;AACnB,KAAI,SAAS,8BAA8B;AACzC,MAAI,OAAO,MAAM,UAAU,SAAU,OAAM,SAAS,MAAM;AAC1D;;AAEF,KACE,SAAS,gCACT,SAAS,6BACT;EACA,MAAM,OAAO,MAAM;AAMnB,iBAAe,MAAM,MAAM;AAM3B,MAAI,SAAS,+BAA+B,KAAK,SAAS,WAAW;GACnE,MAAM,QAAQ,KAAK,WAAW,EAAE,EAAE,KAAK,MAAM,EAAE,QAAQ,GAAG,CAAC,KAAK,GAAG;AACnE,OAAI,KAAM,OAAM,QAAQ;;AAE1B;;AAEF,KAAI,SAAS,WAAW,SAAS,mBAAmB;AAClD,QAAM,KAAK;AACX;;AAEF,KAAI,SAAS,kBACX,eACE,MAAM,MACN,OACA,UACD;;;AAKL,SAAS,iBACP,SACA,SACA,UACQ;AACR,QAAO,KAAK,UAAU;EACpB;EACA,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,OAAO,GAAG,EAAE;EACjD,GAAI,WAAW,EAAE,UAAU,GAAG,EAAE;EAChC,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,aAAa,GAAG,EAAE;EACpE,CAAC;;;;;;;;AASJ,SAAgB,iBAAiB,SAAwC;CACvE,MAAM,WAAW,QAAQ,QAAQ;CACjC,MAAM,YAAY,QAAQ,aAAa;CACvC,IAAI;AAEJ,QAAO,EACL,MAAM,KAAK,SAAuC;EAGhD,MAAM,SAAS,YAAY,QAAQ,UAAU;EAC7C,IAAI;AACJ,MAAI;AACF,SAAM,MAAM,MAAM,GAAG,QAAQ,UAAU,YAAY;IACjD,QAAQ;IACR,SAAS;KAAE,gBAAgB;KAAoB,GAAG,QAAQ;KAAS;IACnE,MAAM,iBAAiB,SAAS,SAAS,SAAS;IAKlD,UAAU;IACV;IACD,CAAC;UACI;AACN,UAAO;IAAE,OAAO;IAAI,WAAW,EAAE;IAAE,WAAW;IAAO;;AAGvD,MAAI,CAAC,IAAI,MAAM,CAAC,IAAI,KAClB,QAAO;GACL,OAAO;GACP,WAAW,EAAE;GACb,WAAW;GACX,WAAW;GACZ;EAGH,MAAM,QAAoB;GACxB,OAAO;GACP,WAAW,EAAE;GACb,sBAAM,IAAI,KAAa;GACvB,IAAI;GACL;EACD,MAAM,aAAa,OAAe;AAChC,cAAW;;AAEb,MAAI;AACF,cAAW,MAAM,EAAE,OAAO,UAAU,cAAc,IAAI,MAAM,OAAO,EAAE;AACnE,QAAI,SAAS,SAAU;AAIvB,QAAI,UAAU,QAAS,OAAM,KAAK;AAClC,QAAI;AACF,gBACE,KAAK,MAAM,KAAK,EAChB,OACA,UACD;YACK;;UAIJ;AAEN,SAAM,KAAK;;AAIb,MAAI,OAAO,QAAS,OAAM,KAAK;AAE/B,SAAO;GACL,OAAO,MAAM;GACb,WAAW,MAAM;GACjB,WAAW,MAAM;GACjB,WAAW;GACX,SAAS,MAAM;GAChB;IAEJ"}
@@ -0,0 +1,13 @@
1
+ import { resolveDatabricksAuth } from "../connectors/mlflow/auth.js";
2
+ import { MlflowClient, normalizeHost } from "../connectors/mlflow/client.js";
3
+ import { defineEval } from "./define-eval.js";
4
+ import { discoverEvalFiles } from "./discover.js";
5
+ import { createHttpDriver } from "./http-driver.js";
6
+ import { configureJudge, isJudgeConfigured } from "./judge.js";
7
+ import { equals, includes, matches } from "./matchers.js";
8
+ import { buildAssessments, reportToMlflow } from "./mlflow-report.js";
9
+ import { evalGlyph, formatEvalDetail, formatEvalHeadline, formatEvalResults, formatSummaryLine, summarize } from "./report.js";
10
+ import { runEval } from "./run-eval.js";
11
+ import { runEvalsInDir } from "./run-evals.js";
12
+
13
+ export { };
@@ -0,0 +1,26 @@
1
+ import { MlflowClient } from "../connectors/mlflow/client.js";
2
+
3
+ //#region src/evals/judge.d.ts
4
+ interface JudgeConfig {
5
+ /** Client for the workspace hosting the judge serving endpoint. */
6
+ client: MlflowClient;
7
+ /** Bearer token for the serving endpoint. */
8
+ token: string;
9
+ /** Serving endpoint name used as the judge model. */
10
+ model: string;
11
+ }
12
+ /** A normalized judge result. `score` is 0..1. */
13
+ interface JudgeScore {
14
+ score: number;
15
+ rationale?: string;
16
+ }
17
+ /**
18
+ * Configure the judge once. Sets the OpenAI-compatible client env autoevals
19
+ * reads and the default judge model. No-op-safe: on failure, judging stays
20
+ * disabled and {@link isJudgeConfigured} returns false.
21
+ */
22
+ declare function configureJudge(config: JudgeConfig): Promise<void>;
23
+ declare function isJudgeConfigured(): boolean;
24
+ //#endregion
25
+ export { JudgeConfig, JudgeScore, configureJudge, isJudgeConfigured };
26
+ //# sourceMappingURL=judge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"judge.d.ts","names":[],"sources":["../../src/evals/judge.ts"],"mappings":";;;UAuBiB,WAAA;;EAEf,MAAA,EAAQ,YAAA;EAFkB;EAI1B,KAAA;EAFoB;EAIpB,KAAA;AAAA;;UAIe,UAAA;EACf,KAAA;EACA,SAAA;AAAA;;;;;AAQF;iBAAsB,cAAA,CAAe,MAAA,EAAQ,WAAA,GAAc,OAAA;AAAA,iBAe3C,iBAAA,CAAA"}