@babav/knowledge-core-client 0.17.0 → 0.20.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/README.md CHANGED
@@ -31,9 +31,11 @@ import { KnowledgeCoreClient } from "npm:@babav/knowledge-core-client";
31
31
  ## Usage
32
32
 
33
33
  ```ts
34
+ // You supply baseUrl + your tenant key at construction (from wherever your app keeps
35
+ // its config/secrets). The SDK never reads the environment or defaults a key itself.
34
36
  const kc = new KnowledgeCoreClient({
35
- baseUrl: "https://babav-kc-api-gmtvlr3vta-uc.a.run.app",
36
- apiKey: Deno.env.get("BABAV_KC_TENANT_KEY")!, // or process.env on Node
37
+ baseUrl: "https://knowledgecore.babav.ai",
38
+ apiKey: myTenantKey, // your tenant key, provided by your app
37
39
  });
38
40
 
39
41
  // One-shot grounded query
package/dist/index.d.ts CHANGED
@@ -12,15 +12,15 @@
12
12
  * key for all data ops; use AdminClient with the ADMIN key for tenant / API-key /
13
13
  * agent management. The key is server-side only — never ship it to a browser.
14
14
  *
15
- * Configuration — environment variables (Node `process.env` or Deno `Deno.env`):
16
- * `new KnowledgeCoreClient()` reads BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY
17
- * `new AdminClient()` reads BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY
18
- * Passing { baseUrl } / { apiKey } explicitly overrides the env var for that field
19
- * (the app is then responsible for supplying it). A missing value (no param AND no
20
- * env var) throws at construction with a clear message. In Deno, env access needs
21
- * `--allow-env` (without it, pass the values explicitly).
15
+ * Configuration — the CALLER supplies credentials at construction; the SDK never reads
16
+ * the environment or defaults/derives a key itself:
17
+ * new KnowledgeCoreClient({ baseUrl, apiKey }) // apiKey = a TENANT key
18
+ * new AdminClient({ baseUrl, apiKey }) // apiKey = the ADMIN key
19
+ * The app decides where its key comes from (its own env/secret store) and passes it in.
20
+ * A missing baseUrl or apiKey throws at construction with a clear message.
22
21
  */
23
22
  export type UUID = string;
23
+ export type { VisualEnvelope, VisualPayload, VisualElement, VisualComposition, VisualRegister, ChartBlockElement, } from "./visual/types.js";
24
24
  export interface Page<T> {
25
25
  items: T[];
26
26
  next_cursor: string | null;
@@ -65,12 +65,52 @@ export interface QueryOverrides {
65
65
  grounding_enabled?: boolean;
66
66
  citations_enabled?: boolean;
67
67
  }
68
+ /** Per-request visual overrides (see VisualRequest). */
69
+ export interface VisualOverrides {
70
+ concept_rounds_max?: number;
71
+ proposals_n?: number;
72
+ max_revisions?: number;
73
+ claims_check_model?: string;
74
+ vision_judge_model?: string;
75
+ scenic_enabled?: boolean;
76
+ }
77
+ /** Per-request visual control. Absent => mode resolves from the agent default. All visual
78
+ * PROCESSING is server-side; the client only displays the result. */
79
+ export interface VisualRequest {
80
+ mode?: "off" | "on";
81
+ delivery?: "svg" | "json" | "both";
82
+ overrides?: VisualOverrides;
83
+ }
68
84
  export interface QueryRequest {
69
85
  corpus_ids: UUID[];
70
86
  query: string;
71
87
  conversation_id?: UUID | null;
72
88
  overrides?: QueryOverrides;
73
89
  filter?: MetadataFilter;
90
+ visual?: VisualRequest;
91
+ }
92
+ /** A visual attached to a response, anchored by char offsets into `answer` (SAME coordinate
93
+ * system as citations). For delivery="svg", `rendered_svg` is set; scenic sets `image`. */
94
+ export interface Visual {
95
+ id: string;
96
+ anchor: {
97
+ start: number;
98
+ end: number;
99
+ };
100
+ register: "chart" | "structural" | "conceptual" | "scenic";
101
+ priority: number;
102
+ payload: Record<string, unknown>;
103
+ rendered_svg?: string;
104
+ image?: {
105
+ url: string;
106
+ expires_at: number;
107
+ width: number;
108
+ height: number;
109
+ };
110
+ }
111
+ export interface VisualsMeta {
112
+ count: number;
113
+ dropped: number;
74
114
  }
75
115
  export interface QueryResponse {
76
116
  conversation_id: UUID | null;
@@ -81,6 +121,8 @@ export interface QueryResponse {
81
121
  groundedness: Groundedness | null;
82
122
  usage: Record<string, number | null>;
83
123
  standalone_query: string | null;
124
+ visuals?: Visual[];
125
+ visuals_meta?: VisualsMeta | null;
84
126
  }
85
127
  export interface Citation {
86
128
  /** Char span IN THE ANSWER this citation supports (for highlighting). */
@@ -183,6 +225,10 @@ export interface Message {
183
225
  groundedness: Record<string, unknown> | null;
184
226
  retrieval_contents: unknown[] | null;
185
227
  usage: Record<string, unknown> | null;
228
+ visuals?: {
229
+ items: Visual[];
230
+ meta: VisualsMeta;
231
+ } | null;
186
232
  }
187
233
  export interface Attachment {
188
234
  id: UUID;
@@ -251,11 +297,11 @@ export declare class KnowledgeCoreError extends Error {
251
297
  get code(): string | undefined;
252
298
  }
253
299
  export interface ClientOptions {
254
- /** API base URL. Defaults to env BABAV_KC_BASE_URL when omitted. */
255
- baseUrl?: string;
256
- /** Tenant key (KnowledgeCoreClient) or admin key (AdminClient). Defaults to env
257
- * BABAV_KC_TENANT_KEY / BABAV_KC_ADMIN_KEY respectively when omitted. */
258
- apiKey?: string;
300
+ /** API base URL REQUIRED, supplied by the caller. */
301
+ baseUrl: string;
302
+ /** API key REQUIRED, supplied by the caller (a TENANT key for KnowledgeCoreClient,
303
+ * the ADMIN key for AdminClient). The SDK never reads it from the environment. */
304
+ apiKey: string;
259
305
  /** Optional default fetch timeout (ms). Streaming ignores this. */
260
306
  timeoutMs?: number;
261
307
  fetch?: typeof fetch;
@@ -272,7 +318,7 @@ declare class HttpBase {
272
318
  protected readonly apiKey: string;
273
319
  protected readonly timeoutMs?: number;
274
320
  protected readonly _fetch: typeof fetch;
275
- constructor(opts?: ClientOptions, keyEnvVar?: string);
321
+ constructor(opts: ClientOptions);
276
322
  protected url(path: string, query?: RequestOpts["query"]): string;
277
323
  protected raw(method: string, path: string, opts?: RequestOpts): Promise<Response>;
278
324
  protected request<T>(method: string, path: string, opts?: RequestOpts): Promise<T>;
@@ -291,14 +337,19 @@ export interface StreamHandlers {
291
337
  * (native path). Also present in `final`. May be `{}` if scoring was unavailable. */
292
338
  onGroundedness?: (g: Groundedness | Record<string, never>) => void;
293
339
  onFinal?: (d: QueryResponse) => void;
340
+ /** A visual, emitted (out of anchor order) during the trailing visual phase when
341
+ * visual.mode="on". Place by `anchor`, not arrival order. */
342
+ onVisual?: (v: Visual) => void;
343
+ /** Always emitted exactly once to close the visual phase (incl. count=0), before `done`. */
344
+ onVisualsComplete?: (m: VisualsMeta) => void;
294
345
  onError?: (d: unknown) => void;
295
346
  onDone?: () => void;
296
347
  /** Catch-all for any event (incl. unknown ones). */
297
348
  onEvent?: (event: string, data: unknown) => void;
298
349
  }
299
350
  export declare class KnowledgeCoreClient extends HttpBase {
300
- /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY when not passed. */
301
- constructor(opts?: ClientOptions);
351
+ /** @param opts.apiKey a TENANT key, supplied by the caller. */
352
+ constructor(opts: ClientOptions);
302
353
  query(agentId: UUID, body: QueryRequest): Promise<QueryResponse>;
303
354
  /** Streaming query (SSE). Resolves when the stream ends. */
304
355
  queryStream(agentId: UUID, body: QueryRequest, handlers: StreamHandlers, signal?: AbortSignal): Promise<void>;
@@ -484,8 +535,8 @@ export declare class KnowledgeCoreClient extends HttpBase {
484
535
  };
485
536
  }
486
537
  export declare class AdminClient extends HttpBase {
487
- /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY when not passed. */
488
- constructor(opts?: ClientOptions);
538
+ /** @param opts.apiKey the ADMIN key, supplied by the caller. */
539
+ constructor(opts: ClientOptions);
489
540
  tenants: {
490
541
  create: (b: Partial<Tenant> & {
491
542
  name: string;
@@ -529,4 +580,3 @@ export declare class AdminClient extends HttpBase {
529
580
  delete: (id: UUID) => Promise<void>;
530
581
  };
531
582
  }
532
- export {};
package/dist/index.js CHANGED
@@ -12,13 +12,12 @@
12
12
  * key for all data ops; use AdminClient with the ADMIN key for tenant / API-key /
13
13
  * agent management. The key is server-side only — never ship it to a browser.
14
14
  *
15
- * Configuration — environment variables (Node `process.env` or Deno `Deno.env`):
16
- * `new KnowledgeCoreClient()` reads BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY
17
- * `new AdminClient()` reads BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY
18
- * Passing { baseUrl } / { apiKey } explicitly overrides the env var for that field
19
- * (the app is then responsible for supplying it). A missing value (no param AND no
20
- * env var) throws at construction with a clear message. In Deno, env access needs
21
- * `--allow-env` (without it, pass the values explicitly).
15
+ * Configuration — the CALLER supplies credentials at construction; the SDK never reads
16
+ * the environment or defaults/derives a key itself:
17
+ * new KnowledgeCoreClient({ baseUrl, apiKey }) // apiKey = a TENANT key
18
+ * new AdminClient({ baseUrl, apiKey }) // apiKey = the ADMIN key
19
+ * The app decides where its key comes from (its own env/secret store) and passes it in.
20
+ * A missing baseUrl or apiKey throws at construction with a clear message.
22
21
  */
23
22
  // ---------------------------------------------------------------------------
24
23
  // Errors
@@ -43,37 +42,19 @@ export class KnowledgeCoreError extends Error {
43
42
  return d?.detail?.error ?? d?.error;
44
43
  }
45
44
  }
46
- /** Read an env var under Node (process.env) or Deno (Deno.env); undefined if unset
47
- * or inaccessible (e.g. Deno without --allow-env). */
48
- function readEnv(name) {
49
- const g = globalThis;
50
- const fromNode = g.process?.env?.[name];
51
- if (fromNode)
52
- return fromNode;
53
- try {
54
- const fromDeno = g.Deno?.env?.get?.(name);
55
- if (fromDeno)
56
- return fromDeno;
57
- }
58
- catch {
59
- /* Deno env access denied (no --allow-env) — treat as unset */
60
- }
61
- return undefined;
62
- }
63
45
  class HttpBase {
64
46
  baseUrl;
65
47
  apiKey;
66
48
  timeoutMs;
67
49
  _fetch;
68
- constructor(opts = {}, keyEnvVar = "BABAV_KC_TENANT_KEY") {
69
- const baseUrl = opts.baseUrl ?? readEnv("BABAV_KC_BASE_URL");
70
- const apiKey = opts.apiKey ?? readEnv(keyEnvVar);
71
- if (!baseUrl)
72
- throw new Error("KnowledgeCore: no base URL — pass { baseUrl } or set BABAV_KC_BASE_URL");
73
- if (!apiKey)
74
- throw new Error(`KnowledgeCore: no API key — pass { apiKey } or set ${keyEnvVar}`);
75
- this.baseUrl = baseUrl.replace(/\/+$/, "");
76
- this.apiKey = apiKey;
50
+ constructor(opts) {
51
+ // Credentials come from the CALLER at init — the SDK never sources a key itself.
52
+ if (!opts?.baseUrl)
53
+ throw new Error("KnowledgeCore: { baseUrl } is required");
54
+ if (!opts?.apiKey)
55
+ throw new Error("KnowledgeCore: { apiKey } is required — the caller must supply the key at init");
56
+ this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
57
+ this.apiKey = opts.apiKey;
77
58
  this.timeoutMs = opts.timeoutMs;
78
59
  this._fetch = opts.fetch ?? fetch;
79
60
  }
@@ -162,9 +143,9 @@ const MULTIPART_MAX_BYTES = 30 * 1024 * 1024;
162
143
  // Tenant client (data ops) — use a TENANT key
163
144
  // ---------------------------------------------------------------------------
164
145
  export class KnowledgeCoreClient extends HttpBase {
165
- /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY when not passed. */
166
- constructor(opts = {}) {
167
- super(opts, "BABAV_KC_TENANT_KEY");
146
+ /** @param opts.apiKey a TENANT key, supplied by the caller. */
147
+ constructor(opts) {
148
+ super(opts);
168
149
  }
169
150
  // --- query (agent-anchored) ---
170
151
  query(agentId, body) {
@@ -329,9 +310,9 @@ export class KnowledgeCoreClient extends HttpBase {
329
310
  // Admin client (tenant + key + agent management) — use the ADMIN key
330
311
  // ---------------------------------------------------------------------------
331
312
  export class AdminClient extends HttpBase {
332
- /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY when not passed. */
333
- constructor(opts = {}) {
334
- super(opts, "BABAV_KC_ADMIN_KEY");
313
+ /** @param opts.apiKey the ADMIN key, supplied by the caller. */
314
+ constructor(opts) {
315
+ super(opts);
335
316
  }
336
317
  tenants = {
337
318
  create: (b) => this.request("POST", "/v1/tenants", { json: b }),
@@ -383,6 +364,12 @@ function dispatchSse(frame, h) {
383
364
  case "final":
384
365
  h.onFinal?.(data);
385
366
  break;
367
+ case "visual":
368
+ h.onVisual?.(data);
369
+ break;
370
+ case "visuals_complete":
371
+ h.onVisualsComplete?.(data);
372
+ break;
386
373
  case "error":
387
374
  h.onError?.(data);
388
375
  break;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `@babav/knowledge-core-client/visual` — the babav.visual grammar TYPES.
3
+ *
4
+ * Types only (zero runtime, zero deps): they let a client type the visual payloads it may
5
+ * receive. All visual PROCESSING — grammar validation, layout, and SVG/PNG rendering — runs
6
+ * SERVER-SIDE in render-service; the client only displays the server-produced SVG/image.
7
+ * The schema + validators + renderers therefore live in render-service, not here.
8
+ */
9
+ export * from "./types.js";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `@babav/knowledge-core-client/visual` — the babav.visual grammar TYPES.
3
+ *
4
+ * Types only (zero runtime, zero deps): they let a client type the visual payloads it may
5
+ * receive. All visual PROCESSING — grammar validation, layout, and SVG/PNG rendering — runs
6
+ * SERVER-SIDE in render-service; the client only displays the server-produced SVG/image.
7
+ * The schema + validators + renderers therefore live in render-service, not here.
8
+ */
9
+ export * from "./types.js";
@@ -0,0 +1,105 @@
1
+ /**
2
+ * `babav.visual` grammar — TypeScript types (WP1a).
3
+ *
4
+ * The typed structural-composition language the visual pipeline emits for the
5
+ * `structural` register (and the envelope that wraps every register's payload). Uses
6
+ * vocabulary models already know — nodes, connectors (edges), containers/panels
7
+ * (clusters), composition operators. LLMs compose in this grammar; a deterministic
8
+ * engine (ELK) does layout — models never emit coordinates.
9
+ *
10
+ * NB: the grammar uses its own camelCase convention (`chartBlock`, `vegaLiteSpec`,
11
+ * `focalElement`) per the visual spec — distinct from the KC API's snake_case wire
12
+ * shapes. Versioned from the first commit (`version: "1"`).
13
+ */
14
+ export type VisualRegister = "chart" | "structural" | "conceptual" | "scenic";
15
+ export type EmphasisLevel = "normal" | "strong" | "muted";
16
+ export type ConnectorKind = "flow" | "dependency" | "association" | "containment" | "bidirectional";
17
+ export interface NodeElement {
18
+ type: "node";
19
+ id: string;
20
+ label: string;
21
+ role?: string;
22
+ emphasis?: EmphasisLevel;
23
+ }
24
+ export interface ConnectorElement {
25
+ type: "connector";
26
+ id?: string;
27
+ from: string;
28
+ to: string;
29
+ label?: string;
30
+ kind: ConnectorKind;
31
+ }
32
+ export interface ContainerElement {
33
+ type: "container";
34
+ id: string;
35
+ label?: string;
36
+ children: string[];
37
+ }
38
+ export interface PanelElement {
39
+ type: "panel";
40
+ id: string;
41
+ title?: string;
42
+ children: string[];
43
+ }
44
+ export interface AnnotationElement {
45
+ type: "annotation";
46
+ id?: string;
47
+ target: string;
48
+ text: string;
49
+ }
50
+ export interface ChartBlockElement {
51
+ type: "chartBlock";
52
+ id: string;
53
+ vegaLiteSpec: Record<string, unknown>;
54
+ }
55
+ export type VisualElement = NodeElement | ConnectorElement | ContainerElement | PanelElement | AnnotationElement | ChartBlockElement;
56
+ export interface NestComposition {
57
+ op: "nest";
58
+ parent: string;
59
+ children: string[];
60
+ }
61
+ export interface SequenceComposition {
62
+ op: "sequence";
63
+ items: string[];
64
+ }
65
+ export interface JuxtaposeComposition {
66
+ op: "juxtapose";
67
+ items: string[];
68
+ }
69
+ export interface LayerComposition {
70
+ op: "layer";
71
+ items: string[];
72
+ }
73
+ export interface LinkComposition {
74
+ op: "link";
75
+ from: string;
76
+ to: string;
77
+ label?: string;
78
+ kind?: ConnectorKind;
79
+ }
80
+ export type VisualComposition = NestComposition | SequenceComposition | JuxtaposeComposition | LayerComposition | LinkComposition;
81
+ export type Orientation = "horizontal" | "vertical" | "radial";
82
+ export type Density = "compact" | "comfortable" | "spacious";
83
+ export interface LayoutIntent {
84
+ orientation?: Orientation;
85
+ density?: Density;
86
+ focalElement?: string;
87
+ }
88
+ export interface VisualPayload {
89
+ elements: VisualElement[];
90
+ composition?: VisualComposition[];
91
+ layout?: LayoutIntent;
92
+ }
93
+ /** Per-element source provenance. Present from v1; populated in Phase 2. */
94
+ export interface VisualProvenance {
95
+ [k: string]: unknown;
96
+ }
97
+ /** The envelope wrapping every register's payload. */
98
+ export interface VisualEnvelope {
99
+ schema: "babav.visual";
100
+ version: "1";
101
+ register: VisualRegister;
102
+ gist?: string;
103
+ provenance?: VisualProvenance[];
104
+ payload: VisualPayload;
105
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `babav.visual` grammar — TypeScript types (WP1a).
3
+ *
4
+ * The typed structural-composition language the visual pipeline emits for the
5
+ * `structural` register (and the envelope that wraps every register's payload). Uses
6
+ * vocabulary models already know — nodes, connectors (edges), containers/panels
7
+ * (clusters), composition operators. LLMs compose in this grammar; a deterministic
8
+ * engine (ELK) does layout — models never emit coordinates.
9
+ *
10
+ * NB: the grammar uses its own camelCase convention (`chartBlock`, `vegaLiteSpec`,
11
+ * `focalElement`) per the visual spec — distinct from the KC API's snake_case wire
12
+ * shapes. Versioned from the first commit (`version: "1"`).
13
+ */
14
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@babav/knowledge-core-client",
3
- "version": "0.17.0",
4
- "description": "TypeScript client for the Babav Knowledge Core API (Deno + Node 18+, zero deps).",
3
+ "version": "0.20.0",
4
+ "description": "TypeScript client for the Babav Knowledge Core API (Deno + Node 18+, zero deps). Includes the babav.visual grammar TYPES at the ./visual subpath (types only; all visual rendering is server-side).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -9,9 +9,16 @@
9
9
  ".": {
10
10
  "types": "./dist/index.d.ts",
11
11
  "import": "./dist/index.js"
12
+ },
13
+ "./visual": {
14
+ "types": "./dist/visual/index.d.ts",
15
+ "import": "./dist/visual/index.js"
12
16
  }
13
17
  },
14
- "files": ["dist", "src"],
18
+ "files": [
19
+ "dist",
20
+ "src"
21
+ ],
15
22
  "publishConfig": {
16
23
  "access": "public",
17
24
  "registry": "https://registry.npmjs.org/"
@@ -23,12 +30,15 @@
23
30
  },
24
31
  "scripts": {
25
32
  "build": "tsc -p tsconfig.json",
26
- "typecheck": "tsc -p tsconfig.json --noEmit"
33
+ "typecheck": "tsc -p tsconfig.json --noEmit",
34
+ "test": "npm run build && node --test test/"
27
35
  },
28
36
  "devDependencies": {
29
37
  "typescript": "^5.6.0"
30
38
  },
31
- "engines": { "node": ">=18" },
39
+ "engines": {
40
+ "node": ">=18"
41
+ },
32
42
  "sideEffects": false,
33
43
  "private": false,
34
44
  "license": "UNLICENSED"
package/src/index.ts CHANGED
@@ -12,13 +12,12 @@
12
12
  * key for all data ops; use AdminClient with the ADMIN key for tenant / API-key /
13
13
  * agent management. The key is server-side only — never ship it to a browser.
14
14
  *
15
- * Configuration — environment variables (Node `process.env` or Deno `Deno.env`):
16
- * `new KnowledgeCoreClient()` reads BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY
17
- * `new AdminClient()` reads BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY
18
- * Passing { baseUrl } / { apiKey } explicitly overrides the env var for that field
19
- * (the app is then responsible for supplying it). A missing value (no param AND no
20
- * env var) throws at construction with a clear message. In Deno, env access needs
21
- * `--allow-env` (without it, pass the values explicitly).
15
+ * Configuration — the CALLER supplies credentials at construction; the SDK never reads
16
+ * the environment or defaults/derives a key itself:
17
+ * new KnowledgeCoreClient({ baseUrl, apiKey }) // apiKey = a TENANT key
18
+ * new AdminClient({ baseUrl, apiKey }) // apiKey = the ADMIN key
19
+ * The app decides where its key comes from (its own env/secret store) and passes it in.
20
+ * A missing baseUrl or apiKey throws at construction with a clear message.
22
21
  */
23
22
 
24
23
  // ---------------------------------------------------------------------------
@@ -26,6 +25,18 @@
26
25
  // ---------------------------------------------------------------------------
27
26
  export type UUID = string;
28
27
 
28
+ // The babav.visual grammar (WP1a). Types are re-exported here for convenience; the full
29
+ // grammar (schema + validators + fixtures) lives at the `./visual` subpath so it can pull
30
+ // its optional `ajv` peer without adding any dependency to this main entry.
31
+ export type {
32
+ VisualEnvelope,
33
+ VisualPayload,
34
+ VisualElement,
35
+ VisualComposition,
36
+ VisualRegister,
37
+ ChartBlockElement,
38
+ } from "./visual/types.js";
39
+
29
40
  export interface Page<T> {
30
41
  items: T[];
31
42
  next_cursor: string | null;
@@ -76,12 +87,48 @@ export interface QueryOverrides {
76
87
  citations_enabled?: boolean; // source attribution (default on)
77
88
  }
78
89
 
90
+ /** Per-request visual overrides (see VisualRequest). */
91
+ export interface VisualOverrides {
92
+ concept_rounds_max?: number;
93
+ proposals_n?: number;
94
+ max_revisions?: number;
95
+ claims_check_model?: string;
96
+ vision_judge_model?: string;
97
+ scenic_enabled?: boolean;
98
+ }
99
+
100
+ /** Per-request visual control. Absent => mode resolves from the agent default. All visual
101
+ * PROCESSING is server-side; the client only displays the result. */
102
+ export interface VisualRequest {
103
+ mode?: "off" | "on";
104
+ delivery?: "svg" | "json" | "both";
105
+ overrides?: VisualOverrides;
106
+ }
107
+
79
108
  export interface QueryRequest {
80
109
  corpus_ids: UUID[];
81
110
  query: string;
82
111
  conversation_id?: UUID | null; // present => conversational; absent => one-shot
83
112
  overrides?: QueryOverrides;
84
113
  filter?: MetadataFilter;
114
+ visual?: VisualRequest;
115
+ }
116
+
117
+ /** A visual attached to a response, anchored by char offsets into `answer` (SAME coordinate
118
+ * system as citations). For delivery="svg", `rendered_svg` is set; scenic sets `image`. */
119
+ export interface Visual {
120
+ id: string;
121
+ anchor: { start: number; end: number };
122
+ register: "chart" | "structural" | "conceptual" | "scenic";
123
+ priority: number;
124
+ payload: Record<string, unknown>; // {kind: "grammar"|"vega_lite"|"svg"|"image", ...}
125
+ rendered_svg?: string;
126
+ image?: { url: string; expires_at: number; width: number; height: number };
127
+ }
128
+
129
+ export interface VisualsMeta {
130
+ count: number;
131
+ dropped: number;
85
132
  }
86
133
 
87
134
  export interface QueryResponse {
@@ -93,6 +140,8 @@ export interface QueryResponse {
93
140
  groundedness: Groundedness | null;
94
141
  usage: Record<string, number | null>;
95
142
  standalone_query: string | null;
143
+ visuals?: Visual[]; // [] when off/none
144
+ visuals_meta?: VisualsMeta | null;
96
145
  }
97
146
 
98
147
  export interface Citation {
@@ -196,6 +245,7 @@ export interface Message {
196
245
  groundedness: Record<string, unknown> | null;
197
246
  retrieval_contents: unknown[] | null;
198
247
  usage: Record<string, unknown> | null;
248
+ visuals?: { items: Visual[]; meta: VisualsMeta } | null; // post-generation visuals (re-signed URLs on read)
199
249
  }
200
250
  export interface Attachment {
201
251
  id: UUID;
@@ -279,34 +329,16 @@ export class KnowledgeCoreError extends Error {
279
329
  // Base HTTP
280
330
  // ---------------------------------------------------------------------------
281
331
  export interface ClientOptions {
282
- /** API base URL. Defaults to env BABAV_KC_BASE_URL when omitted. */
283
- baseUrl?: string;
284
- /** Tenant key (KnowledgeCoreClient) or admin key (AdminClient). Defaults to env
285
- * BABAV_KC_TENANT_KEY / BABAV_KC_ADMIN_KEY respectively when omitted. */
286
- apiKey?: string;
332
+ /** API base URL REQUIRED, supplied by the caller. */
333
+ baseUrl: string;
334
+ /** API key REQUIRED, supplied by the caller (a TENANT key for KnowledgeCoreClient,
335
+ * the ADMIN key for AdminClient). The SDK never reads it from the environment. */
336
+ apiKey: string;
287
337
  /** Optional default fetch timeout (ms). Streaming ignores this. */
288
338
  timeoutMs?: number;
289
339
  fetch?: typeof fetch; // override for tests
290
340
  }
291
341
 
292
- /** Read an env var under Node (process.env) or Deno (Deno.env); undefined if unset
293
- * or inaccessible (e.g. Deno without --allow-env). */
294
- function readEnv(name: string): string | undefined {
295
- const g = globalThis as {
296
- process?: { env?: Record<string, string | undefined> };
297
- Deno?: { env?: { get?: (n: string) => string | undefined } };
298
- };
299
- const fromNode = g.process?.env?.[name];
300
- if (fromNode) return fromNode;
301
- try {
302
- const fromDeno = g.Deno?.env?.get?.(name);
303
- if (fromDeno) return fromDeno;
304
- } catch {
305
- /* Deno env access denied (no --allow-env) — treat as unset */
306
- }
307
- return undefined;
308
- }
309
-
310
342
  interface RequestOpts {
311
343
  query?: Record<string, string | number | boolean | undefined>;
312
344
  json?: unknown;
@@ -321,13 +353,12 @@ class HttpBase {
321
353
  protected readonly timeoutMs?: number;
322
354
  protected readonly _fetch: typeof fetch;
323
355
 
324
- constructor(opts: ClientOptions = {}, keyEnvVar = "BABAV_KC_TENANT_KEY") {
325
- const baseUrl = opts.baseUrl ?? readEnv("BABAV_KC_BASE_URL");
326
- const apiKey = opts.apiKey ?? readEnv(keyEnvVar);
327
- if (!baseUrl) throw new Error("KnowledgeCore: no base URLpass { baseUrl } or set BABAV_KC_BASE_URL");
328
- if (!apiKey) throw new Error(`KnowledgeCore: no API key — pass { apiKey } or set ${keyEnvVar}`);
329
- this.baseUrl = baseUrl.replace(/\/+$/, "");
330
- this.apiKey = apiKey;
356
+ constructor(opts: ClientOptions) {
357
+ // Credentials come from the CALLER at init — the SDK never sources a key itself.
358
+ if (!opts?.baseUrl) throw new Error("KnowledgeCore: { baseUrl } is required");
359
+ if (!opts?.apiKey) throw new Error("KnowledgeCore: { apiKey } is required the caller must supply the key at init");
360
+ this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
361
+ this.apiKey = opts.apiKey;
331
362
  this.timeoutMs = opts.timeoutMs;
332
363
  this._fetch = opts.fetch ?? fetch;
333
364
  }
@@ -425,6 +456,11 @@ export interface StreamHandlers {
425
456
  * (native path). Also present in `final`. May be `{}` if scoring was unavailable. */
426
457
  onGroundedness?: (g: Groundedness | Record<string, never>) => void;
427
458
  onFinal?: (d: QueryResponse) => void;
459
+ /** A visual, emitted (out of anchor order) during the trailing visual phase when
460
+ * visual.mode="on". Place by `anchor`, not arrival order. */
461
+ onVisual?: (v: Visual) => void;
462
+ /** Always emitted exactly once to close the visual phase (incl. count=0), before `done`. */
463
+ onVisualsComplete?: (m: VisualsMeta) => void;
428
464
  onError?: (d: unknown) => void;
429
465
  onDone?: () => void;
430
466
  /** Catch-all for any event (incl. unknown ones). */
@@ -435,9 +471,9 @@ export interface StreamHandlers {
435
471
  // Tenant client (data ops) — use a TENANT key
436
472
  // ---------------------------------------------------------------------------
437
473
  export class KnowledgeCoreClient extends HttpBase {
438
- /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY when not passed. */
439
- constructor(opts: ClientOptions = {}) {
440
- super(opts, "BABAV_KC_TENANT_KEY");
474
+ /** @param opts.apiKey a TENANT key, supplied by the caller. */
475
+ constructor(opts: ClientOptions) {
476
+ super(opts);
441
477
  }
442
478
 
443
479
  // --- query (agent-anchored) ---
@@ -627,9 +663,9 @@ export class KnowledgeCoreClient extends HttpBase {
627
663
  // Admin client (tenant + key + agent management) — use the ADMIN key
628
664
  // ---------------------------------------------------------------------------
629
665
  export class AdminClient extends HttpBase {
630
- /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY when not passed. */
631
- constructor(opts: ClientOptions = {}) {
632
- super(opts, "BABAV_KC_ADMIN_KEY");
666
+ /** @param opts.apiKey the ADMIN key, supplied by the caller. */
667
+ constructor(opts: ClientOptions) {
668
+ super(opts);
633
669
  }
634
670
 
635
671
  tenants = {
@@ -673,6 +709,8 @@ function dispatchSse(frame: string, h: StreamHandlers): void {
673
709
  case "citation": h.onCitation?.(data as Citation); break;
674
710
  case "groundedness": h.onGroundedness?.(data as never); break;
675
711
  case "final": h.onFinal?.(data as QueryResponse); break;
712
+ case "visual": h.onVisual?.(data as Visual); break;
713
+ case "visuals_complete": h.onVisualsComplete?.(data as VisualsMeta); break;
676
714
  case "error": h.onError?.(data); break;
677
715
  case "done": h.onDone?.(); break;
678
716
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `@babav/knowledge-core-client/visual` — the babav.visual grammar TYPES.
3
+ *
4
+ * Types only (zero runtime, zero deps): they let a client type the visual payloads it may
5
+ * receive. All visual PROCESSING — grammar validation, layout, and SVG/PNG rendering — runs
6
+ * SERVER-SIDE in render-service; the client only displays the server-produced SVG/image.
7
+ * The schema + validators + renderers therefore live in render-service, not here.
8
+ */
9
+
10
+ export * from "./types.js";
@@ -0,0 +1,132 @@
1
+ /**
2
+ * `babav.visual` grammar — TypeScript types (WP1a).
3
+ *
4
+ * The typed structural-composition language the visual pipeline emits for the
5
+ * `structural` register (and the envelope that wraps every register's payload). Uses
6
+ * vocabulary models already know — nodes, connectors (edges), containers/panels
7
+ * (clusters), composition operators. LLMs compose in this grammar; a deterministic
8
+ * engine (ELK) does layout — models never emit coordinates.
9
+ *
10
+ * NB: the grammar uses its own camelCase convention (`chartBlock`, `vegaLiteSpec`,
11
+ * `focalElement`) per the visual spec — distinct from the KC API's snake_case wire
12
+ * shapes. Versioned from the first commit (`version: "1"`).
13
+ */
14
+
15
+ export type VisualRegister = "chart" | "structural" | "conceptual" | "scenic";
16
+
17
+ export type EmphasisLevel = "normal" | "strong" | "muted";
18
+ export type ConnectorKind =
19
+ | "flow"
20
+ | "dependency"
21
+ | "association"
22
+ | "containment"
23
+ | "bidirectional";
24
+
25
+ // --- Layer A: primitives (each drawable element; `type`-discriminated) --------
26
+ export interface NodeElement {
27
+ type: "node";
28
+ id: string;
29
+ label: string;
30
+ role?: string;
31
+ emphasis?: EmphasisLevel;
32
+ }
33
+ export interface ConnectorElement {
34
+ type: "connector";
35
+ id?: string;
36
+ from: string; // node id
37
+ to: string; // node id
38
+ label?: string;
39
+ kind: ConnectorKind;
40
+ }
41
+ export interface ContainerElement {
42
+ type: "container";
43
+ id: string;
44
+ label?: string;
45
+ children: string[]; // element ids
46
+ }
47
+ export interface PanelElement {
48
+ type: "panel";
49
+ id: string;
50
+ title?: string;
51
+ children: string[]; // element ids
52
+ }
53
+ export interface AnnotationElement {
54
+ type: "annotation";
55
+ id?: string;
56
+ target: string; // element id
57
+ text: string;
58
+ }
59
+ export interface ChartBlockElement {
60
+ type: "chartBlock";
61
+ id: string;
62
+ vegaLiteSpec: Record<string, unknown>; // whitelisted subset enforced at render time
63
+ }
64
+ export type VisualElement =
65
+ | NodeElement
66
+ | ConnectorElement
67
+ | ContainerElement
68
+ | PanelElement
69
+ | AnnotationElement
70
+ | ChartBlockElement;
71
+
72
+ // --- Layer B: composition (arrangement over element ids; `op`-discriminated) --
73
+ export interface NestComposition {
74
+ op: "nest";
75
+ parent: string;
76
+ children: string[];
77
+ }
78
+ export interface SequenceComposition {
79
+ op: "sequence";
80
+ items: string[];
81
+ }
82
+ export interface JuxtaposeComposition {
83
+ op: "juxtapose";
84
+ items: string[];
85
+ }
86
+ export interface LayerComposition {
87
+ op: "layer";
88
+ items: string[];
89
+ }
90
+ export interface LinkComposition {
91
+ op: "link";
92
+ from: string;
93
+ to: string;
94
+ label?: string;
95
+ kind?: ConnectorKind;
96
+ }
97
+ export type VisualComposition =
98
+ | NestComposition
99
+ | SequenceComposition
100
+ | JuxtaposeComposition
101
+ | LayerComposition
102
+ | LinkComposition;
103
+
104
+ // --- Layout intent: hints only; the engine decides -------------------------
105
+ export type Orientation = "horizontal" | "vertical" | "radial";
106
+ export type Density = "compact" | "comfortable" | "spacious";
107
+ export interface LayoutIntent {
108
+ orientation?: Orientation;
109
+ density?: Density;
110
+ focalElement?: string; // element id
111
+ }
112
+
113
+ export interface VisualPayload {
114
+ elements: VisualElement[];
115
+ composition?: VisualComposition[];
116
+ layout?: LayoutIntent;
117
+ }
118
+
119
+ /** Per-element source provenance. Present from v1; populated in Phase 2. */
120
+ export interface VisualProvenance {
121
+ [k: string]: unknown;
122
+ }
123
+
124
+ /** The envelope wrapping every register's payload. */
125
+ export interface VisualEnvelope {
126
+ schema: "babav.visual";
127
+ version: "1";
128
+ register: VisualRegister;
129
+ gist?: string;
130
+ provenance?: VisualProvenance[];
131
+ payload: VisualPayload;
132
+ }