@modootoday/pmcp 0.9.1 → 0.10.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,116 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const operationSchema: z.ZodEnum<{
4
+ read: "read";
5
+ send: "send";
6
+ inbox: "inbox";
7
+ ack: "ack";
8
+ sessions: "sessions";
9
+ }>;
10
+ type MailboxOperation = z.output<typeof operationSchema>;
11
+ declare const sessionsSchema: z.ZodObject<{}, z.core.$strict>;
12
+ declare const sendSchema: z.ZodObject<{
13
+ recipients: z.ZodArray<z.ZodObject<{
14
+ actorId: z.ZodString;
15
+ sessionId: z.ZodOptional<z.ZodUUID>;
16
+ }, z.core.$strict>>;
17
+ body: z.ZodString;
18
+ idempotencyKey: z.ZodString;
19
+ replyToId: z.ZodOptional<z.ZodUUID>;
20
+ }, z.core.$strict>;
21
+ declare const inboxSchema: z.ZodObject<{
22
+ limit: z.ZodDefault<z.ZodNumber>;
23
+ state: z.ZodDefault<z.ZodEnum<{
24
+ all: "all";
25
+ unread: "unread";
26
+ unacknowledged: "unacknowledged";
27
+ }>>;
28
+ threadId: z.ZodOptional<z.ZodUUID>;
29
+ cursor: z.ZodOptional<z.ZodString>;
30
+ }, z.core.$strict>;
31
+ declare const messageSchema: z.ZodObject<{
32
+ messageId: z.ZodUUID;
33
+ }, z.core.$strict>;
34
+
35
+ type SqlValue = string | number | null;
36
+ type Row = Record<string, unknown>;
37
+ interface Database {
38
+ exec(sql: string): void;
39
+ get(sql: string, ...values: SqlValue[]): Row | undefined;
40
+ all(sql: string, ...values: SqlValue[]): Row[];
41
+ run(sql: string, ...values: SqlValue[]): void;
42
+ close(): void;
43
+ }
44
+ interface ActorCredential {
45
+ projectId: string;
46
+ actorId: string;
47
+ secret: string;
48
+ }
49
+ interface Binding {
50
+ projectId: string;
51
+ actorId: string;
52
+ sessionId: string;
53
+ generation: number;
54
+ secret: string;
55
+ }
56
+ interface MailboxConfig {
57
+ projectId: string;
58
+ databasePath: string;
59
+ enabled: boolean;
60
+ }
61
+ type Result<T> = {
62
+ ok: true;
63
+ data: T;
64
+ } | {
65
+ ok: false;
66
+ error: {
67
+ code: string;
68
+ message: string;
69
+ retryable: boolean;
70
+ };
71
+ };
72
+ declare class MailboxError extends Error {
73
+ readonly code: string;
74
+ readonly retryable: boolean;
75
+ constructor(code: string, message: string, retryable?: boolean);
76
+ }
77
+ declare function failure(error: unknown): Result<never>;
78
+
79
+ declare const PRESENCE_TTL_MS = 120000;
80
+ declare const HEARTBEAT_INTERVAL_MS = 30000;
81
+ declare class MailboxStore {
82
+ private pending;
83
+ private closed;
84
+ readonly config: MailboxConfig;
85
+ readonly database: Database;
86
+ readonly now: () => number;
87
+ private constructor();
88
+ static open(config: MailboxConfig, now?: () => number): Promise<MailboxStore>;
89
+ checkSchema(): void;
90
+ run<T>(operation: () => T, write?: boolean): Promise<T>;
91
+ registerActor(actorId: string, parentActorId?: string, persist?: (credential: ActorCredential) => void, authorityTtlMs?: number, parentBinding?: Binding): Promise<ActorCredential>;
92
+ registerChild(parent: Binding, actorId: string, ttlMs?: number): Promise<ActorCredential>;
93
+ attach(credential: ActorCredential, runtime: string, replaceSessionId?: string, nativeSessionId?: string): Promise<Binding>;
94
+ assertBinding(binding: Binding): Row;
95
+ rotateActor(actorId: string, persist?: (credential: ActorCredential) => void): Promise<ActorCredential>;
96
+ heartbeat(binding: Binding): Promise<void>;
97
+ detach(binding: Binding): Promise<void>;
98
+ exportRecords(actorId?: string): Promise<object[]>;
99
+ backup(destination: string): Promise<void>;
100
+ close(): Promise<void>;
101
+ }
102
+ declare function restoreBackup(source: string, config: MailboxConfig): Promise<void>;
103
+
104
+ declare class MailboxService {
105
+ readonly store: MailboxStore;
106
+ readonly binding: Binding;
107
+ constructor(store: MailboxStore, binding: Binding);
108
+ call(operation: MailboxOperation, input: unknown): Promise<Result<object>>;
109
+ private send;
110
+ private visibleMessage;
111
+ private inbox;
112
+ private receipt;
113
+ private sessions;
114
+ }
115
+
116
+ export { type ActorCredential as A, type Binding as B, HEARTBEAT_INTERVAL_MS as H, MailboxService as M, PRESENCE_TTL_MS as P, type Row as R, type MailboxConfig as a, MailboxStore as b, MailboxError as c, type MailboxOperation as d, type Result as e, failure as f, sessionsSchema as g, inboxSchema as i, messageSchema as m, operationSchema as o, restoreBackup as r, sendSchema as s };
package/dist/stdio.d.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { C as CatalogOptions } from './catalog-BU5VLLyd.js';
2
+ import { serveStdio } from '@modelcontextprotocol/server/stdio';
3
+ import { S as SkillServerOptions, C as CatalogOptions } from './server-CwWcRjqz.js';
4
+ import { M as MailboxService } from './service-qpCsOXkQ.js';
5
+ import { M as MailboxAttachmentOptions } from './attachment-tU5FZrgZ.js';
6
+ import 'zod';
3
7
 
4
8
  /**
5
9
  * The stdio entry a host launches.
@@ -11,11 +15,13 @@ import { C as CatalogOptions } from './catalog-BU5VLLyd.js';
11
15
 
12
16
  /** `node_modules` roots to walk, from argv or from the working directory. */
13
17
  declare function resolveRoots(argv: readonly string[], cwd: string): string[];
14
- interface ServeOptions {
18
+ interface ServeOptions extends Pick<SkillServerOptions, "embedder" | "vectors" | "loadCatalog"> {
19
+ readonly mailbox?: MailboxService;
15
20
  /** One prompt per skill, for hosts that offer prompts as commands. */
16
21
  readonly prompts?: boolean;
17
22
  }
18
- declare function serve(catalog: CatalogOptions, serving?: ServeOptions): void;
23
+ declare function serve(catalog: CatalogOptions, serving?: ServeOptions): ReturnType<typeof serveStdio>;
24
+ declare function serveWithMailbox(catalog: CatalogOptions, mailbox: MailboxAttachmentOptions, serving?: ServeOptions): Promise<() => Promise<void>>;
19
25
  declare function main(argv?: readonly string[]): void;
20
26
 
21
- export { type ServeOptions, main, resolveRoots, serve };
27
+ export { type ServeOptions, main, resolveRoots, serve, serveWithMailbox };
package/dist/stdio.js CHANGED
@@ -2,12 +2,17 @@
2
2
  import {
3
3
  main,
4
4
  resolveRoots,
5
- serve
6
- } from "./chunk-AJYMOFVX.js";
7
- import "./chunk-VQHNG3KM.js";
5
+ serve,
6
+ serveWithMailbox
7
+ } from "./chunk-JRSR4JBR.js";
8
+ import "./chunk-VRZFM2M5.js";
9
+ import "./chunk-HNAVQG6R.js";
10
+ import "./chunk-MSTKY33C.js";
11
+ import "./chunk-XOH2CW6P.js";
8
12
  export {
9
13
  main,
10
14
  resolveRoots,
11
- serve
15
+ serve,
16
+ serveWithMailbox
12
17
  };
13
18
  //# sourceMappingURL=stdio.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modootoday/pmcp",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "license": "Elastic-2.0",
5
5
  "author": "modootoday",
6
6
  "description": "An MCP server that finds the skills your installed packages already ship. Reads SKILL.md out of node_modules by shape rather than by path, so it sees the skills, .agent, .claude, .gemini and .codex conventions alike, and keeps session startup flat as the number of installed skills grows.",
@@ -44,6 +44,10 @@
44
44
  "./cli": {
45
45
  "types": "./dist/cli.d.ts",
46
46
  "import": "./dist/cli.js"
47
+ },
48
+ "./mailbox": {
49
+ "types": "./dist/mailbox/index.d.ts",
50
+ "import": "./dist/mailbox/index.js"
47
51
  }
48
52
  },
49
53
  "bin": {
@@ -1,121 +0,0 @@
1
- /**
2
- * SKILL.md frontmatter, read as YAML the way a host reads it, and checked against
3
- * the Agent Skills specification (https://agentskills.io/specification).
4
- */
5
- type MetadataLeaf = string | readonly string[];
6
- /** A metadata value: a leaf, or a map of leaves one level below the metadata key. */
7
- type MetadataValue = MetadataLeaf | {
8
- readonly [key: string]: MetadataLeaf;
9
- };
10
- /**
11
- * Parsed frontmatter. A value with an unquoted ": " is a common authoring slip
12
- * the client guide says to recover from by quoting it, so that is retried once.
13
- */
14
- declare function frontmatterObject(source: string): Record<string, unknown> | null;
15
- declare const NAME_MAX = 64;
16
- declare const DESCRIPTION_MAX = 1024;
17
- declare const COMPATIBILITY_MAX = 500;
18
- /**
19
- * What the specification requires of one SKILL.md, as messages; empty when valid.
20
- * directory is the name of the folder holding it.
21
- */
22
- declare function specProblems(front: Record<string, unknown> | null, directory: string): string[];
23
-
24
- /**
25
- * The skills the installed packages brought with them.
26
- *
27
- * Derived from `node_modules` at read time rather than from a list somebody
28
- * maintains: installing a package is how its skill arrives and uninstalling it
29
- * is how the skill leaves, so the catalog is always true of the project.
30
- */
31
-
32
- interface SkillEntry {
33
- /** `<package>/<slug>`. Unique, and stable while the package is installed. */
34
- readonly name: string;
35
- readonly package: string;
36
- readonly slug: string;
37
- /** Frontmatter description. The only text `find` ranks against. */
38
- readonly description: string;
39
- /** Absolute path to the SKILL.md. `call` reads the body from here. */
40
- readonly path: string;
41
- /** open, free or paid. Set for marketplace skills, absent for packages. */
42
- readonly tier?: string;
43
- /** The frontmatter `metadata:` block; maps such as `requires` are kept one level down. */
44
- readonly metadata?: Readonly<Record<string, MetadataValue>>;
45
- /** Extra search terms. Ranked with the description, never used as triggers. */
46
- readonly keywords?: readonly string[];
47
- /** What the entry is. Absent means a skill. */
48
- readonly kind?: AssetKind;
49
- /**
50
- * What describe and read may open: a directory, or one file for an asset
51
- * that is a single file. Absent means the directory holding `path`.
52
- */
53
- readonly root?: string;
54
- }
55
- /** Everything the catalog serves. Commands are skills; workflows have no kind of their own. */
56
- declare const ASSET_KINDS: readonly ["skill", "agent", "hook", "mcp"];
57
- type AssetKind = (typeof ASSET_KINDS)[number];
58
- declare const kindOf: (entry: Pick<SkillEntry, "kind">) => AssetKind;
59
- interface CatalogOptions {
60
- /** Directories to walk. Each is a `node_modules` root, not a package root. */
61
- readonly roots: readonly string[];
62
- /** Package name prefixes to consider. Empty means every package. */
63
- readonly scopes?: readonly string[];
64
- /** Marketplace directories, each holding .claude-plugin/marketplace.json. */
65
- readonly marketplaces?: readonly string[];
66
- /** Package directories read directly, for a package no node_modules links. */
67
- readonly packages?: readonly string[];
68
- /** Workspace roots: the root and every member its workspaces globs name. */
69
- readonly workspaces?: readonly string[];
70
- /** Told about every marketplace skill left out, and why. */
71
- readonly onReject?: (rejection: {
72
- path: string;
73
- reason: string;
74
- }) => void;
75
- /**
76
- * Told about a skill that is served but breaks the Agent Skills specification,
77
- * or that lost a name collision. Loading stays lenient, as the client guide asks.
78
- */
79
- readonly onWarning?: (warning: {
80
- path: string;
81
- reason: string;
82
- }) => void;
83
- }
84
- /**
85
- * Directories a walk does not enter. Kept as short as the evidence allows: a
86
- * name here is a package whose skill will never be found, and the cost of one
87
- * extra readdir is far below the cost of missing a skill. Measured 20260906 --
88
- * `lib` was on this list and hid playwright's `lib/skill/SKILL.md`, which is
89
- * the most widely installed skill in the real world.
90
- */
91
- declare const SKIPPED_DIRS: ReadonlySet<string>;
92
- /**
93
- * How deep below a package root a SKILL.md is looked for. Three reaches every
94
- * convention in use (skills, .agent/skills, .claude/skills, .gemini/skills,
95
- * .codex/skills); four was measured to find nothing more.
96
- */
97
- declare const MAX_SKILL_DEPTH = 3;
98
- /**
99
- * Top-level scalar fields of a SKILL.md frontmatter, parsed as YAML, so a folded
100
- * `description: >-` arrives as its text rather than as the indicator.
101
- */
102
- declare function readFrontmatter(source: string): Record<string, string>;
103
- /** The body after the frontmatter, or the whole file when there is none. */
104
- declare function readBody(source: string): string;
105
- /**
106
- * Every skill reachable from the given roots, sorted by name.
107
- *
108
- * A package with no skills directory contributes nothing and costs one
109
- * `existsSync` per convention. A skill with no description is skipped rather
110
- * than listed:
111
- * `find` ranks descriptions, so one without a description can only ever be
112
- * found by already knowing its name, and listing it would spend a session's
113
- * tokens on an entry that cannot answer.
114
- */
115
- declare function readCatalog(options: CatalogOptions): SkillEntry[];
116
- /** A catalog entry without its body, which is what `catalog` returns. */
117
- declare function withoutBody({ path, ...rest }: SkillEntry): Omit<SkillEntry, "path">;
118
- /** Path relative to a root, for a message a reader can act on. */
119
- declare function displayPath(root: string, entry: SkillEntry): string;
120
-
121
- export { type AssetKind as A, type CatalogOptions as C, DESCRIPTION_MAX as D, type MetadataValue as M, NAME_MAX as N, type SkillEntry as S, ASSET_KINDS as a, COMPATIBILITY_MAX as b, MAX_SKILL_DEPTH as c, type MetadataLeaf as d, SKIPPED_DIRS as e, displayPath as f, frontmatterObject as g, readCatalog as h, readFrontmatter as i, kindOf as k, readBody as r, specProblems as s, withoutBody as w };
@@ -1,160 +0,0 @@
1
- import {
2
- openDatabase
3
- } from "./chunk-R2QPKEI4.js";
4
-
5
- // src/embed.ts
6
- var DEFAULT_MODEL_ID = "Xenova/multilingual-e5-small";
7
- var PLAIN = {
8
- queryPrefix: "",
9
- passagePrefix: "",
10
- pooling: "mean"
11
- };
12
- var E5 = {
13
- queryPrefix: "query: ",
14
- passagePrefix: "passage: ",
15
- pooling: "mean"
16
- };
17
- var KNOWN_ENCODERS = {
18
- "Xenova/multilingual-e5-small": E5,
19
- "Xenova/multilingual-e5-base": E5,
20
- "Xenova/multilingual-e5-large": E5,
21
- "Xenova/bge-m3": { ...PLAIN, pooling: "cls" }
22
- };
23
- function encoderSpec(modelId) {
24
- return KNOWN_ENCODERS[modelId] ?? PLAIN;
25
- }
26
- var ENTRY_SCHEMA = `
27
- CREATE TABLE IF NOT EXISTS entry_vector (
28
- name TEXT NOT NULL,
29
- model_id TEXT NOT NULL,
30
- dims INTEGER NOT NULL,
31
- description_hash TEXT NOT NULL,
32
- vector BLOB NOT NULL,
33
- PRIMARY KEY (name, model_id)
34
- );
35
- `;
36
- var TRANSFORMERS = "@huggingface/transformers";
37
- async function loadEmbedder(modelId = DEFAULT_MODEL_ID, options = {}) {
38
- let module;
39
- try {
40
- module = options.module ?? await import(TRANSFORMERS);
41
- } catch {
42
- return null;
43
- }
44
- const factory = module.pipeline;
45
- if (typeof factory !== "function") return null;
46
- if (module.env) {
47
- if (options.cacheDir !== void 0)
48
- module.env["cacheDir"] = options.cacheDir;
49
- if (options.localOnly) module.env["allowRemoteModels"] = false;
50
- }
51
- const encode = await factory("feature-extraction", modelId, {
52
- dtype: options.dtype ?? "q8"
53
- });
54
- const spec = encoderSpec(modelId);
55
- let dims = 0;
56
- const run = async (text) => {
57
- const output = await encode(text, {
58
- pooling: spec.pooling,
59
- normalize: true
60
- });
61
- const vector = Float32Array.from(output.data);
62
- dims = vector.length;
63
- return vector;
64
- };
65
- return {
66
- modelId,
67
- get dims() {
68
- return dims;
69
- },
70
- embed: (text) => run(spec.queryPrefix + text),
71
- embedPassage: (text) => run(spec.passagePrefix + text)
72
- };
73
- }
74
- async function encodeEntries(embedder, entries, previous = /* @__PURE__ */ new Map()) {
75
- const out = /* @__PURE__ */ new Map();
76
- const passage = (text) => embedder.embedPassage ? embedder.embedPassage(text) : embedder.embed(text);
77
- for (const entry of entries) {
78
- const hash = describeHash(entry.description);
79
- const kept = previous.get(entry.name);
80
- if (kept && kept.hash === hash) {
81
- out.set(entry.name, kept);
82
- continue;
83
- }
84
- out.set(entry.name, { hash, vector: await passage(entry.description) });
85
- }
86
- return out;
87
- }
88
- function describeHash(description) {
89
- let hash = 2166136261;
90
- for (let i = 0; i < description.length; i++) {
91
- hash ^= description.charCodeAt(i);
92
- hash = Math.imul(hash, 16777619) >>> 0;
93
- }
94
- return hash.toString(16).padStart(8, "0");
95
- }
96
- async function openEntryVectors(path) {
97
- const opened = await openDatabase(path);
98
- if (opened === null) return null;
99
- const db = opened.db;
100
- db.exec(ENTRY_SCHEMA);
101
- return {
102
- read(modelId) {
103
- const out = /* @__PURE__ */ new Map();
104
- const rows = db.prepare(
105
- "SELECT name, dims, vector FROM entry_vector WHERE model_id = ?"
106
- ).all(modelId);
107
- for (const row of rows) {
108
- const bytes = row.vector;
109
- const vector = new Float32Array(
110
- bytes.buffer.slice(
111
- bytes.byteOffset,
112
- bytes.byteOffset + bytes.byteLength
113
- )
114
- );
115
- if (vector.length === row.dims) out.set(row.name, vector);
116
- }
117
- return out;
118
- },
119
- stale(modelId, entries) {
120
- const rows = db.prepare(
121
- "SELECT name, description_hash FROM entry_vector WHERE model_id = ?"
122
- ).all(modelId);
123
- const stored = new Map(
124
- rows.map((row) => [row.name, row.description_hash])
125
- );
126
- const out = [];
127
- for (const [name, description] of entries) {
128
- if (stored.get(name) !== describeHash(description)) out.push(name);
129
- }
130
- return out;
131
- },
132
- write(modelId, name, description, vector) {
133
- db.prepare(
134
- `INSERT INTO entry_vector (name, model_id, dims, description_hash, vector)
135
- VALUES (?, ?, ?, ?, ?)
136
- ON CONFLICT(name, model_id) DO UPDATE SET
137
- dims = excluded.dims,
138
- description_hash = excluded.description_hash,
139
- vector = excluded.vector`
140
- ).run(
141
- name,
142
- modelId,
143
- vector.length,
144
- describeHash(description),
145
- new Uint8Array(vector.buffer, vector.byteOffset, vector.byteLength)
146
- );
147
- },
148
- close() {
149
- db.close();
150
- }
151
- };
152
- }
153
-
154
- export {
155
- DEFAULT_MODEL_ID,
156
- loadEmbedder,
157
- encodeEntries,
158
- openEntryVectors
159
- };
160
- //# sourceMappingURL=chunk-7DD2ADL2.js.map
@@ -1,41 +0,0 @@
1
- import {
2
- createSkillServer
3
- } from "./chunk-VQHNG3KM.js";
4
-
5
- // src/stdio.ts
6
- import { serveStdio } from "@modelcontextprotocol/server/stdio";
7
- function resolveRoots(argv, cwd) {
8
- const given = argv.filter((arg) => !arg.startsWith("-"));
9
- return given.length > 0 ? given : [`${cwd}/node_modules`];
10
- }
11
- function serve(catalog, serving = {}) {
12
- const options = {
13
- ...catalog,
14
- ...serving,
15
- onReject: ({ path, reason }) => process.stderr.write(`pmcp: skipped ${path}: ${reason}
16
- `),
17
- onWarning: ({ path, reason }) => process.stderr.write(`pmcp: warning ${path}: ${reason}
18
- `)
19
- };
20
- serveStdio(() => createSkillServer(options), {
21
- // stdout is the protocol. A diagnostic written there corrupts the stream,
22
- // so out-of-band errors go to stderr and nowhere else.
23
- onerror: (error) => process.stderr.write(`pmcp: ${error.message}
24
- `)
25
- });
26
- }
27
- function main(argv = process.argv.slice(2)) {
28
- const scopeArg = argv.find((arg) => arg.startsWith("--scope="));
29
- const scopes = scopeArg ? scopeArg.slice("--scope=".length).split(",") : [];
30
- serve({
31
- roots: resolveRoots(argv, process.cwd()),
32
- ...scopes.length > 0 ? { scopes } : {}
33
- });
34
- }
35
-
36
- export {
37
- resolveRoots,
38
- serve,
39
- main
40
- };
41
- //# sourceMappingURL=chunk-AJYMOFVX.js.map
@@ -1,134 +0,0 @@
1
- import {
2
- normaliseIntent
3
- } from "./chunk-VQHNG3KM.js";
4
-
5
- // src/cache.ts
6
- async function openDatabase(path) {
7
- try {
8
- const { DatabaseSync } = await import(`node:${"sqlite"}`);
9
- return { db: new DatabaseSync(path), provider: "node:sqlite" };
10
- } catch {
11
- }
12
- try {
13
- const { Database } = await import(`bun:${"sqlite"}`);
14
- const bun = new Database(path, { create: true });
15
- return {
16
- db: {
17
- exec: (sql) => bun.run(sql),
18
- prepare: (sql) => bun.query(sql),
19
- close: () => bun.close()
20
- },
21
- provider: "bun:sqlite"
22
- };
23
- } catch {
24
- return null;
25
- }
26
- }
27
- var DEFAULT_INTENT_CACHE_LIMIT = 4096;
28
- var SCHEMA = `
29
- CREATE TABLE IF NOT EXISTS intent_vector (
30
- intent TEXT NOT NULL PRIMARY KEY,
31
- model_id TEXT NOT NULL,
32
- dims INTEGER NOT NULL,
33
- vector BLOB NOT NULL,
34
- used_at INTEGER NOT NULL
35
- );
36
- CREATE INDEX IF NOT EXISTS intent_vector_used_at ON intent_vector (used_at);
37
- `;
38
- async function openIntentCache(options) {
39
- const opened = await openDatabase(options.path);
40
- if (!opened) return null;
41
- const { db, provider } = opened;
42
- try {
43
- db.exec("PRAGMA journal_mode = WAL");
44
- db.exec(SCHEMA);
45
- } catch {
46
- db.close();
47
- return null;
48
- }
49
- const limit = options.limit ?? DEFAULT_INTENT_CACHE_LIMIT;
50
- const now = options.now ?? (() => Date.now());
51
- let hits = 0;
52
- let misses = 0;
53
- let mismatched = 0;
54
- const selectRow = db.prepare("SELECT model_id, dims, vector FROM intent_vector WHERE intent = ?");
55
- const touch = db.prepare(
56
- "UPDATE intent_vector SET used_at = ? WHERE intent = ?"
57
- );
58
- const upsert = db.prepare(
59
- `INSERT INTO intent_vector (intent, model_id, dims, vector, used_at)
60
- VALUES (?, ?, ?, ?, ?)
61
- ON CONFLICT(intent) DO UPDATE SET
62
- model_id = excluded.model_id,
63
- dims = excluded.dims,
64
- vector = excluded.vector,
65
- used_at = excluded.used_at`
66
- );
67
- const countRows = db.prepare(
68
- "SELECT count(*) AS n FROM intent_vector"
69
- );
70
- const evict = db.prepare(
71
- `DELETE FROM intent_vector WHERE intent IN (
72
- SELECT intent FROM intent_vector ORDER BY used_at ASC LIMIT ?
73
- )`
74
- );
75
- return {
76
- get(intent) {
77
- const key = normaliseIntent(intent);
78
- const row = selectRow.get(key) ?? null;
79
- if (!row) {
80
- misses += 1;
81
- return null;
82
- }
83
- if (row.model_id !== options.modelId || row.dims !== options.dims) {
84
- mismatched += 1;
85
- return null;
86
- }
87
- hits += 1;
88
- touch.run(now(), key);
89
- const bytes = row.vector;
90
- return new Float32Array(
91
- bytes.buffer.slice(
92
- bytes.byteOffset,
93
- bytes.byteOffset + bytes.byteLength
94
- )
95
- );
96
- },
97
- put(intent, vector) {
98
- if (vector.length !== options.dims) {
99
- throw new Error(
100
- `intent vector is ${vector.length} wide, cache holds ${options.dims}`
101
- );
102
- }
103
- const key = normaliseIntent(intent);
104
- upsert.run(
105
- key,
106
- options.modelId,
107
- options.dims,
108
- new Uint8Array(vector.buffer, vector.byteOffset, vector.byteLength),
109
- now()
110
- );
111
- const rows = countRows.get()?.n ?? 0;
112
- if (rows > limit) evict.run(rows - limit);
113
- },
114
- stats() {
115
- return {
116
- hits,
117
- misses,
118
- mismatched,
119
- rows: countRows.get()?.n ?? 0,
120
- provider
121
- };
122
- },
123
- close() {
124
- db.close();
125
- }
126
- };
127
- }
128
-
129
- export {
130
- openDatabase,
131
- DEFAULT_INTENT_CACHE_LIMIT,
132
- openIntentCache
133
- };
134
- //# sourceMappingURL=chunk-R2QPKEI4.js.map