@retinue/agentkit 0.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/LICENSE +22 -0
- package/README.md +310 -0
- package/dist/adapters/bullmq/consumer.d.ts +33 -0
- package/dist/adapters/bullmq/consumer.js +41 -0
- package/dist/adapters/bullmq/dispatcher.d.ts +74 -0
- package/dist/adapters/bullmq/dispatcher.js +160 -0
- package/dist/adapters/bullmq/export.d.ts +31 -0
- package/dist/adapters/bullmq/export.js +53 -0
- package/dist/adapters/bullmq/extraction.d.ts +42 -0
- package/dist/adapters/bullmq/extraction.js +63 -0
- package/dist/adapters/bullmq/index.d.ts +13 -0
- package/dist/adapters/bullmq/index.js +13 -0
- package/dist/adapters/bullmq/lock.d.ts +77 -0
- package/dist/adapters/bullmq/lock.js +126 -0
- package/dist/adapters/bullmq/queue.d.ts +50 -0
- package/dist/adapters/bullmq/queue.js +81 -0
- package/dist/adapters/memory/artifact-exports.d.ts +11 -0
- package/dist/adapters/memory/artifact-exports.js +102 -0
- package/dist/adapters/memory/artifacts.d.ts +15 -0
- package/dist/adapters/memory/artifacts.js +134 -0
- package/dist/adapters/memory/blobs.d.ts +7 -0
- package/dist/adapters/memory/blobs.js +27 -0
- package/dist/adapters/memory/evaluation.d.ts +18 -0
- package/dist/adapters/memory/evaluation.js +148 -0
- package/dist/adapters/memory/files.d.ts +27 -0
- package/dist/adapters/memory/files.js +0 -0
- package/dist/adapters/memory/flows.d.ts +16 -0
- package/dist/adapters/memory/flows.js +117 -0
- package/dist/adapters/memory/hitl.d.ts +9 -0
- package/dist/adapters/memory/hitl.js +130 -0
- package/dist/adapters/memory/idempotency.d.ts +13 -0
- package/dist/adapters/memory/idempotency.js +32 -0
- package/dist/adapters/memory/index.d.ts +39 -0
- package/dist/adapters/memory/index.js +107 -0
- package/dist/adapters/memory/knowledge.d.ts +43 -0
- package/dist/adapters/memory/knowledge.js +248 -0
- package/dist/adapters/memory/mcp.d.ts +9 -0
- package/dist/adapters/memory/mcp.js +37 -0
- package/dist/adapters/memory/message-store.d.ts +17 -0
- package/dist/adapters/memory/message-store.js +70 -0
- package/dist/adapters/memory/principal-memory.d.ts +7 -0
- package/dist/adapters/memory/principal-memory.js +83 -0
- package/dist/adapters/memory/runtime.d.ts +29 -0
- package/dist/adapters/memory/runtime.js +0 -0
- package/dist/adapters/memory/sessions.d.ts +29 -0
- package/dist/adapters/memory/sessions.js +0 -0
- package/dist/adapters/memory/skills.d.ts +10 -0
- package/dist/adapters/memory/skills.js +41 -0
- package/dist/adapters/memory/thread-summary.d.ts +7 -0
- package/dist/adapters/memory/thread-summary.js +29 -0
- package/dist/adapters/memory/usage-limits.d.ts +13 -0
- package/dist/adapters/memory/usage-limits.js +72 -0
- package/dist/adapters/memory/usage.d.ts +16 -0
- package/dist/adapters/memory/usage.js +279 -0
- package/dist/adapters/otel/index.d.ts +111 -0
- package/dist/adapters/otel/index.js +133 -0
- package/dist/adapters/postgres/artifact-exports.d.ts +12 -0
- package/dist/adapters/postgres/artifact-exports.js +117 -0
- package/dist/adapters/postgres/artifacts.d.ts +16 -0
- package/dist/adapters/postgres/artifacts.js +172 -0
- package/dist/adapters/postgres/checkpoint-store.d.ts +16 -0
- package/dist/adapters/postgres/checkpoint-store.js +34 -0
- package/dist/adapters/postgres/config.d.ts +15 -0
- package/dist/adapters/postgres/config.js +187 -0
- package/dist/adapters/postgres/conversation-store.d.ts +4 -0
- package/dist/adapters/postgres/conversation-store.js +82 -0
- package/dist/adapters/postgres/evaluation.d.ts +17 -0
- package/dist/adapters/postgres/evaluation.js +193 -0
- package/dist/adapters/postgres/file-content.d.ts +30 -0
- package/dist/adapters/postgres/file-content.js +111 -0
- package/dist/adapters/postgres/files.d.ts +19 -0
- package/dist/adapters/postgres/files.js +209 -0
- package/dist/adapters/postgres/flows.d.ts +20 -0
- package/dist/adapters/postgres/flows.js +206 -0
- package/dist/adapters/postgres/hitl.d.ts +5 -0
- package/dist/adapters/postgres/hitl.js +247 -0
- package/dist/adapters/postgres/index.d.ts +35 -0
- package/dist/adapters/postgres/index.js +35 -0
- package/dist/adapters/postgres/knowledge.d.ts +48 -0
- package/dist/adapters/postgres/knowledge.js +255 -0
- package/dist/adapters/postgres/memory.d.ts +14 -0
- package/dist/adapters/postgres/memory.js +194 -0
- package/dist/adapters/postgres/message-store.d.ts +11 -0
- package/dist/adapters/postgres/message-store.js +145 -0
- package/dist/adapters/postgres/migrations.d.ts +69 -0
- package/dist/adapters/postgres/migrations.js +1594 -0
- package/dist/adapters/postgres/pg-executor.d.ts +19 -0
- package/dist/adapters/postgres/pg-executor.js +32 -0
- package/dist/adapters/postgres/retention.d.ts +26 -0
- package/dist/adapters/postgres/retention.js +59 -0
- package/dist/adapters/postgres/rollups.d.ts +17 -0
- package/dist/adapters/postgres/rollups.js +267 -0
- package/dist/adapters/postgres/run-coordinator.d.ts +5 -0
- package/dist/adapters/postgres/run-coordinator.js +98 -0
- package/dist/adapters/postgres/run-event-log.d.ts +26 -0
- package/dist/adapters/postgres/run-event-log.js +30 -0
- package/dist/adapters/postgres/run-store.d.ts +4 -0
- package/dist/adapters/postgres/run-store.js +199 -0
- package/dist/adapters/postgres/schema.d.ts +39 -0
- package/dist/adapters/postgres/schema.js +70 -0
- package/dist/adapters/postgres/session-state.d.ts +7 -0
- package/dist/adapters/postgres/session-state.js +99 -0
- package/dist/adapters/postgres/sql.d.ts +8 -0
- package/dist/adapters/postgres/sql.js +2 -0
- package/dist/adapters/postgres/transaction.d.ts +37 -0
- package/dist/adapters/postgres/transaction.js +93 -0
- package/dist/adapters/postgres/unit-of-work.d.ts +18 -0
- package/dist/adapters/postgres/unit-of-work.js +8 -0
- package/dist/adapters/postgres/usage-limits.d.ts +15 -0
- package/dist/adapters/postgres/usage-limits.js +136 -0
- package/dist/adapters/postgres/usage.d.ts +15 -0
- package/dist/adapters/postgres/usage.js +226 -0
- package/dist/adapters/redis/index.d.ts +9 -0
- package/dist/adapters/redis/index.js +9 -0
- package/dist/adapters/redis/realtime.d.ts +74 -0
- package/dist/adapters/redis/realtime.js +112 -0
- package/dist/adapters/supabase/index.d.ts +88 -0
- package/dist/adapters/supabase/index.js +84 -0
- package/dist/adapters/supabase/realtime.d.ts +30 -0
- package/dist/adapters/supabase/realtime.js +53 -0
- package/dist/adapters/supabase/rls.d.ts +99 -0
- package/dist/adapters/supabase/rls.js +216 -0
- package/dist/adapters/supabase/storage.d.ts +50 -0
- package/dist/adapters/supabase/storage.js +207 -0
- package/dist/agents/agent.d.ts +66 -0
- package/dist/agents/agent.js +209 -0
- package/dist/agents/define.d.ts +21 -0
- package/dist/agents/define.js +63 -0
- package/dist/agents/engine.d.ts +98 -0
- package/dist/agents/engine.js +462 -0
- package/dist/agents/index.d.ts +50 -0
- package/dist/agents/index.js +17 -0
- package/dist/artifacts/index.d.ts +114 -0
- package/dist/artifacts/index.js +219 -0
- package/dist/authorization/index.d.ts +76 -0
- package/dist/authorization/index.js +76 -0
- package/dist/capabilities/index.d.ts +120 -0
- package/dist/capabilities/index.js +167 -0
- package/dist/capabilities/runtime.d.ts +89 -0
- package/dist/capabilities/runtime.js +84 -0
- package/dist/citations/index.d.ts +161 -0
- package/dist/citations/index.js +182 -0
- package/dist/context/assembler.d.ts +82 -0
- package/dist/context/assembler.js +129 -0
- package/dist/context/compaction.d.ts +45 -0
- package/dist/context/compaction.js +55 -0
- package/dist/context/index.d.ts +75 -0
- package/dist/context/index.js +17 -0
- package/dist/core/content-parts.d.ts +194 -0
- package/dist/core/content-parts.js +23 -0
- package/dist/core/context.d.ts +51 -0
- package/dist/core/context.js +9 -0
- package/dist/core/env.d.ts +25 -0
- package/dist/core/env.js +41 -0
- package/dist/core/errors.d.ts +30 -0
- package/dist/core/errors.js +65 -0
- package/dist/core/events.d.ts +139 -0
- package/dist/core/events.js +99 -0
- package/dist/core/ids.d.ts +52 -0
- package/dist/core/ids.js +13 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +9 -0
- package/dist/core/tokens.d.ts +22 -0
- package/dist/core/tokens.js +22 -0
- package/dist/core/validation.d.ts +34 -0
- package/dist/core/validation.js +176 -0
- package/dist/documents/extraction.d.ts +121 -0
- package/dist/documents/extraction.js +293 -0
- package/dist/documents/index.d.ts +199 -0
- package/dist/documents/index.js +65 -0
- package/dist/documents/parsers/pdf.d.ts +47 -0
- package/dist/documents/parsers/pdf.js +508 -0
- package/dist/documents/parsers/text.d.ts +59 -0
- package/dist/documents/parsers/text.js +325 -0
- package/dist/documents/read-tool.d.ts +52 -0
- package/dist/documents/read-tool.js +109 -0
- package/dist/documents/render.d.ts +29 -0
- package/dist/documents/render.js +59 -0
- package/dist/documents/vision.d.ts +159 -0
- package/dist/documents/vision.js +214 -0
- package/dist/entries/adapters-bullmq.d.ts +8 -0
- package/dist/entries/adapters-bullmq.js +8 -0
- package/dist/entries/adapters-otel.d.ts +13 -0
- package/dist/entries/adapters-otel.js +13 -0
- package/dist/entries/adapters-postgres.d.ts +10 -0
- package/dist/entries/adapters-postgres.js +10 -0
- package/dist/entries/adapters-redis.d.ts +3 -0
- package/dist/entries/adapters-redis.js +3 -0
- package/dist/entries/context.d.ts +20 -0
- package/dist/entries/context.js +20 -0
- package/dist/entries/flows.d.ts +15 -0
- package/dist/entries/flows.js +15 -0
- package/dist/entries/hitl.d.ts +10 -0
- package/dist/entries/hitl.js +10 -0
- package/dist/entries/knowledge.d.ts +18 -0
- package/dist/entries/knowledge.js +19 -0
- package/dist/entries/mcp.d.ts +10 -0
- package/dist/entries/mcp.js +10 -0
- package/dist/entries/observability.d.ts +14 -0
- package/dist/entries/observability.js +16 -0
- package/dist/entries/persistence.d.ts +11 -0
- package/dist/entries/persistence.js +11 -0
- package/dist/entries/providers.d.ts +14 -0
- package/dist/entries/providers.js +14 -0
- package/dist/entries/runtime.d.ts +13 -0
- package/dist/entries/runtime.js +13 -0
- package/dist/entries/server.d.ts +24 -0
- package/dist/entries/server.js +24 -0
- package/dist/entries/tools.d.ts +21 -0
- package/dist/entries/tools.js +21 -0
- package/dist/entries/usage.d.ts +10 -0
- package/dist/entries/usage.js +10 -0
- package/dist/evaluation/gate.d.ts +168 -0
- package/dist/evaluation/gate.js +180 -0
- package/dist/evaluation/graders.d.ts +125 -0
- package/dist/evaluation/graders.js +203 -0
- package/dist/evaluation/index.d.ts +120 -0
- package/dist/evaluation/index.js +183 -0
- package/dist/evaluation/judge.d.ts +75 -0
- package/dist/evaluation/judge.js +111 -0
- package/dist/export/index.d.ts +162 -0
- package/dist/export/index.js +363 -0
- package/dist/export/markdown.d.ts +19 -0
- package/dist/export/markdown.js +29 -0
- package/dist/export/pdf.d.ts +73 -0
- package/dist/export/pdf.js +407 -0
- package/dist/files/context.d.ts +97 -0
- package/dist/files/context.js +185 -0
- package/dist/files/index.d.ts +210 -0
- package/dist/files/index.js +338 -0
- package/dist/files/read-tool.d.ts +81 -0
- package/dist/files/read-tool.js +163 -0
- package/dist/files/turn-parts.d.ts +96 -0
- package/dist/files/turn-parts.js +171 -0
- package/dist/flows/index.d.ts +270 -0
- package/dist/flows/index.js +62 -0
- package/dist/flows/interpreter.d.ts +146 -0
- package/dist/flows/interpreter.js +426 -0
- package/dist/flows/runner.d.ts +145 -0
- package/dist/flows/runner.js +270 -0
- package/dist/graphql/index.d.ts +8 -0
- package/dist/graphql/index.js +8 -0
- package/dist/graphql/resolvers.d.ts +237 -0
- package/dist/graphql/resolvers.js +253 -0
- package/dist/graphql/schema.d.ts +11 -0
- package/dist/graphql/schema.js +258 -0
- package/dist/graphql/sse.d.ts +77 -0
- package/dist/graphql/sse.js +100 -0
- package/dist/hitl/approved-execution.d.ts +127 -0
- package/dist/hitl/approved-execution.js +177 -0
- package/dist/hitl/index.d.ts +79 -0
- package/dist/hitl/index.js +12 -0
- package/dist/hitl/service.d.ts +221 -0
- package/dist/hitl/service.js +268 -0
- package/dist/idempotency/index.d.ts +70 -0
- package/dist/idempotency/index.js +59 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.js +60 -0
- package/dist/knowledge/chunking.d.ts +57 -0
- package/dist/knowledge/chunking.js +158 -0
- package/dist/knowledge/index.d.ts +119 -0
- package/dist/knowledge/index.js +166 -0
- package/dist/knowledge/retrieval.d.ts +146 -0
- package/dist/knowledge/retrieval.js +170 -0
- package/dist/loadtest/harness.d.ts +168 -0
- package/dist/loadtest/harness.js +507 -0
- package/dist/loadtest/index.d.ts +13 -0
- package/dist/loadtest/index.js +13 -0
- package/dist/loadtest/injection.d.ts +89 -0
- package/dist/loadtest/injection.js +147 -0
- package/dist/loadtest/metrics.d.ts +197 -0
- package/dist/loadtest/metrics.js +160 -0
- package/dist/loadtest/runbooks.d.ts +28 -0
- package/dist/loadtest/runbooks.js +159 -0
- package/dist/loadtest/scenario.d.ts +104 -0
- package/dist/loadtest/scenario.js +208 -0
- package/dist/mcp/egress.d.ts +53 -0
- package/dist/mcp/egress.js +115 -0
- package/dist/mcp/index.d.ts +93 -0
- package/dist/mcp/index.js +33 -0
- package/dist/mcp/provider.d.ts +62 -0
- package/dist/mcp/provider.js +0 -0
- package/dist/models/index.d.ts +98 -0
- package/dist/models/index.js +74 -0
- package/dist/models/pricing.d.ts +24 -0
- package/dist/models/pricing.js +37 -0
- package/dist/models/provider-factory.d.ts +31 -0
- package/dist/models/provider-factory.js +67 -0
- package/dist/models/streaming.d.ts +145 -0
- package/dist/models/streaming.js +272 -0
- package/dist/models/vision.d.ts +38 -0
- package/dist/models/vision.js +62 -0
- package/dist/persistence/index.d.ts +1654 -0
- package/dist/persistence/index.js +226 -0
- package/dist/principal-memory/index.d.ts +106 -0
- package/dist/principal-memory/index.js +89 -0
- package/dist/retention/index.d.ts +89 -0
- package/dist/retention/index.js +70 -0
- package/dist/runtime/checkpoint.d.ts +37 -0
- package/dist/runtime/checkpoint.js +22 -0
- package/dist/runtime/index.d.ts +118 -0
- package/dist/runtime/index.js +69 -0
- package/dist/runtime/retry.d.ts +95 -0
- package/dist/runtime/retry.js +126 -0
- package/dist/runtime/serialization.d.ts +85 -0
- package/dist/runtime/serialization.js +95 -0
- package/dist/runtime/streaming.d.ts +54 -0
- package/dist/runtime/streaming.js +115 -0
- package/dist/runtime/worker.d.ts +130 -0
- package/dist/runtime/worker.js +405 -0
- package/dist/security/checklist.d.ts +53 -0
- package/dist/security/checklist.js +204 -0
- package/dist/security/findings.d.ts +56 -0
- package/dist/security/findings.js +168 -0
- package/dist/security/index.d.ts +14 -0
- package/dist/security/index.js +14 -0
- package/dist/security/prompt-safety.d.ts +100 -0
- package/dist/security/prompt-safety.js +133 -0
- package/dist/server/boot.d.ts +32 -0
- package/dist/server/boot.js +36 -0
- package/dist/server/cli-worker.d.ts +37 -0
- package/dist/server/cli-worker.js +151 -0
- package/dist/server/cli.d.ts +27 -0
- package/dist/server/cli.js +74 -0
- package/dist/server/config.d.ts +42 -0
- package/dist/server/config.js +127 -0
- package/dist/server/health.d.ts +59 -0
- package/dist/server/health.js +90 -0
- package/dist/server/host.d.ts +39 -0
- package/dist/server/host.js +124 -0
- package/dist/server/index.d.ts +15 -0
- package/dist/server/index.js +15 -0
- package/dist/server/main.d.ts +16 -0
- package/dist/server/main.js +31 -0
- package/dist/server/sse-route.d.ts +21 -0
- package/dist/server/sse-route.js +282 -0
- package/dist/skills/index.d.ts +67 -0
- package/dist/skills/index.js +31 -0
- package/dist/skills/resolver.d.ts +54 -0
- package/dist/skills/resolver.js +121 -0
- package/dist/teams/index.d.ts +93 -0
- package/dist/teams/index.js +207 -0
- package/dist/telemetry/index.d.ts +157 -0
- package/dist/telemetry/index.js +71 -0
- package/dist/telemetry/instrument.d.ts +108 -0
- package/dist/telemetry/instrument.js +232 -0
- package/dist/telemetry/log-events.d.ts +17 -0
- package/dist/telemetry/log-events.js +58 -0
- package/dist/telemetry/metrics.d.ts +123 -0
- package/dist/telemetry/metrics.js +135 -0
- package/dist/telemetry/noop.d.ts +39 -0
- package/dist/telemetry/noop.js +143 -0
- package/dist/telemetry/redaction.d.ts +64 -0
- package/dist/telemetry/redaction.js +153 -0
- package/dist/telemetry/spans.d.ts +56 -0
- package/dist/telemetry/spans.js +78 -0
- package/dist/telemetry/trace-context.d.ts +55 -0
- package/dist/telemetry/trace-context.js +60 -0
- package/dist/toolkit/compute.d.ts +53 -0
- package/dist/toolkit/compute.js +152 -0
- package/dist/toolkit/data.d.ts +98 -0
- package/dist/toolkit/data.js +235 -0
- package/dist/toolkit/http.d.ts +113 -0
- package/dist/toolkit/http.js +205 -0
- package/dist/toolkit/index.d.ts +21 -0
- package/dist/toolkit/index.js +17 -0
- package/dist/toolkit/web.d.ts +107 -0
- package/dist/toolkit/web.js +147 -0
- package/dist/tools/define.d.ts +25 -0
- package/dist/tools/define.js +45 -0
- package/dist/tools/delegating.d.ts +132 -0
- package/dist/tools/delegating.js +211 -0
- package/dist/tools/index.d.ts +129 -0
- package/dist/tools/index.js +33 -0
- package/dist/tools/library/compute.d.ts +11 -0
- package/dist/tools/library/compute.js +46 -0
- package/dist/tools/library/data.d.ts +16 -0
- package/dist/tools/library/data.js +92 -0
- package/dist/tools/library/http.d.ts +28 -0
- package/dist/tools/library/http.js +71 -0
- package/dist/tools/library/index.d.ts +97 -0
- package/dist/tools/library/index.js +134 -0
- package/dist/tools/library/knowledge.d.ts +39 -0
- package/dist/tools/library/knowledge.js +58 -0
- package/dist/tools/library/web.d.ts +19 -0
- package/dist/tools/library/web.js +65 -0
- package/dist/tools/meta-tools.d.ts +19 -0
- package/dist/tools/meta-tools.js +36 -0
- package/dist/tools/registry.d.ts +146 -0
- package/dist/tools/registry.js +291 -0
- package/dist/usage/index.d.ts +105 -0
- package/dist/usage/index.js +20 -0
- package/dist/usage/quota.d.ts +258 -0
- package/dist/usage/quota.js +510 -0
- package/dist/usage/recorder.d.ts +29 -0
- package/dist/usage/recorder.js +96 -0
- package/dist/usage/rollups.d.ts +121 -0
- package/dist/usage/rollups.js +157 -0
- package/dist/worker/export.d.ts +57 -0
- package/dist/worker/export.js +81 -0
- package/dist/worker/extraction.d.ts +57 -0
- package/dist/worker/extraction.js +84 -0
- package/dist/worker/main.d.ts +103 -0
- package/dist/worker/main.js +159 -0
- package/package.json +187 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory HITL adapters — `docs/04` → Questions & Approvals. Durable pending questions/approvals
|
|
3
|
+
* and standing approval grants for tests/dev. `answerQuestion`/`decideApproval` are idempotent so a
|
|
4
|
+
* continuation is queued exactly once; grants respect revocation and expiry.
|
|
5
|
+
*/
|
|
6
|
+
import { AgentPlatformError } from "../../core/errors.js";
|
|
7
|
+
const notFound = (id) => new AgentPlatformError({ code: "not_found", message: `Interaction ${id} not found`, retryable: false });
|
|
8
|
+
export const createMemoryInteractionStore = () => {
|
|
9
|
+
const questions = new Map(); // tenant -> id -> q
|
|
10
|
+
const approvals = new Map();
|
|
11
|
+
const qMap = (t) => {
|
|
12
|
+
let m = questions.get(t);
|
|
13
|
+
if (!m)
|
|
14
|
+
questions.set(t, (m = new Map()));
|
|
15
|
+
return m;
|
|
16
|
+
};
|
|
17
|
+
const aMap = (t) => {
|
|
18
|
+
let m = approvals.get(t);
|
|
19
|
+
if (!m)
|
|
20
|
+
approvals.set(t, (m = new Map()));
|
|
21
|
+
return m;
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
async createQuestion({ tenantId, question }) {
|
|
25
|
+
qMap(tenantId).set(question.id, question);
|
|
26
|
+
},
|
|
27
|
+
async findPendingQuestion({ tenantId, runId }) {
|
|
28
|
+
for (const q of qMap(tenantId).values())
|
|
29
|
+
if (q.runId === runId && q.answeredAt === undefined)
|
|
30
|
+
return q;
|
|
31
|
+
return null;
|
|
32
|
+
},
|
|
33
|
+
/**
|
|
34
|
+
* The newest answered question for the run — #163. Newest, because a run may ask more than once and only
|
|
35
|
+
* the latest answer is news; the earlier ones are already in the model's history from earlier turns.
|
|
36
|
+
*/
|
|
37
|
+
async findAnsweredQuestion({ tenantId, runId }) {
|
|
38
|
+
let latest = null;
|
|
39
|
+
for (const q of qMap(tenantId).values()) {
|
|
40
|
+
if (q.runId !== runId || q.answeredAt === undefined)
|
|
41
|
+
continue;
|
|
42
|
+
if (latest?.answeredAt === undefined || q.answeredAt > latest.answeredAt)
|
|
43
|
+
latest = q;
|
|
44
|
+
}
|
|
45
|
+
return latest;
|
|
46
|
+
},
|
|
47
|
+
async answerQuestion({ tenantId, interactionId, answers, at }) {
|
|
48
|
+
const q = qMap(tenantId).get(interactionId);
|
|
49
|
+
if (!q)
|
|
50
|
+
throw notFound(interactionId);
|
|
51
|
+
if (q.answeredAt !== undefined)
|
|
52
|
+
return { question: q, alreadyResolved: true };
|
|
53
|
+
const answered = { ...q, answeredAt: at, answers };
|
|
54
|
+
qMap(tenantId).set(interactionId, answered);
|
|
55
|
+
return { question: answered, alreadyResolved: false };
|
|
56
|
+
},
|
|
57
|
+
async createApproval({ tenantId, approval }) {
|
|
58
|
+
aMap(tenantId).set(approval.id, approval);
|
|
59
|
+
},
|
|
60
|
+
async findPendingApproval({ tenantId, runId }) {
|
|
61
|
+
for (const a of aMap(tenantId).values())
|
|
62
|
+
if (a.runId === runId && a.decidedAt === undefined)
|
|
63
|
+
return a;
|
|
64
|
+
return null;
|
|
65
|
+
},
|
|
66
|
+
async decideApproval({ tenantId, interactionId, decision, at }) {
|
|
67
|
+
const a = aMap(tenantId).get(interactionId);
|
|
68
|
+
if (!a)
|
|
69
|
+
throw notFound(interactionId);
|
|
70
|
+
if (a.decidedAt !== undefined)
|
|
71
|
+
return { approval: a, alreadyResolved: true };
|
|
72
|
+
const decided = { ...a, decidedAt: at, decision };
|
|
73
|
+
aMap(tenantId).set(interactionId, decided);
|
|
74
|
+
return { approval: decided, alreadyResolved: false };
|
|
75
|
+
},
|
|
76
|
+
async findApproval({ tenantId, interactionId }) {
|
|
77
|
+
return aMap(tenantId).get(interactionId) ?? null;
|
|
78
|
+
},
|
|
79
|
+
async findDecidedApproval({ tenantId, runId }) {
|
|
80
|
+
for (const a of aMap(tenantId).values())
|
|
81
|
+
if (a.runId === runId && a.decidedAt !== undefined && a.consumedAt === undefined)
|
|
82
|
+
return a;
|
|
83
|
+
return null;
|
|
84
|
+
},
|
|
85
|
+
async claimApproval({ tenantId, interactionId, at }) {
|
|
86
|
+
const a = aMap(tenantId).get(interactionId);
|
|
87
|
+
if (!a)
|
|
88
|
+
throw notFound(interactionId);
|
|
89
|
+
// Undecided is refused, not deferred: an interaction nobody has decided must never become
|
|
90
|
+
// permission just because something asked for it.
|
|
91
|
+
if (a.decidedAt === undefined || a.consumedAt !== undefined)
|
|
92
|
+
return { approval: a, claimed: false };
|
|
93
|
+
const claimed = { ...a, consumedAt: at };
|
|
94
|
+
aMap(tenantId).set(interactionId, claimed);
|
|
95
|
+
return { approval: claimed, claimed: true };
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
export const createMemoryApprovalGrantStore = () => {
|
|
100
|
+
const byTenant = new Map();
|
|
101
|
+
const grants = (t) => {
|
|
102
|
+
let m = byTenant.get(t);
|
|
103
|
+
if (!m)
|
|
104
|
+
byTenant.set(t, (m = new Map()));
|
|
105
|
+
return m;
|
|
106
|
+
};
|
|
107
|
+
const active = (g, now) => g.revokedAt === undefined && (g.expiresAt === undefined || g.expiresAt > now);
|
|
108
|
+
return {
|
|
109
|
+
async grant({ tenantId, grant }) {
|
|
110
|
+
grants(tenantId).set(grant.id, grant);
|
|
111
|
+
},
|
|
112
|
+
async findActive({ tenantId, toolNameOrCategory, now, conversationId }) {
|
|
113
|
+
for (const g of grants(tenantId).values()) {
|
|
114
|
+
if (!active(g, now) || g.toolNameOrCategory !== toolNameOrCategory)
|
|
115
|
+
continue;
|
|
116
|
+
// A conversation-scoped grant only applies within its own conversation.
|
|
117
|
+
if (g.scope === "conversation" && g.conversationId !== conversationId)
|
|
118
|
+
continue;
|
|
119
|
+
return g;
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
},
|
|
123
|
+
async revoke({ tenantId, grantId, at }) {
|
|
124
|
+
const g = grants(tenantId).get(grantId);
|
|
125
|
+
if (g)
|
|
126
|
+
grants(tenantId).set(g.id, { ...g, revokedAt: at });
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=hitl.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory idempotency store — `docs/04` → Idempotency. Persists a tool result by key so a resumed
|
|
3
|
+
* or retried external/destructive call returns the original result instead of repeating the side
|
|
4
|
+
* effect. Tenant-partitioned. Verified indirectly through the tool registry's replay path.
|
|
5
|
+
*
|
|
6
|
+
* The registry's `get`-then-`put` is safe here because the durable runtime serializes work per run
|
|
7
|
+
* (one worker per run; `runWithRetry` retries sequentially), so no two executions of the same
|
|
8
|
+
* `(tenant, run, tool-call)` race. A DISTRIBUTED adapter must instead implement an atomic
|
|
9
|
+
* put-if-absent / reserve so concurrent duplicates cannot both execute before either writes.
|
|
10
|
+
*/
|
|
11
|
+
import type { IdempotencyStore } from "../../idempotency/index.js";
|
|
12
|
+
export declare const createMemoryIdempotencyStore: () => IdempotencyStore;
|
|
13
|
+
//# sourceMappingURL=idempotency.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory idempotency store — `docs/04` → Idempotency. Persists a tool result by key so a resumed
|
|
3
|
+
* or retried external/destructive call returns the original result instead of repeating the side
|
|
4
|
+
* effect. Tenant-partitioned. Verified indirectly through the tool registry's replay path.
|
|
5
|
+
*
|
|
6
|
+
* The registry's `get`-then-`put` is safe here because the durable runtime serializes work per run
|
|
7
|
+
* (one worker per run; `runWithRetry` retries sequentially), so no two executions of the same
|
|
8
|
+
* `(tenant, run, tool-call)` race. A DISTRIBUTED adapter must instead implement an atomic
|
|
9
|
+
* put-if-absent / reserve so concurrent duplicates cannot both execute before either writes.
|
|
10
|
+
*/
|
|
11
|
+
export const createMemoryIdempotencyStore = () => {
|
|
12
|
+
const byTenant = new Map();
|
|
13
|
+
const tenant = (t) => {
|
|
14
|
+
let m = byTenant.get(t);
|
|
15
|
+
if (!m)
|
|
16
|
+
byTenant.set(t, (m = new Map()));
|
|
17
|
+
return m;
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
async get({ tenantId, key }) {
|
|
21
|
+
const rows = tenant(tenantId);
|
|
22
|
+
if (!rows.has(key))
|
|
23
|
+
return null;
|
|
24
|
+
// A stored value means the call already ran: firstSeen is false for the replay.
|
|
25
|
+
return { key, firstSeen: false, result: rows.get(key) };
|
|
26
|
+
},
|
|
27
|
+
async put({ tenantId, key, result }) {
|
|
28
|
+
tenant(tenantId).set(key, result);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=idempotency.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory reference adapters — `docs/02-core-and-persistence.md`.
|
|
3
|
+
*
|
|
4
|
+
* The reference implementation used for tests and dev. It implements the storage ports (never
|
|
5
|
+
* the other way round) and is verified by the shared conformance harness, so a Postgres/Supabase
|
|
6
|
+
* adapter can be checked against the exact same behavior.
|
|
7
|
+
*/
|
|
8
|
+
import type { ConversationStore } from "../../persistence/index.js";
|
|
9
|
+
export declare const createMemoryConversationStore: (clock?: () => string) => ConversationStore;
|
|
10
|
+
/** Durable-runtime reference adapters (RunStore, CheckpointStore, JobDispatcher, locks). */
|
|
11
|
+
export * from "./runtime.js";
|
|
12
|
+
/** Session/thread reference adapters (SessionStateStore, run coordinator, binding, unit of work). */
|
|
13
|
+
export * from "./sessions.js";
|
|
14
|
+
/** Append-only usage ledger reference adapter. */
|
|
15
|
+
export * from "./usage.js";
|
|
16
|
+
export * from "./usage-limits.js";
|
|
17
|
+
/** Blob store reference adapter (spilled tool output). */
|
|
18
|
+
export * from "./blobs.js";
|
|
19
|
+
export * from "./files.js";
|
|
20
|
+
export * from "./artifacts.js";
|
|
21
|
+
export * from "./artifact-exports.js";
|
|
22
|
+
export * from "./knowledge.js";
|
|
23
|
+
export * from "./evaluation.js";
|
|
24
|
+
/** Idempotency store reference adapter. */
|
|
25
|
+
export * from "./idempotency.js";
|
|
26
|
+
/** Skill store reference adapter. */
|
|
27
|
+
export * from "./skills.js";
|
|
28
|
+
/** Thread summary reference adapter (compacted history). */
|
|
29
|
+
export * from "./thread-summary.js";
|
|
30
|
+
/** HITL reference adapters (interactions + approval grants). */
|
|
31
|
+
export * from "./hitl.js";
|
|
32
|
+
/** MCP connection reference adapter. */
|
|
33
|
+
export * from "./mcp.js";
|
|
34
|
+
/** Principal (user-level) memory reference adapter. */
|
|
35
|
+
export * from "./principal-memory.js";
|
|
36
|
+
/** Message + agent reference adapters (used by the embedded agent facade). */
|
|
37
|
+
export * from "./message-store.js";
|
|
38
|
+
export * from "./flows.js";
|
|
39
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory reference adapters — `docs/02-core-and-persistence.md`.
|
|
3
|
+
*
|
|
4
|
+
* The reference implementation used for tests and dev. It implements the storage ports (never
|
|
5
|
+
* the other way round) and is verified by the shared conformance harness, so a Postgres/Supabase
|
|
6
|
+
* adapter can be checked against the exact same behavior.
|
|
7
|
+
*/
|
|
8
|
+
import { AgentPlatformError } from "../../core/errors.js";
|
|
9
|
+
const notFound = (id) => new AgentPlatformError({ code: "not_found", message: `Conversation ${id} not found`, retryable: false });
|
|
10
|
+
const conflict = (message) => new AgentPlatformError({ code: "conflict", message, retryable: false });
|
|
11
|
+
/** Monotonic ISO clock, injectable for deterministic tests. */
|
|
12
|
+
const defaultClock = () => {
|
|
13
|
+
let n = 0;
|
|
14
|
+
return () => new Date(Date.UTC(2020, 0, 1, 0, 0, 0, ++n)).toISOString();
|
|
15
|
+
};
|
|
16
|
+
export const createMemoryConversationStore = (clock = defaultClock()) => {
|
|
17
|
+
// tenantId → (id → row). Partitioning by tenant makes cross-tenant reads structurally impossible.
|
|
18
|
+
const byTenant = new Map();
|
|
19
|
+
const tenant = (t) => {
|
|
20
|
+
let m = byTenant.get(t);
|
|
21
|
+
if (!m)
|
|
22
|
+
byTenant.set(t, (m = new Map()));
|
|
23
|
+
return m;
|
|
24
|
+
};
|
|
25
|
+
const visible = (c) => c.deletedAt === undefined;
|
|
26
|
+
return {
|
|
27
|
+
async create({ tenantId, id, title }) {
|
|
28
|
+
const rows = tenant(tenantId);
|
|
29
|
+
if (rows.has(id))
|
|
30
|
+
throw conflict(`Conversation ${id} already exists`);
|
|
31
|
+
const now = clock();
|
|
32
|
+
const row = { id, tenantId, title, version: 1, createdAt: now, updatedAt: now };
|
|
33
|
+
rows.set(id, row);
|
|
34
|
+
return row;
|
|
35
|
+
},
|
|
36
|
+
async findById({ tenantId, id }) {
|
|
37
|
+
const row = tenant(tenantId).get(id);
|
|
38
|
+
return row && visible(row) ? row : null;
|
|
39
|
+
},
|
|
40
|
+
async list({ tenantId, limit, cursor }) {
|
|
41
|
+
const all = [...tenant(tenantId).values()]
|
|
42
|
+
.filter(visible)
|
|
43
|
+
.sort((a, b) => (a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : a.id < b.id ? -1 : 1));
|
|
44
|
+
const start = cursor ? all.findIndex((c) => c.id === cursor) + 1 : 0;
|
|
45
|
+
const items = all.slice(start, start + limit);
|
|
46
|
+
const nextCursor = start + limit < all.length ? items[items.length - 1]?.id : undefined;
|
|
47
|
+
const page = nextCursor === undefined ? { items } : { items, nextCursor };
|
|
48
|
+
return page;
|
|
49
|
+
},
|
|
50
|
+
async update({ tenantId, id, expectedVersion, patch }) {
|
|
51
|
+
const rows = tenant(tenantId);
|
|
52
|
+
const row = rows.get(id);
|
|
53
|
+
if (!row || !visible(row))
|
|
54
|
+
throw notFound(id);
|
|
55
|
+
if (row.version !== expectedVersion)
|
|
56
|
+
throw conflict(`Conversation ${id} version ${expectedVersion} is stale (current ${row.version})`);
|
|
57
|
+
const next = {
|
|
58
|
+
...row,
|
|
59
|
+
...(patch.title === undefined ? {} : { title: patch.title }),
|
|
60
|
+
...(patch.archivedAt === undefined
|
|
61
|
+
? {}
|
|
62
|
+
: { archivedAt: patch.archivedAt === null ? undefined : patch.archivedAt }),
|
|
63
|
+
version: row.version + 1,
|
|
64
|
+
updatedAt: clock(),
|
|
65
|
+
};
|
|
66
|
+
rows.set(id, next);
|
|
67
|
+
return next;
|
|
68
|
+
},
|
|
69
|
+
async softDelete({ tenantId, id }) {
|
|
70
|
+
const rows = tenant(tenantId);
|
|
71
|
+
const row = rows.get(id);
|
|
72
|
+
if (!row || !visible(row))
|
|
73
|
+
throw notFound(id);
|
|
74
|
+
rows.set(id, { ...row, deletedAt: clock(), version: row.version + 1, updatedAt: clock() });
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
/** Durable-runtime reference adapters (RunStore, CheckpointStore, JobDispatcher, locks). */
|
|
79
|
+
export * from "./runtime.js";
|
|
80
|
+
/** Session/thread reference adapters (SessionStateStore, run coordinator, binding, unit of work). */
|
|
81
|
+
export * from "./sessions.js";
|
|
82
|
+
/** Append-only usage ledger reference adapter. */
|
|
83
|
+
export * from "./usage.js";
|
|
84
|
+
export * from "./usage-limits.js";
|
|
85
|
+
/** Blob store reference adapter (spilled tool output). */
|
|
86
|
+
export * from "./blobs.js";
|
|
87
|
+
export * from "./files.js";
|
|
88
|
+
export * from "./artifacts.js";
|
|
89
|
+
export * from "./artifact-exports.js";
|
|
90
|
+
export * from "./knowledge.js";
|
|
91
|
+
export * from "./evaluation.js";
|
|
92
|
+
/** Idempotency store reference adapter. */
|
|
93
|
+
export * from "./idempotency.js";
|
|
94
|
+
/** Skill store reference adapter. */
|
|
95
|
+
export * from "./skills.js";
|
|
96
|
+
/** Thread summary reference adapter (compacted history). */
|
|
97
|
+
export * from "./thread-summary.js";
|
|
98
|
+
/** HITL reference adapters (interactions + approval grants). */
|
|
99
|
+
export * from "./hitl.js";
|
|
100
|
+
/** MCP connection reference adapter. */
|
|
101
|
+
export * from "./mcp.js";
|
|
102
|
+
/** Principal (user-level) memory reference adapter. */
|
|
103
|
+
export * from "./principal-memory.js";
|
|
104
|
+
/** Message + agent reference adapters (used by the embedded agent facade). */
|
|
105
|
+
export * from "./message-store.js";
|
|
106
|
+
export * from "./flows.js";
|
|
107
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory `KnowledgeStore` and `VectorIndex` — the reference implementations (#135).
|
|
3
|
+
*
|
|
4
|
+
* The vector search is an exact brute-force scan, which is the right choice for a reference adapter: pgvector's
|
|
5
|
+
* HNSW index is *approximate*, so a conformance test asserting "the nearest chunk is returned first" would be
|
|
6
|
+
* asserting something the production adapter is allowed to miss. Making the reference exact means the harness
|
|
7
|
+
* tests the **contract** — tenant isolation, permission filtering, score ordering, the limit — and recall is
|
|
8
|
+
* measured separately against a fixed query set, which is what AC-2 actually asks for.
|
|
9
|
+
*
|
|
10
|
+
* Two things it takes as seriously as the pgvector adapter:
|
|
11
|
+
*
|
|
12
|
+
* - **Permission filtering happens during the scan**, not after. Filtering afterwards would satisfy every
|
|
13
|
+
* assertion about the results and still leak through counts in production.
|
|
14
|
+
* - **`replaceSource` is a replace.** Appending would leave a changed document's old chunks searchable, and a
|
|
15
|
+
* stale chunk is a citation pointing at text that is no longer there.
|
|
16
|
+
*/
|
|
17
|
+
import type { KeywordIndex, KnowledgeStore, VectorIndex } from "../../persistence/index.js";
|
|
18
|
+
/**
|
|
19
|
+
* Cosine similarity, mapped to 0–1.
|
|
20
|
+
*
|
|
21
|
+
* Cosine rather than raw dot product, because an embedding's magnitude carries no meaning for most providers
|
|
22
|
+
* and an unnormalised dot product would rank a long chunk above a relevant one. Mapped from [-1,1] to [0,1] so
|
|
23
|
+
* a caller's `minScore` means the same thing whatever metric an adapter uses underneath.
|
|
24
|
+
*/
|
|
25
|
+
export declare const cosineScore: (a: readonly number[], b: readonly number[]) => number;
|
|
26
|
+
/** Shared state, so the two ports can be backed by one table the way pgvector backs them. */
|
|
27
|
+
export declare const createMemoryKnowledgeBackend: () => {
|
|
28
|
+
store: KnowledgeStore;
|
|
29
|
+
index: VectorIndex;
|
|
30
|
+
keyword: KeywordIndex;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Tokens for BM25.
|
|
34
|
+
*
|
|
35
|
+
* Lowercased, split on non-alphanumerics, and **`-` is kept inside a token** — `ERR-4021` and `Q3-2026` are
|
|
36
|
+
* single terms, and splitting them is how an exact-code query stops matching the document containing that
|
|
37
|
+
* exact code. That is the one case keyword retrieval exists for.
|
|
38
|
+
*/
|
|
39
|
+
export declare const tokenize: (text: string) => readonly string[];
|
|
40
|
+
export declare const createMemoryKnowledgeStore: () => KnowledgeStore;
|
|
41
|
+
export declare const createMemoryVectorIndex: () => VectorIndex;
|
|
42
|
+
export declare const createMemoryKeywordIndex: () => KeywordIndex;
|
|
43
|
+
//# sourceMappingURL=knowledge.d.ts.map
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory `KnowledgeStore` and `VectorIndex` — the reference implementations (#135).
|
|
3
|
+
*
|
|
4
|
+
* The vector search is an exact brute-force scan, which is the right choice for a reference adapter: pgvector's
|
|
5
|
+
* HNSW index is *approximate*, so a conformance test asserting "the nearest chunk is returned first" would be
|
|
6
|
+
* asserting something the production adapter is allowed to miss. Making the reference exact means the harness
|
|
7
|
+
* tests the **contract** — tenant isolation, permission filtering, score ordering, the limit — and recall is
|
|
8
|
+
* measured separately against a fixed query set, which is what AC-2 actually asks for.
|
|
9
|
+
*
|
|
10
|
+
* Two things it takes as seriously as the pgvector adapter:
|
|
11
|
+
*
|
|
12
|
+
* - **Permission filtering happens during the scan**, not after. Filtering afterwards would satisfy every
|
|
13
|
+
* assertion about the results and still leak through counts in production.
|
|
14
|
+
* - **`replaceSource` is a replace.** Appending would leave a changed document's old chunks searchable, and a
|
|
15
|
+
* stale chunk is a citation pointing at text that is no longer there.
|
|
16
|
+
*/
|
|
17
|
+
import { AgentPlatformError } from "../../core/errors.js";
|
|
18
|
+
import { EMBEDDING_DIMENSIONS, stripStopwords } from "../../persistence/index.js";
|
|
19
|
+
const tenantMap = (outer, tenantId) => {
|
|
20
|
+
let inner = outer.get(tenantId);
|
|
21
|
+
if (!inner)
|
|
22
|
+
outer.set(tenantId, (inner = new Map()));
|
|
23
|
+
return inner;
|
|
24
|
+
};
|
|
25
|
+
const sourceKey = (sourceType, sourceId) => `${sourceType} ${sourceId}`;
|
|
26
|
+
/**
|
|
27
|
+
* Cosine similarity, mapped to 0–1.
|
|
28
|
+
*
|
|
29
|
+
* Cosine rather than raw dot product, because an embedding's magnitude carries no meaning for most providers
|
|
30
|
+
* and an unnormalised dot product would rank a long chunk above a relevant one. Mapped from [-1,1] to [0,1] so
|
|
31
|
+
* a caller's `minScore` means the same thing whatever metric an adapter uses underneath.
|
|
32
|
+
*/
|
|
33
|
+
export const cosineScore = (a, b) => {
|
|
34
|
+
let dot = 0;
|
|
35
|
+
let na = 0;
|
|
36
|
+
let nb = 0;
|
|
37
|
+
const n = Math.min(a.length, b.length);
|
|
38
|
+
for (let i = 0; i < n; i += 1) {
|
|
39
|
+
const x = a[i] ?? 0;
|
|
40
|
+
const y = b[i] ?? 0;
|
|
41
|
+
dot += x * y;
|
|
42
|
+
na += x * x;
|
|
43
|
+
nb += y * y;
|
|
44
|
+
}
|
|
45
|
+
if (na === 0 || nb === 0)
|
|
46
|
+
return 0;
|
|
47
|
+
return (dot / (Math.sqrt(na) * Math.sqrt(nb)) + 1) / 2;
|
|
48
|
+
};
|
|
49
|
+
/** Shared state, so the two ports can be backed by one table the way pgvector backs them. */
|
|
50
|
+
export const createMemoryKnowledgeBackend = () => {
|
|
51
|
+
const byTenant = new Map();
|
|
52
|
+
const store = {
|
|
53
|
+
async replaceSource({ tenantId, sourceType, sourceId, chunks }) {
|
|
54
|
+
const rows = tenantMap(byTenant, tenantId);
|
|
55
|
+
const key = sourceKey(sourceType, sourceId);
|
|
56
|
+
let removed = 0;
|
|
57
|
+
for (const [id, chunk] of rows) {
|
|
58
|
+
if (sourceKey(chunk.sourceType, chunk.sourceId) === key) {
|
|
59
|
+
rows.delete(id);
|
|
60
|
+
removed += 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const chunk of chunks) {
|
|
64
|
+
// Two checks, and they catch different mistakes: a vector whose length contradicts its own declared
|
|
65
|
+
// dimensions is a caller bug, and a vector of a width this platform does not store is a *configuration*
|
|
66
|
+
// one. The reference adapter enforces both so it is exactly as strict as pgvector -- it previously
|
|
67
|
+
// accepted 768 while pgvector refused it, which is the laxness #129 named.
|
|
68
|
+
if (chunk.embedding.length !== EMBEDDING_DIMENSIONS)
|
|
69
|
+
throw new AgentPlatformError({
|
|
70
|
+
code: "invalid_input",
|
|
71
|
+
message: `chunk ${chunk.id} has ${chunk.embedding.length} dimensions but this platform stores ${EMBEDDING_DIMENSIONS}`,
|
|
72
|
+
retryable: false,
|
|
73
|
+
});
|
|
74
|
+
if (chunk.embedding.length !== chunk.embeddingModel.dimensions)
|
|
75
|
+
// Caught here rather than at search time: a vector of the wrong length would silently score against
|
|
76
|
+
// whatever prefix happened to overlap, which looks like bad relevance rather than a bug.
|
|
77
|
+
throw new AgentPlatformError({
|
|
78
|
+
code: "invalid_input",
|
|
79
|
+
message: `chunk ${chunk.id} has ${chunk.embedding.length} dimensions but its model declares ${chunk.embeddingModel.dimensions}`,
|
|
80
|
+
retryable: false,
|
|
81
|
+
});
|
|
82
|
+
rows.set(chunk.id, { ...chunk, sourceType, sourceId });
|
|
83
|
+
}
|
|
84
|
+
return { written: chunks.length, removed };
|
|
85
|
+
},
|
|
86
|
+
async listBySource({ tenantId, sourceType, sourceId, limit, cursor }) {
|
|
87
|
+
const key = sourceKey(sourceType, sourceId);
|
|
88
|
+
const rows = [...tenantMap(byTenant, tenantId).values()]
|
|
89
|
+
.filter((c) => sourceKey(c.sourceType, c.sourceId) === key)
|
|
90
|
+
// By chunk index, because "the chunks of this document" is an ordered thing and reading around a hit
|
|
91
|
+
// depends on that order being the document's.
|
|
92
|
+
.sort((a, b) => a.chunkIndex - b.chunkIndex);
|
|
93
|
+
const after = cursor === undefined ? -1 : Number.parseInt(cursor, 10);
|
|
94
|
+
const from = Number.isSafeInteger(after) ? after : -1;
|
|
95
|
+
const items = rows.filter((c) => c.chunkIndex > from).slice(0, limit).map(withoutEmbedding);
|
|
96
|
+
const last = items[items.length - 1];
|
|
97
|
+
return last !== undefined && rows.some((c) => c.chunkIndex > last.chunkIndex)
|
|
98
|
+
? { items, nextCursor: String(last.chunkIndex) }
|
|
99
|
+
: { items };
|
|
100
|
+
},
|
|
101
|
+
async get({ tenantId, id }) {
|
|
102
|
+
// Absent from *this tenant's* map, so a foreign id is null without a comparison anyone could get wrong.
|
|
103
|
+
const found = tenantMap(byTenant, tenantId).get(id);
|
|
104
|
+
return found === undefined ? null : withoutEmbedding(found);
|
|
105
|
+
},
|
|
106
|
+
async deleteSource({ tenantId, sourceType, sourceId }) {
|
|
107
|
+
const rows = tenantMap(byTenant, tenantId);
|
|
108
|
+
const key = sourceKey(sourceType, sourceId);
|
|
109
|
+
let removed = 0;
|
|
110
|
+
for (const [id, chunk] of rows) {
|
|
111
|
+
if (sourceKey(chunk.sourceType, chunk.sourceId) === key) {
|
|
112
|
+
rows.delete(id);
|
|
113
|
+
removed += 1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return { removed };
|
|
117
|
+
},
|
|
118
|
+
async listStaleSources({ tenantId, current, limit, cursor }) {
|
|
119
|
+
const grouped = new Map();
|
|
120
|
+
for (const chunk of tenantMap(byTenant, tenantId).values()) {
|
|
121
|
+
if (sameModel(chunk.embeddingModel, current))
|
|
122
|
+
continue;
|
|
123
|
+
const key = sourceKey(chunk.sourceType, chunk.sourceId);
|
|
124
|
+
const entry = grouped.get(key);
|
|
125
|
+
if (entry === undefined)
|
|
126
|
+
grouped.set(key, { sourceType: chunk.sourceType, sourceId: chunk.sourceId, chunkCount: 1 });
|
|
127
|
+
else
|
|
128
|
+
entry.chunkCount += 1;
|
|
129
|
+
}
|
|
130
|
+
// Sorted so a resumed re-index sees a stable order; an unstable one would revisit sources it had done
|
|
131
|
+
// and skip ones it had not.
|
|
132
|
+
const sorted = [...grouped.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
133
|
+
const from = cursor === undefined ? 0 : sorted.findIndex(([k]) => k > cursor);
|
|
134
|
+
const start = from < 0 ? sorted.length : from;
|
|
135
|
+
const items = sorted.slice(start, start + limit).map(([, v]) => v);
|
|
136
|
+
const lastKey = sorted[start + items.length - 1]?.[0];
|
|
137
|
+
return start + limit < sorted.length && lastKey !== undefined
|
|
138
|
+
? { items, nextCursor: lastKey }
|
|
139
|
+
: { items };
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
const index = {
|
|
143
|
+
async search({ tenantId, embedding, authSubjects, limit, minScore, sourceTypes }) {
|
|
144
|
+
const allowed = new Set(authSubjects);
|
|
145
|
+
const types = sourceTypes === undefined ? null : new Set(sourceTypes);
|
|
146
|
+
const hits = [];
|
|
147
|
+
for (const chunk of tenantMap(byTenant, tenantId).values()) {
|
|
148
|
+
// Filtered *during* the scan. Filtering the result set afterwards satisfies every assertion about the
|
|
149
|
+
// results and still leaks through counts, which is the failure AC-3 names.
|
|
150
|
+
if (!allowed.has(chunk.authSubject))
|
|
151
|
+
continue;
|
|
152
|
+
if (types !== null && !types.has(chunk.sourceType))
|
|
153
|
+
continue;
|
|
154
|
+
const score = cosineScore(embedding, chunk.embedding);
|
|
155
|
+
if (minScore !== undefined && score < minScore)
|
|
156
|
+
continue;
|
|
157
|
+
hits.push({ chunk: withoutEmbedding(chunk), score });
|
|
158
|
+
}
|
|
159
|
+
// Ties broken by id, so two chunks with identical scores come back in a stable order — otherwise a
|
|
160
|
+
// recall measurement moves between runs for reasons unrelated to relevance.
|
|
161
|
+
return hits.sort((a, b) => (b.score !== a.score ? b.score - a.score : a.chunk.id.localeCompare(b.chunk.id))).slice(0, limit);
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* BM25 over the same rows.
|
|
166
|
+
*
|
|
167
|
+
* A real ranking function rather than a substring match, because the whole point of #136 is that keyword and
|
|
168
|
+
* semantic retrieval have *different* strengths — and a substring matcher has none. BM25 gives rarer terms
|
|
169
|
+
* more weight and saturates on repetition, which is what makes `ERR-4021` beat a document that says
|
|
170
|
+
* "error" forty times.
|
|
171
|
+
*
|
|
172
|
+
* `k1 = 1.2, b = 0.75` are the standard parameters and the same operating point Postgres's `ts_rank_cd`
|
|
173
|
+
* approximates, so the reference adapter and the production one rank alike on the cases that matter.
|
|
174
|
+
*/
|
|
175
|
+
const keyword = {
|
|
176
|
+
async search({ tenantId, query, authSubjects, limit, minScore, sourceTypes }) {
|
|
177
|
+
const allowed = new Set(authSubjects);
|
|
178
|
+
const types = sourceTypes === undefined ? null : new Set(sourceTypes);
|
|
179
|
+
// Filtered *before* scoring, so an excluded chunk never enters the corpus statistics either — otherwise
|
|
180
|
+
// it would shift every other document's IDF and influence ranking without appearing.
|
|
181
|
+
const corpus = [...tenantMap(byTenant, tenantId).values()].filter((c) => allowed.has(c.authSubject) && (types === null || types.has(c.sourceType)));
|
|
182
|
+
if (corpus.length === 0)
|
|
183
|
+
return [];
|
|
184
|
+
// Stopwords come off the *query*, not the index: the index keeps everything so an exact identifier is
|
|
185
|
+
// still findable, and the query drops the terms that carry no signal. See `KEYWORD_STOPWORDS`.
|
|
186
|
+
const terms = tokenize(stripStopwords(query));
|
|
187
|
+
if (terms.length === 0)
|
|
188
|
+
return [];
|
|
189
|
+
const docs = corpus.map((chunk) => ({ chunk, tokens: tokenize(chunk.content) }));
|
|
190
|
+
const avgLength = docs.reduce((n, d) => n + d.tokens.length, 0) / docs.length;
|
|
191
|
+
const documentFrequency = new Map();
|
|
192
|
+
for (const term of new Set(terms)) {
|
|
193
|
+
documentFrequency.set(term, docs.filter((d) => d.tokens.includes(term)).length);
|
|
194
|
+
}
|
|
195
|
+
const K1 = 1.2;
|
|
196
|
+
const B = 0.75;
|
|
197
|
+
const raw = docs.map(({ chunk, tokens }) => {
|
|
198
|
+
let score = 0;
|
|
199
|
+
for (const term of new Set(terms)) {
|
|
200
|
+
const tf = tokens.filter((t) => t === term).length;
|
|
201
|
+
if (tf === 0)
|
|
202
|
+
continue;
|
|
203
|
+
const df = documentFrequency.get(term) ?? 0;
|
|
204
|
+
// The +0.5/+0.5 smoothing is Robertson's; without it a term present in every document gets a
|
|
205
|
+
// negative weight and a matching document scores *worse* than a non-matching one.
|
|
206
|
+
const idf = Math.log(1 + (docs.length - df + 0.5) / (df + 0.5));
|
|
207
|
+
const norm = tf * (K1 + 1);
|
|
208
|
+
const denom = tf + K1 * (1 - B + (B * tokens.length) / (avgLength || 1));
|
|
209
|
+
score += idf * (norm / denom);
|
|
210
|
+
}
|
|
211
|
+
return { chunk, score };
|
|
212
|
+
});
|
|
213
|
+
const best = Math.max(...raw.map((r) => r.score), 0);
|
|
214
|
+
// Normalised against the best hit in this result set, so `minScore` means "relative to the best match
|
|
215
|
+
// here" for both signals. An absolute BM25 threshold would be meaningless: the scale depends on the
|
|
216
|
+
// corpus.
|
|
217
|
+
const hits = raw
|
|
218
|
+
.filter((r) => r.score > 0)
|
|
219
|
+
.map((r) => ({ chunk: withoutEmbedding(r.chunk), score: best === 0 ? 0 : r.score / best }))
|
|
220
|
+
.filter((h) => minScore === undefined || h.score >= minScore);
|
|
221
|
+
return hits
|
|
222
|
+
// Ties broken by id, so two equally-matching chunks come back in a stable order and a measured figure
|
|
223
|
+
// does not move between runs.
|
|
224
|
+
.sort((a, b) => (b.score !== a.score ? b.score - a.score : a.chunk.id.localeCompare(b.chunk.id)))
|
|
225
|
+
.slice(0, limit);
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
return { store, index, keyword };
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Tokens for BM25.
|
|
232
|
+
*
|
|
233
|
+
* Lowercased, split on non-alphanumerics, and **`-` is kept inside a token** — `ERR-4021` and `Q3-2026` are
|
|
234
|
+
* single terms, and splitting them is how an exact-code query stops matching the document containing that
|
|
235
|
+
* exact code. That is the one case keyword retrieval exists for.
|
|
236
|
+
*/
|
|
237
|
+
export const tokenize = (text) => (text.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []).filter((t) => t.length > 1 || /[0-9]/.test(t));
|
|
238
|
+
const sameModel = (a, b) => a.modelId === b.modelId && a.version === b.version && a.dimensions === b.dimensions;
|
|
239
|
+
/** Read paths never return the vector: nothing above this layer needs it, and it is by far the largest field. */
|
|
240
|
+
const withoutEmbedding = (chunk) => {
|
|
241
|
+
const { embedding, ...rest } = chunk;
|
|
242
|
+
void embedding;
|
|
243
|
+
return rest;
|
|
244
|
+
};
|
|
245
|
+
export const createMemoryKnowledgeStore = () => createMemoryKnowledgeBackend().store;
|
|
246
|
+
export const createMemoryVectorIndex = () => createMemoryKnowledgeBackend().index;
|
|
247
|
+
export const createMemoryKeywordIndex = () => createMemoryKnowledgeBackend().keyword;
|
|
248
|
+
//# sourceMappingURL=knowledge.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory MCP connection store — `docs/10`. Tenant-partitioned so one tenant can never see
|
|
3
|
+
* another's connections/tools. Endpoints are egress-validated at registration; a credential is
|
|
4
|
+
* only ever a `credentialRef`, never an inlined secret (structurally enforced by `McpAuth`).
|
|
5
|
+
*/
|
|
6
|
+
import { type EgressPolicy } from "../../mcp/egress.js";
|
|
7
|
+
import type { McpConnectionStore } from "../../mcp/provider.js";
|
|
8
|
+
export declare const createMemoryMcpConnectionStore: (egress: EgressPolicy) => McpConnectionStore;
|
|
9
|
+
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory MCP connection store — `docs/10`. Tenant-partitioned so one tenant can never see
|
|
3
|
+
* another's connections/tools. Endpoints are egress-validated at registration; a credential is
|
|
4
|
+
* only ever a `credentialRef`, never an inlined secret (structurally enforced by `McpAuth`).
|
|
5
|
+
*/
|
|
6
|
+
import { AgentPlatformError } from "../../core/errors.js";
|
|
7
|
+
import { validateEndpoint } from "../../mcp/egress.js";
|
|
8
|
+
export const createMemoryMcpConnectionStore = (egress) => {
|
|
9
|
+
const byTenant = new Map();
|
|
10
|
+
const tenant = (t) => {
|
|
11
|
+
let m = byTenant.get(t);
|
|
12
|
+
if (!m)
|
|
13
|
+
byTenant.set(t, (m = new Map()));
|
|
14
|
+
return m;
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
async register({ tenantId, connection }) {
|
|
18
|
+
// Reject endpoints failing egress at registration (again at handshake by the client).
|
|
19
|
+
validateEndpoint(egress, connection.transport, connection.endpoint);
|
|
20
|
+
if (connection.tenantId !== tenantId)
|
|
21
|
+
throw new AgentPlatformError({ code: "forbidden", message: "connection tenant mismatch", retryable: false });
|
|
22
|
+
tenant(tenantId).set(connection.id, connection);
|
|
23
|
+
},
|
|
24
|
+
async get({ tenantId, id }) {
|
|
25
|
+
return tenant(tenantId).get(id) ?? null;
|
|
26
|
+
},
|
|
27
|
+
async list({ tenantId }) {
|
|
28
|
+
return [...tenant(tenantId).values()];
|
|
29
|
+
},
|
|
30
|
+
async setEnabled({ tenantId, id, enabled }) {
|
|
31
|
+
const c = tenant(tenantId).get(id);
|
|
32
|
+
if (c)
|
|
33
|
+
tenant(tenantId).set(id, { ...c, enabled });
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=mcp.js.map
|