gencow 0.1.227 → 0.1.229
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/core/index.js +16 -0
- package/lib/component-readme.mjs +23 -10
- package/lib/db-migration-command-steps.mjs +11 -5
- package/lib/init-command.mjs +1 -1
- package/lib/readme-codegen.mjs +10 -10
- package/package.json +7 -3
- package/runtime/server.mjs +1849 -1102
- package/templateFeature/memory/ai.ts +3 -0
- package/templateFeature/memory/manifest.json +11 -6
- package/templateFeature/memory/memory-errors.ts +10 -0
- package/templateFeature/memory/memory-policy.ts +39 -0
- package/templateFeature/memory/memory-scope.ts +135 -0
- package/templateFeature/memory/memory-types.ts +77 -0
- package/templateFeature/memory/memory.ts +502 -262
- package/templateFeature/memory/schema-memory.ts +141 -38
- package/templateFeature/memory/schema-search.ts +3 -0
- package/templateFeature/search/manifest.json +3 -2
- package/templateFeature/search/schema-search.ts +16 -1
- package/templates/SECURITY.md +10 -12
- package/templates/ai-chat/README.md +7 -7
- package/templates/ai-chat/prompt.md +4 -4
- package/templates/fullstack/prompt.md +1 -1
package/core/index.js
CHANGED
|
@@ -268,6 +268,21 @@ function createGencowHttpRouteBuilders() {
|
|
|
268
268
|
};
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
// ../core/src/search-index-contract.ts
|
|
272
|
+
function buildSearchIndexNames(table, field) {
|
|
273
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(table) || !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(field)) {
|
|
274
|
+
throw new Error("Search index identity contains an invalid identifier");
|
|
275
|
+
}
|
|
276
|
+
const identity = `${table}:${field}`;
|
|
277
|
+
let hash = 0xcbf29ce484222325n;
|
|
278
|
+
for (const character of identity) {
|
|
279
|
+
hash ^= BigInt(character.codePointAt(0) ?? 0);
|
|
280
|
+
hash = BigInt.asUintN(64, hash * 0x100000001b3n);
|
|
281
|
+
}
|
|
282
|
+
const prefix = `gencow_search_${hash.toString(16).padStart(16, "0")}`;
|
|
283
|
+
return { fts: `${prefix}_fts_idx`, trigram: `${prefix}_trgm_idx` };
|
|
284
|
+
}
|
|
285
|
+
|
|
271
286
|
// ../core/src/document-types.ts
|
|
272
287
|
function normalizeCachePart(value) {
|
|
273
288
|
return value.trim() || "unknown";
|
|
@@ -5138,6 +5153,7 @@ export {
|
|
|
5138
5153
|
buildRealtimeCtx,
|
|
5139
5154
|
buildRealtimeQueryScopeHash,
|
|
5140
5155
|
buildRealtimeQuerySubscriptionKey,
|
|
5156
|
+
buildSearchIndexNames,
|
|
5141
5157
|
buildWakeAppBootFailedResult,
|
|
5142
5158
|
buildWakeAppSuccessResult,
|
|
5143
5159
|
compareUnreadCursors,
|
package/lib/component-readme.mjs
CHANGED
|
@@ -325,13 +325,14 @@ const sdkReply = await generateText({
|
|
|
325
325
|
ai.chat({ messages, tools }) // respond with tool calls`,
|
|
326
326
|
},
|
|
327
327
|
"memory.ts": {
|
|
328
|
-
title: "🧠
|
|
328
|
+
title: "🧠 Memory Toolkit",
|
|
329
329
|
table: `| Function | Type | Description |
|
|
330
330
|
| :--- | :--- | :--- |
|
|
331
331
|
| \`createMemory()\` | \`factory\` | Compose memory with an extraction model and explicit embedding profile |
|
|
332
|
-
| \`memory.
|
|
333
|
-
| \`memory.search()\` | \`function\` |
|
|
334
|
-
| \`memory.
|
|
332
|
+
| \`memory.remember()\` | \`function\` | Add raw or extracted durable memory with an idempotency receipt |
|
|
333
|
+
| \`memory.search()\` | \`function\` | Owner/scope-isolated hybrid memory retrieval |
|
|
334
|
+
| \`memory.selectContext()\` | \`function\` | Deterministically select untrusted prompt data within a token budget |
|
|
335
|
+
| \`memory.update()\` / \`forget()\` | \`function\` | Revision-safe correction and privacy deletion |`,
|
|
335
336
|
usage: `\`\`\`typescript
|
|
336
337
|
import { createGencowAI } from "@/gencow/ai";
|
|
337
338
|
import { createMemory, memory, MEMORY_EMBEDDING_PROFILE } from "@/gencow/memory";
|
|
@@ -342,17 +343,29 @@ const customMemory = createMemory({
|
|
|
342
343
|
embeddingProfile: MEMORY_EMBEDDING_PROFILE,
|
|
343
344
|
});
|
|
344
345
|
|
|
345
|
-
const
|
|
346
|
+
const receipt = await customMemory.remember(ctx, {
|
|
347
|
+
namespace: "assistant-memory",
|
|
348
|
+
scope: { conversation: conversationId },
|
|
349
|
+
input: [{ role: "user", content: query }],
|
|
350
|
+
mode: "extract",
|
|
351
|
+
source: { type: "message", id: messageId, revision: "1" },
|
|
352
|
+
idempotencyKey: \`memory:\${messageId}:1\`,
|
|
353
|
+
});
|
|
354
|
+
const matches = await customMemory.search(ctx, {
|
|
355
|
+
namespace: "assistant-memory",
|
|
356
|
+
scope: { conversation: conversationId },
|
|
357
|
+
query,
|
|
358
|
+
});
|
|
346
359
|
const reply = await ai.chat({
|
|
347
|
-
|
|
348
|
-
messages: [...memCtx.recentMessages, { role: "user", content: query }],
|
|
360
|
+
messages: [{ role: "user", content: query }],
|
|
349
361
|
});
|
|
350
362
|
\`\`\``,
|
|
351
|
-
vibePrompt: `
|
|
363
|
+
vibePrompt: `Memory Toolkit API:
|
|
352
364
|
import { memory } from "@/gencow/memory";
|
|
353
365
|
createMemory({ extractionModel, embeddingProfile }) // pure AI SDK composition with explicit model+dimensions
|
|
354
|
-
memory.
|
|
355
|
-
memory.
|
|
366
|
+
memory.remember(ctx, input) // raw/extract durable memory
|
|
367
|
+
memory.search(ctx, input) // owner/scope hybrid recall
|
|
368
|
+
memory.selectContext(ctx, input) // app-owned prompt data`,
|
|
356
369
|
},
|
|
357
370
|
"analytics.ts": {
|
|
358
371
|
title: "📊 Analytics",
|
|
@@ -63,11 +63,17 @@ export function handleLocalMigrationCommand({
|
|
|
63
63
|
deps.infoImpl("Pushing schema.ts → database (no migration files)...");
|
|
64
64
|
}
|
|
65
65
|
try {
|
|
66
|
-
deps.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
const env = deps.buildEnv(config);
|
|
67
|
+
const childOptions = jsonMode ? { infoImpl: () => {}, stdio: "pipe" } : undefined;
|
|
68
|
+
if (hasDb) {
|
|
69
|
+
deps.runInServer("pnpm db:push --force", env, childOptions);
|
|
70
|
+
} else {
|
|
71
|
+
// Drizzle Kit cannot attach PGlite's packaged pgvector/pg_trgm modules.
|
|
72
|
+
// Generate the same desired-state migration, then apply it through the
|
|
73
|
+
// runtime connection that owns those extensions.
|
|
74
|
+
deps.runInServer("pnpm db:generate", env, childOptions);
|
|
75
|
+
deps.runInServer("bun src/local-pglite-migrate.ts", env, childOptions);
|
|
76
|
+
}
|
|
71
77
|
} catch (error) {
|
|
72
78
|
if (!jsonMode) throw error;
|
|
73
79
|
emitFailure(buildLocalExecutionFailureDiagnostic(correlationId()));
|
package/lib/init-command.mjs
CHANGED
|
@@ -259,7 +259,7 @@ ${
|
|
|
259
259
|
${CYAN}gencow add sso${RESET} ${DIM}← Add Google SSO login${RESET}
|
|
260
260
|
${CYAN}gencow add Agent${RESET} ${DIM}← Add workflow agent starter${RESET}
|
|
261
261
|
${CYAN}gencow add RAG${RESET} ${DIM}← Add RAG component${RESET}
|
|
262
|
-
${CYAN}gencow add Memory${RESET} ${DIM}← Add
|
|
262
|
+
${CYAN}gencow add Memory${RESET} ${DIM}← Add Memory Toolkit${RESET}
|
|
263
263
|
`
|
|
264
264
|
}
|
|
265
265
|
`;
|
package/lib/readme-codegen.mjs
CHANGED
|
@@ -306,7 +306,8 @@ export function buildAiPrompt(apiObj, namespaces) {
|
|
|
306
306
|
md += `- Use rerank({ model: createGencowAI().rerankingModel(...) }) or ai.rerank() only after retrieval authorization; do not pass private metadata as provider payload.\n`;
|
|
307
307
|
md += `- createGencowAI().rerankingModel(...) is proxy-only today. Local direct mode with only OPENAI_API_KEY must fail fast for reranking.\n`;
|
|
308
308
|
md += `- The generated reranker starter may use an explicit fallbackModel compatibility path, but strict providerPreference such as azure_cohere must still fail closed on auth/config errors.\n`;
|
|
309
|
-
md += `-
|
|
309
|
+
md += `- Use stable virtual profiles for every new hosted chat call: llm/economy for Hobby, llm/balanced for general Startup/Enterprise workloads, llm/quality for maximum quality, and llm/code for tool-using code work.\n`;
|
|
310
|
+
md += `- Direct chat model IDs are only for local direct-provider development, compatibility, or explicitly verified model-specific capabilities.\n`;
|
|
310
311
|
md += `- Use ai.generateObject() with a Zod schema for structured output. Do not use ai.chat() + JSON.parse().\n`;
|
|
311
312
|
md += `- Install AI with gencow add AI, RAG with gencow add RAG, Memory with gencow add Memory, and durable agents with gencow add Agent.\n`;
|
|
312
313
|
md += `\`\`\`\n\n`;
|
|
@@ -315,14 +316,14 @@ export function buildAiPrompt(apiObj, namespaces) {
|
|
|
315
316
|
|
|
316
317
|
export function buildAiUsageSection() {
|
|
317
318
|
let md = `---\n\n## AI Usage\n\n`;
|
|
318
|
-
md += `Gencow
|
|
319
|
-
md +=
|
|
319
|
+
md += `Gencow recommends stable virtual chat profiles for every new hosted chat call: \`llm/economy\` on Hobby and \`llm/balanced\`, \`llm/quality\`, or \`llm/code\` on Startup/Enterprise. The platform can replace each profile's concrete model without requiring app changes. Direct chat model IDs remain compatibility or model-specific escape hatches, not recommended defaults. Image and embedding models use their dedicated model IDs.\n`;
|
|
320
|
+
md += `The stable \`llm/*\` profiles are resolved by the hosted Gencow AI proxy. \`OPENAI_API_KEY\` is only for local direct/fallback usage, where code must keep using an OpenAI concrete model ID. Cloud deployments use platform-managed internal credentials and service credits.\n\n`;
|
|
320
321
|
md += `\`\`\`typescript\n`;
|
|
321
322
|
md += `import { generateImage, generateText, rerank } from "ai";\n`;
|
|
322
323
|
md += `import { createGencowAI, ai } from "./ai";\n\n`;
|
|
323
324
|
md += `const gencow = createGencowAI();\n\n`;
|
|
324
325
|
md += `const result = await generateText({\n`;
|
|
325
|
-
md += ` model: gencow.languageModel("
|
|
326
|
+
md += ` model: gencow.languageModel("llm/balanced"),\n`;
|
|
326
327
|
md += ` system: "You are a helpful assistant.",\n`;
|
|
327
328
|
md += ` messages: [{ role: "user", content: "Hello" }],\n`;
|
|
328
329
|
md += `});\n\n`;
|
|
@@ -355,12 +356,11 @@ export function buildAiUsageSection() {
|
|
|
355
356
|
md += `\`\`\`\n\n`;
|
|
356
357
|
md += `| Purpose | Recommended model |\n`;
|
|
357
358
|
md += `|---|---|\n`;
|
|
358
|
-
md += `|
|
|
359
|
-
md += `| Most production chat
|
|
360
|
-
md += `|
|
|
361
|
-
md += `|
|
|
362
|
-
md += `|
|
|
363
|
-
md += `| Image-to-text / OCR-style extraction | \`gpt-5.4-mini\` or \`gpt-5-mini\` |\n`;
|
|
359
|
+
md += `| Lowest-cost chat, classification, and extraction | \`llm/economy\` |\n`;
|
|
360
|
+
md += `| Most production chat and agent tasks | \`llm/balanced\` (Startup/Enterprise) |\n`;
|
|
361
|
+
md += `| Highest quality / complex reasoning | \`llm/quality\` (Startup/Enterprise) |\n`;
|
|
362
|
+
md += `| Coding and function tools | \`llm/code\` (Startup/Enterprise) |\n`;
|
|
363
|
+
md += `| Image-to-text / OCR-style extraction | \`llm/balanced\` |\n`;
|
|
364
364
|
md += `| Image generation | \`gpt-image-2\` or \`gpt-image-1-mini\` |\n\n`;
|
|
365
365
|
md += `Do not wire \`@ai-sdk/openai\` or provider keys directly. Use \`createGencowAI()\` or the \`ai.*\` facade from \`./ai\`.\n\n`;
|
|
366
366
|
md += `\`\`\`typescript\n`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gencow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.229",
|
|
4
4
|
"description": "Gencow — AI Backend Engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,11 +31,15 @@
|
|
|
31
31
|
"zod": "^4.4.3"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
+
"@ai-sdk/openai": "^3.0.0",
|
|
35
|
+
"@ai-sdk/provider": "^3.0.0",
|
|
36
|
+
"@ai-sdk/provider-utils": "^4.0.0",
|
|
37
|
+
"ai": "^6.0.233",
|
|
34
38
|
"@types/node": "^25.9.5",
|
|
35
39
|
"better-auth": "^1.6.23",
|
|
36
|
-
"@gencow/
|
|
40
|
+
"@gencow/core": "0.1.44",
|
|
37
41
|
"@gencow/react": "0.2.7",
|
|
38
|
-
"@gencow/
|
|
42
|
+
"@gencow/client": "0.2.7"
|
|
39
43
|
},
|
|
40
44
|
"scripts": {
|
|
41
45
|
"prebuild": "pnpm --filter @gencow/migration-contract run build && pnpm --filter @gencow/server run build",
|