melchizedek-agents 0.9.6 → 0.16.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/.env.example +272 -0
- package/AGENT_SETUP.md +39 -23
- package/CHANGELOG.md +623 -0
- package/DOCUMENTATION.md +429 -109
- package/QUICKSTART.md +92 -18
- package/README.md +45 -5
- package/config/agents/README.md +46 -1
- package/config/agents/examples/ares.yaml +117 -40
- package/config/agents/examples/assistant.yaml +159 -0
- package/config/agents/examples/augustin.yaml +154 -51
- package/config/agents/examples/cartographers.yaml +91 -23
- package/config/agents/examples/claude.yaml +45 -6
- package/config/agents/examples/council.yaml +23 -8
- package/config/agents/examples/critic.yaml +57 -21
- package/config/agents/examples/delegation.yaml +76 -9
- package/config/agents/examples/hierarchical.yaml +55 -8
- package/config/agents/examples/image_production.yaml +54 -16
- package/config/agents/examples/librarian.yaml +70 -14
- package/config/agents/examples/model_zoo.yaml +105 -29
- package/config/agents/examples/patient_advocate.yaml +102 -34
- package/config/agents/examples/research.yaml +250 -0
- package/config/agents/examples/scribe.yaml +242 -0
- package/config/agents/examples/scriptorium.yaml +87 -24
- package/config/agents/examples/style_council.yaml +31 -9
- package/config/agents/examples/syndicate.yaml +29 -10
- package/config/agents/examples/tutor.yaml +10 -3
- package/config/agents/syndicateSchema.yaml +44 -9
- package/config/agents/templates/README.md +58 -0
- package/config/agents/templates/account_memory.yaml +153 -0
- package/config/agents/templates/case_desk.yaml +188 -0
- package/config/agents/templates/conversational.yaml +110 -0
- package/config/agents/templates/draft_review.yaml +130 -0
- package/config/agents/templates/intake_extractor.yaml +152 -0
- package/config/agents/templates/research_brief.yaml +169 -0
- package/config/agents/templates/research_desk.yaml +152 -0
- package/config/agents/templates/review_panel.yaml +188 -0
- package/config/agents/templates/support_triage.yaml +273 -0
- package/config/agents/templates/systems_operator.yaml +157 -0
- package/db/hardening.sql +202 -0
- package/db/memory_v2.sql +77 -0
- package/db/migrations/0001_base.sql +167 -0
- package/db/migrations/0002_erase_scope.sql +172 -0
- package/db/migrations/0003_postgres_storage.sql +58 -0
- package/db/migrations/0004_usage.sql +94 -0
- package/db/migrations/0005_agent_registry.sql +200 -0
- package/db/migrations/0006_task_leases.sql +25 -0
- package/db/migrations/0007_memory_commit.sql +134 -0
- package/db/migrations/0008_memory_retention.sql +37 -0
- package/db/migrations/0009_task_queue.sql +51 -0
- package/db/migrations/0010_erase_complete.sql +180 -0
- package/db/telemetry.sql +401 -0
- package/demo/README.md +77 -0
- package/demo/a2a_demo.mjs +95 -0
- package/dist/lib/a2a/app.d.ts +253 -0
- package/dist/lib/a2a/app.js +751 -0
- package/dist/lib/a2a/executor.d.ts +167 -0
- package/dist/lib/a2a/executor.js +548 -0
- package/dist/lib/a2a/identity.d.ts +122 -0
- package/dist/lib/a2a/identity.js +254 -0
- package/dist/lib/a2a/policy.d.ts +110 -0
- package/dist/lib/a2a/policy.js +178 -0
- package/dist/lib/a2a/redisLimits.d.ts +26 -0
- package/dist/lib/a2a/redisLimits.js +31 -0
- package/dist/lib/a2a/remoteAgent.d.ts +91 -0
- package/dist/lib/a2a/remoteAgent.js +268 -0
- package/dist/lib/a2a/turnLock.d.ts +28 -0
- package/dist/lib/a2a/turnLock.js +76 -0
- package/dist/lib/compile.d.ts +11 -1
- package/dist/lib/compile.js +109 -8
- package/dist/lib/config.d.ts +7 -7
- package/dist/lib/config.js +7 -7
- package/dist/lib/doctor.d.ts +145 -0
- package/dist/lib/doctor.js +517 -0
- package/dist/lib/grounding.d.ts +2 -2
- package/dist/lib/grounding.js +2 -2
- package/dist/lib/guards/index.d.ts +37 -0
- package/dist/lib/guards/index.js +66 -0
- package/dist/lib/guards/science.d.ts +117 -0
- package/dist/lib/guards/science.js +444 -0
- package/dist/lib/index.d.ts +37 -2
- package/dist/lib/index.js +35 -4
- package/dist/lib/loadEnv.d.ts +23 -11
- package/dist/lib/loadEnv.js +77 -29
- package/dist/lib/loadSyndicate.d.ts +97 -5
- package/dist/lib/loadSyndicate.js +176 -76
- package/dist/lib/memory/erase.d.ts +24 -0
- package/dist/lib/memory/erase.js +47 -0
- package/dist/lib/memory/namespace.d.ts +37 -0
- package/dist/lib/memory/namespace.js +74 -0
- package/dist/lib/memory/providers.d.ts +99 -0
- package/dist/lib/memory/providers.js +240 -0
- package/dist/lib/memory/store.d.ts +74 -0
- package/dist/lib/memory/store.js +108 -0
- package/dist/lib/memory/supabaseMemoryService.d.ts +69 -9
- package/dist/lib/memory/supabaseMemoryService.js +239 -125
- package/dist/lib/models/capabilities.d.ts +103 -0
- package/dist/lib/models/capabilities.js +291 -0
- package/dist/lib/models/claudeLlm.d.ts +10 -1
- package/dist/lib/models/claudeLlm.js +57 -27
- package/dist/lib/models/endpoints.d.ts +100 -0
- package/dist/lib/models/endpoints.js +282 -0
- package/dist/lib/models/gateway.d.ts +107 -0
- package/dist/lib/models/gateway.js +157 -0
- package/dist/lib/models/gatewayLlm.d.ts +57 -0
- package/dist/lib/models/gatewayLlm.js +105 -0
- package/dist/lib/models/gptLlm.d.ts +51 -2
- package/dist/lib/models/gptLlm.js +169 -26
- package/dist/lib/models/grokLlm.d.ts +19 -10
- package/dist/lib/models/grokLlm.js +29 -14
- package/dist/lib/models/ollamaLlm.d.ts +26 -0
- package/dist/lib/models/ollamaLlm.js +43 -1
- package/dist/lib/models/openAiCompatibleLlm.d.ts +67 -2
- package/dist/lib/models/openAiCompatibleLlm.js +200 -37
- package/dist/lib/models/providerMap.js +9 -2
- package/dist/lib/models/registry.d.ts +42 -5
- package/dist/lib/models/registry.js +140 -29
- package/dist/lib/models/retry.d.ts +146 -0
- package/dist/lib/models/retry.js +305 -0
- package/dist/lib/models/schemaNormalize.d.ts +20 -0
- package/dist/lib/models/schemaNormalize.js +43 -0
- package/dist/lib/net/addressGuard.d.ts +60 -0
- package/dist/lib/net/addressGuard.js +216 -0
- package/dist/lib/observability/embeddings.d.ts +11 -2
- package/dist/lib/observability/embeddings.js +18 -14
- package/dist/lib/observability/metrics.d.ts +36 -0
- package/dist/lib/observability/metrics.js +86 -0
- package/dist/lib/observability/redact.d.ts +29 -0
- package/dist/lib/observability/redact.js +102 -0
- package/dist/lib/observability/supabaseSpanExporter.d.ts +2 -0
- package/dist/lib/observability/supabaseSpanExporter.js +22 -6
- package/dist/lib/observability/tracer.d.ts +10 -0
- package/dist/lib/observability/tracer.js +70 -3
- package/dist/lib/persistence/supabaseProvider.d.ts +11 -0
- package/dist/lib/persistence/supabaseProvider.js +32 -6
- package/dist/lib/registry.d.ts +87 -0
- package/dist/lib/registry.js +146 -0
- package/dist/lib/runtime/approvals.d.ts +44 -0
- package/dist/lib/runtime/approvals.js +67 -0
- package/dist/lib/runtime/syndicateTurn.d.ts +233 -0
- package/dist/lib/runtime/syndicateTurn.js +597 -0
- package/dist/lib/runtime/turnControl.d.ts +90 -0
- package/dist/lib/runtime/turnControl.js +131 -0
- package/dist/lib/session/supabaseSessionService.js +1 -1
- package/dist/lib/session/transcript.d.ts +9 -1
- package/dist/lib/session/transcript.js +15 -7
- package/dist/lib/skills.d.ts +50 -0
- package/dist/lib/skills.js +138 -0
- package/dist/lib/storage/postgres/index.d.ts +98 -0
- package/dist/lib/storage/postgres/index.js +146 -0
- package/dist/lib/storage/postgres/memoryStore.d.ts +11 -0
- package/dist/lib/storage/postgres/memoryStore.js +82 -0
- package/dist/lib/storage/postgres/sessionService.d.ts +51 -0
- package/dist/lib/storage/postgres/sessionService.js +225 -0
- package/dist/lib/storage/postgres/taskQueue.d.ts +22 -0
- package/dist/lib/storage/postgres/taskQueue.js +101 -0
- package/dist/lib/storage/postgres/taskStore.d.ts +55 -0
- package/dist/lib/storage/postgres/taskStore.js +168 -0
- package/dist/lib/storage/schema.d.ts +27 -0
- package/dist/lib/storage/schema.js +50 -0
- package/dist/lib/storage/schemaVersion.d.ts +25 -0
- package/dist/lib/storage/schemaVersion.js +46 -0
- package/dist/lib/syndicateSchema.d.ts +275 -0
- package/dist/lib/syndicateSchema.js +534 -0
- package/dist/lib/toolRegistry.d.ts +15 -0
- package/dist/lib/toolRegistry.js +59 -10
- package/dist/lib/tools/collectionsSearchTool.js +1 -1
- package/dist/lib/tools/mcpServe.d.ts +45 -0
- package/dist/lib/tools/mcpServe.js +88 -0
- package/dist/lib/tools/mcpToolFactory.d.ts +2 -0
- package/dist/lib/tools/mcpToolFactory.js +41 -21
- package/dist/lib/tools/science/corrections.d.ts +42 -0
- package/dist/lib/tools/science/corrections.js +115 -0
- package/dist/lib/tools/science/europepmc.d.ts +51 -0
- package/dist/lib/tools/science/europepmc.js +127 -0
- package/dist/lib/tools/science/http.d.ts +31 -0
- package/dist/lib/tools/science/http.js +104 -0
- package/dist/lib/tools/science/openalex.d.ts +30 -0
- package/dist/lib/tools/science/openalex.js +81 -0
- package/dist/lib/tools/science/trials.d.ts +58 -0
- package/dist/lib/tools/science/trials.js +203 -0
- package/dist/lib/tools/science/types.d.ts +69 -0
- package/dist/lib/tools/science/types.js +48 -0
- package/dist/lib/tools/scienceTools.d.ts +100 -0
- package/dist/lib/tools/scienceTools.js +277 -0
- package/dist/lib/tools/taskTools.d.ts +204 -0
- package/dist/lib/tools/taskTools.js +374 -0
- package/dist/lib/tools/toolContract.d.ts +17 -4
- package/dist/lib/tools/toolContract.js +13 -5
- package/dist/lib/tools/webExtractTool.d.ts +13 -10
- package/dist/lib/tools/webExtractTool.js +23 -55
- package/dist/lib/tools/webSearchTool.d.ts +2 -1
- package/dist/lib/tools/webSearchTool.js +4 -3
- package/dist/lib/tools/wikiTools.d.ts +2 -2
- package/dist/lib/tools/wikiTools.js +12 -12
- package/dist/lib/tools/xApiSearchTool.d.ts +194 -0
- package/dist/lib/tools/xApiSearchTool.js +583 -0
- package/dist/lib/tools/xSearchTool.js +1 -1
- package/dist/lib/wiki/agentRun.js +1 -1
- package/dist/lib/wiki/builder.js +21 -6
- package/dist/lib/wiki/dive.js +2 -2
- package/dist/lib/wiki/entities.d.ts +2 -2
- package/dist/lib/wiki/entities.js +7 -7
- package/dist/lib/wiki/extract.js +5 -1
- package/dist/lib/wiki/fill.js +5 -5
- package/dist/lib/wiki/format.d.ts +7 -5
- package/dist/lib/wiki/format.js +2 -2
- package/dist/lib/wiki/graph.js +2 -2
- package/dist/lib/wiki/lint.js +2 -2
- package/dist/lib/wiki/markdown.d.ts +1 -1
- package/dist/lib/wiki/markdown.js +6 -1
- package/dist/lib/wiki/pipeline.js +2 -2
- package/dist/lib/wiki/vault.js +2 -2
- package/dist/scripts/a2a_server.d.ts +17 -11
- package/dist/scripts/a2a_server.js +405 -1028
- package/dist/scripts/assistant_worker.d.ts +33 -0
- package/dist/scripts/assistant_worker.js +193 -0
- package/dist/scripts/db.d.ts +26 -0
- package/dist/scripts/db.js +179 -0
- package/dist/scripts/doctor.d.ts +23 -0
- package/dist/scripts/doctor.js +90 -0
- package/dist/scripts/init.d.ts +43 -0
- package/dist/scripts/init.js +152 -0
- package/dist/scripts/registry.d.ts +25 -0
- package/dist/scripts/registry.js +182 -0
- package/dist/scripts/skills_install.d.ts +7 -0
- package/dist/scripts/skills_install.js +137 -0
- package/dist/scripts/syndicate_chat.js +159 -276
- package/package.json +90 -16
- package/skills/README.md +74 -0
- package/skills/briefs/README.md +16 -0
- package/skills/briefs/_shared.md +16 -0
- package/skills/briefs/melchizedek-author.md +60 -0
- package/skills/briefs/melchizedek-memory.md +47 -0
- package/skills/briefs/melchizedek-models.md +47 -0
- package/skills/briefs/melchizedek-scribe.md +37 -0
- package/skills/briefs/melchizedek-serve.md +56 -0
- package/skills/briefs/melchizedek.md +69 -0
- package/skills/briefs/skills-readme.md +33 -0
- package/skills/melchizedek/SKILL.md +159 -0
- package/skills/melchizedek-author/SKILL.md +99 -0
- package/skills/melchizedek-author/templates/minimal.yaml +60 -0
- package/skills/melchizedek-memory/SKILL.md +138 -0
- package/skills/melchizedek-models/SKILL.md +137 -0
- package/skills/melchizedek-scribe/SKILL.md +72 -0
- package/skills/melchizedek-scribe/templates/brief.md +27 -0
- package/skills/melchizedek-serve/SKILL.md +119 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { memoryProvidersFromEnv, modelExtractor } from './providers.js';
|
|
2
|
+
import { providerForModel } from '../models/providerMap.js';
|
|
3
|
+
import { isSupabaseClient, supabaseMemoryStore } from './store.js';
|
|
3
4
|
/**
|
|
4
|
-
* Harness-injected blocks
|
|
5
|
-
*
|
|
5
|
+
* Harness-injected blocks a calling surface prefixes to a user message: the
|
|
6
|
+
* `[System Context: …]` date marker and a labelled data block (`[… Payload]`
|
|
7
|
+
* or `[… Sheet]` followed by a fenced block).
|
|
6
8
|
* Neither is something the USER said. Stripped from the memory SEARCH query
|
|
7
9
|
* (a portfolio JSON would dominate the embedding and drown the question) and
|
|
8
10
|
* from the extraction transcript (a snapshot that changes daily would be
|
|
@@ -12,7 +14,9 @@ import { EMBEDDING_MODEL, EMBEDDING_DIMENSIONS, MEMORY_EXTRACTION_MODEL } from "
|
|
|
12
14
|
export function stripHarnessBlocks(text) {
|
|
13
15
|
return text
|
|
14
16
|
.replace(/\[System Context:[^\]]*\]\s*/g, '')
|
|
15
|
-
|
|
17
|
+
// A data block a calling surface attaches: a bracketed label ending in
|
|
18
|
+
// "Payload" or "Sheet" (e.g. `[Account Payload]`), then a fenced block.
|
|
19
|
+
.replace(/\[[A-Za-z][A-Za-z ]{0,60}(?:Payload|Sheet)\]\s*```[\s\S]*?```\s*/g, '')
|
|
16
20
|
.trim();
|
|
17
21
|
}
|
|
18
22
|
const FACT_EXTRACTION_PROMPT = `You are a precise record-keeping engine. Your job is to analyze a conversation transcript and distill it into structured, self-contained memory records that can be trusted and evolved in future conversations.
|
|
@@ -100,21 +104,47 @@ const MONTH_NAMES = [
|
|
|
100
104
|
'july', 'august', 'september', 'october', 'november', 'december',
|
|
101
105
|
];
|
|
102
106
|
export class SupabaseVectorMemoryService {
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
extractor;
|
|
108
|
+
embedder;
|
|
109
|
+
store;
|
|
105
110
|
/**
|
|
106
111
|
* How many of each session's events have already been distilled.
|
|
107
112
|
* Keyed `{userKey}::{sessionId}`.
|
|
108
113
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
114
|
+
* The durable marker lives in the store when it has one
|
|
115
|
+
* (`melchizedek_memory_ingest`, migration 0007), committed with the
|
|
116
|
+
* facts; this map is the fallback for a store without it, where a restart
|
|
117
|
+
* re-reads a session once and the semantic dedup absorbs that batch.
|
|
112
118
|
*/
|
|
113
119
|
ingestedEventCount = new Map();
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
/** The server's Gemini key, for a per-syndicate Gemini extraction model. */
|
|
121
|
+
geminiKey;
|
|
122
|
+
/** Extractors for per-syndicate models (memory_extraction_model), built once. */
|
|
123
|
+
extractors = new Map();
|
|
124
|
+
buildExtractor;
|
|
125
|
+
/**
|
|
126
|
+
* @param config.apiKey The server's Gemini key, used only when the
|
|
127
|
+
* configured extraction model or embedder is Gemini (the default).
|
|
128
|
+
* @param config.extractor / config.embedder What computes memory
|
|
129
|
+
* (lib/memory/providers.ts, ADR 0020). Default: from the environment
|
|
130
|
+
* (MEMORY_EXTRACTION_MODEL, MEMORY_EMBEDDING_*), which is Gemini when
|
|
131
|
+
* nothing is set.
|
|
132
|
+
*/
|
|
133
|
+
/**
|
|
134
|
+
* @param backend Where facts live: a Supabase client (REST), or any
|
|
135
|
+
* MemoryStore, e.g. the direct-Postgres one (lib/storage/postgres).
|
|
136
|
+
*/
|
|
137
|
+
constructor(config, backend) {
|
|
138
|
+
const fromEnv = config.extractor && config.embedder
|
|
139
|
+
? undefined
|
|
140
|
+
: memoryProvidersFromEnv(process.env, config.apiKey);
|
|
141
|
+
this.extractor = config.extractor ?? fromEnv.extractor;
|
|
142
|
+
this.geminiKey = config.apiKey;
|
|
143
|
+
this.buildExtractor = config.extractorFor;
|
|
144
|
+
this.embedder = config.embedder ?? fromEnv.embedder;
|
|
145
|
+
this.store = isSupabaseClient(backend) ? supabaseMemoryStore(backend) : backend;
|
|
146
|
+
console.log(`[MemoryService] Initialized — backend: Supabase Vector Search (pgvector), structured records; `
|
|
147
|
+
+ `extraction ${this.extractor.model}, embeddings ${this.embedder.provider}/${this.embedder.model} (${this.embedder.dimensions}d)`);
|
|
118
148
|
}
|
|
119
149
|
/**
|
|
120
150
|
* @param extractionRules Optional domain rules appended to the shared
|
|
@@ -123,10 +153,20 @@ export class SupabaseVectorMemoryService {
|
|
|
123
153
|
* "never store a market quote" — arrives here rather than being edited
|
|
124
154
|
* into it. Declared as `memory_extraction_rules` on the served syndicate.
|
|
125
155
|
*/
|
|
126
|
-
async addSessionToMemory(session, extractionRules) {
|
|
156
|
+
async addSessionToMemory(session, extractionRules, options = {}) {
|
|
127
157
|
const userKey = `${session.appName}/${session.userId}`;
|
|
128
158
|
const watermarkKey = `${userKey}::${session.id}`;
|
|
129
|
-
|
|
159
|
+
let alreadyIngested = this.ingestedEventCount.get(watermarkKey) ?? 0;
|
|
160
|
+
// The durable marker (migration 0007), when the store has one: it
|
|
161
|
+
// survives a restart, so a turn is never distilled twice.
|
|
162
|
+
if (this.store.ingestedEvents) {
|
|
163
|
+
try {
|
|
164
|
+
alreadyIngested = Math.max(alreadyIngested, await this.store.ingestedEvents(userKey, session.id));
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
console.error(`[MemoryService] Processed marker unreadable (using this process's):`, err instanceof Error ? err.message : err);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
130
170
|
const fresh = eventsToIngest(session.events ?? [], alreadyIngested);
|
|
131
171
|
if (fresh.length === 0) {
|
|
132
172
|
console.log(`[MemoryService] Session ${session.id}: no new turns since last ingestion — skipping.`);
|
|
@@ -139,18 +179,43 @@ export class SupabaseVectorMemoryService {
|
|
|
139
179
|
}
|
|
140
180
|
console.log(`[MemoryService] Extracting records from session: ${session.id} `
|
|
141
181
|
+ `(${fresh.length} new turn(s) of ${(session.events ?? []).length})`);
|
|
142
|
-
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
|
|
182
|
+
// Every step below THROWS on failure, and the watermark advances only
|
|
183
|
+
// after the records are stored. A provider 429 during extraction, a
|
|
184
|
+
// failed embedding or a rejected insert therefore leaves these turns
|
|
185
|
+
// pending, and the next task in this session retries them. (Before,
|
|
186
|
+
// each failure was swallowed and the watermark moved past turns that
|
|
187
|
+
// were never distilled — a lost fact costs the user something they
|
|
188
|
+
// said.) A retried batch that partly landed is absorbed by the
|
|
189
|
+
// semantic dedup below.
|
|
190
|
+
const total = (session.events ?? []).length;
|
|
191
|
+
const marker = { sessionId: session.id, events: total };
|
|
192
|
+
const advance = () => this.ingestedEventCount.set(watermarkKey, total);
|
|
193
|
+
const records = await this.extractRecords(transcript, extractionRules, this.extractorFor(options.extractionModel));
|
|
146
194
|
if (records.length === 0) {
|
|
195
|
+
// "No facts" is an answer: record that these turns are done.
|
|
196
|
+
if (this.store.commit)
|
|
197
|
+
await this.commitOrThrow(userKey, marker, [], []);
|
|
198
|
+
advance();
|
|
147
199
|
console.log(`[MemoryService] No records extracted from session ${session.id}.`);
|
|
148
200
|
return;
|
|
149
201
|
}
|
|
150
202
|
console.log(`[MemoryService] Extracted ${records.length} record(s) from session ${session.id}.`);
|
|
151
203
|
const embeddings = await this.embedTexts(records.map(r => r.line));
|
|
204
|
+
if (this.store.commit) {
|
|
205
|
+
// One transaction: the new facts, the rows they supersede, and the
|
|
206
|
+
// marker. A failure leaves all three as they were, and the turns
|
|
207
|
+
// pending for the next task to retry (ADR 0020 item 6).
|
|
208
|
+
const payload = await this.prepareFacts(userKey, records, embeddings);
|
|
209
|
+
const kept = new Set(payload.map(p => p.fact));
|
|
210
|
+
const retire = await this.findRetirements(userKey, records.filter(r => r.supersedes && kept.has(r.line)), new Set());
|
|
211
|
+
const inserted = await this.commitOrThrow(userKey, marker, payload, retire);
|
|
212
|
+
advance();
|
|
213
|
+
console.log(`[MemoryService] Stored ${inserted.length} record(s), superseded ${retire.length}, for user key: ${userKey}`);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
152
216
|
const inserted = await this.upsertToSupabase(userKey, records, embeddings);
|
|
153
217
|
await this.applySupersessions(userKey, records, inserted);
|
|
218
|
+
advance();
|
|
154
219
|
console.log(`[MemoryService] Stored ${inserted.size} record(s) for user key: ${userKey}`);
|
|
155
220
|
}
|
|
156
221
|
/**
|
|
@@ -163,15 +228,15 @@ export class SupabaseVectorMemoryService {
|
|
|
163
228
|
* `adk_sessions` and must be cleared separately if full erasure is needed.
|
|
164
229
|
*/
|
|
165
230
|
async deleteUserMemory(userKey) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
.eq('user_key', userKey);
|
|
170
|
-
if (error) {
|
|
171
|
-
throw new Error(`Memory deletion failed for ${userKey}: ${error.message}`);
|
|
231
|
+
let count;
|
|
232
|
+
try {
|
|
233
|
+
count = await this.store.deleteUser(userKey);
|
|
172
234
|
}
|
|
173
|
-
|
|
174
|
-
|
|
235
|
+
catch (err) {
|
|
236
|
+
throw new Error(`Memory deletion failed for ${userKey}: ${err instanceof Error ? err.message : String(err)}`);
|
|
237
|
+
}
|
|
238
|
+
console.log(`[MemoryService] Deleted ${count} fact(s) for user key: ${userKey}`);
|
|
239
|
+
return count;
|
|
175
240
|
}
|
|
176
241
|
async searchMemory(request) {
|
|
177
242
|
const query = stripHarnessBlocks(request.query) || request.query;
|
|
@@ -179,7 +244,27 @@ export class SupabaseVectorMemoryService {
|
|
|
179
244
|
const userKey = `${request.appName}/${request.userId}`;
|
|
180
245
|
return this.searchSupabase(userKey, query);
|
|
181
246
|
}
|
|
182
|
-
|
|
247
|
+
/**
|
|
248
|
+
* The extractor for a syndicate's `memory_extraction_model` (ADR 0020
|
|
249
|
+
* item 4), or the deployment's. The server's Gemini key reaches only a
|
|
250
|
+
* Gemini model; another provider reads its own key from the environment.
|
|
251
|
+
*/
|
|
252
|
+
extractorFor(model) {
|
|
253
|
+
if (!model || model === this.extractor.model)
|
|
254
|
+
return this.extractor;
|
|
255
|
+
let extractor = this.extractors.get(model);
|
|
256
|
+
if (!extractor) {
|
|
257
|
+
extractor = this.buildExtractor
|
|
258
|
+
? this.buildExtractor(model)
|
|
259
|
+
: modelExtractor({
|
|
260
|
+
model,
|
|
261
|
+
...(this.geminiKey && providerForModel(model) === 'gemini' ? { apiKey: this.geminiKey } : {}),
|
|
262
|
+
});
|
|
263
|
+
this.extractors.set(model, extractor);
|
|
264
|
+
}
|
|
265
|
+
return extractor;
|
|
266
|
+
}
|
|
267
|
+
async extractRecords(transcript, extractionRules, extractor = this.extractor) {
|
|
183
268
|
const sessionDate = new Date().toISOString().slice(0, 10);
|
|
184
269
|
// Empty by default, so a consumer that declares no rules gets exactly
|
|
185
270
|
// the prompt it got before this slot existed.
|
|
@@ -191,15 +276,7 @@ export class SupabaseVectorMemoryService {
|
|
|
191
276
|
.replace('{domain_rules}', domainRules)
|
|
192
277
|
.replace('{transcript}', transcript);
|
|
193
278
|
try {
|
|
194
|
-
const
|
|
195
|
-
model: MEMORY_EXTRACTION_MODEL,
|
|
196
|
-
contents: [{ role: 'user', parts: [{ text: prompt }] }],
|
|
197
|
-
config: {
|
|
198
|
-
temperature: 0.1,
|
|
199
|
-
maxOutputTokens: 4096,
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
const text = response.text?.trim() ?? '';
|
|
279
|
+
const text = (await extractor.extract(prompt)).trim();
|
|
203
280
|
if (!text || text === 'NO_FACTS_EXTRACTED')
|
|
204
281
|
return [];
|
|
205
282
|
return text
|
|
@@ -209,8 +286,7 @@ export class SupabaseVectorMemoryService {
|
|
|
209
286
|
}
|
|
210
287
|
catch (err) {
|
|
211
288
|
const msg = err instanceof Error ? err.message : String(err);
|
|
212
|
-
|
|
213
|
-
return [];
|
|
289
|
+
throw new Error(`Record extraction failed (turns stay pending for retry): ${msg}`);
|
|
214
290
|
}
|
|
215
291
|
}
|
|
216
292
|
/**
|
|
@@ -262,31 +338,13 @@ export class SupabaseVectorMemoryService {
|
|
|
262
338
|
return { line, tag, factDate, source, status, keys, supersedes };
|
|
263
339
|
}
|
|
264
340
|
async embedTexts(texts) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
config: {
|
|
272
|
-
outputDimensionality: EMBEDDING_DIMENSIONS
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
if (response.embeddings && response.embeddings.length > 0) {
|
|
276
|
-
embeddings.push(response.embeddings[0].values ?? []);
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
console.warn(`[MemoryService] Empty embedding returned for: "${text.slice(0, 50)}..."`);
|
|
280
|
-
embeddings.push([]);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
catch (err) {
|
|
284
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
285
|
-
console.error(`[MemoryService] Embedding failed: ${msg}`);
|
|
286
|
-
embeddings.push([]);
|
|
287
|
-
}
|
|
341
|
+
try {
|
|
342
|
+
return await this.embedder.embed(texts);
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
346
|
+
throw new Error(`Embedding failed (turns stay pending for retry): ${msg}`);
|
|
288
347
|
}
|
|
289
|
-
return embeddings;
|
|
290
348
|
}
|
|
291
349
|
serializeEvents(events) {
|
|
292
350
|
const lines = [];
|
|
@@ -314,17 +372,41 @@ export class SupabaseVectorMemoryService {
|
|
|
314
372
|
*/
|
|
315
373
|
async upsertToSupabase(userKey, records, embeddings) {
|
|
316
374
|
const insertedIds = new Map();
|
|
375
|
+
const payload = await this.prepareFacts(userKey, records, embeddings);
|
|
376
|
+
if (payload.length === 0)
|
|
377
|
+
return insertedIds;
|
|
378
|
+
let data;
|
|
379
|
+
try {
|
|
380
|
+
data = await this.store.insert(payload);
|
|
381
|
+
}
|
|
382
|
+
catch (err) {
|
|
383
|
+
throw new Error(`Failed to store memory records (turns stay pending for retry): ${err instanceof Error ? err.message : String(err)}`);
|
|
384
|
+
}
|
|
385
|
+
for (const row of data) {
|
|
386
|
+
insertedIds.set(row.fact, row.id);
|
|
387
|
+
}
|
|
388
|
+
return insertedIds;
|
|
389
|
+
}
|
|
390
|
+
/** The atomic commit, with a failure worded for the operator. */
|
|
391
|
+
async commitOrThrow(userKey, marker, rows, retire) {
|
|
392
|
+
try {
|
|
393
|
+
return await this.store.commit(userKey, marker, rows, retire);
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
throw new Error(`Failed to store memory records (turns stay pending for retry): ${err instanceof Error ? err.message : String(err)}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* The rows worth storing: records not already stored byte for byte and
|
|
401
|
+
* not a semantic restatement of an active row of the same tag.
|
|
402
|
+
*/
|
|
403
|
+
async prepareFacts(userKey, records, embeddings) {
|
|
317
404
|
// First pass: byte-identical lines already stored. Cheap, and catches
|
|
318
405
|
// a genuinely repeated extraction. It is NOT sufficient on its own —
|
|
319
406
|
// the extraction model rephrases, so the semantic pass below is what
|
|
320
407
|
// actually stops "the user has $831 in a forgotten Roth IRA" being
|
|
321
408
|
// stored six ways.
|
|
322
|
-
const
|
|
323
|
-
.from('adk_memory_facts')
|
|
324
|
-
.select('fact')
|
|
325
|
-
.eq('user_key', userKey)
|
|
326
|
-
.in('fact', records.map(r => r.line));
|
|
327
|
-
const known = new Set((existing ?? []).map((row) => row.fact));
|
|
409
|
+
const known = await this.store.existingFacts(userKey, records.map(r => r.line));
|
|
328
410
|
// Second pass: semantic. One similarity probe per surviving candidate —
|
|
329
411
|
// the embedding is already computed, and the same RPC the supersession
|
|
330
412
|
// path uses. A restatement is dropped; the stored row keeps its
|
|
@@ -334,17 +416,16 @@ export class SupabaseVectorMemoryService {
|
|
|
334
416
|
const record = records[i];
|
|
335
417
|
if (embeddings[i].length === 0 || known.has(record.line))
|
|
336
418
|
continue;
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (nearErr) {
|
|
419
|
+
let near;
|
|
420
|
+
try {
|
|
421
|
+
near = await this.store.match(userKey, embeddings[i], 5);
|
|
422
|
+
}
|
|
423
|
+
catch (err) {
|
|
343
424
|
// Fail OPEN: a dedup probe that errors must not silently drop a
|
|
344
425
|
// record. A duplicate is recoverable; a lost fact is not.
|
|
345
|
-
console.error(`[MemoryService] Dedup probe failed (storing anyway):`,
|
|
426
|
+
console.error(`[MemoryService] Dedup probe failed (storing anyway):`, err instanceof Error ? err.message : err);
|
|
346
427
|
}
|
|
347
|
-
|
|
428
|
+
if (near && isSemanticDuplicate(record, near)) {
|
|
348
429
|
console.log(`[MemoryService] Duplicate skipped [${record.tag}]: "${record.line.slice(0, 70)}..."`);
|
|
349
430
|
continue;
|
|
350
431
|
}
|
|
@@ -359,20 +440,7 @@ export class SupabaseVectorMemoryService {
|
|
|
359
440
|
keys: record.keys,
|
|
360
441
|
});
|
|
361
442
|
}
|
|
362
|
-
|
|
363
|
-
return insertedIds;
|
|
364
|
-
const { data, error } = await this.supabase
|
|
365
|
-
.from('adk_memory_facts')
|
|
366
|
-
.insert(payload)
|
|
367
|
-
.select('id, fact');
|
|
368
|
-
if (error) {
|
|
369
|
-
console.error(`[MemoryService] Failed to upsert to Supabase:`, error.message);
|
|
370
|
-
return insertedIds;
|
|
371
|
-
}
|
|
372
|
-
for (const row of data ?? []) {
|
|
373
|
-
insertedIds.set(row.fact, row.id);
|
|
374
|
-
}
|
|
375
|
-
return insertedIds;
|
|
443
|
+
return payload;
|
|
376
444
|
}
|
|
377
445
|
/**
|
|
378
446
|
* The record evolves: a stored record carrying a `supersedes` header
|
|
@@ -385,47 +453,93 @@ export class SupabaseVectorMemoryService {
|
|
|
385
453
|
*/
|
|
386
454
|
async applySupersessions(userKey, records, insertedIds) {
|
|
387
455
|
const corrections = records.filter(r => r.supersedes && insertedIds.has(r.line));
|
|
456
|
+
const retire = await this.findRetirements(userKey, corrections, new Set(insertedIds.values()));
|
|
457
|
+
for (const r of retire) {
|
|
458
|
+
const correctionId = insertedIds.get(r.byFact);
|
|
459
|
+
try {
|
|
460
|
+
await this.store.retire(userKey, r.id, correctionId);
|
|
461
|
+
console.log(`[MemoryService] Superseded ${r.id} → ${correctionId}`);
|
|
462
|
+
}
|
|
463
|
+
catch (err) {
|
|
464
|
+
console.error(`[MemoryService] Failed to retire superseded record:`, err instanceof Error ? err.message : err);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Which active rows each correction retires: rows found by embedding the
|
|
470
|
+
* superseded quote, retired when semantically close and sharing an index
|
|
471
|
+
* key with the correction (or a near-exact match). `exclude` holds ids
|
|
472
|
+
* from this same ingestion, which are never retired.
|
|
473
|
+
*/
|
|
474
|
+
async findRetirements(userKey, corrections, exclude) {
|
|
388
475
|
if (corrections.length === 0)
|
|
389
|
-
return;
|
|
390
|
-
const newIds = new Set(insertedIds.values());
|
|
476
|
+
return [];
|
|
391
477
|
const targetEmbeddings = await this.embedTexts(corrections.map(r => r.supersedes));
|
|
478
|
+
const out = [];
|
|
479
|
+
const taken = new Set();
|
|
392
480
|
for (let i = 0; i < corrections.length; i++) {
|
|
393
481
|
const correction = corrections[i];
|
|
394
482
|
const vec = targetEmbeddings[i];
|
|
395
483
|
if (!vec || vec.length === 0)
|
|
396
484
|
continue;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
console.error(`[MemoryService] Supersession lookup failed:`, error.message);
|
|
485
|
+
let data;
|
|
486
|
+
try {
|
|
487
|
+
data = await this.store.match(userKey, vec, 5);
|
|
488
|
+
}
|
|
489
|
+
catch (err) {
|
|
490
|
+
console.error(`[MemoryService] Supersession lookup failed:`, err instanceof Error ? err.message : err);
|
|
404
491
|
continue;
|
|
405
492
|
}
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
continue; // never retire a record from this same ingestion
|
|
493
|
+
for (const row of data) {
|
|
494
|
+
if (exclude.has(row.id) || taken.has(row.id))
|
|
495
|
+
continue;
|
|
410
496
|
if ((row.status ?? 'active') !== 'active')
|
|
411
497
|
continue;
|
|
412
498
|
const sharesKey = (row.keys ?? []).some(k => correction.keys.includes(k));
|
|
413
499
|
const retire = row.similarity >= 0.85 || (row.similarity >= 0.6 && sharesKey);
|
|
414
500
|
if (!retire)
|
|
415
501
|
continue;
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
.eq('id', row.id)
|
|
420
|
-
.eq('user_key', userKey);
|
|
421
|
-
if (updateError) {
|
|
422
|
-
console.error(`[MemoryService] Failed to retire superseded record:`, updateError.message);
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
console.log(`[MemoryService] Superseded: "${row.fact.slice(0, 60)}..." → ${correctionId}`);
|
|
426
|
-
}
|
|
502
|
+
taken.add(row.id);
|
|
503
|
+
out.push({ id: row.id, byFact: correction.line });
|
|
504
|
+
console.log(`[MemoryService] Supersedes: "${row.fact.slice(0, 60)}..."`);
|
|
427
505
|
}
|
|
428
506
|
}
|
|
507
|
+
return out;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Deletes facts in `namespace` older than `days` (a syndicate's
|
|
511
|
+
* memory_retention_days, ADR 0020 item 7). Returns how many; null when
|
|
512
|
+
* the store cannot prune.
|
|
513
|
+
*/
|
|
514
|
+
async pruneExpired(namespace, days) {
|
|
515
|
+
if (!this.store.pruneNamespace)
|
|
516
|
+
return null;
|
|
517
|
+
const n = await this.store.pruneNamespace(namespace, days);
|
|
518
|
+
if (n)
|
|
519
|
+
console.log(`[MemoryService] Retention: deleted ${n} fact(s) older than ${days} day(s) in ${namespace}.`);
|
|
520
|
+
return n;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Refuses an embedder whose vectors would not fit the stored column
|
|
524
|
+
* (ADR 0020 item 5): every insert would fail, and the fix is a re-embed
|
|
525
|
+
* job, not dropping the table. Silent when the store cannot say.
|
|
526
|
+
*/
|
|
527
|
+
async verifyEmbeddingDimensions() {
|
|
528
|
+
if (!this.store.embeddingDimensions)
|
|
529
|
+
return;
|
|
530
|
+
let stored;
|
|
531
|
+
try {
|
|
532
|
+
stored = await this.store.embeddingDimensions();
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
return; // migration 0007 not applied: the schema check reports that
|
|
536
|
+
}
|
|
537
|
+
if (stored != null && stored !== this.embedder.dimensions) {
|
|
538
|
+
throw new Error(`The memory table stores ${stored}-dimension embeddings, but the embedder `
|
|
539
|
+
+ `${this.embedder.provider}/${this.embedder.model} produces ${this.embedder.dimensions}. `
|
|
540
|
+
+ 'Set MEMORY_EMBEDDING_* to a model of that size, or re-embed the stored facts into a column of the new size; '
|
|
541
|
+
+ 'never drop the table.');
|
|
542
|
+
}
|
|
429
543
|
}
|
|
430
544
|
/**
|
|
431
545
|
* Hybrid recall: candidates come from pgvector cosine similarity, then
|
|
@@ -441,20 +555,20 @@ export class SupabaseVectorMemoryService {
|
|
|
441
555
|
return { memories: [] };
|
|
442
556
|
}
|
|
443
557
|
try {
|
|
444
|
-
//
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
console.error(`[MemoryService] Failed to query
|
|
558
|
+
// The `match_memory_facts` function (db/migrations/0001_base.sql),
|
|
559
|
+
// through whichever store this service was given.
|
|
560
|
+
let data;
|
|
561
|
+
try {
|
|
562
|
+
data = await this.store.match(userKey, queryVec, 24);
|
|
563
|
+
}
|
|
564
|
+
catch (err) {
|
|
565
|
+
console.error(`[MemoryService] Failed to query memory:`, err instanceof Error ? err.message : err);
|
|
452
566
|
return { memories: [] };
|
|
453
567
|
}
|
|
454
568
|
const q = query.toLowerCase();
|
|
455
569
|
const queryYears = q.match(/\b20\d{2}\b/g) ?? [];
|
|
456
570
|
const queryMonths = MONTH_NAMES.filter(mn => q.includes(mn));
|
|
457
|
-
const scored =
|
|
571
|
+
const scored = data.map(row => {
|
|
458
572
|
let score = row.similarity;
|
|
459
573
|
const status = row.status ?? 'active';
|
|
460
574
|
// Index-key channel: the query names an entity the record is filed under.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/models/capabilities.ts — what an agent keeps and loses on the path
|
|
3
|
+
* its model will actually take.
|
|
4
|
+
*
|
|
5
|
+
* WHY this file exists:
|
|
6
|
+
* A model id is the whole routing decision, and some of what an agent
|
|
7
|
+
* declares only exists on one provider: Gemini grounding, xAI's x_search
|
|
8
|
+
* and Collections, the server-side web_search sentinel that every cloud
|
|
9
|
+
* adapter turns into its provider's native search. Until now the only
|
|
10
|
+
* defence against losing one was a one-time console.warn inside an
|
|
11
|
+
* adapter. This module states the loss BEFORE a request is made, per
|
|
12
|
+
* agent, on the RESOLVED transport (direct or gateway), so the doctor
|
|
13
|
+
* (lib/doctor.ts), the A2A startup log (lib/compile.ts) and the ledger
|
|
14
|
+
* (llm.capability.dropped) all say the same thing.
|
|
15
|
+
*
|
|
16
|
+
* It knows nothing about gateways beyond planTransport(); a future
|
|
17
|
+
* transport that restores a native feature changes the table here, not
|
|
18
|
+
* the callers.
|
|
19
|
+
*/
|
|
20
|
+
import type { ProviderId } from './providerMap.ts';
|
|
21
|
+
import type { TransportPlan } from './gateway.ts';
|
|
22
|
+
import type { Platform } from './endpoints.ts';
|
|
23
|
+
/**
|
|
24
|
+
* Server-side tool sentinels and the providers whose DIRECT adapter
|
|
25
|
+
* honours them natively. Anything not listed is a client-side function
|
|
26
|
+
* tool and travels on every path.
|
|
27
|
+
*/
|
|
28
|
+
export declare const SERVER_SIDE_TOOLS: Record<string, ProviderId[]>;
|
|
29
|
+
export interface CapabilityReport {
|
|
30
|
+
model: string;
|
|
31
|
+
provider: ProviderId;
|
|
32
|
+
providerLabel: string;
|
|
33
|
+
transport: TransportPlan['transport'];
|
|
34
|
+
/** Gateway id when transport is 'gateway'. */
|
|
35
|
+
gateway?: string;
|
|
36
|
+
/** The cloud platform of a direct path (ADR 0023); absent on Ollama and the gateway. */
|
|
37
|
+
platform?: Platform;
|
|
38
|
+
/** False when neither a direct key nor a gateway can serve this id. */
|
|
39
|
+
funded: boolean;
|
|
40
|
+
/** The env var that would fund the direct path. */
|
|
41
|
+
keyEnv: string | null;
|
|
42
|
+
/** Declared server-side tools this path runs natively. */
|
|
43
|
+
native: string[];
|
|
44
|
+
/** Declared server-side tools this path cannot run — they are omitted. */
|
|
45
|
+
dropped: string[];
|
|
46
|
+
/** Declared client-side tools; portable across every path. */
|
|
47
|
+
portable: string[];
|
|
48
|
+
}
|
|
49
|
+
export declare function describeCapabilities(model: string, tools?: readonly string[], opts?: {
|
|
50
|
+
callerKey?: boolean;
|
|
51
|
+
}): CapabilityReport;
|
|
52
|
+
/**
|
|
53
|
+
* One line for a startup log, or undefined when there is nothing to say —
|
|
54
|
+
* a funded direct path with no dropped tool is the quiet default.
|
|
55
|
+
*/
|
|
56
|
+
export declare function capabilitySummary(agentName: string, r: CapabilityReport): string | undefined;
|
|
57
|
+
export declare const CAPABILITIES: readonly ['delegation', 'memory_tools', 'structured_output', 'thinking_with_tools', 'streaming', 'vision', 'native_search'];
|
|
58
|
+
export type Capability = (typeof CAPABILITIES)[number];
|
|
59
|
+
export declare const CAPABILITY_LABELS: Record<Capability, string>;
|
|
60
|
+
export type Support = 'supported' | 'degraded' | 'unsupported';
|
|
61
|
+
export interface CapabilityCell {
|
|
62
|
+
support: Support;
|
|
63
|
+
/** What is lost, or what happens instead. Present unless `supported` is the whole story. */
|
|
64
|
+
note?: string;
|
|
65
|
+
evidence: 'test' | 'adk';
|
|
66
|
+
}
|
|
67
|
+
/** Matrix rows: each direct provider, plus the gateway transport. */
|
|
68
|
+
export type MatrixRow = ProviderId | 'gateway';
|
|
69
|
+
export declare const CAPABILITY_MATRIX: Record<MatrixRow, Record<Capability, CapabilityCell>>;
|
|
70
|
+
/** The matrix cell for a model on the path it will actually take. */
|
|
71
|
+
export declare function capabilityOf(model: string, capability: Capability, opts?: {
|
|
72
|
+
callerKey?: boolean;
|
|
73
|
+
}): CapabilityCell & {
|
|
74
|
+
row: MatrixRow;
|
|
75
|
+
};
|
|
76
|
+
/** What one agent's YAML asks of its model. */
|
|
77
|
+
export interface AgentNeedsInput {
|
|
78
|
+
tools?: readonly string[];
|
|
79
|
+
outputSchema?: unknown;
|
|
80
|
+
generateContentConfig?: {
|
|
81
|
+
thinkingConfig?: {
|
|
82
|
+
thinkingBudget?: number;
|
|
83
|
+
includeThoughts?: boolean;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
/** True when this agent delegates to subagents through tools (DELEGATE mode). */
|
|
87
|
+
delegates?: boolean;
|
|
88
|
+
}
|
|
89
|
+
export declare function requiredCapabilities(agent: AgentNeedsInput): Capability[];
|
|
90
|
+
export interface CapabilityGap {
|
|
91
|
+
capability: Capability;
|
|
92
|
+
support: Exclude<Support, 'supported'>;
|
|
93
|
+
row: MatrixRow;
|
|
94
|
+
note?: string;
|
|
95
|
+
}
|
|
96
|
+
/** The capabilities an agent needs that its resolved path does not fully give it. */
|
|
97
|
+
export declare function capabilityGaps(model: string, agent: AgentNeedsInput, opts?: {
|
|
98
|
+
callerKey?: boolean;
|
|
99
|
+
}): CapabilityGap[];
|
|
100
|
+
/** The cell that differs from the provider's row on a platform, or undefined. */
|
|
101
|
+
export declare function platformCell(provider: ProviderId, platform: Platform, capability: Capability): CapabilityCell | undefined;
|
|
102
|
+
/** The matrix as a Markdown table plus notes, for documentation and the doctor. */
|
|
103
|
+
export declare function renderCapabilityMatrix(): string;
|