@adhd/agent-store-prompts 2.1.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/drizzle/0000_third_nitro.sql +19 -0
- package/drizzle/0001_agents_and_taxonomy.sql +26 -0
- package/drizzle/0002_agent_components_junction.sql +13 -0
- package/drizzle/0003_usecase_context_rules.sql +31 -0
- package/drizzle/0004_composed_prompts.sql +10 -0
- package/drizzle/0005_warm_norman_osborn.sql +48 -0
- package/drizzle/0006_component_head_version_split.sql +78 -0
- package/drizzle/meta/0000_snapshot.json +137 -0
- package/drizzle/meta/0001_snapshot.json +302 -0
- package/drizzle/meta/0002_snapshot.json +381 -0
- package/drizzle/meta/0004_snapshot.json +594 -0
- package/drizzle/meta/0005_snapshot.json +594 -0
- package/drizzle/meta/0006_snapshot.json +672 -0
- package/drizzle/meta/_journal.json +55 -0
- package/package.json +51 -0
- package/src/db/client.d.ts +7 -0
- package/src/db/client.d.ts.map +1 -0
- package/src/db/client.js +24 -0
- package/src/db/client.js.map +1 -0
- package/src/db/migrate-runner.d.ts +31 -0
- package/src/db/migrate-runner.d.ts.map +1 -0
- package/src/db/migrate-runner.js +35 -0
- package/src/db/migrate-runner.js.map +1 -0
- package/src/db/migrate.d.ts +9 -0
- package/src/db/migrate.d.ts.map +1 -0
- package/src/db/migrate.js +13 -0
- package/src/db/migrate.js.map +1 -0
- package/src/db/schema.d.ts +997 -0
- package/src/db/schema.d.ts.map +1 -0
- package/src/db/schema.js +323 -0
- package/src/db/schema.js.map +1 -0
- package/src/index.d.ts +21 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +23 -0
- package/src/index.js.map +1 -0
- package/src/seed/components.d.ts +23 -0
- package/src/seed/components.d.ts.map +1 -0
- package/src/seed/components.js +399 -0
- package/src/seed/components.js.map +1 -0
- package/src/seed/index.d.ts +30 -0
- package/src/seed/index.d.ts.map +1 -0
- package/src/seed/index.js +95 -0
- package/src/seed/index.js.map +1 -0
- package/src/seed/prompt-types.d.ts +17 -0
- package/src/seed/prompt-types.d.ts.map +1 -0
- package/src/seed/prompt-types.js +107 -0
- package/src/seed/prompt-types.js.map +1 -0
- package/src/store/agent-store.d.ts +113 -0
- package/src/store/agent-store.d.ts.map +1 -0
- package/src/store/agent-store.js +211 -0
- package/src/store/agent-store.js.map +1 -0
- package/src/store/component-store.d.ts +87 -0
- package/src/store/component-store.d.ts.map +1 -0
- package/src/store/component-store.js +305 -0
- package/src/store/component-store.js.map +1 -0
- package/src/store/composed-prompt-store.d.ts +72 -0
- package/src/store/composed-prompt-store.d.ts.map +1 -0
- package/src/store/composed-prompt-store.js +147 -0
- package/src/store/composed-prompt-store.js.map +1 -0
- package/src/store/composition-store.d.ts +117 -0
- package/src/store/composition-store.d.ts.map +1 -0
- package/src/store/composition-store.js +270 -0
- package/src/store/composition-store.js.map +1 -0
- package/src/store/usecase-store.d.ts +104 -0
- package/src/store/usecase-store.d.ts.map +1 -0
- package/src/store/usecase-store.js +158 -0
- package/src/store/usecase-store.js.map +1 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { and, desc, eq, max } from 'drizzle-orm';
|
|
2
|
+
import { componentsTable, componentVersionsTable, promptTypesTable, } from '../db/schema.js';
|
|
3
|
+
// ──────────────────────────────────────────────
|
|
4
|
+
// Error codes
|
|
5
|
+
// ──────────────────────────────────────────────
|
|
6
|
+
export class ComponentError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
constructor(code, message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.name = 'ComponentError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
// ──────────────────────────────────────────────
|
|
15
|
+
// ComponentStore
|
|
16
|
+
//
|
|
17
|
+
// Thin Drizzle wrapper for the head/version split (Decision 5):
|
|
18
|
+
// registry_components — identity (slug PK, type, is_shared)
|
|
19
|
+
// registry_component_versions — history (version_id PK, slug FK, version, content)
|
|
20
|
+
// registry_prompt_types — type lookup
|
|
21
|
+
//
|
|
22
|
+
// Public method names + semantics are preserved from the pre-split store: create()
|
|
23
|
+
// returns version 1, read() returns the latest version, version() appends a new
|
|
24
|
+
// version row. The PromptComponent shape gains `versionId` (the stable surrogate a
|
|
25
|
+
// junction version_pin stores).
|
|
26
|
+
//
|
|
27
|
+
// Mirrors the store-class pattern in entrypoint/agent-mcp's AgentStore (agent-cache-store pattern)
|
|
28
|
+
// [ref:store-class] (contexts/_shared.md)
|
|
29
|
+
// ──────────────────────────────────────────────
|
|
30
|
+
export class ComponentStore {
|
|
31
|
+
db;
|
|
32
|
+
constructor(
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
db) {
|
|
35
|
+
this.db = db;
|
|
36
|
+
}
|
|
37
|
+
// ── Prompt Types ──────────────────────────
|
|
38
|
+
/** Insert a prompt type. No-op if the slug already exists. */
|
|
39
|
+
upsertType(input) {
|
|
40
|
+
this.db
|
|
41
|
+
.insert(promptTypesTable)
|
|
42
|
+
.values({
|
|
43
|
+
slug: input.slug,
|
|
44
|
+
description: input.description,
|
|
45
|
+
isSystem: input.isSystem,
|
|
46
|
+
})
|
|
47
|
+
.onConflictDoNothing()
|
|
48
|
+
.run();
|
|
49
|
+
return input;
|
|
50
|
+
}
|
|
51
|
+
/** Read a prompt type by slug. Throws COMPONENT_TYPE_NOT_FOUND if absent. */
|
|
52
|
+
readType(slug) {
|
|
53
|
+
const row = this.db
|
|
54
|
+
.select()
|
|
55
|
+
.from(promptTypesTable)
|
|
56
|
+
.where(eq(promptTypesTable.slug, slug))
|
|
57
|
+
.get();
|
|
58
|
+
if (!row) {
|
|
59
|
+
throw new ComponentError('COMPONENT_TYPE_NOT_FOUND', `Prompt type '${slug}' not found`);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
slug: row.slug,
|
|
63
|
+
description: row.description,
|
|
64
|
+
isSystem: Boolean(row.isSystem),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// ── Prompt Components ─────────────────────
|
|
68
|
+
/**
|
|
69
|
+
* Create a new component: insert the head identity row plus its version-1
|
|
70
|
+
* history row, in ONE transaction (Decision 5).
|
|
71
|
+
*
|
|
72
|
+
* [inv:version-retained]: each later call to version() appends a NEW version
|
|
73
|
+
* row at version+1; it never overwrites a prior version.
|
|
74
|
+
*/
|
|
75
|
+
create(input) {
|
|
76
|
+
const now = new Date().toISOString();
|
|
77
|
+
const isShared = input.isShared ?? false;
|
|
78
|
+
// Head + first version are written atomically: a version row must never
|
|
79
|
+
// exist without its head, and a head must always have at least v1.
|
|
80
|
+
const versionId = this.db.transaction((tx) => {
|
|
81
|
+
tx.insert(componentsTable)
|
|
82
|
+
.values({
|
|
83
|
+
slug: input.slug,
|
|
84
|
+
type: input.type,
|
|
85
|
+
isShared,
|
|
86
|
+
createdAt: now,
|
|
87
|
+
})
|
|
88
|
+
.run();
|
|
89
|
+
const inserted = tx
|
|
90
|
+
.insert(componentVersionsTable)
|
|
91
|
+
.values({
|
|
92
|
+
slug: input.slug,
|
|
93
|
+
version: 1,
|
|
94
|
+
content: input.content,
|
|
95
|
+
createdAt: now,
|
|
96
|
+
updatedAt: now,
|
|
97
|
+
})
|
|
98
|
+
.returning({ versionId: componentVersionsTable.versionId })
|
|
99
|
+
.get();
|
|
100
|
+
if (!inserted) {
|
|
101
|
+
throw new Error(`insert failed: component '${input.slug}' version 1`);
|
|
102
|
+
}
|
|
103
|
+
return inserted.versionId;
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
slug: input.slug,
|
|
107
|
+
type: input.type,
|
|
108
|
+
version: 1,
|
|
109
|
+
versionId,
|
|
110
|
+
content: input.content,
|
|
111
|
+
isShared,
|
|
112
|
+
createdAt: now,
|
|
113
|
+
updatedAt: now,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Read the latest version of a component by slug (head joined to its highest
|
|
118
|
+
* version row).
|
|
119
|
+
* Throws COMPONENT_NOT_FOUND if no head row exists for the slug.
|
|
120
|
+
*/
|
|
121
|
+
read(slug) {
|
|
122
|
+
const head = this.db
|
|
123
|
+
.select()
|
|
124
|
+
.from(componentsTable)
|
|
125
|
+
.where(eq(componentsTable.slug, slug))
|
|
126
|
+
.get();
|
|
127
|
+
if (!head) {
|
|
128
|
+
throw new ComponentError('COMPONENT_NOT_FOUND', `Component '${slug}' not found`);
|
|
129
|
+
}
|
|
130
|
+
const latest = this.db
|
|
131
|
+
.select()
|
|
132
|
+
.from(componentVersionsTable)
|
|
133
|
+
.where(eq(componentVersionsTable.slug, slug))
|
|
134
|
+
.orderBy(desc(componentVersionsTable.version))
|
|
135
|
+
.limit(1)
|
|
136
|
+
.get();
|
|
137
|
+
if (!latest) {
|
|
138
|
+
// Head with no version is an integrity violation (create() writes both
|
|
139
|
+
// atomically) — surface it rather than returning a malformed component.
|
|
140
|
+
throw new ComponentError('COMPONENT_NOT_FOUND', `Component '${slug}' has a head row but no versions`);
|
|
141
|
+
}
|
|
142
|
+
return this._join(head, latest);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Read a specific version of a component.
|
|
146
|
+
* Throws COMPONENT_VERSION_NOT_FOUND if that exact (slug, version) row is absent,
|
|
147
|
+
* or COMPONENT_NOT_FOUND if the head identity row is absent.
|
|
148
|
+
*/
|
|
149
|
+
readVersion(slug, version) {
|
|
150
|
+
const head = this.db
|
|
151
|
+
.select()
|
|
152
|
+
.from(componentsTable)
|
|
153
|
+
.where(eq(componentsTable.slug, slug))
|
|
154
|
+
.get();
|
|
155
|
+
if (!head) {
|
|
156
|
+
throw new ComponentError('COMPONENT_NOT_FOUND', `Component '${slug}' not found`);
|
|
157
|
+
}
|
|
158
|
+
const row = this.db
|
|
159
|
+
.select()
|
|
160
|
+
.from(componentVersionsTable)
|
|
161
|
+
.where(and(eq(componentVersionsTable.slug, slug), eq(componentVersionsTable.version, version)))
|
|
162
|
+
.get();
|
|
163
|
+
if (!row) {
|
|
164
|
+
throw new ComponentError('COMPONENT_VERSION_NOT_FOUND', `Component '${slug}' version ${version} not found`);
|
|
165
|
+
}
|
|
166
|
+
return this._join(head, row);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Resolve a `(slug, version)` pair to its stable registry_component_versions
|
|
170
|
+
* .version_id surrogate — the value a junction `version_pin` stores when an
|
|
171
|
+
* exact version is pinned (Decision 5).
|
|
172
|
+
*
|
|
173
|
+
* Throws COMPONENT_VERSION_NOT_FOUND if that exact version row is absent.
|
|
174
|
+
*/
|
|
175
|
+
resolveVersionId(slug, version) {
|
|
176
|
+
const row = this.db
|
|
177
|
+
.select({ versionId: componentVersionsTable.versionId })
|
|
178
|
+
.from(componentVersionsTable)
|
|
179
|
+
.where(and(eq(componentVersionsTable.slug, slug), eq(componentVersionsTable.version, version)))
|
|
180
|
+
.get();
|
|
181
|
+
if (!row) {
|
|
182
|
+
throw new ComponentError('COMPONENT_VERSION_NOT_FOUND', `Component '${slug}' version ${version} not found`);
|
|
183
|
+
}
|
|
184
|
+
return row.versionId;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Bump a component to a new version with updated content.
|
|
188
|
+
* Appends a NEW version row at max(existing version) + 1 for the slug — the head
|
|
189
|
+
* identity row is unchanged and old version rows are NEVER deleted.
|
|
190
|
+
* [inv:version-retained]
|
|
191
|
+
*/
|
|
192
|
+
version(slug, newContent) {
|
|
193
|
+
const head = this.db
|
|
194
|
+
.select()
|
|
195
|
+
.from(componentsTable)
|
|
196
|
+
.where(eq(componentsTable.slug, slug))
|
|
197
|
+
.get();
|
|
198
|
+
if (!head) {
|
|
199
|
+
throw new ComponentError('COMPONENT_NOT_FOUND', `Component '${slug}' not found`);
|
|
200
|
+
}
|
|
201
|
+
// Highest existing version for the slug → next is +1.
|
|
202
|
+
const maxRow = this.db
|
|
203
|
+
.select({
|
|
204
|
+
maxVersion: max(componentVersionsTable.version).as('max_version'),
|
|
205
|
+
})
|
|
206
|
+
.from(componentVersionsTable)
|
|
207
|
+
.where(eq(componentVersionsTable.slug, slug))
|
|
208
|
+
.get();
|
|
209
|
+
const nextVersion = (maxRow?.maxVersion ?? 0) + 1;
|
|
210
|
+
const now = new Date().toISOString();
|
|
211
|
+
const inserted = this.db
|
|
212
|
+
.insert(componentVersionsTable)
|
|
213
|
+
.values({
|
|
214
|
+
slug,
|
|
215
|
+
version: nextVersion,
|
|
216
|
+
content: newContent,
|
|
217
|
+
createdAt: now,
|
|
218
|
+
updatedAt: now,
|
|
219
|
+
})
|
|
220
|
+
.returning({ versionId: componentVersionsTable.versionId })
|
|
221
|
+
.get();
|
|
222
|
+
if (!inserted) {
|
|
223
|
+
throw new Error(`insert failed: component '${slug}' version ${nextVersion}`);
|
|
224
|
+
}
|
|
225
|
+
const versionId = inserted.versionId;
|
|
226
|
+
return {
|
|
227
|
+
slug,
|
|
228
|
+
type: head.type,
|
|
229
|
+
version: nextVersion,
|
|
230
|
+
versionId,
|
|
231
|
+
content: newContent,
|
|
232
|
+
isShared: Boolean(head.isShared),
|
|
233
|
+
createdAt: head.createdAt,
|
|
234
|
+
updatedAt: now,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* List the latest version of each component, optionally filtered by type
|
|
239
|
+
* and/or shared flag. Joins each head to its highest version row.
|
|
240
|
+
*/
|
|
241
|
+
list(filter = {}) {
|
|
242
|
+
// Subquery: for each slug, find the max version.
|
|
243
|
+
const maxVersions = this.db
|
|
244
|
+
.select({
|
|
245
|
+
slug: componentVersionsTable.slug,
|
|
246
|
+
maxVersion: max(componentVersionsTable.version).as('max_version'),
|
|
247
|
+
})
|
|
248
|
+
.from(componentVersionsTable)
|
|
249
|
+
.groupBy(componentVersionsTable.slug)
|
|
250
|
+
.as('max_versions');
|
|
251
|
+
// Join head identity → its latest version row.
|
|
252
|
+
let query = this.db
|
|
253
|
+
.select({
|
|
254
|
+
slug: componentsTable.slug,
|
|
255
|
+
type: componentsTable.type,
|
|
256
|
+
isShared: componentsTable.isShared,
|
|
257
|
+
headCreatedAt: componentsTable.createdAt,
|
|
258
|
+
versionId: componentVersionsTable.versionId,
|
|
259
|
+
version: componentVersionsTable.version,
|
|
260
|
+
content: componentVersionsTable.content,
|
|
261
|
+
versionUpdatedAt: componentVersionsTable.updatedAt,
|
|
262
|
+
})
|
|
263
|
+
.from(componentsTable)
|
|
264
|
+
.innerJoin(componentVersionsTable, eq(componentsTable.slug, componentVersionsTable.slug))
|
|
265
|
+
.innerJoin(maxVersions, and(eq(componentVersionsTable.slug, maxVersions.slug), eq(componentVersionsTable.version, maxVersions.maxVersion)));
|
|
266
|
+
// Apply filters (Drizzle's .where() can only be called once, so accumulate conditions)
|
|
267
|
+
const conditions = [];
|
|
268
|
+
if (filter.type !== undefined) {
|
|
269
|
+
conditions.push(eq(componentsTable.type, filter.type));
|
|
270
|
+
}
|
|
271
|
+
if (filter.shared !== undefined) {
|
|
272
|
+
conditions.push(eq(componentsTable.isShared, filter.shared));
|
|
273
|
+
}
|
|
274
|
+
if (conditions.length > 0) {
|
|
275
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
276
|
+
query = query.where(and(...conditions));
|
|
277
|
+
}
|
|
278
|
+
const rows = query.all();
|
|
279
|
+
return rows.map((r) => ({
|
|
280
|
+
slug: r.slug,
|
|
281
|
+
type: r.type,
|
|
282
|
+
version: r.version,
|
|
283
|
+
versionId: r.versionId,
|
|
284
|
+
content: r.content,
|
|
285
|
+
isShared: Boolean(r.isShared),
|
|
286
|
+
createdAt: r.headCreatedAt,
|
|
287
|
+
updatedAt: r.versionUpdatedAt,
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
// ── Private helpers ───────────────────────
|
|
291
|
+
/** Join a head identity row to one version row into a PromptComponent. */
|
|
292
|
+
_join(head, ver) {
|
|
293
|
+
return {
|
|
294
|
+
slug: head.slug,
|
|
295
|
+
type: head.type,
|
|
296
|
+
version: ver.version,
|
|
297
|
+
versionId: ver.versionId,
|
|
298
|
+
content: ver.content,
|
|
299
|
+
isShared: Boolean(head.isShared),
|
|
300
|
+
createdAt: head.createdAt,
|
|
301
|
+
updatedAt: ver.updatedAt,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=component-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-store.js","sourceRoot":"","sources":["../../../../../../packages/agent/agent-store-prompts/src/store/component-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
|
|
2
|
+
/**
|
|
3
|
+
* Compute a deterministic, order-independent SHA-256 hash for a context map.
|
|
4
|
+
*
|
|
5
|
+
* Sorted-key JSON canonicalization → SHA-256 hex.
|
|
6
|
+
* `{ b: "2", a: "1" }` and `{ a: "1", b: "2" }` both hash to the same value.
|
|
7
|
+
*
|
|
8
|
+
* This is the canonical implementation reused by @adhd/agent-engine-compiler — import
|
|
9
|
+
* it from `@adhd/agent-registry` rather than reimplementing.
|
|
10
|
+
*
|
|
11
|
+
* @param context - Runtime context key/value map.
|
|
12
|
+
* @returns 64-character lowercase hex SHA-256 string.
|
|
13
|
+
*/
|
|
14
|
+
export declare function contextHash(context: Record<string, string>): string;
|
|
15
|
+
export declare class ComposedPromptError extends Error {
|
|
16
|
+
readonly code: 'NOT_FOUND';
|
|
17
|
+
constructor(code: 'NOT_FOUND', message: string);
|
|
18
|
+
}
|
|
19
|
+
/** A fully-hydrated composed_prompts row with parsed component versions. */
|
|
20
|
+
export interface ComposedPrompt {
|
|
21
|
+
id: number;
|
|
22
|
+
agentSlug: string;
|
|
23
|
+
contextHash: string;
|
|
24
|
+
content: string;
|
|
25
|
+
/** Parsed audit map: `{ [componentSlug]: version }`. */
|
|
26
|
+
componentVersions: Record<string, number>;
|
|
27
|
+
createdAt: string;
|
|
28
|
+
}
|
|
29
|
+
/** Input required to write a new composed prompt cache entry. */
|
|
30
|
+
export interface ComposedPromptWriteInput {
|
|
31
|
+
agentSlug: string;
|
|
32
|
+
contextHash: string;
|
|
33
|
+
content: string;
|
|
34
|
+
/** Audit map: `{ [componentSlug]: version }` — which version was used for each component. */
|
|
35
|
+
componentVersions: Record<string, number>;
|
|
36
|
+
}
|
|
37
|
+
export declare class ComposedPromptStore {
|
|
38
|
+
private readonly db;
|
|
39
|
+
constructor(db: BetterSQLite3Database<any>);
|
|
40
|
+
/**
|
|
41
|
+
* Write a new composed prompt cache entry.
|
|
42
|
+
*
|
|
43
|
+
* The caller is responsible for computing the `contextHash` using the
|
|
44
|
+
* exported `contextHash()` helper. Inserting a duplicate (agent_slug,
|
|
45
|
+
* context_hash) is allowed — the cache layer may accumulate redundant rows;
|
|
46
|
+
* `lookup` will return the most recently written one.
|
|
47
|
+
*
|
|
48
|
+
* @returns The inserted row with its generated `id`.
|
|
49
|
+
*/
|
|
50
|
+
write(input: ComposedPromptWriteInput): ComposedPrompt;
|
|
51
|
+
/**
|
|
52
|
+
* Look up a composed prompt by (agent_slug, context_hash).
|
|
53
|
+
*
|
|
54
|
+
* Returns the most recently written row for this cache key, or null if no
|
|
55
|
+
* entry exists. O(1) via the `registry_composed_prompts_agent_hash_idx` index.
|
|
56
|
+
*
|
|
57
|
+
* @param agentSlug - The agent whose cached prompt to retrieve.
|
|
58
|
+
* @param hash - The context hash produced by `contextHash()`.
|
|
59
|
+
* @returns The composed prompt row, or null on a cache miss.
|
|
60
|
+
*/
|
|
61
|
+
lookup(agentSlug: string, hash: string): ComposedPrompt | null;
|
|
62
|
+
/**
|
|
63
|
+
* Read a single composed prompt row by its PK id.
|
|
64
|
+
*
|
|
65
|
+
* @param id - The row id returned by `write()`.
|
|
66
|
+
* @returns The composed prompt row.
|
|
67
|
+
* @throws ComposedPromptError('NOT_FOUND') if no row with that id exists.
|
|
68
|
+
*/
|
|
69
|
+
read(id: number): ComposedPrompt;
|
|
70
|
+
private _rowToComposedPrompt;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=composed-prompt-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composed-prompt-store.d.ts","sourceRoot":"","sources":["../../../../../../packages/agent/agent-store-prompts/src/store/composed-prompt-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAqBxE;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CASnE;AAMD,qBAAa,mBAAoB,SAAQ,KAAK;aAChB,IAAI,EAAE,WAAW;gBAAjB,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM;CAI/D;AAMD,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,iEAAiE;AACjE,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAeD,qBAAa,mBAAmB;IAG5B,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,qBAAqB,CAAC,GAAG,CAAC;IAGjD;;;;;;;;;OASG;IACH,KAAK,CAAC,KAAK,EAAE,wBAAwB,GAAG,cAAc;IAmBtD;;;;;;;;;OASG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAkB9D;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc;IAmBhC,OAAO,CAAC,oBAAoB;CAoB7B"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { and, eq } from 'drizzle-orm';
|
|
3
|
+
import { composedPromptsTable } from '../db/schema.js';
|
|
4
|
+
// ──────────────────────────────────────────────
|
|
5
|
+
// context_hash helper
|
|
6
|
+
//
|
|
7
|
+
// Deterministic + stable: sorts the keys of the context map, JSON-serialises
|
|
8
|
+
// the result, then SHA-256s it. The same input map in any key order always
|
|
9
|
+
// produces an identical hash — the precondition for the composed_prompts cache
|
|
10
|
+
// (Decision 2, decisions.md: the total-order assembly rule makes (agent, context)
|
|
11
|
+
// → byte-identical output; the hash encodes the cache key for that output).
|
|
12
|
+
//
|
|
13
|
+
// This function is EXPORTED from the package barrel so @adhd/agent-engine-compiler can
|
|
14
|
+
// reuse the exact same algorithm (it must match — a different hash in the
|
|
15
|
+
// compiler and the registry would produce permanent cache misses).
|
|
16
|
+
//
|
|
17
|
+
// @param context - An arbitrary key/value string map (runtime context).
|
|
18
|
+
// @returns A hex SHA-256 string; same map with any key ordering → same string.
|
|
19
|
+
// ──────────────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Compute a deterministic, order-independent SHA-256 hash for a context map.
|
|
22
|
+
*
|
|
23
|
+
* Sorted-key JSON canonicalization → SHA-256 hex.
|
|
24
|
+
* `{ b: "2", a: "1" }` and `{ a: "1", b: "2" }` both hash to the same value.
|
|
25
|
+
*
|
|
26
|
+
* This is the canonical implementation reused by @adhd/agent-engine-compiler — import
|
|
27
|
+
* it from `@adhd/agent-registry` rather than reimplementing.
|
|
28
|
+
*
|
|
29
|
+
* @param context - Runtime context key/value map.
|
|
30
|
+
* @returns 64-character lowercase hex SHA-256 string.
|
|
31
|
+
*/
|
|
32
|
+
export function contextHash(context) {
|
|
33
|
+
// Sort keys to guarantee identical canonical form regardless of insertion order.
|
|
34
|
+
const sorted = Object.fromEntries(Object.keys(context)
|
|
35
|
+
.sort()
|
|
36
|
+
.map((k) => [k, context[k]]));
|
|
37
|
+
const canonical = JSON.stringify(sorted);
|
|
38
|
+
return createHash('sha256').update(canonical, 'utf8').digest('hex');
|
|
39
|
+
}
|
|
40
|
+
// ──────────────────────────────────────────────
|
|
41
|
+
// Error codes
|
|
42
|
+
// ──────────────────────────────────────────────
|
|
43
|
+
export class ComposedPromptError extends Error {
|
|
44
|
+
code;
|
|
45
|
+
constructor(code, message) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.code = code;
|
|
48
|
+
this.name = 'ComposedPromptError';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// ──────────────────────────────────────────────
|
|
52
|
+
// ComposedPromptStore
|
|
53
|
+
//
|
|
54
|
+
// Thin Drizzle wrapper for registry_composed_prompts.
|
|
55
|
+
// Three methods:
|
|
56
|
+
// write(...) — insert a new cache entry
|
|
57
|
+
// lookup(slug, hash) — O(1) cache lookup by (agent_slug, context_hash)
|
|
58
|
+
// read(id) — read a single row by PK id
|
|
59
|
+
//
|
|
60
|
+
// [ref:store-class] (contexts/_shared.md)
|
|
61
|
+
// [def:composed-prompt] (contexts/_shared.md)
|
|
62
|
+
// ──────────────────────────────────────────────
|
|
63
|
+
export class ComposedPromptStore {
|
|
64
|
+
db;
|
|
65
|
+
constructor(
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
+
db) {
|
|
68
|
+
this.db = db;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Write a new composed prompt cache entry.
|
|
72
|
+
*
|
|
73
|
+
* The caller is responsible for computing the `contextHash` using the
|
|
74
|
+
* exported `contextHash()` helper. Inserting a duplicate (agent_slug,
|
|
75
|
+
* context_hash) is allowed — the cache layer may accumulate redundant rows;
|
|
76
|
+
* `lookup` will return the most recently written one.
|
|
77
|
+
*
|
|
78
|
+
* @returns The inserted row with its generated `id`.
|
|
79
|
+
*/
|
|
80
|
+
write(input) {
|
|
81
|
+
const now = new Date().toISOString();
|
|
82
|
+
const componentVersionsJson = JSON.stringify(input.componentVersions);
|
|
83
|
+
const result = this.db
|
|
84
|
+
.insert(composedPromptsTable)
|
|
85
|
+
.values({
|
|
86
|
+
agentSlug: input.agentSlug,
|
|
87
|
+
contextHash: input.contextHash,
|
|
88
|
+
content: input.content,
|
|
89
|
+
componentVersions: componentVersionsJson,
|
|
90
|
+
createdAt: now,
|
|
91
|
+
})
|
|
92
|
+
.returning()
|
|
93
|
+
.get();
|
|
94
|
+
return this._rowToComposedPrompt(result);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Look up a composed prompt by (agent_slug, context_hash).
|
|
98
|
+
*
|
|
99
|
+
* Returns the most recently written row for this cache key, or null if no
|
|
100
|
+
* entry exists. O(1) via the `registry_composed_prompts_agent_hash_idx` index.
|
|
101
|
+
*
|
|
102
|
+
* @param agentSlug - The agent whose cached prompt to retrieve.
|
|
103
|
+
* @param hash - The context hash produced by `contextHash()`.
|
|
104
|
+
* @returns The composed prompt row, or null on a cache miss.
|
|
105
|
+
*/
|
|
106
|
+
lookup(agentSlug, hash) {
|
|
107
|
+
// Order by id DESC to get the most recently written row first.
|
|
108
|
+
const row = this.db
|
|
109
|
+
.select()
|
|
110
|
+
.from(composedPromptsTable)
|
|
111
|
+
.where(and(eq(composedPromptsTable.agentSlug, agentSlug), eq(composedPromptsTable.contextHash, hash)))
|
|
112
|
+
.orderBy(composedPromptsTable.id)
|
|
113
|
+
.all()
|
|
114
|
+
.at(-1); // last (highest id) = most recently written
|
|
115
|
+
return row ? this._rowToComposedPrompt(row) : null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Read a single composed prompt row by its PK id.
|
|
119
|
+
*
|
|
120
|
+
* @param id - The row id returned by `write()`.
|
|
121
|
+
* @returns The composed prompt row.
|
|
122
|
+
* @throws ComposedPromptError('NOT_FOUND') if no row with that id exists.
|
|
123
|
+
*/
|
|
124
|
+
read(id) {
|
|
125
|
+
const row = this.db
|
|
126
|
+
.select()
|
|
127
|
+
.from(composedPromptsTable)
|
|
128
|
+
.where(eq(composedPromptsTable.id, id))
|
|
129
|
+
.get();
|
|
130
|
+
if (!row) {
|
|
131
|
+
throw new ComposedPromptError('NOT_FOUND', `Composed prompt id=${id} not found`);
|
|
132
|
+
}
|
|
133
|
+
return this._rowToComposedPrompt(row);
|
|
134
|
+
}
|
|
135
|
+
// ── Private helpers ───────────────────────
|
|
136
|
+
_rowToComposedPrompt(row) {
|
|
137
|
+
return {
|
|
138
|
+
id: row.id,
|
|
139
|
+
agentSlug: row.agentSlug,
|
|
140
|
+
contextHash: row.contextHash,
|
|
141
|
+
content: row.content,
|
|
142
|
+
componentVersions: JSON.parse(row.componentVersions),
|
|
143
|
+
createdAt: row.createdAt,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=composed-prompt-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composed-prompt-store.js","sourceRoot":"","sources":["../../../../../../packages/agent/agent-store-prompts/src/store/composed-prompt-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
|
|
2
|
+
import type { PromptComponent } from './component-store.js';
|
|
3
|
+
export declare class CompositionError extends Error {
|
|
4
|
+
readonly code: 'AGENT_NOT_FOUND' | 'COMPONENT_VERSION_NOT_FOUND' | 'REQUIRED_COMPONENT_EXCLUDED';
|
|
5
|
+
constructor(code: 'AGENT_NOT_FOUND' | 'COMPONENT_VERSION_NOT_FOUND' | 'REQUIRED_COMPONENT_EXCLUDED', message: string);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A runtime context object — arbitrary key/value string pairs.
|
|
9
|
+
* The context-condition evaluator checks that every key in a predicate
|
|
10
|
+
* equals the corresponding value in the context (Decision 2, decisions.md).
|
|
11
|
+
*/
|
|
12
|
+
export type CompositionContext = Record<string, string>;
|
|
13
|
+
/** One row from registry_agent_components with its resolved component payload. */
|
|
14
|
+
export interface ResolvedComponent {
|
|
15
|
+
/** The junction row's component slug. */
|
|
16
|
+
componentSlug: string;
|
|
17
|
+
/** Assembly ordering position from the junction row. */
|
|
18
|
+
position: number;
|
|
19
|
+
/** The actual version resolved (either pinned or latest-at-resolve-time). */
|
|
20
|
+
resolvedVersion: number;
|
|
21
|
+
/** The full component row at the resolved version. */
|
|
22
|
+
component: PromptComponent;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Evaluate a context condition predicate against a runtime context.
|
|
26
|
+
*
|
|
27
|
+
* @param condition - JSON text predicate, or null for "always include".
|
|
28
|
+
* @param ctx - The runtime context key/value map.
|
|
29
|
+
* @returns true if the component should be included for this context.
|
|
30
|
+
*/
|
|
31
|
+
export declare function evaluateCondition(condition: string | null, ctx: CompositionContext): boolean;
|
|
32
|
+
export declare class CompositionStore {
|
|
33
|
+
private readonly db;
|
|
34
|
+
constructor(db: BetterSQLite3Database<any>);
|
|
35
|
+
/**
|
|
36
|
+
* Attach a component to an agent at a given position.
|
|
37
|
+
*
|
|
38
|
+
* Inserts one row into registry_agent_components. No conflict handling —
|
|
39
|
+
* callers must ensure the (agent_slug, component_slug, position) triple
|
|
40
|
+
* is unique or use separate positions for multiple rows at the same position.
|
|
41
|
+
*
|
|
42
|
+
* Pinning ergonomics (Decision 5): the stored `version_pin` is a
|
|
43
|
+
* registry_component_versions.version_id (an ENFORCED nullable FK). Callers may
|
|
44
|
+
* pin either way:
|
|
45
|
+
* - `versionPin` — a version_id directly (already resolved).
|
|
46
|
+
* - `pinVersion` — a human `version` number; this store resolves it to the
|
|
47
|
+
* matching version_id for `componentSlug` via
|
|
48
|
+
* {@link resolvePinVersionId}.
|
|
49
|
+
* Supplying both, or a `pinVersion` for a (slug, version) that does not exist,
|
|
50
|
+
* throws. Omitting both leaves the pin null = resolve latest at resolve-time.
|
|
51
|
+
*/
|
|
52
|
+
attach(input: {
|
|
53
|
+
agentSlug: string;
|
|
54
|
+
componentSlug: string;
|
|
55
|
+
position: number;
|
|
56
|
+
/** A registry_component_versions.version_id to pin to (already resolved). */
|
|
57
|
+
versionPin?: number | null;
|
|
58
|
+
/** A human version number to pin to; resolved to a version_id for componentSlug. */
|
|
59
|
+
pinVersion?: number | null;
|
|
60
|
+
contextCondition?: string | null;
|
|
61
|
+
isRequired?: boolean;
|
|
62
|
+
}): void;
|
|
63
|
+
/**
|
|
64
|
+
* Resolve a `(slug, version)` pair to its registry_component_versions.version_id
|
|
65
|
+
* — the value stored as a junction `version_pin` when pinning an exact version.
|
|
66
|
+
*
|
|
67
|
+
* Throws COMPONENT_VERSION_NOT_FOUND if that exact version row is absent.
|
|
68
|
+
*/
|
|
69
|
+
resolvePinVersionId(componentSlug: string, version: number): number;
|
|
70
|
+
/**
|
|
71
|
+
* Normalize the two pinning inputs into a single nullable version_id.
|
|
72
|
+
* Rejects supplying both forms at once.
|
|
73
|
+
*/
|
|
74
|
+
private _resolvePinInput;
|
|
75
|
+
/**
|
|
76
|
+
* Resolve an agent's composition for a given runtime context.
|
|
77
|
+
*
|
|
78
|
+
* Steps (per composition-junction context + decisions.md):
|
|
79
|
+
* 1. Read all junction rows for the agent ordered by position ASC.
|
|
80
|
+
* 2. For each row, resolve the component version:
|
|
81
|
+
* - version_pin IS NULL → latest version of that component slug.
|
|
82
|
+
* - version_pin IS INT → exactly that version row.
|
|
83
|
+
* 3. Evaluate context_condition with the shared predicate evaluator:
|
|
84
|
+
* - null condition → always included.
|
|
85
|
+
* - non-null JSON → included iff every predicate key matches ctx.
|
|
86
|
+
* Exclude non-matching rows.
|
|
87
|
+
* 4. If a row has is_required = true AND its condition did not match →
|
|
88
|
+
* throw CompositionError('REQUIRED_COMPONENT_EXCLUDED').
|
|
89
|
+
* 5. Return the INCLUDED set ordered by:
|
|
90
|
+
* (position ASC, resolvedVersion DESC, componentSlug ASC)
|
|
91
|
+
* [Decision 2: total, stable, deterministic order]
|
|
92
|
+
*
|
|
93
|
+
* This is the single place ordering + filtering happen — do not duplicate.
|
|
94
|
+
*
|
|
95
|
+
* @param agentSlug - The agent whose composition to resolve.
|
|
96
|
+
* @param ctx - Runtime context key/value map.
|
|
97
|
+
* @returns Ordered, filtered list of resolved components. NOT a rendered prompt.
|
|
98
|
+
* Markdown assembly is @adhd/agent-engine-compiler's responsibility.
|
|
99
|
+
*/
|
|
100
|
+
resolveComposition(agentSlug: string, ctx?: CompositionContext): ResolvedComponent[];
|
|
101
|
+
/**
|
|
102
|
+
* Resolve a junction row to a specific component version.
|
|
103
|
+
*
|
|
104
|
+
* Decision 4 + Decision 5 (decisions.md):
|
|
105
|
+
* - pin IS NULL → max(version) for `slug` at resolution time ("latest").
|
|
106
|
+
* - pin IS INT → the registry_component_versions row whose version_id = pin.
|
|
107
|
+
* That row's `slug` MUST equal the junction's component_slug —
|
|
108
|
+
* a mismatch is an integrity violation and throws.
|
|
109
|
+
*
|
|
110
|
+
* Throws COMPONENT_VERSION_NOT_FOUND if the row does not exist (the DB FK makes
|
|
111
|
+
* this unreachable for a pinned id, but we still guard defensively).
|
|
112
|
+
*/
|
|
113
|
+
private _resolveComponentVersion;
|
|
114
|
+
/** Join a head identity row to one version row into a PromptComponent. */
|
|
115
|
+
private _join;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=composition-store.d.ts.map
|