@diegoaltoworks/chatter 0.21.0 → 0.23.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.
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Brain hook — the seam where a consumer replaces the completion call.
3
+ *
4
+ * Every chat surface (widget routes, OpenAI-compatible routes, channels,
5
+ * programmatic use) asks for an answer through `answerOnce`/`answerStream`
6
+ * instead of calling `completeOnce`/`completeStream` directly. When
7
+ * `ChatterConfig.answerFn` is set it produces the answer — an agent
8
+ * framework, a graph runtime, a remote service, anything — and Chatter keeps
9
+ * owning the rest: retrieval and prompt assembly upstream, auth, rate
10
+ * limiting, transports and output guardrails around it.
11
+ *
12
+ * With no `answerFn` configured these are thin pass-throughs, so behaviour is
13
+ * exactly what it has always been.
14
+ */
15
+ import type OpenAI from "openai";
16
+ import type { PipelineMessage, PipelineMode } from "./pipeline";
17
+ /** Token accounting in the OpenAI wire shape */
18
+ export interface AnswerUsage {
19
+ prompt_tokens: number;
20
+ completion_tokens: number;
21
+ total_tokens: number;
22
+ }
23
+ /** What a brain is given: the assembled prompt plus who is asking */
24
+ export interface AnswerFnInput {
25
+ /** Fully assembled system prompt from `prepareChat` */
26
+ system: string;
27
+ /** Conversation messages */
28
+ messages: PipelineMessage[];
29
+ /** Which pipeline the answer is for */
30
+ mode: PipelineMode;
31
+ /** Channel-specific sender identity, when the surface knows one */
32
+ sender?: string;
33
+ }
34
+ /**
35
+ * What a brain returns: the answer text, optionally with token usage for
36
+ * surfaces that report it.
37
+ */
38
+ export type AnswerFnResult = string | {
39
+ content: string;
40
+ usage?: AnswerUsage;
41
+ };
42
+ /**
43
+ * Replaces the completion call on every chat surface. Rejections propagate to
44
+ * the caller like any other completion failure.
45
+ */
46
+ export type AnswerFn = (input: AnswerFnInput) => Promise<AnswerFnResult> | AnswerFnResult;
47
+ export interface AnswerOptions {
48
+ /** Caller-supplied brain; falls back to the built-in completion when unset */
49
+ answerFn?: AnswerFn;
50
+ /** Used by the built-in completion; untouched when `answerFn` answers */
51
+ client: OpenAI;
52
+ system: string;
53
+ messages: PipelineMessage[];
54
+ mode: PipelineMode;
55
+ sender?: string;
56
+ temperature?: number;
57
+ model?: string;
58
+ }
59
+ /**
60
+ * Produce one complete answer, from `answerFn` when configured.
61
+ */
62
+ export declare function answerOnce({ answerFn, client, system, messages, mode, sender, temperature, model, }: AnswerOptions): Promise<{
63
+ content: string;
64
+ usage: AnswerUsage;
65
+ }>;
66
+ /**
67
+ * Stream an answer. A brain that returns a plain string cannot stream, so its
68
+ * answer degrades to a single chunk followed by the stream's normal end —
69
+ * streaming surfaces keep their wire format either way.
70
+ *
71
+ * Because a brain's answer arrives whole, it gets the full guardrails; the
72
+ * built-in stream only scrubs each delta, since leakage detection needs text
73
+ * no streamed chunk has yet.
74
+ */
75
+ export declare function answerStream({ answerFn, client, system, messages, mode, sender, temperature, model, }: AnswerOptions): AsyncGenerator<string>;
76
+ //# sourceMappingURL=answer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../src/core/answer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhE,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAKD,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,uCAAuC;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC;AAE/E;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAAC;AAE1F,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqBD;;GAEG;AACH,wBAAsB,UAAU,CAAC,EAC/B,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,WAAW,EACX,KAAK,GACN,EAAE,aAAa,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC,CAKlE;AAED;;;;;;;;GAQG;AACH,wBAAuB,YAAY,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,WAAW,EACX,KAAK,GACN,EAAE,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,CAOxC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Brain hook tests
3
+ *
4
+ * Uses a faked OpenAI client so both the built-in completion path and the
5
+ * `answerFn` path can be asserted without touching OpenAI.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=answer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"answer.test.d.ts","sourceRoot":"","sources":["../../src/core/answer.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -1,3 +1,11 @@
1
1
  export declare function detectLeakage(text: string): boolean;
2
2
  export declare function scrubOutput(text: string): string;
3
+ /**
4
+ * Apply the full output guardrails to a complete answer: refuse leaked
5
+ * instructions outright, then scrub credentials from whatever remains.
6
+ *
7
+ * Used for every non-streamed answer, whoever produced it — the built-in
8
+ * completion or a caller-supplied brain (`answerFn`).
9
+ */
10
+ export declare function guardOutput(text: string): string;
3
11
  //# sourceMappingURL=guardrails.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/core/guardrails.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CASnD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIhD"}
1
+ {"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/core/guardrails.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CASnD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../../src/core/llm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,eAAO,MAAM,aAAa,WAAW,CAAC;AAEtC,wBAAsB,YAAY,CAAC,EACjC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,WAAiB,EACjB,KAAqB,GACtB,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;GAaA;AAGD,wBAAuB,cAAc,CAAC,EACpC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,WAAiB,EACjB,KAAqB,GACtB,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,yCAYA"}
1
+ {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../../src/core/llm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,eAAO,MAAM,aAAa,WAAW,CAAC;AAEtC,wBAAsB,YAAY,CAAC,EACjC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,WAAiB,EACjB,KAAqB,GACtB,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;GAWA;AAGD,wBAAuB,cAAc,CAAC,EACpC,MAAM,EACN,MAAM,EACN,QAAQ,EACR,WAAiB,EACjB,KAAqB,GACtB,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,yCAYA"}
@@ -4,7 +4,9 @@
4
4
  * This is the single place where a conversation is turned into a
5
5
  * retrieval-augmented completion request. Every surface (widget routes,
6
6
  * OpenAI-compatible routes, MCP, programmatic use) should go through here
7
- * so behaviour stays identical regardless of how the chat is consumed.
7
+ * so behaviour stays identical regardless of how the chat is consumed, and
8
+ * then ask for the answer via `answerOnce`/`answerStream` (see ./answer.ts)
9
+ * so a caller-supplied brain is honoured.
8
10
  */
9
11
  import type { PromptLoader } from "./prompts";
10
12
  import type { VectorStore } from "./retrieval";
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAOD;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,EAChC,KAAK,EACL,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,YAAY,CAAC,CAmBxB"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAOD;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,EAChC,KAAK,EACL,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,YAAY,CAAC,CAmBxB"}
@@ -1,14 +1,39 @@
1
+ import { type Client as LibsqlClient } from "@libsql/client";
1
2
  import type OpenAI from "openai";
2
3
  import { type Bucket } from "./loaders";
4
+ /**
5
+ * How a {@link VectorStore} obtains its database connection: either credentials
6
+ * to open its own, or an existing libsql client to reuse.
7
+ *
8
+ * Reusing a client is preferred when the store lives alongside other consumers
9
+ * of the same database (route factories, custom routes) — the process then
10
+ * holds one connection instead of one per consumer.
11
+ */
12
+ export type VectorStoreOptions = {
13
+ /** Directory of markdown knowledge files. Default: `./config/knowledge` */
14
+ knowledgeDir?: string;
15
+ } & ({
16
+ /** Turso database URL */
17
+ databaseUrl: string;
18
+ /** Turso auth token */
19
+ databaseAuthToken: string;
20
+ databaseClient?: never;
21
+ } | {
22
+ /** An existing libsql client to reuse instead of opening a second connection */
23
+ databaseClient: LibsqlClient;
24
+ databaseUrl?: never;
25
+ databaseAuthToken?: never;
26
+ });
3
27
  export declare class VectorStore {
4
28
  private client;
5
- private db;
29
+ /**
30
+ * The libsql client backing this store. When a client was injected this is
31
+ * that same instance, so callers holding it (e.g. `ServerDependencies.db`)
32
+ * and the store share one connection.
33
+ */
34
+ readonly db: LibsqlClient;
6
35
  private knowledgeDir;
7
- constructor(client: OpenAI, options: {
8
- databaseUrl: string;
9
- databaseAuthToken: string;
10
- knowledgeDir?: string;
11
- });
36
+ constructor(client: OpenAI, options: VectorStoreOptions);
12
37
  build(): Promise<void>;
13
38
  private cleanupStaleChunks;
14
39
  private static cosine;
@@ -1 +1 @@
1
- {"version":3,"file":"retrieval.d.ts","sourceRoot":"","sources":["../../src/core/retrieval.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,WAAW,CAAC;AAyBvD,qBAAa,WAAW;IAKpB,OAAO,CAAC,MAAM;IAJhB,OAAO,CAAC,EAAE,CAAe;IACzB,OAAO,CAAC,YAAY,CAAS;IAE7B,YACU,MAAM,EAAE,MAAM,EACtB,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,EAOF;IAGK,KAAK,kBA0FV;YAGa,kBAAkB;IA8BhC,OAAO,CAAC,MAAM,CAAC,MAAM;IAaf,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,SAAI,EAAE,OAAO,GAAE,MAAM,EAAa,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAsB7E;CACF"}
1
+ {"version":3,"file":"retrieval.d.ts","sourceRoot":"","sources":["../../src/core/retrieval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,WAAW,CAAC;AAyBvD;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,CACA;IACE,yBAAyB;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,KAAK,CAAC;CACxB,GACD;IACE,gFAAgF;IAChF,cAAc,EAAE,YAAY,CAAC;IAC7B,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,iBAAiB,CAAC,EAAE,KAAK,CAAC;CAC3B,CACJ,CAAC;AAEF,qBAAa,WAAW;IAUpB,OAAO,CAAC,MAAM;IAThB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAC1B,OAAO,CAAC,YAAY,CAAS;IAE7B,YACU,MAAM,EAAE,MAAM,EACtB,OAAO,EAAE,kBAAkB,EAS5B;IAGK,KAAK,kBA0FV;YAGa,kBAAkB;IA8BhC,OAAO,CAAC,MAAM,CAAC,MAAM;IAaf,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,SAAI,EAAE,OAAO,GAAE,MAAM,EAAa,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAsB7E;CACF"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Vector store connection tests
3
+ *
4
+ * Uses a faked libsql client and OpenAI client so the connection wiring can be
5
+ * asserted without touching Turso or OpenAI.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=retrieval.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retrieval.test.d.ts","sourceRoot":"","sources":["../../src/core/retrieval.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
package/dist/index.d.ts CHANGED
@@ -14,12 +14,15 @@ export type { ApiKeyOptions, ApiKeyPayload, VerifyResult } from "./auth";
14
14
  export { ApiKeyManager } from "./auth";
15
15
  export type { ChatBotConfig, ChatButtonConfig, ChatConfig, ChatMessage, ChatMode, StreamCallbacks, } from "./client";
16
16
  export { Chat, ChatBot, ChatButton } from "./client";
17
- export { detectLeakage, scrubOutput } from "./core/guardrails";
17
+ export type { AnswerFn, AnswerFnInput, AnswerFnResult, AnswerOptions, AnswerUsage, } from "./core/answer";
18
+ export { answerOnce, answerStream } from "./core/answer";
19
+ export { detectLeakage, guardOutput, scrubOutput } from "./core/guardrails";
18
20
  export { completeOnce, completeStream, DEFAULT_MODEL } from "./core/llm";
19
21
  export { loadKnowledge } from "./core/loaders";
20
22
  export type { PipelineMessage, PipelineMode, PreparedChat } from "./core/pipeline";
21
23
  export { prepareChat } from "./core/pipeline";
22
24
  export { PromptLoader } from "./core/prompts";
25
+ export type { VectorStoreOptions } from "./core/retrieval";
23
26
  export { VectorStore } from "./core/retrieval";
24
27
  export { createSession, getActiveSessions, getSessionInfo, validateSession, } from "./core/session";
25
28
  export type { MCPServerOptions, MCPTransportMode } from "./mcp-server";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,QAAQ,EACR,eAAe,GAChB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,YAAY,EACV,WAAW,EACX,aAAa,EACb,WAAW,EACX,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,QAAQ,EACR,eAAe,GAChB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACrD,YAAY,EACV,QAAQ,EACR,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,YAAY,EACV,WAAW,EACX,aAAa,EACb,WAAW,EACX,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -7531,12 +7531,14 @@ var require_dist2 = __commonJS({
7531
7531
  var index_exports = {};
7532
7532
  __export(index_exports, {
7533
7533
  ApiKeyManager: () => ApiKeyManager,
7534
- Chat: () => import_client2.Chat,
7535
- ChatBot: () => import_client2.ChatBot,
7536
- ChatButton: () => import_client2.ChatButton,
7534
+ Chat: () => import_client3.Chat,
7535
+ ChatBot: () => import_client3.ChatBot,
7536
+ ChatButton: () => import_client3.ChatButton,
7537
7537
  DEFAULT_MODEL: () => DEFAULT_MODEL,
7538
7538
  PromptLoader: () => PromptLoader,
7539
7539
  VectorStore: () => VectorStore,
7540
+ answerOnce: () => answerOnce,
7541
+ answerStream: () => answerStream,
7540
7542
  completeOnce: () => completeOnce,
7541
7543
  completeStream: () => completeStream,
7542
7544
  cors: () => cors,
@@ -7552,6 +7554,7 @@ __export(index_exports, {
7552
7554
  detectLeakage: () => detectLeakage,
7553
7555
  getActiveSessions: () => getActiveSessions,
7554
7556
  getSessionInfo: () => getSessionInfo,
7557
+ guardOutput: () => guardOutput,
7555
7558
  loadKnowledge: () => loadKnowledge,
7556
7559
  openaiRoutes: () => openaiRoutes,
7557
7560
  prepareChat: () => prepareChat,
@@ -7674,7 +7677,7 @@ var ApiKeyManager = class {
7674
7677
  };
7675
7678
 
7676
7679
  // src/index.ts
7677
- var import_client2 = __toESM(require_dist());
7680
+ var import_client3 = __toESM(require_dist());
7678
7681
 
7679
7682
  // src/core/guardrails.ts
7680
7683
  function detectLeakage(text) {
@@ -7690,6 +7693,12 @@ function detectLeakage(text) {
7690
7693
  function scrubOutput(text) {
7691
7694
  return text.replace(/sk-[A-Za-z0-9]{15,}/g, "[REDACTED]").replace(/AIza[0-9A-Za-z_-]{30,}/g, "[REDACTED]");
7692
7695
  }
7696
+ function guardOutput(text) {
7697
+ if (detectLeakage(text)) {
7698
+ return "Sorry, I can't share internal instructions. How else can I help?";
7699
+ }
7700
+ return scrubOutput(text);
7701
+ }
7693
7702
 
7694
7703
  // src/core/llm.ts
7695
7704
  var DEFAULT_MODEL = "gpt-4o";
@@ -7705,11 +7714,9 @@ async function completeOnce({
7705
7714
  temperature,
7706
7715
  messages: [{ role: "system", content: system }, ...messages]
7707
7716
  });
7708
- let text = res.choices[0]?.message?.content ?? "";
7709
- if (detectLeakage(text))
7710
- text = "Sorry, I can't share internal instructions. How else can I help?";
7717
+ const text = res.choices[0]?.message?.content ?? "";
7711
7718
  return {
7712
- content: scrubOutput(text),
7719
+ content: guardOutput(text),
7713
7720
  usage: res.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
7714
7721
  };
7715
7722
  }
@@ -7732,6 +7739,50 @@ async function* completeStream({
7732
7739
  }
7733
7740
  }
7734
7741
 
7742
+ // src/core/answer.ts
7743
+ var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
7744
+ async function runAnswerFn(answerFn, { system, messages, mode, sender }) {
7745
+ const result = await answerFn({ system, messages, mode, sender });
7746
+ const raw = typeof result === "string" ? result : result?.content;
7747
+ const usage = typeof result === "string" ? void 0 : result?.usage;
7748
+ return {
7749
+ content: guardOutput(typeof raw === "string" ? raw : ""),
7750
+ usage: usage ?? noUsage()
7751
+ };
7752
+ }
7753
+ async function answerOnce({
7754
+ answerFn,
7755
+ client,
7756
+ system,
7757
+ messages,
7758
+ mode,
7759
+ sender,
7760
+ temperature,
7761
+ model
7762
+ }) {
7763
+ if (answerFn) {
7764
+ return runAnswerFn(answerFn, { system, messages, mode, sender });
7765
+ }
7766
+ return completeOnce({ client, system, messages, temperature, model });
7767
+ }
7768
+ async function* answerStream({
7769
+ answerFn,
7770
+ client,
7771
+ system,
7772
+ messages,
7773
+ mode,
7774
+ sender,
7775
+ temperature,
7776
+ model
7777
+ }) {
7778
+ if (answerFn) {
7779
+ const { content } = await runAnswerFn(answerFn, { system, messages, mode, sender });
7780
+ if (content) yield content;
7781
+ return;
7782
+ }
7783
+ yield* completeStream({ client, system, messages, temperature, model });
7784
+ }
7785
+
7735
7786
  // src/core/loaders.ts
7736
7787
  var import_node_fs = require("node:fs");
7737
7788
  var import_node_path = require("node:path");
@@ -7848,13 +7899,18 @@ async function sha256(input) {
7848
7899
  var VectorStore = class _VectorStore {
7849
7900
  constructor(client, options) {
7850
7901
  this.client = client;
7851
- this.db = (0, import_client.createClient)({
7902
+ this.db = options.databaseClient ? options.databaseClient : (0, import_client.createClient)({
7852
7903
  url: options.databaseUrl,
7853
7904
  authToken: options.databaseAuthToken
7854
7905
  });
7855
7906
  this.knowledgeDir = options.knowledgeDir || "./config/knowledge";
7856
7907
  }
7857
7908
  client;
7909
+ /**
7910
+ * The libsql client backing this store. When a client was injected this is
7911
+ * that same instance, so callers holding it (e.g. `ServerDependencies.db`)
7912
+ * and the store share one connection.
7913
+ */
7858
7914
  db;
7859
7915
  knowledgeDir;
7860
7916
  // On boot: ingest new chunks and embed only missing ones.
@@ -32248,10 +32304,12 @@ async function createMCPServer(config2) {
32248
32304
  `Context:
32249
32305
  ${ctx.join("\n\n")}`
32250
32306
  ].join("\n\n");
32251
- const result = await completeOnce({
32307
+ const result = await answerOnce({
32308
+ answerFn: config2.answerFn,
32252
32309
  client,
32253
32310
  system,
32254
- messages: conversationMessages
32311
+ messages: conversationMessages,
32312
+ mode: "public"
32255
32313
  });
32256
32314
  const cost = calculateCost(result.usage);
32257
32315
  const duration3 = Date.now() - startTime;
@@ -32332,10 +32390,12 @@ ${ctx.join("\n\n")}`
32332
32390
  `Internal Context:
32333
32391
  ${ctx.join("\n\n")}`
32334
32392
  ].join("\n\n");
32335
- const result = await completeOnce({
32393
+ const result = await answerOnce({
32394
+ answerFn: config2.answerFn,
32336
32395
  client,
32337
32396
  system,
32338
- messages: conversationMessages
32397
+ messages: conversationMessages,
32398
+ mode: "private"
32339
32399
  });
32340
32400
  const cost = calculateCost(result.usage);
32341
32401
  const duration3 = Date.now() - startTime;
@@ -32753,7 +32813,13 @@ ${ctx.join("\n\n")}`
32753
32813
  const wantsStream3 = url2.searchParams.get("stream") === "1";
32754
32814
  if (wantsStream3) {
32755
32815
  return (0, import_streaming.stream)(c, async (s) => {
32756
- for await (const delta of completeStream({ client, system, messages })) {
32816
+ for await (const delta of answerStream({
32817
+ answerFn: config2.answerFn,
32818
+ client,
32819
+ system,
32820
+ messages,
32821
+ mode: "public"
32822
+ })) {
32757
32823
  await s.write(`data: ${JSON.stringify({ delta })}
32758
32824
 
32759
32825
  `);
@@ -32761,7 +32827,13 @@ ${ctx.join("\n\n")}`
32761
32827
  await s.write("event: end\ndata: {}\n\n");
32762
32828
  });
32763
32829
  }
32764
- const out = await completeOnce({ client, system, messages });
32830
+ const out = await answerOnce({
32831
+ answerFn: config2.answerFn,
32832
+ client,
32833
+ system,
32834
+ messages,
32835
+ mode: "public"
32836
+ });
32765
32837
  return c.json({ reply: out.content });
32766
32838
  } catch (error51) {
32767
32839
  const err = error51;
@@ -32858,10 +32930,12 @@ function openaiRoutes(deps) {
32858
32930
 
32859
32931
  `;
32860
32932
  await s.write(chunk2({ role: "assistant", content: "" }, null));
32861
- for await (const delta of completeStream({
32933
+ for await (const delta of answerStream({
32934
+ answerFn: config2.answerFn,
32862
32935
  client,
32863
32936
  system,
32864
32937
  messages,
32938
+ mode,
32865
32939
  temperature,
32866
32940
  model: serverModel
32867
32941
  })) {
@@ -32871,7 +32945,15 @@ function openaiRoutes(deps) {
32871
32945
  await s.write("data: [DONE]\n\n");
32872
32946
  });
32873
32947
  }
32874
- const out = await completeOnce({ client, system, messages, temperature, model: serverModel });
32948
+ const out = await answerOnce({
32949
+ answerFn: config2.answerFn,
32950
+ client,
32951
+ system,
32952
+ messages,
32953
+ mode,
32954
+ temperature,
32955
+ model: serverModel
32956
+ });
32875
32957
  return c.json({
32876
32958
  id,
32877
32959
  object: "chat.completion",
@@ -32932,9 +33014,17 @@ function privateRoutes(deps) {
32932
33014
  return c.json({ error: "no user message found in conversation" }, 400);
32933
33015
  }
32934
33016
  const model = config2.openai.model;
33017
+ const answerFn = config2.answerFn;
32935
33018
  if (wantsStream(c)) {
32936
33019
  return (0, import_streaming3.stream)(c, async (s) => {
32937
- for await (const delta of completeStream({ client, system, messages, model })) {
33020
+ for await (const delta of answerStream({
33021
+ answerFn,
33022
+ client,
33023
+ system,
33024
+ messages,
33025
+ mode: "private",
33026
+ model
33027
+ })) {
32938
33028
  await s.write(`data: ${JSON.stringify({ delta })}
32939
33029
 
32940
33030
  `);
@@ -32942,7 +33032,7 @@ function privateRoutes(deps) {
32942
33032
  await s.write("event: end\ndata: {}\n\n");
32943
33033
  });
32944
33034
  }
32945
- const out = await completeOnce({ client, system, messages, model });
33035
+ const out = await answerOnce({ answerFn, client, system, messages, mode: "private", model });
32946
33036
  return c.json({ reply: out.content });
32947
33037
  });
32948
33038
  return app;
@@ -32987,9 +33077,17 @@ function publicRoutes(deps) {
32987
33077
  return c.json({ error: "no user message found in conversation" }, 400);
32988
33078
  }
32989
33079
  const model = config2.openai.model;
33080
+ const answerFn = config2.answerFn;
32990
33081
  if (wantsStream2(c)) {
32991
33082
  return (0, import_streaming4.stream)(c, async (s) => {
32992
- for await (const delta of completeStream({ client, system, messages, model })) {
33083
+ for await (const delta of answerStream({
33084
+ answerFn,
33085
+ client,
33086
+ system,
33087
+ messages,
33088
+ mode: "public",
33089
+ model
33090
+ })) {
32993
33091
  await s.write(`data: ${JSON.stringify({ delta })}
32994
33092
 
32995
33093
  `);
@@ -32997,13 +33095,14 @@ function publicRoutes(deps) {
32997
33095
  await s.write("event: end\ndata: {}\n\n");
32998
33096
  });
32999
33097
  }
33000
- const out = await completeOnce({ client, system, messages, model });
33098
+ const out = await answerOnce({ answerFn, client, system, messages, mode: "public", model });
33001
33099
  return c.json({ reply: out.content });
33002
33100
  });
33003
33101
  return app;
33004
33102
  }
33005
33103
 
33006
33104
  // src/server.ts
33105
+ var import_client2 = require("@libsql/client");
33007
33106
  var import_hono5 = require("hono");
33008
33107
  var import_bun = require("hono/bun");
33009
33108
  var import_openai2 = __toESM(require("openai"));
@@ -33050,9 +33149,12 @@ async function createServer(config2) {
33050
33149
  const client = new import_openai2.default({
33051
33150
  apiKey: config2.openai.apiKey
33052
33151
  });
33152
+ const db = (0, import_client2.createClient)({
33153
+ url: config2.database.url,
33154
+ authToken: config2.database.authToken
33155
+ });
33053
33156
  const store = new VectorStore(client, {
33054
- databaseUrl: config2.database.url,
33055
- databaseAuthToken: config2.database.authToken,
33157
+ databaseClient: db,
33056
33158
  knowledgeDir
33057
33159
  });
33058
33160
  await store.build();
@@ -33130,7 +33232,7 @@ async function createServer(config2) {
33130
33232
  app.get("/demo/chatbot", (0, import_bun.serveStatic)({ path: `${publicDir}/demo-chatbot.html` }));
33131
33233
  app.get("/demo/react", (0, import_bun.serveStatic)({ path: `${publicDir}/react-demo.html` }));
33132
33234
  }
33133
- const deps = { client, store, config: config2, prompts, apiKeyManager };
33235
+ const deps = { client, store, db, config: config2, prompts, apiKeyManager };
33134
33236
  if (enablePublic) {
33135
33237
  app.route("/", publicRoutes(deps));
33136
33238
  }
@@ -33158,6 +33260,8 @@ async function createServer(config2) {
33158
33260
  DEFAULT_MODEL,
33159
33261
  PromptLoader,
33160
33262
  VectorStore,
33263
+ answerOnce,
33264
+ answerStream,
33161
33265
  completeOnce,
33162
33266
  completeStream,
33163
33267
  cors,
@@ -33173,6 +33277,7 @@ async function createServer(config2) {
33173
33277
  detectLeakage,
33174
33278
  getActiveSessions,
33175
33279
  getSessionInfo,
33280
+ guardOutput,
33176
33281
  loadKnowledge,
33177
33282
  openaiRoutes,
33178
33283
  prepareChat,
package/dist/index.mjs CHANGED
@@ -7643,7 +7643,7 @@ var ApiKeyManager = class {
7643
7643
  };
7644
7644
 
7645
7645
  // src/index.ts
7646
- var import_client2 = __toESM(require_dist());
7646
+ var import_client3 = __toESM(require_dist());
7647
7647
 
7648
7648
  // src/core/guardrails.ts
7649
7649
  function detectLeakage(text) {
@@ -7659,6 +7659,12 @@ function detectLeakage(text) {
7659
7659
  function scrubOutput(text) {
7660
7660
  return text.replace(/sk-[A-Za-z0-9]{15,}/g, "[REDACTED]").replace(/AIza[0-9A-Za-z_-]{30,}/g, "[REDACTED]");
7661
7661
  }
7662
+ function guardOutput(text) {
7663
+ if (detectLeakage(text)) {
7664
+ return "Sorry, I can't share internal instructions. How else can I help?";
7665
+ }
7666
+ return scrubOutput(text);
7667
+ }
7662
7668
 
7663
7669
  // src/core/llm.ts
7664
7670
  var DEFAULT_MODEL = "gpt-4o";
@@ -7674,11 +7680,9 @@ async function completeOnce({
7674
7680
  temperature,
7675
7681
  messages: [{ role: "system", content: system }, ...messages]
7676
7682
  });
7677
- let text = res.choices[0]?.message?.content ?? "";
7678
- if (detectLeakage(text))
7679
- text = "Sorry, I can't share internal instructions. How else can I help?";
7683
+ const text = res.choices[0]?.message?.content ?? "";
7680
7684
  return {
7681
- content: scrubOutput(text),
7685
+ content: guardOutput(text),
7682
7686
  usage: res.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
7683
7687
  };
7684
7688
  }
@@ -7701,6 +7705,50 @@ async function* completeStream({
7701
7705
  }
7702
7706
  }
7703
7707
 
7708
+ // src/core/answer.ts
7709
+ var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
7710
+ async function runAnswerFn(answerFn, { system, messages, mode, sender }) {
7711
+ const result = await answerFn({ system, messages, mode, sender });
7712
+ const raw = typeof result === "string" ? result : result?.content;
7713
+ const usage = typeof result === "string" ? void 0 : result?.usage;
7714
+ return {
7715
+ content: guardOutput(typeof raw === "string" ? raw : ""),
7716
+ usage: usage ?? noUsage()
7717
+ };
7718
+ }
7719
+ async function answerOnce({
7720
+ answerFn,
7721
+ client,
7722
+ system,
7723
+ messages,
7724
+ mode,
7725
+ sender,
7726
+ temperature,
7727
+ model
7728
+ }) {
7729
+ if (answerFn) {
7730
+ return runAnswerFn(answerFn, { system, messages, mode, sender });
7731
+ }
7732
+ return completeOnce({ client, system, messages, temperature, model });
7733
+ }
7734
+ async function* answerStream({
7735
+ answerFn,
7736
+ client,
7737
+ system,
7738
+ messages,
7739
+ mode,
7740
+ sender,
7741
+ temperature,
7742
+ model
7743
+ }) {
7744
+ if (answerFn) {
7745
+ const { content } = await runAnswerFn(answerFn, { system, messages, mode, sender });
7746
+ if (content) yield content;
7747
+ return;
7748
+ }
7749
+ yield* completeStream({ client, system, messages, temperature, model });
7750
+ }
7751
+
7704
7752
  // src/core/loaders.ts
7705
7753
  import { readdirSync, readFileSync, statSync } from "node:fs";
7706
7754
  import { join } from "node:path";
@@ -7817,13 +7865,18 @@ async function sha256(input) {
7817
7865
  var VectorStore = class _VectorStore {
7818
7866
  constructor(client, options) {
7819
7867
  this.client = client;
7820
- this.db = createClient({
7868
+ this.db = options.databaseClient ? options.databaseClient : createClient({
7821
7869
  url: options.databaseUrl,
7822
7870
  authToken: options.databaseAuthToken
7823
7871
  });
7824
7872
  this.knowledgeDir = options.knowledgeDir || "./config/knowledge";
7825
7873
  }
7826
7874
  client;
7875
+ /**
7876
+ * The libsql client backing this store. When a client was injected this is
7877
+ * that same instance, so callers holding it (e.g. `ServerDependencies.db`)
7878
+ * and the store share one connection.
7879
+ */
7827
7880
  db;
7828
7881
  knowledgeDir;
7829
7882
  // On boot: ingest new chunks and embed only missing ones.
@@ -32217,10 +32270,12 @@ async function createMCPServer(config2) {
32217
32270
  `Context:
32218
32271
  ${ctx.join("\n\n")}`
32219
32272
  ].join("\n\n");
32220
- const result = await completeOnce({
32273
+ const result = await answerOnce({
32274
+ answerFn: config2.answerFn,
32221
32275
  client,
32222
32276
  system,
32223
- messages: conversationMessages
32277
+ messages: conversationMessages,
32278
+ mode: "public"
32224
32279
  });
32225
32280
  const cost = calculateCost(result.usage);
32226
32281
  const duration3 = Date.now() - startTime;
@@ -32301,10 +32356,12 @@ ${ctx.join("\n\n")}`
32301
32356
  `Internal Context:
32302
32357
  ${ctx.join("\n\n")}`
32303
32358
  ].join("\n\n");
32304
- const result = await completeOnce({
32359
+ const result = await answerOnce({
32360
+ answerFn: config2.answerFn,
32305
32361
  client,
32306
32362
  system,
32307
- messages: conversationMessages
32363
+ messages: conversationMessages,
32364
+ mode: "private"
32308
32365
  });
32309
32366
  const cost = calculateCost(result.usage);
32310
32367
  const duration3 = Date.now() - startTime;
@@ -32722,7 +32779,13 @@ ${ctx.join("\n\n")}`
32722
32779
  const wantsStream3 = url2.searchParams.get("stream") === "1";
32723
32780
  if (wantsStream3) {
32724
32781
  return stream(c, async (s) => {
32725
- for await (const delta of completeStream({ client, system, messages })) {
32782
+ for await (const delta of answerStream({
32783
+ answerFn: config2.answerFn,
32784
+ client,
32785
+ system,
32786
+ messages,
32787
+ mode: "public"
32788
+ })) {
32726
32789
  await s.write(`data: ${JSON.stringify({ delta })}
32727
32790
 
32728
32791
  `);
@@ -32730,7 +32793,13 @@ ${ctx.join("\n\n")}`
32730
32793
  await s.write("event: end\ndata: {}\n\n");
32731
32794
  });
32732
32795
  }
32733
- const out = await completeOnce({ client, system, messages });
32796
+ const out = await answerOnce({
32797
+ answerFn: config2.answerFn,
32798
+ client,
32799
+ system,
32800
+ messages,
32801
+ mode: "public"
32802
+ });
32734
32803
  return c.json({ reply: out.content });
32735
32804
  } catch (error51) {
32736
32805
  const err = error51;
@@ -32827,10 +32896,12 @@ function openaiRoutes(deps) {
32827
32896
 
32828
32897
  `;
32829
32898
  await s.write(chunk2({ role: "assistant", content: "" }, null));
32830
- for await (const delta of completeStream({
32899
+ for await (const delta of answerStream({
32900
+ answerFn: config2.answerFn,
32831
32901
  client,
32832
32902
  system,
32833
32903
  messages,
32904
+ mode,
32834
32905
  temperature,
32835
32906
  model: serverModel
32836
32907
  })) {
@@ -32840,7 +32911,15 @@ function openaiRoutes(deps) {
32840
32911
  await s.write("data: [DONE]\n\n");
32841
32912
  });
32842
32913
  }
32843
- const out = await completeOnce({ client, system, messages, temperature, model: serverModel });
32914
+ const out = await answerOnce({
32915
+ answerFn: config2.answerFn,
32916
+ client,
32917
+ system,
32918
+ messages,
32919
+ mode,
32920
+ temperature,
32921
+ model: serverModel
32922
+ });
32844
32923
  return c.json({
32845
32924
  id,
32846
32925
  object: "chat.completion",
@@ -32901,9 +32980,17 @@ function privateRoutes(deps) {
32901
32980
  return c.json({ error: "no user message found in conversation" }, 400);
32902
32981
  }
32903
32982
  const model = config2.openai.model;
32983
+ const answerFn = config2.answerFn;
32904
32984
  if (wantsStream(c)) {
32905
32985
  return stream3(c, async (s) => {
32906
- for await (const delta of completeStream({ client, system, messages, model })) {
32986
+ for await (const delta of answerStream({
32987
+ answerFn,
32988
+ client,
32989
+ system,
32990
+ messages,
32991
+ mode: "private",
32992
+ model
32993
+ })) {
32907
32994
  await s.write(`data: ${JSON.stringify({ delta })}
32908
32995
 
32909
32996
  `);
@@ -32911,7 +32998,7 @@ function privateRoutes(deps) {
32911
32998
  await s.write("event: end\ndata: {}\n\n");
32912
32999
  });
32913
33000
  }
32914
- const out = await completeOnce({ client, system, messages, model });
33001
+ const out = await answerOnce({ answerFn, client, system, messages, mode: "private", model });
32915
33002
  return c.json({ reply: out.content });
32916
33003
  });
32917
33004
  return app;
@@ -32956,9 +33043,17 @@ function publicRoutes(deps) {
32956
33043
  return c.json({ error: "no user message found in conversation" }, 400);
32957
33044
  }
32958
33045
  const model = config2.openai.model;
33046
+ const answerFn = config2.answerFn;
32959
33047
  if (wantsStream2(c)) {
32960
33048
  return stream4(c, async (s) => {
32961
- for await (const delta of completeStream({ client, system, messages, model })) {
33049
+ for await (const delta of answerStream({
33050
+ answerFn,
33051
+ client,
33052
+ system,
33053
+ messages,
33054
+ mode: "public",
33055
+ model
33056
+ })) {
32962
33057
  await s.write(`data: ${JSON.stringify({ delta })}
32963
33058
 
32964
33059
  `);
@@ -32966,13 +33061,14 @@ function publicRoutes(deps) {
32966
33061
  await s.write("event: end\ndata: {}\n\n");
32967
33062
  });
32968
33063
  }
32969
- const out = await completeOnce({ client, system, messages, model });
33064
+ const out = await answerOnce({ answerFn, client, system, messages, mode: "public", model });
32970
33065
  return c.json({ reply: out.content });
32971
33066
  });
32972
33067
  return app;
32973
33068
  }
32974
33069
 
32975
33070
  // src/server.ts
33071
+ import { createClient as createClient2 } from "@libsql/client";
32976
33072
  import { Hono as Hono5 } from "hono";
32977
33073
  import { serveStatic } from "hono/bun";
32978
33074
  import OpenAI2 from "openai";
@@ -33018,9 +33114,12 @@ async function createServer(config2) {
33018
33114
  const client = new OpenAI2({
33019
33115
  apiKey: config2.openai.apiKey
33020
33116
  });
33117
+ const db = createClient2({
33118
+ url: config2.database.url,
33119
+ authToken: config2.database.authToken
33120
+ });
33021
33121
  const store = new VectorStore(client, {
33022
- databaseUrl: config2.database.url,
33023
- databaseAuthToken: config2.database.authToken,
33122
+ databaseClient: db,
33024
33123
  knowledgeDir
33025
33124
  });
33026
33125
  await store.build();
@@ -33098,7 +33197,7 @@ async function createServer(config2) {
33098
33197
  app.get("/demo/chatbot", serveStatic({ path: `${publicDir}/demo-chatbot.html` }));
33099
33198
  app.get("/demo/react", serveStatic({ path: `${publicDir}/react-demo.html` }));
33100
33199
  }
33101
- const deps = { client, store, config: config2, prompts, apiKeyManager };
33200
+ const deps = { client, store, db, config: config2, prompts, apiKeyManager };
33102
33201
  if (enablePublic) {
33103
33202
  app.route("/", publicRoutes(deps));
33104
33203
  }
@@ -33117,9 +33216,9 @@ async function createServer(config2) {
33117
33216
  console.log(`\u2705 ${config2.bot.name} server ready`);
33118
33217
  return app;
33119
33218
  }
33120
- var export_Chat = import_client2.Chat;
33121
- var export_ChatBot = import_client2.ChatBot;
33122
- var export_ChatButton = import_client2.ChatButton;
33219
+ var export_Chat = import_client3.Chat;
33220
+ var export_ChatBot = import_client3.ChatBot;
33221
+ var export_ChatButton = import_client3.ChatButton;
33123
33222
  export {
33124
33223
  ApiKeyManager,
33125
33224
  export_Chat as Chat,
@@ -33128,6 +33227,8 @@ export {
33128
33227
  DEFAULT_MODEL,
33129
33228
  PromptLoader,
33130
33229
  VectorStore,
33230
+ answerOnce,
33231
+ answerStream,
33131
33232
  completeOnce,
33132
33233
  completeStream,
33133
33234
  cors,
@@ -33143,6 +33244,7 @@ export {
33143
33244
  detectLeakage,
33144
33245
  getActiveSessions,
33145
33246
  getSessionInfo,
33247
+ guardOutput,
33146
33248
  loadKnowledge,
33147
33249
  openaiRoutes,
33148
33250
  prepareChat,
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYpE,YAAY,EACV,WAAW,EACX,QAAQ,EACR,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,gBAAgB,sBAuS7D"}
1
+ {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYpE,YAAY,EACV,WAAW,EACX,QAAQ,EACR,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,gBAAgB,sBA2S7D"}
@@ -51,6 +51,12 @@ function detectLeakage(text) {
51
51
  function scrubOutput(text) {
52
52
  return text.replace(/sk-[A-Za-z0-9]{15,}/g, "[REDACTED]").replace(/AIza[0-9A-Za-z_-]{30,}/g, "[REDACTED]");
53
53
  }
54
+ function guardOutput(text) {
55
+ if (detectLeakage(text)) {
56
+ return "Sorry, I can't share internal instructions. How else can I help?";
57
+ }
58
+ return scrubOutput(text);
59
+ }
54
60
 
55
61
  // src/core/llm.ts
56
62
  var DEFAULT_MODEL = "gpt-4o";
@@ -66,15 +72,40 @@ async function completeOnce({
66
72
  temperature,
67
73
  messages: [{ role: "system", content: system }, ...messages]
68
74
  });
69
- let text = res.choices[0]?.message?.content ?? "";
70
- if (detectLeakage(text))
71
- text = "Sorry, I can't share internal instructions. How else can I help?";
75
+ const text = res.choices[0]?.message?.content ?? "";
72
76
  return {
73
- content: scrubOutput(text),
77
+ content: guardOutput(text),
74
78
  usage: res.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
75
79
  };
76
80
  }
77
81
 
82
+ // src/core/answer.ts
83
+ var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
84
+ async function runAnswerFn(answerFn, { system, messages, mode, sender }) {
85
+ const result = await answerFn({ system, messages, mode, sender });
86
+ const raw = typeof result === "string" ? result : result?.content;
87
+ const usage = typeof result === "string" ? void 0 : result?.usage;
88
+ return {
89
+ content: guardOutput(typeof raw === "string" ? raw : ""),
90
+ usage: usage ?? noUsage()
91
+ };
92
+ }
93
+ async function answerOnce({
94
+ answerFn,
95
+ client,
96
+ system,
97
+ messages,
98
+ mode,
99
+ sender,
100
+ temperature,
101
+ model
102
+ }) {
103
+ if (answerFn) {
104
+ return runAnswerFn(answerFn, { system, messages, mode, sender });
105
+ }
106
+ return completeOnce({ client, system, messages, temperature, model });
107
+ }
108
+
78
109
  // src/core/prompts.ts
79
110
  var import_node_fs = require("node:fs");
80
111
  var import_node_path = require("node:path");
@@ -162,13 +193,18 @@ async function sha256(input) {
162
193
  var VectorStore = class _VectorStore {
163
194
  constructor(client, options) {
164
195
  this.client = client;
165
- this.db = (0, import_client.createClient)({
196
+ this.db = options.databaseClient ? options.databaseClient : (0, import_client.createClient)({
166
197
  url: options.databaseUrl,
167
198
  authToken: options.databaseAuthToken
168
199
  });
169
200
  this.knowledgeDir = options.knowledgeDir || "./config/knowledge";
170
201
  }
171
202
  client;
203
+ /**
204
+ * The libsql client backing this store. When a client was injected this is
205
+ * that same instance, so callers holding it (e.g. `ServerDependencies.db`)
206
+ * and the store share one connection.
207
+ */
172
208
  db;
173
209
  knowledgeDir;
174
210
  // On boot: ingest new chunks and embed only missing ones.
@@ -531,10 +567,12 @@ async function createMCPServer(config) {
531
567
  `Context:
532
568
  ${ctx.join("\n\n")}`
533
569
  ].join("\n\n");
534
- const result = await completeOnce({
570
+ const result = await answerOnce({
571
+ answerFn: config.answerFn,
535
572
  client,
536
573
  system,
537
- messages: conversationMessages
574
+ messages: conversationMessages,
575
+ mode: "public"
538
576
  });
539
577
  const cost = calculateCost(result.usage);
540
578
  const duration = Date.now() - startTime;
@@ -615,10 +653,12 @@ ${ctx.join("\n\n")}`
615
653
  `Internal Context:
616
654
  ${ctx.join("\n\n")}`
617
655
  ].join("\n\n");
618
- const result = await completeOnce({
656
+ const result = await answerOnce({
657
+ answerFn: config.answerFn,
619
658
  client,
620
659
  system,
621
- messages: conversationMessages
660
+ messages: conversationMessages,
661
+ mode: "private"
622
662
  });
623
663
  const cost = calculateCost(result.usage);
624
664
  const duration = Date.now() - startTime;
@@ -17,6 +17,12 @@ function detectLeakage(text) {
17
17
  function scrubOutput(text) {
18
18
  return text.replace(/sk-[A-Za-z0-9]{15,}/g, "[REDACTED]").replace(/AIza[0-9A-Za-z_-]{30,}/g, "[REDACTED]");
19
19
  }
20
+ function guardOutput(text) {
21
+ if (detectLeakage(text)) {
22
+ return "Sorry, I can't share internal instructions. How else can I help?";
23
+ }
24
+ return scrubOutput(text);
25
+ }
20
26
 
21
27
  // src/core/llm.ts
22
28
  var DEFAULT_MODEL = "gpt-4o";
@@ -32,15 +38,40 @@ async function completeOnce({
32
38
  temperature,
33
39
  messages: [{ role: "system", content: system }, ...messages]
34
40
  });
35
- let text = res.choices[0]?.message?.content ?? "";
36
- if (detectLeakage(text))
37
- text = "Sorry, I can't share internal instructions. How else can I help?";
41
+ const text = res.choices[0]?.message?.content ?? "";
38
42
  return {
39
- content: scrubOutput(text),
43
+ content: guardOutput(text),
40
44
  usage: res.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
41
45
  };
42
46
  }
43
47
 
48
+ // src/core/answer.ts
49
+ var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
50
+ async function runAnswerFn(answerFn, { system, messages, mode, sender }) {
51
+ const result = await answerFn({ system, messages, mode, sender });
52
+ const raw = typeof result === "string" ? result : result?.content;
53
+ const usage = typeof result === "string" ? void 0 : result?.usage;
54
+ return {
55
+ content: guardOutput(typeof raw === "string" ? raw : ""),
56
+ usage: usage ?? noUsage()
57
+ };
58
+ }
59
+ async function answerOnce({
60
+ answerFn,
61
+ client,
62
+ system,
63
+ messages,
64
+ mode,
65
+ sender,
66
+ temperature,
67
+ model
68
+ }) {
69
+ if (answerFn) {
70
+ return runAnswerFn(answerFn, { system, messages, mode, sender });
71
+ }
72
+ return completeOnce({ client, system, messages, temperature, model });
73
+ }
74
+
44
75
  // src/core/prompts.ts
45
76
  import { readFileSync } from "node:fs";
46
77
  import { join } from "node:path";
@@ -128,13 +159,18 @@ async function sha256(input) {
128
159
  var VectorStore = class _VectorStore {
129
160
  constructor(client, options) {
130
161
  this.client = client;
131
- this.db = createClient({
162
+ this.db = options.databaseClient ? options.databaseClient : createClient({
132
163
  url: options.databaseUrl,
133
164
  authToken: options.databaseAuthToken
134
165
  });
135
166
  this.knowledgeDir = options.knowledgeDir || "./config/knowledge";
136
167
  }
137
168
  client;
169
+ /**
170
+ * The libsql client backing this store. When a client was injected this is
171
+ * that same instance, so callers holding it (e.g. `ServerDependencies.db`)
172
+ * and the store share one connection.
173
+ */
138
174
  db;
139
175
  knowledgeDir;
140
176
  // On boot: ingest new chunks and embed only missing ones.
@@ -497,10 +533,12 @@ async function createMCPServer(config) {
497
533
  `Context:
498
534
  ${ctx.join("\n\n")}`
499
535
  ].join("\n\n");
500
- const result = await completeOnce({
536
+ const result = await answerOnce({
537
+ answerFn: config.answerFn,
501
538
  client,
502
539
  system,
503
- messages: conversationMessages
540
+ messages: conversationMessages,
541
+ mode: "public"
504
542
  });
505
543
  const cost = calculateCost(result.usage);
506
544
  const duration = Date.now() - startTime;
@@ -581,10 +619,12 @@ ${ctx.join("\n\n")}`
581
619
  `Internal Context:
582
620
  ${ctx.join("\n\n")}`
583
621
  ].join("\n\n");
584
- const result = await completeOnce({
622
+ const result = await answerOnce({
623
+ answerFn: config.answerFn,
585
624
  client,
586
625
  system,
587
- messages: conversationMessages
626
+ messages: conversationMessages,
627
+ mode: "private"
588
628
  });
589
629
  const cost = calculateCost(result.usage);
590
630
  const duration = Date.now() - startTime;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Widget chat route tests
3
+ *
4
+ * Covers the brain hook on the public and private chat routes with faked
5
+ * dependencies: no OpenAI, no Turso, and a locally generated RS256 keypair
6
+ * for the private route's JWT so real verification runs offline.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=chat.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.test.d.ts","sourceRoot":"","sources":["../../src/routes/chat.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -1 +1 @@
1
- {"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../src/routes/demo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAsHnD,wBAAgB,UAAU,CAAC,IAAI,EAAE,kBAAkB,8EAqGlD"}
1
+ {"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../src/routes/demo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAsHnD,wBAAgB,UAAU,CAAC,IAAI,EAAE,kBAAkB,8EAiHlD"}
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/routes/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAE,KAAK,eAAe,EAAkC,MAAM,kBAAkB,CAAC;AAGxF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,EAAE,GAAG,IAAI,CA8BxE;AASD,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,8EAsHpD"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/routes/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,EAAE,KAAK,eAAe,EAAkC,MAAM,kBAAkB,CAAC;AAGxF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,EAAE,GAAG,IAAI,CA8BxE;AASD,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,8EAgIpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"private.d.ts","sourceRoot":"","sources":["../../src/routes/private.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAM5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AASnD,wBAAgB,aAAa,CAAC,IAAI,EAAE,kBAAkB,8EAqDrD"}
1
+ {"version":3,"file":"private.d.ts","sourceRoot":"","sources":["../../src/routes/private.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAM5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AASnD,wBAAgB,aAAa,CAAC,IAAI,EAAE,kBAAkB,8EA6DrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/routes/public.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AASnD,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,8EA8DpD"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/routes/public.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AASnD,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,8EAsEpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAY5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,aAAa,uFAwJvD"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAY5B,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,SAAS,CAAC;AAEjE;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,aAAa,uFA8JvD"}
package/dist/types.d.ts CHANGED
@@ -4,8 +4,10 @@
4
4
  * Core types for the Chatter chatbot framework.
5
5
  * These types define the public API and configuration interface.
6
6
  */
7
+ import type { Client as LibsqlClient } from "@libsql/client";
7
8
  import type { Hono } from "hono";
8
9
  import type OpenAI from "openai";
10
+ import type { AnswerFn } from "./core/answer";
9
11
  import type { PromptLoader } from "./core/prompts";
10
12
  import type { VectorStore } from "./core/retrieval";
11
13
  /**
@@ -143,6 +145,17 @@ export interface ChatterConfig {
143
145
  */
144
146
  allowedOrigins?: string[];
145
147
  };
148
+ /**
149
+ * Replaces the completion call on every chat surface with your own brain —
150
+ * an agent framework, a graph runtime, a remote service. Receives the
151
+ * assembled system prompt and conversation from the pipeline and returns
152
+ * the answer text (optionally with token usage).
153
+ *
154
+ * Retrieval, prompt assembly, auth, rate limiting, transports and output
155
+ * guardrails all stay Chatter's. Errors surface as normal completion
156
+ * errors. Unset: the built-in OpenAI completion is used.
157
+ */
158
+ answerFn?: AnswerFn;
146
159
  /** Custom route handler for advanced use cases */
147
160
  customRoutes?: (app: Hono, deps: ServerDependencies) => void;
148
161
  }
@@ -154,6 +167,12 @@ export interface ServerDependencies {
154
167
  client: OpenAI;
155
168
  /** Vector store instance */
156
169
  store: VectorStore;
170
+ /**
171
+ * The libsql database client, shared with the vector store. Custom routes
172
+ * should use this handle rather than opening their own connection to the
173
+ * same database.
174
+ */
175
+ db: LibsqlClient;
157
176
  /** Chatter configuration */
158
177
  config: ChatterConfig;
159
178
  /** Prompt loader instance */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegoaltoworks/chatter",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "Embeddable AI chatbot framework with RAG, authentication, and customizable widgets",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -59,6 +59,13 @@
59
59
  "security:audit:fix": "bun update",
60
60
  "security:check": "bun audit --audit-level=moderate",
61
61
  "check": "bun run typecheck && bun run lint && bun run test && bun run security:check",
62
+ "work": "bun ./.claude/work.ts run",
63
+ "work:continuous": "bun ./.claude/work.ts run --continuous",
64
+ "work:status": "bun ./.claude/work.ts status",
65
+ "work:reset": "bun ./.claude/work.ts reset",
66
+ "work:watch": "bun ./.claude/work.ts watch",
67
+ "work:resume": "bun ./.claude/work.ts resume",
68
+ "work:halt": "bun ./.claude/work.ts halt",
62
69
  "prepublishOnly": "bun run lint && bun run typecheck && bun run test && bun run security:check && bun run build",
63
70
  "prepare": "husky || true"
64
71
  },