@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
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Azeem Sarwar <https://github.com/azeem-sarwar>
|
|
4
|
+
Copyright (c) 2026 Rise Experts <https://github.com/Rise-Experts>
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# @retinue/agentkit
|
|
2
|
+
|
|
3
|
+
Server-side half of the reusable AI platform. Implements the specifications in
|
|
4
|
+
[`../docs`](../docs).
|
|
5
|
+
|
|
6
|
+
## Status
|
|
7
|
+
|
|
8
|
+
Implemented and exercised end to end. 174 source files, ~36,000 lines, 2,143 tests.
|
|
9
|
+
|
|
10
|
+
This section said *"Contracts only … there is no execution logic yet"* until 2026-08-24,
|
|
11
|
+
which stopped being true a long time before it was corrected — a README that understates
|
|
12
|
+
a package this far is worse than none, because the reader concludes it does nothing and
|
|
13
|
+
looks elsewhere.
|
|
14
|
+
|
|
15
|
+
What exists now: a durable run loop with leases, checkpointing and recovery; a streaming
|
|
16
|
+
agent engine with tool calls, approvals, questions and citations; three storage adapter
|
|
17
|
+
families held to one conformance suite; usage accounting with quota enforcement; MCP tool
|
|
18
|
+
import; and a GraphQL surface. `examples/` is a runnable application over all of it, and
|
|
19
|
+
`shareflow/` is the first real integration.
|
|
20
|
+
|
|
21
|
+
Verified against real Postgres, Redis and a live model provider, not only in memory — the
|
|
22
|
+
distinction matters, and `docs/09` records which claims rest on which.
|
|
23
|
+
|
|
24
|
+
## Modules
|
|
25
|
+
|
|
26
|
+
| Module | Specification | Contains |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `core` | [02](../docs/02-core-and-persistence.md) | `ExecutionContext`, branded IDs, typed message parts, error and event contracts |
|
|
29
|
+
| `capabilities` | #198, #196 | `createRuntime` — the composition root. The wired set is **derived** from the dependencies supplied, not declared beside them, so there is one statement of intent and one of fact. A capability that is off is **enforced**: `runtime.stores.messages` throws when `history` is off, so no caller has to remember to check |
|
|
30
|
+
| `capabilities` *(model)* | #198 | What a runtime does, **declared and cross-checked**. A capability on with nothing wired refuses to construct; so does one wired that nothing declares — the second direction is what catches a feature present, tested and reachable from nothing. Profiles for the two common shapes: a chat assistant, and a headless automation |
|
|
31
|
+
| `models` | [03](../docs/03-intelligence-runtime.md) | Model definitions, capabilities, pricing, resolution policy |
|
|
32
|
+
| `agents` | [03](../docs/03-intelligence-runtime.md) | `AgentManifest` — declarative, stored, versioned |
|
|
33
|
+
| `tools` | [03](../docs/03-intelligence-runtime.md) | Tool descriptors, effect classification, result envelope, meta-tools |
|
|
34
|
+
| `skills` | [03](../docs/03-intelligence-runtime.md) | Versioned skills with a compact catalog entry and lazily loaded body |
|
|
35
|
+
| `context` | [03](../docs/03-intelligence-runtime.md) | Context providers, section metadata, prompt budgets |
|
|
36
|
+
| `runtime` | [04](../docs/04-durable-runtime-and-hitl.md) | Run lifecycle states and execution limits |
|
|
37
|
+
| `hitl` | [04](../docs/04-durable-runtime-and-hitl.md) | Durable questions, approvals and idempotency |
|
|
38
|
+
| `persistence` | [02](../docs/02-core-and-persistence.md) | Tenant-scoped store ports and infrastructure ports |
|
|
39
|
+
| `usage` | [12](../docs/12-usage-and-accounting.md) | Recomputed rollups keyed on tenant, period and principal; quota enforcement at admission across **every** applicable limit — calendar windows, rolling windows and per-model allowances — with a warning below the limit; provider reconciliation that reports rather than corrects |
|
|
40
|
+
| `evaluation` | [09](../docs/09-quality-and-release.md) | Deterministic graders for six of seven expectation kinds, a pinned and cached judge for the seventh, and a release comparison that names the cases that moved |
|
|
41
|
+
| `mcp` | [10](../docs/10-mcp-integration.md) | Outbound MCP-server connections, tool import with safe-by-default effect classification, per-run catalog snapshots for drift detection, and an HTTP egress policy |
|
|
42
|
+
| `files` | [05](../docs/05-knowledge-and-documents.md) | The attachment lifecycle: capped uploads, mediated reads, scheduled deletion, orphan reconciliation; the reference-not-inject context provider and the bounded `read_attachment` step |
|
|
43
|
+
| `documents` | [05](../docs/05-knowledge-and-documents.md) | Extraction to structured blocks (headings, tables, lists), bounded parsers for PDF/Markdown/CSV/JSON, OCR and vision ports, confidence flagging, typed failures, and the bounded `read_document` step |
|
|
44
|
+
| `artifacts` | [05](../docs/05-knowledge-and-documents.md) | Named, versioned assistant output: content by reference, compare-and-set versioning, required provenance, restore, and conversation-scoped access |
|
|
45
|
+
| `export` | [05](../docs/05-knowledge-and-documents.md) | Deterministic PDF and Markdown rendering, one export per version per format, downloads through the mediated file path |
|
|
46
|
+
| `knowledge` | [05](../docs/05-knowledge-and-documents.md) | Structure-aware chunking, the batched embedding pipeline, incremental resumable re-indexing, the freshness target, and hybrid rank-fusion retrieval with an honest empty result |
|
|
47
|
+
| `citations` | [05](../docs/05-knowledge-and-documents.md) | Per-claim provenance as a durable snapshot, groundedness derived from the citation graph, permission checked at citation time |
|
|
48
|
+
| `adapters` | [02](../docs/02-core-and-persistence.md) | Every storage and infrastructure implementation: `memory` (18 files, the reference), `postgres` (26), `supabase` (RLS over the Postgres adapters), `redis`, `bullmq`, `otel`. All three store families are held to the same conformance suite — 29 memory / 28 postgres (1 n/a) / 29 supabase, with no unaccounted cells |
|
|
49
|
+
| `authorization` | [11](../docs/11-authorization.md) | The policy port. **Frozen v1.** Tools are filtered before discovery and re-authorized during execution; untrusted text can never widen capability |
|
|
50
|
+
| `graphql` | [06](../docs/06-graphql-and-frontend.md) | SDL plus a thin resolver map the host mounts on its own server, so the library takes no GraphQL server dependency |
|
|
51
|
+
| `idempotency` | [04](../docs/04-durable-runtime-and-hitl.md) | The idempotency contract. **Frozen v1.** Every external or destructive call carries a key derived from tenant, run and tool-call identity |
|
|
52
|
+
| `principal-memory` | [15](../docs/15-user-memory.md) | Per-person memory, scoped to the principal as well as the tenant — enforced in the adapters and by RLS, not by a `WHERE` clause the caller has to remember |
|
|
53
|
+
| `retention` | [18](../docs/18-data-retention.md) | Retention windows and the deletion path |
|
|
54
|
+
| `security` | [17](../docs/17-security-review.md) | The security review as executable acceptances, each with a revisit date the release gate checks |
|
|
55
|
+
| `telemetry` | [16](../docs/16-load-and-resilience.md) | The telemetry port and its OTel adapter |
|
|
56
|
+
| `loadtest` | [16](../docs/16-load-and-resilience.md) | Load, soak and failure injection harnesses |
|
|
57
|
+
| `worker` | [05](../docs/05-knowledge-and-documents.md) | The export worker |
|
|
58
|
+
| `server` | [06](../docs/06-graphql-and-frontend.md) | The reference GraphQL host, SSE endpoint, boot, config, health and the runnable API and worker commands. Reached at the `./server` subpath; `graphql`, `graphql-yoga` and `@whatwg-node/server` are **optional peers**, so a consumer embedding the runtime in their own server installs none of them. Rules **R12** and **R13** keep the dependency one-way |
|
|
59
|
+
| `tools/library` | — | The first-party tools (#188), reached at the `./tools` subpath: web fetch and search, HTTP, CSV, JSON, read-only SQL, knowledge search, attachments, time and arithmetic. Envelopes only — rule **R7** forbids I/O here |
|
|
60
|
+
| `toolkit` | — | The deterministic functions those tools delegate to, and the only place the outbound HTTP client is built. Separate from `tools/` precisely because it *does* perform I/O |
|
|
61
|
+
| `testing` | [09](../docs/09-quality-and-release.md) | The conformance suite every adapter runs, plus PGlite fixtures. Named `testing` and shipped deliberately: an adapter written outside this repository has to be holdable to the same behaviour |
|
|
62
|
+
|
|
63
|
+
## Rules these contracts encode
|
|
64
|
+
|
|
65
|
+
1. Every tenant-sensitive operation takes an explicit tenant context. `findById(id)` is
|
|
66
|
+
forbidden; ports use `findById({ tenantId, id })`.
|
|
67
|
+
2. `ExecutionContext` identity is constructed by the host application. Model-generated
|
|
68
|
+
input can never override it.
|
|
69
|
+
3. Tools are authorization-filtered before discovery **and** re-authorized during
|
|
70
|
+
execution.
|
|
71
|
+
4. Every external or destructive tool call carries an idempotency key derived from
|
|
72
|
+
tenant, run and tool-call identity.
|
|
73
|
+
5. Untrusted text — tenant-authored skill bodies, MCP tool descriptions — can never
|
|
74
|
+
widen capability. Authorization lives in the policy layer, never in the prompt.
|
|
75
|
+
|
|
76
|
+
## Scripts
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm run typecheck -w @retinue/agentkit
|
|
80
|
+
npm test -w @retinue/agentkit
|
|
81
|
+
npm run build -w @retinue/agentkit
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
From the repository root, the checks that gate a change:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm run conformance # the adapter matrix, and it fails on an unaccounted cell
|
|
88
|
+
npm run check:boundaries # the dependency rules between workspaces
|
|
89
|
+
npm run check:reachability # every declared capability is wired, every run event is emitted
|
|
90
|
+
npm run security:review # the acceptances in `security`, and their revisit dates
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`check:reachability` exists because the recurring defect in this codebase is not code that
|
|
94
|
+
is wrong — it is code that is **correct, tested and unreachable**. Citations, questions,
|
|
95
|
+
usage recording, compaction, skills and MCP import were each built, each passing tests,
|
|
96
|
+
and each wired to nothing.
|
|
97
|
+
|
|
98
|
+
## Subpaths
|
|
99
|
+
|
|
100
|
+
The root is the **semver boundary**: what is exported from it is API, and what is not exported from it cannot be
|
|
101
|
+
broken. So it is **five values** — it was 392 (#199).
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { createRuntime, resolveCapabilities, defineAgent, asId, AgentPlatformError } from "@retinue/agentkit";
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Every **type** is still exported from the root, by `export type *`, which emits no import. That is what makes the
|
|
108
|
+
cut affordable: a consumer holding an `ExecutionContext` does not have to know which layer defined it, and a type
|
|
109
|
+
cannot be broken by being imported. The split is **types by subject, values by consumer**.
|
|
110
|
+
|
|
111
|
+
The root's runtime graph now reaches *nothing* — not `ai`, not `zod`. Those are still real dependencies of the
|
|
112
|
+
package, because `./runtime` and `./tools` need them, and they stay `dependencies` rather than peers: a consumer
|
|
113
|
+
who installs this will use at least one subpath and should not have to install two more things to do it.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { createDefaultEngine } from "@retinue/agentkit/runtime";
|
|
117
|
+
import { defineTool, createStandardToolProvider } from "@retinue/agentkit/tools"; // no peer: uses global fetch
|
|
118
|
+
import { createMemoryRunStore } from "@retinue/agentkit/persistence"; // no peer at all
|
|
119
|
+
import { createPostgresRunStore } from "@retinue/agentkit/adapters/postgres"; // peer: pg
|
|
120
|
+
import { typeDefs, createResolvers } from "@retinue/agentkit/server"; // peer: graphql, graphql-yoga
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`src/entries/README.md` lists them all, including why there is no `./testing` yet.
|
|
124
|
+
|
|
125
|
+
## Capabilities
|
|
126
|
+
|
|
127
|
+
Eight booleans, declared and cross-checked against the wiring — REQ-043 (#197).
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const runtime = createRuntime({
|
|
131
|
+
profile: "automation", // or "assistant", or no profile and set them yourself
|
|
132
|
+
capabilities: { memory: "on" }, // an override, without restating the rest
|
|
133
|
+
floor: { runs }, // what every runtime needs
|
|
134
|
+
stores: { usage, principalMemory },
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
| Capability | Needs | On means |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| `history` | `messages` | Prior turns reach the model |
|
|
141
|
+
| `memory` | `principalMemory` | Per-person memory is read and written |
|
|
142
|
+
| `compaction` | `summaries`, `summarizer` | A long thread is condensed rather than refused |
|
|
143
|
+
| `citations` | `citations` | Claims carry provenance |
|
|
144
|
+
| `questions` | `interactions` | A run can park on a question and resume |
|
|
145
|
+
| `skills` | `skills` | Named instruction blocks load on demand |
|
|
146
|
+
| `mcp` | `mcpConnections`, `mcpClient` | Another server's tools are importable |
|
|
147
|
+
| `usage` | `usage` | Spend is metered |
|
|
148
|
+
|
|
149
|
+
**A declaration that disagrees with the wiring refuses to start**, in both directions and naming every
|
|
150
|
+
mismatch at once. Declared on with nothing wired is the obvious half. Wired but *not* declared is the half that
|
|
151
|
+
matters more: it is how a declaration drifts into a lie, and this repo has found the same defect six times
|
|
152
|
+
(#157, #159, #161, #163, #165, #185) — a capability that existed, passed its tests, and was wired to nothing.
|
|
153
|
+
|
|
154
|
+
**Off removes the cost.** No store is required, no query is issued, and reading the dependency of an off
|
|
155
|
+
capability throws rather than returning undefined — access is the gate, so no caller has to remember to check.
|
|
156
|
+
|
|
157
|
+
**Approvals and quotas are not on this list, deliberately.** They have no off switch. An automation that needs no
|
|
158
|
+
human approves through a *policy* that records what it approved, which is auditable; a boolean that removed the
|
|
159
|
+
gate would remove the record with it. That distinction is the difference between "nobody had to approve this" and
|
|
160
|
+
"nobody knows whether anybody approved this".
|
|
161
|
+
|
|
162
|
+
### The minimum viable configuration
|
|
163
|
+
|
|
164
|
+
The smallest thing that runs a tool-calling automation — no conversation, no memory, no human in the loop:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
const runtime = createRuntime({ profile: "automation", floor: { runs }, stores: { usage } });
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
One store beyond the floor. A run in this configuration has **no `conversationId`** — absent, not invented
|
|
171
|
+
(#198): the conversation-scoped capabilities are unavailable rather than operating on a fabricated id, which is
|
|
172
|
+
what #164 did with `principalId` and why every per-person figure silently read as a machine's.
|
|
173
|
+
|
|
174
|
+
All 256 combinations of the eight are constructed and gate-checked in `capabilities/__tests__/runtime.test.ts`.
|
|
175
|
+
That test used to enumerate six hand-picked mixes, on the reasoning that the matrix would "assert that
|
|
176
|
+
combinations nobody has thought about work" — which is backwards for a surface of eight independent booleans,
|
|
177
|
+
where the combination nobody thought about is the one a customer picks first.
|
|
178
|
+
|
|
179
|
+
## Tools
|
|
180
|
+
|
|
181
|
+
Fifteen first-party tools, at `@retinue/agentkit/tools`. **Wiring is the toggle** — a tool exists when its
|
|
182
|
+
dependency was supplied and not otherwise, because a separate `enable` flag beside a `sqlQuery` function is how a
|
|
183
|
+
deployment ends up with a tool that is enabled and unwired:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
const tools = createStandardToolProvider({
|
|
187
|
+
deps: { authorization, idempotency, approvals },
|
|
188
|
+
http: {}, // fetch_url, fetch_json, http_request, http_write
|
|
189
|
+
search: braveProvider, // web_search — omitted, and the tool does not exist
|
|
190
|
+
sql: { query: readOnlyPool, readOnly: true, schemas: ["app"] },
|
|
191
|
+
knowledge: { retriever, authSubjects: (ctx) => [String(ctx.conversationId)] },
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
| | |
|
|
196
|
+
|---|---|
|
|
197
|
+
| `fetch_url`, `fetch_json`, `http_request` | `read`. Egress-policy checked before any request, redirects refused rather than followed, bodies bounded while reading and fenced as untrusted content |
|
|
198
|
+
| `http_write` | `external-write`, so approval and an idempotency key are required by the registry. Two tools rather than one with a `method` argument, because effect is classified per *tool* — a single tool could only be gated for every call or none |
|
|
199
|
+
| `parse_csv`, `query_json` | `read`, pure. They take text, not a path or a URL: reading is `read_attachment`'s or `fetch_url`'s job, and each should be checked by the thing that should check it |
|
|
200
|
+
| `sql_query`, `sql_schema` | `read`, and only honest because `createSqlQuery` demands a `readOnly: true` acknowledgement. The keyword scan inside it is a second line of defence; the connection is the control |
|
|
201
|
+
| `search_knowledge` | `read`. `authSubjects` comes from the host, never from tool input — a model must not widen its own read scope by asking |
|
|
202
|
+
| `read_attachment`, `list_attachments`, `read_document` | `read`, through `FileService` so the entitlement check is not duplicated |
|
|
203
|
+
| `now`, `calculate` | `read`, pure. `calculate` is a parser, not `eval`: the expression comes from a model |
|
|
204
|
+
|
|
205
|
+
Credentials are configured per host (`headersFor`) and never appear in a tool's input schema. The client refuses
|
|
206
|
+
an `authorization` or `cookie` header supplied by a caller rather than forwarding it.
|
|
207
|
+
|
|
208
|
+
## Flows and teams
|
|
209
|
+
|
|
210
|
+
`@retinue/agentkit/flows` — REQ-038 ([#187](https://github.com/Rise-Experts/retinue/issues/187)) and REQ-037
|
|
211
|
+
([#186](https://github.com/Rise-Experts/retinue/issues/186)).
|
|
212
|
+
|
|
213
|
+
**A team is a kind of flow step, and a team compiles to a flow.** Both issues say they share design, and they are
|
|
214
|
+
right: a flow's step and a team's member turn are the same idea, and modelling them separately produces two
|
|
215
|
+
overlapping notions of "a step" to keep in agreement forever.
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { compileTeam, createFlowRunner } from "@retinue/agentkit/flows";
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### The interpreter is a pure function
|
|
222
|
+
|
|
223
|
+
`advance(definition, execution, outcome)` returns the next execution and **one effect** for the caller to perform.
|
|
224
|
+
It performs nothing itself — no agent call, no tool call, no clock read, no store write. Every property these two
|
|
225
|
+
REQs ask for is a consequence rather than a separate mechanism:
|
|
226
|
+
|
|
227
|
+
| Property | Why it follows |
|
|
228
|
+
|---|---|
|
|
229
|
+
| Durable resume | The returned execution *is* the position. A host persists it; after a restart it calls `advance` again and gets the same effect. There is no interpreter instance to rebuild |
|
|
230
|
+
| Idempotency across a resume | The effect's key is `(executionId, step, attempt)` — all three are in the stored state, so a step that wrote externally and crashed produces the *same* key and the idempotency store answers with the first result |
|
|
231
|
+
| Budgets | Checked before the effect is produced, so an over-budget flow performs nothing rather than spending and then noticing |
|
|
232
|
+
| Tests | Feeding outcomes to a function needs no agent, no database and no clock. The awkward cases — a crash mid-step, a retry surviving a process death — are testable at all |
|
|
233
|
+
|
|
234
|
+
The alternative, an async interpreter that awaits its own effects, is shorter and cannot be made durable without a
|
|
235
|
+
checkpoint after every `await` — which is the same state machine with the states implicit.
|
|
236
|
+
|
|
237
|
+
### A definition and an execution are different things
|
|
238
|
+
|
|
239
|
+
`FlowExecution.flowVersion` is pinned at start and the definition is read at that version for the execution's whole
|
|
240
|
+
life. **Editing a flow does not change one already running.** Proven live: a v2 with a completely different shape
|
|
241
|
+
was published while an execution sat parked at a checkpoint, and it still finished through v1.
|
|
242
|
+
|
|
243
|
+
The store refuses to overwrite a version at all — `(tenant_id, flow_id, version)` is the primary key with no
|
|
244
|
+
`ON CONFLICT` — which is what makes the pin worth having.
|
|
245
|
+
|
|
246
|
+
### Step kinds
|
|
247
|
+
|
|
248
|
+
`agent`, `team`, `tool`, `branch`, `wait`, `checkpoint`, `subflow`, `done`. A `checkpoint` uses the **existing**
|
|
249
|
+
HITL path, so a parked flow is the same object the assistant surface already answers.
|
|
250
|
+
|
|
251
|
+
Two arithmetic details that were bugs first: `done` consumes no budget and is not gated by one, because a budget
|
|
252
|
+
stops *work* and finishing is not work. Counting it meant a flow whose ceiling exactly matched its work always
|
|
253
|
+
failed on the last step — so `maxSteps: 3` really meant two steps and a marker. A `branch` does count, because a
|
|
254
|
+
branch can loop.
|
|
255
|
+
|
|
256
|
+
### Failure is chosen in the definition
|
|
257
|
+
|
|
258
|
+
`retry` (bounded, with backoff), `skip`, `escalate`, `fail`. A retry gets a **different** idempotency key, because
|
|
259
|
+
reusing it would have the store answer with the *failed* first result — a retry policy that silently does nothing.
|
|
260
|
+
A failed step that spent money is still charged, or a retrying flow costs more than its ceiling allows.
|
|
261
|
+
|
|
262
|
+
### Teams
|
|
263
|
+
|
|
264
|
+
`sequential` chains one agent step per member, each reading the previous one's output *and* the original brief —
|
|
265
|
+
passing only the previous output loses the request by the third member, which is how a chain of agents drifts off
|
|
266
|
+
the question. `manager-led` compiles to **one** agent step whose tools include a delegation tool, so the engine's
|
|
267
|
+
own turn loop does the iterating and a delegation is a real tool call: authorised, approved, deduplicated and
|
|
268
|
+
accounted for unchanged, because it *is* one rather than resembling one.
|
|
269
|
+
|
|
270
|
+
A member's tools are an **intersection**, never a union: a member cannot reach a tool the delegating context could
|
|
271
|
+
not, and the delegation tool itself is always stripped — a member that could delegate would be a manager.
|
|
272
|
+
|
|
273
|
+
### An agent step is a child run
|
|
274
|
+
|
|
275
|
+
Each agent step creates a `Run` of its own and the flow parks on it (#202). A run rather than an inline model
|
|
276
|
+
call, because a `Run` is what earns checkpointing, recovery, quota admission and its own usage rows — calling a
|
|
277
|
+
model from the runner would be a second turn implementation with none of those, and it would pass a demo.
|
|
278
|
+
|
|
279
|
+
Three decisions inside that, each with an appealing wrong answer:
|
|
280
|
+
|
|
281
|
+
- **The child has no conversation.** `ConversationRunCoordinator` claims a *conversation's* single run slot, so a
|
|
282
|
+
flow inside a conversation whose steps also claimed it would deadlock against the conversation's own turn — the
|
|
283
|
+
parent holds the slot and waits for a child that can never get it. A conversation-less run (#198) has no slot to
|
|
284
|
+
contend for, and what the member needs travels in the run's `input`.
|
|
285
|
+
- **The ceiling is the flow's remainder**, re-derived per step. Handing each member the flow's original budget
|
|
286
|
+
would let every one of them spend the whole thing. Visible live: the first member gets 6 steps, the second 5.
|
|
287
|
+
- **Two ways to wake up, and only one of them is load-bearing.** `onRunSettled` on the worker is the fast path;
|
|
288
|
+
correctness is a *poll* of the child's state at the top of every resume. A crash between the child completing
|
|
289
|
+
and the notification being sent loses the message, and a parent that only woke on notifications would sit
|
|
290
|
+
forever with nothing looking again.
|
|
291
|
+
|
|
292
|
+
`Run.input` and `Run.limits` exist because of this. #198 made a run able to exist without a conversation, but the
|
|
293
|
+
only place a request could live was a `Message` — and a message requires a conversation. So the run shape said "no
|
|
294
|
+
conversation needed" while the storage said its input still needed one.
|
|
295
|
+
|
|
296
|
+
`npm run flow -w @retinue/example-app` drives all of it against Postgres: a flow straight through, one parked for
|
|
297
|
+
a person, a reload from storage by something that never held it, the version pin against a published v2, a team
|
|
298
|
+
whose members become child runs with shrinking ceilings, a lost notification recovered by the poll, and a failing
|
|
299
|
+
member routed into its step's policy.
|
|
300
|
+
|
|
301
|
+
## Import convention
|
|
302
|
+
|
|
303
|
+
The package is ESM with `NodeNext` resolution, so relative imports carry an explicit
|
|
304
|
+
`.js` extension even in TypeScript sources.
|
|
305
|
+
|
|
306
|
+
## Licence
|
|
307
|
+
|
|
308
|
+
MIT — see [LICENSE](./LICENSE).
|
|
309
|
+
|
|
310
|
+
Copyright (c) 2026 Azeem Sarwar and Rise Experts.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BullMQ consumer side (#107) — the other half of the queue from `dispatcher.ts`.
|
|
3
|
+
*
|
|
4
|
+
* Lives here rather than in `src/worker/` so the `bullmq` coupling stays inside the adapter directory,
|
|
5
|
+
* and so the entrypoint's ordering decisions remain testable without Redis.
|
|
6
|
+
*/
|
|
7
|
+
import type { JobConsumer } from "../../worker/main.js";
|
|
8
|
+
import { type RunJobData } from "./dispatcher.js";
|
|
9
|
+
/** What BullMQ's `Worker` gives us, narrowed to what this needs. */
|
|
10
|
+
export interface QueueWorkerFactory {
|
|
11
|
+
(queueName: string, handler: (job: {
|
|
12
|
+
readonly data: RunJobData;
|
|
13
|
+
}) => Promise<void>, options: {
|
|
14
|
+
readonly concurrency: number;
|
|
15
|
+
}): {
|
|
16
|
+
close(): Promise<void>;
|
|
17
|
+
pause?(doNotWaitActive?: boolean): Promise<void>;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export type BullMqConsumerOptions = {
|
|
21
|
+
readonly queueName?: string;
|
|
22
|
+
readonly concurrency?: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* A `JobConsumer` over BullMQ's `Worker`.
|
|
26
|
+
*
|
|
27
|
+
* `stop` pauses before closing. BullMQ's `close()` already waits for active jobs, but pausing first is
|
|
28
|
+
* what makes "stop accepting new work" true *immediately* rather than eventually — without it, a
|
|
29
|
+
* worker with free concurrency can pick up another job in the moment between deciding to shut down and
|
|
30
|
+
* `close()` taking effect, which is precisely the job AC-2 says must not be accepted.
|
|
31
|
+
*/
|
|
32
|
+
export declare const createBullMqJobConsumer: (createWorker: QueueWorkerFactory, options?: BullMqConsumerOptions) => JobConsumer;
|
|
33
|
+
//# sourceMappingURL=consumer.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { RUN_QUEUE_NAME } from "./dispatcher.js";
|
|
2
|
+
/**
|
|
3
|
+
* A `JobConsumer` over BullMQ's `Worker`.
|
|
4
|
+
*
|
|
5
|
+
* `stop` pauses before closing. BullMQ's `close()` already waits for active jobs, but pausing first is
|
|
6
|
+
* what makes "stop accepting new work" true *immediately* rather than eventually — without it, a
|
|
7
|
+
* worker with free concurrency can pick up another job in the moment between deciding to shut down and
|
|
8
|
+
* `close()` taking effect, which is precisely the job AC-2 says must not be accepted.
|
|
9
|
+
*/
|
|
10
|
+
export const createBullMqJobConsumer = (createWorker, options = {}) => {
|
|
11
|
+
const queueName = options.queueName ?? RUN_QUEUE_NAME;
|
|
12
|
+
const concurrency = options.concurrency ?? 4;
|
|
13
|
+
let worker = null;
|
|
14
|
+
return {
|
|
15
|
+
start(handler) {
|
|
16
|
+
worker = createWorker(queueName, async (job) => {
|
|
17
|
+
await handler({
|
|
18
|
+
tenantId: job.data.tenantId,
|
|
19
|
+
runId: job.data.runId,
|
|
20
|
+
// Read back off the payload so the worker's span continues the request's trace (#143). Passed
|
|
21
|
+
// through even when absent, because the *instrumented* consumer distinguishes "no traceparent" from
|
|
22
|
+
// "a traceparent that failed to parse" and records which -- a propagation bug otherwise looks
|
|
23
|
+
// exactly like a job enqueued before propagation existed.
|
|
24
|
+
...(job.data.traceparent !== undefined ? { traceparent: job.data.traceparent } : {}),
|
|
25
|
+
...(job.data.enqueuedAt !== undefined ? { enqueuedAt: job.data.enqueuedAt } : {}),
|
|
26
|
+
});
|
|
27
|
+
}, { concurrency });
|
|
28
|
+
},
|
|
29
|
+
async stop() {
|
|
30
|
+
const current = worker;
|
|
31
|
+
worker = null;
|
|
32
|
+
if (!current)
|
|
33
|
+
return;
|
|
34
|
+
// `true` = do not wait for active jobs to finish *pausing*; the runtime's own drain handles
|
|
35
|
+
// waiting, and it owns the grace period.
|
|
36
|
+
await current.pause?.(true).catch(() => undefined);
|
|
37
|
+
await current.close().catch(() => undefined);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=consumer.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { JobDispatcher } from "../../runtime/index.js";
|
|
2
|
+
/** The queue surface this adapter needs. BullMQ's `Queue` satisfies it structurally. */
|
|
3
|
+
export interface JobQueue {
|
|
4
|
+
add(name: string, data: RunJobData, opts?: {
|
|
5
|
+
readonly jobId?: string;
|
|
6
|
+
readonly attempts?: number;
|
|
7
|
+
}): Promise<unknown>;
|
|
8
|
+
getJobCounts(...types: readonly string[]): Promise<Record<string, number>>;
|
|
9
|
+
/**
|
|
10
|
+
* Fetch a job by id, so a *terminal* one can be cleared before a re-enqueue (#156).
|
|
11
|
+
*
|
|
12
|
+
* Optional, because a fake queue in a test has nothing to fetch and the dedup it is emulating cannot occur
|
|
13
|
+
* there. Absent means the clearing step is skipped, which is exactly the old behaviour.
|
|
14
|
+
*/
|
|
15
|
+
getJob?(jobId: string): Promise<JobHandle | null | undefined>;
|
|
16
|
+
close?(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/** The part of a BullMQ job this needs: its state, and the ability to remove it. */
|
|
19
|
+
export interface JobHandle {
|
|
20
|
+
getState(): Promise<string>;
|
|
21
|
+
remove(): Promise<unknown>;
|
|
22
|
+
}
|
|
23
|
+
/** What a worker needs to execute the run. The tenant travels with the job, not with the queue. */
|
|
24
|
+
export type RunJobData = {
|
|
25
|
+
readonly tenantId: string;
|
|
26
|
+
readonly runId: string;
|
|
27
|
+
/**
|
|
28
|
+
* W3C trace context, so the worker's span joins the request's trace (#143, AC-1).
|
|
29
|
+
*
|
|
30
|
+
* Optional, and it has to be: jobs enqueued before this existed are already on the queue, and a worker that
|
|
31
|
+
* required the field would fail every one. Absent means "start a new trace" -- a missing link, not a lost run.
|
|
32
|
+
*/
|
|
33
|
+
readonly traceparent?: string;
|
|
34
|
+
/**
|
|
35
|
+
* The producer's clock when it let go. The only way claim latency is measurable, since neither side alone
|
|
36
|
+
* knows both ends of the wait.
|
|
37
|
+
*/
|
|
38
|
+
readonly enqueuedAt?: string;
|
|
39
|
+
};
|
|
40
|
+
export declare const RUN_QUEUE_NAME = "agentkit-runs";
|
|
41
|
+
export declare const RUN_JOB_NAME = "run";
|
|
42
|
+
export declare const runJobId: (input: {
|
|
43
|
+
readonly tenantId: string;
|
|
44
|
+
readonly runId: string;
|
|
45
|
+
}) => string;
|
|
46
|
+
/**
|
|
47
|
+
* Queue-level attempts. Deliberately 1.
|
|
48
|
+
*
|
|
49
|
+
* The runtime owns retries (`DEFAULT_RETRY_POLICY`: 5 attempts, 0.5s base, ×2, −25% jitter,
|
|
50
|
+
* `retry-after` honoured). A queue-level `attempts > 1` would multiply with it — five runtime attempts
|
|
51
|
+
* inside three queue attempts is fifteen provider calls, with backoff neither layer intended. AC-3 is
|
|
52
|
+
* exactly this constant.
|
|
53
|
+
*/
|
|
54
|
+
export declare const QUEUE_ATTEMPTS = 1;
|
|
55
|
+
export type JobDispatcherOptions = {
|
|
56
|
+
/** Ceiling on a single enqueue. Without one, a half-open connection turns into a stalled request. */
|
|
57
|
+
readonly enqueueTimeoutMs?: number;
|
|
58
|
+
};
|
|
59
|
+
export declare const createBullMqJobDispatcher: (queue: JobQueue, options?: JobDispatcherOptions) => JobDispatcher;
|
|
60
|
+
export type QueueMetrics = {
|
|
61
|
+
readonly waiting: number;
|
|
62
|
+
readonly active: number;
|
|
63
|
+
readonly delayed: number;
|
|
64
|
+
readonly failed: number;
|
|
65
|
+
readonly completed: number;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Queue depth and failure counts (AC-5), for the observability work in REQ-033.
|
|
69
|
+
*
|
|
70
|
+
* Zero-filled rather than optional: a dashboard that has to distinguish "no failures" from "the count
|
|
71
|
+
* was missing" will get it wrong, and the difference is not interesting.
|
|
72
|
+
*/
|
|
73
|
+
export declare const queueMetrics: (queue: JobQueue) => Promise<QueueMetrics>;
|
|
74
|
+
//# sourceMappingURL=dispatcher.d.ts.map
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BullMQ/Redis `JobDispatcher` (#105) — the durable enqueue behind `POST /runs`.
|
|
3
|
+
*
|
|
4
|
+
* `JobDispatcher` has always been documented as *"Adapters: BullMQ, in-memory for tests"* and only the
|
|
5
|
+
* in-memory half existed, so work enqueued by the API died with the process holding it. That is the
|
|
6
|
+
* premise REQ-015 exists to fix.
|
|
7
|
+
*
|
|
8
|
+
* The `bullmq` import is confined to this directory (the boundary checker expects it here), and the
|
|
9
|
+
* dispatcher is written against a small `JobQueue` seam that BullMQ's `Queue` satisfies structurally.
|
|
10
|
+
* That keeps the port testable without Redis and keeps the real client one function away — the same
|
|
11
|
+
* arrangement the Supabase Realtime adapter uses.
|
|
12
|
+
*/
|
|
13
|
+
import { AgentPlatformError } from "../../core/errors.js";
|
|
14
|
+
/**
|
|
15
|
+
* States in which a job is **finished**, so its id is history rather than a live duplicate.
|
|
16
|
+
*
|
|
17
|
+
* This distinction is the whole fix for #156. `runJobId` is deterministic on purpose — two concurrent enqueues of
|
|
18
|
+
* the same run must collapse into one job (#105). But the id is also reused across a *resume*: a run that
|
|
19
|
+
* suspends for an approval completes its job, BullMQ retains it under `removeOnComplete`, and the re-enqueue
|
|
20
|
+
* after the decision is silently dropped because the id already exists. The run then sits in `queued` with no
|
|
21
|
+
* job and no lease, and nothing ever picks it up.
|
|
22
|
+
*
|
|
23
|
+
* Dedup should protect against a job that is still going to run, not against one that already has. So a
|
|
24
|
+
* `waiting`, `active`, `delayed` or `paused` job still collapses — the #105 guarantee is untouched — and only a
|
|
25
|
+
* finished one is cleared out of the way.
|
|
26
|
+
*/
|
|
27
|
+
const FINISHED_JOB_STATES = new Set(["completed", "failed"]);
|
|
28
|
+
// Hyphen, not a colon. BullMQ rejects a queue name containing `:` outright ("Queue name cannot
|
|
29
|
+
// contain :"), because it builds its own Redis keys as `bull:<name>:<...>` and a colon in the name
|
|
30
|
+
// would make those keys ambiguous. Caught by running against real Redis — a fake queue accepts any
|
|
31
|
+
// string, so this would have thrown on the first enqueue in production.
|
|
32
|
+
export const RUN_QUEUE_NAME = "agentkit-runs";
|
|
33
|
+
export const RUN_JOB_NAME = "run";
|
|
34
|
+
/**
|
|
35
|
+
* The job id: tenant-qualified, colon-free, and unambiguous.
|
|
36
|
+
*
|
|
37
|
+
* **Why the tenant is in it.** The SPEC says "job id derived from `runId`". Derived from the run id
|
|
38
|
+
* **alone** it is a cross-tenant fault: `enqueueRun` takes `{ tenantId, runId }`, and BullMQ treats an
|
|
39
|
+
* existing `jobId` as a no-op returning the existing job — so if two tenants ever present the same run
|
|
40
|
+
* id, the second tenant's enqueue is **silently dropped**. The symptom is a run that never executes,
|
|
41
|
+
* with no error anywhere.
|
|
42
|
+
*
|
|
43
|
+
* **Why the encoding looks like this.** BullMQ rejects a custom id containing `:` outright ("Custom Id
|
|
44
|
+
* cannot contain :"), because ids become part of its Redis key structure. Found by running against a
|
|
45
|
+
* real Redis; a fake queue accepts any string, so `tenant:run` passed every offline test and would
|
|
46
|
+
* have failed on the first real enqueue.
|
|
47
|
+
*
|
|
48
|
+
* A naive `${tenantId}-${runId}` would be colon-free but **ambiguous**: tenant `a-b` with run `c` and
|
|
49
|
+
* tenant `a` with run `b-c` produce the same id, and an ambiguous id is a *collision* — one of the two
|
|
50
|
+
* runs disappears. So `:` and `%` are percent-escaped first (an injective mapping), and the escaped
|
|
51
|
+
* tenant is length-prefixed, which makes the split unique regardless of what the ids contain.
|
|
52
|
+
*
|
|
53
|
+
* What this dedup does and does not buy is worth being precise about, because it is easy to mistake
|
|
54
|
+
* for the idempotency story:
|
|
55
|
+
*
|
|
56
|
+
* - it stops an accidental *concurrent* double-enqueue, which is AC-2;
|
|
57
|
+
* - it stops nothing once the job is removed from Redis, so re-enqueuing a *finished* run is the
|
|
58
|
+
* `RunStore`'s problem (#93), not the queue's;
|
|
59
|
+
* - a *tool call* repeating a side effect is `IdempotencyStore`'s problem (#100).
|
|
60
|
+
*/
|
|
61
|
+
const escapeIdPart = (value) => value.replace(/%/g, "%25").replace(/:/g, "%3A");
|
|
62
|
+
export const runJobId = (input) => {
|
|
63
|
+
const tenant = escapeIdPart(input.tenantId);
|
|
64
|
+
const run = escapeIdPart(input.runId);
|
|
65
|
+
return `${tenant.length}-${tenant}-${run}`;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Queue-level attempts. Deliberately 1.
|
|
69
|
+
*
|
|
70
|
+
* The runtime owns retries (`DEFAULT_RETRY_POLICY`: 5 attempts, 0.5s base, ×2, −25% jitter,
|
|
71
|
+
* `retry-after` honoured). A queue-level `attempts > 1` would multiply with it — five runtime attempts
|
|
72
|
+
* inside three queue attempts is fifteen provider calls, with backoff neither layer intended. AC-3 is
|
|
73
|
+
* exactly this constant.
|
|
74
|
+
*/
|
|
75
|
+
export const QUEUE_ATTEMPTS = 1;
|
|
76
|
+
const unavailable = (cause) => new AgentPlatformError({
|
|
77
|
+
code: "provider_unavailable",
|
|
78
|
+
message: "Could not enqueue the run: the job queue is unreachable",
|
|
79
|
+
retryable: true,
|
|
80
|
+
}, { cause });
|
|
81
|
+
export const createBullMqJobDispatcher = (queue, options = {}) => {
|
|
82
|
+
const timeoutMs = options.enqueueTimeoutMs ?? 5_000;
|
|
83
|
+
return {
|
|
84
|
+
async enqueueRun({ tenantId, runId, traceparent, enqueuedAt, }) {
|
|
85
|
+
/**
|
|
86
|
+
* Clear a *finished* job holding this id, so a resumed run can be enqueued again (#156).
|
|
87
|
+
*
|
|
88
|
+
* Before the add, and deliberately not conditional on knowing whether this is a resume: the caller does not
|
|
89
|
+
* know either. `decideApproval` and `answerQuestion` just call `enqueueRun`, and a reaped run does too.
|
|
90
|
+
*
|
|
91
|
+
* A failure here is swallowed. The worst case is the old behaviour — the add is a no-op and the run waits —
|
|
92
|
+
* and turning a transient Redis hiccup during cleanup into a failed *admission* would be a worse trade.
|
|
93
|
+
*/
|
|
94
|
+
if (queue.getJob !== undefined) {
|
|
95
|
+
try {
|
|
96
|
+
const existing = await queue.getJob(runJobId({ tenantId, runId }));
|
|
97
|
+
if (existing !== null && existing !== undefined) {
|
|
98
|
+
const state = await existing.getState();
|
|
99
|
+
if (FINISHED_JOB_STATES.has(state))
|
|
100
|
+
await existing.remove();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Cleanup is best-effort; the add below is the operation that matters.
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const add = queue.add(RUN_JOB_NAME,
|
|
108
|
+
// Spread conditionally rather than passing `traceparent: undefined`: BullMQ serializes the payload to
|
|
109
|
+
// JSON, an explicit undefined disappears anyway, and the two shapes are then indistinguishable in a
|
|
110
|
+
// Redis dump -- which is where someone debugging a broken trace will be looking.
|
|
111
|
+
{
|
|
112
|
+
tenantId,
|
|
113
|
+
runId,
|
|
114
|
+
...(traceparent !== undefined ? { traceparent } : {}),
|
|
115
|
+
...(enqueuedAt !== undefined ? { enqueuedAt } : {}),
|
|
116
|
+
}, { jobId: runJobId({ tenantId, runId }), attempts: QUEUE_ATTEMPTS });
|
|
117
|
+
// Belt as well as braces. `enableOfflineQueue: false` makes ioredis reject rather than buffer,
|
|
118
|
+
// but a connection that is open-but-dead fails neither way — it just never answers. AC-4 is
|
|
119
|
+
// about the caller getting an error, so the timeout is part of the guarantee, not a nicety.
|
|
120
|
+
let timer;
|
|
121
|
+
try {
|
|
122
|
+
await Promise.race([
|
|
123
|
+
add,
|
|
124
|
+
new Promise((_resolve, reject) => {
|
|
125
|
+
timer = setTimeout(() => reject(unavailable(new Error(`enqueue timed out after ${timeoutMs}ms`))), timeoutMs);
|
|
126
|
+
}),
|
|
127
|
+
]);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
// Rethrown unchanged if it is already ours, so the timeout message survives; otherwise wrapped,
|
|
131
|
+
// because a driver error should not reach the API layer as a driver error.
|
|
132
|
+
throw error instanceof AgentPlatformError ? error : unavailable(error);
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
if (timer !== undefined)
|
|
136
|
+
clearTimeout(timer);
|
|
137
|
+
// The losing promise must not become an unhandled rejection when the timeout wins.
|
|
138
|
+
void Promise.resolve(add).catch(() => undefined);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Queue depth and failure counts (AC-5), for the observability work in REQ-033.
|
|
145
|
+
*
|
|
146
|
+
* Zero-filled rather than optional: a dashboard that has to distinguish "no failures" from "the count
|
|
147
|
+
* was missing" will get it wrong, and the difference is not interesting.
|
|
148
|
+
*/
|
|
149
|
+
export const queueMetrics = async (queue) => {
|
|
150
|
+
const counts = await queue.getJobCounts("waiting", "active", "delayed", "failed", "completed");
|
|
151
|
+
const at = (key) => Number(counts[key] ?? 0);
|
|
152
|
+
return {
|
|
153
|
+
waiting: at("waiting"),
|
|
154
|
+
active: at("active"),
|
|
155
|
+
delayed: at("delayed"),
|
|
156
|
+
failed: at("failed"),
|
|
157
|
+
completed: at("completed"),
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
//# sourceMappingURL=dispatcher.js.map
|