@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,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wiring telemetry to the boundaries — AC-1, AC-2, AC-3.
|
|
3
|
+
*
|
|
4
|
+
* The port is inert on its own. These are the wrappers that put it on the path a run actually takes, and they
|
|
5
|
+
* are **decorators over the existing seams** rather than edits inside them: `instrumentDispatcher` wraps a
|
|
6
|
+
* `JobDispatcher`, `instrumentConsumer` wraps a `JobConsumer`. Two reasons, and the second is the real one.
|
|
7
|
+
*
|
|
8
|
+
* The first is that a deployment without telemetry runs the undecorated object, so telemetry cannot be the
|
|
9
|
+
* reason a run fails.
|
|
10
|
+
*
|
|
11
|
+
* The second is that instrumentation *inside* the dispatcher would put a span around the enqueue and nothing
|
|
12
|
+
* around the wait between enqueue and claim — which is the number a user actually experiences. Wrapping both
|
|
13
|
+
* sides is what makes claim latency measurable at all.
|
|
14
|
+
*/
|
|
15
|
+
import { BOUNDARY_SPANS } from "./spans.js";
|
|
16
|
+
import { createRunMetrics } from "./metrics.js";
|
|
17
|
+
import { formatTraceparent, parseTraceparent } from "./trace-context.js";
|
|
18
|
+
import {} from "./index.js";
|
|
19
|
+
import { errorCodeOf, withSpan } from "./index.js";
|
|
20
|
+
/**
|
|
21
|
+
* Wrap a dispatcher so the enqueue is a span and the job carries the trace forward.
|
|
22
|
+
*
|
|
23
|
+
* The traceparent goes down through `enqueueRun`, which #143 widened to accept it. The first version of this
|
|
24
|
+
* remembered the span it had just opened and let the adapter read it back — which is wrong the moment two
|
|
25
|
+
* enqueues overlap, and overlapping enqueues are the normal case, not the edge one. A racy trace link is worse
|
|
26
|
+
* than none: it attributes one tenant's run to another tenant's request.
|
|
27
|
+
*/
|
|
28
|
+
export const instrumentDispatcher = (inner, deps) => {
|
|
29
|
+
const clock = deps.now ?? (() => Date.now());
|
|
30
|
+
return {
|
|
31
|
+
async enqueueRun(input) {
|
|
32
|
+
await withSpan(deps.telemetry.tracer, BOUNDARY_SPANS.enqueue, {
|
|
33
|
+
// `producer`, so a collector renders the queue hop as a queue hop rather than as a nested call. The
|
|
34
|
+
// distinction matters in a trace view: a producer span is expected to end long before its consumer.
|
|
35
|
+
kind: "producer",
|
|
36
|
+
// The *caller's* traceparent is this span's parent — the request that is enqueueing. Missing in the
|
|
37
|
+
// first version, and the end-to-end test caught it immediately: the enqueue span opened its own trace,
|
|
38
|
+
// so the worker faithfully continued a trace containing nothing but the queue hop. Every span existed,
|
|
39
|
+
// every parent link was present, and the trace was still useless.
|
|
40
|
+
...(input.traceparent !== undefined ? { parent: input.traceparent } : {}),
|
|
41
|
+
attributes: { tenantId: input.tenantId, runId: input.runId },
|
|
42
|
+
}, async (span) => {
|
|
43
|
+
try {
|
|
44
|
+
await inner.enqueueRun({
|
|
45
|
+
...input,
|
|
46
|
+
// The *enqueue span's* context, not the request's: the consumer's span is a child of this hop, and
|
|
47
|
+
// parenting it to the request instead would flatten the queue out of the trace entirely.
|
|
48
|
+
traceparent: formatTraceparent(span.context),
|
|
49
|
+
enqueuedAt: input.enqueuedAt ?? new Date(clock()).toISOString(),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
deps.telemetry.logger.log("error", "run.enqueue-failed", {
|
|
54
|
+
tenantId: input.tenantId,
|
|
55
|
+
runId: input.runId,
|
|
56
|
+
errorCode: errorCodeOf(error),
|
|
57
|
+
});
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
deps.telemetry.logger.log("info", "run.enqueued", { tenantId: input.tenantId, runId: input.runId });
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Wrap a consumer so each job continues its producer's trace and claim latency is recorded.
|
|
67
|
+
*
|
|
68
|
+
* The handler runs *inside* the claim span, so everything the run does — model calls, tool calls, an approval
|
|
69
|
+
* wait — is a descendant of it and of the original request. That is AC-1: one user request, one trace, across
|
|
70
|
+
* three processes.
|
|
71
|
+
*/
|
|
72
|
+
export const instrumentConsumer = (inner, deps) => {
|
|
73
|
+
const metrics = deps.metrics ?? createRunMetrics(deps.telemetry.meter);
|
|
74
|
+
const now = deps.now ?? (() => Date.now());
|
|
75
|
+
const workerId = deps.workerId;
|
|
76
|
+
return {
|
|
77
|
+
start(handler) {
|
|
78
|
+
return inner.start(async (job) => {
|
|
79
|
+
const parent = parseTraceparent(job.traceparent);
|
|
80
|
+
const claimedAt = now();
|
|
81
|
+
// Claim latency, from the producer's stamp. Recorded before the handler runs, so it is reported even if
|
|
82
|
+
// the run then fails -- a queue backing up and a run failing are different incidents and a metric that
|
|
83
|
+
// only appeared on success would hide the first behind the second.
|
|
84
|
+
if (job.enqueuedAt !== undefined) {
|
|
85
|
+
const enqueued = Date.parse(job.enqueuedAt);
|
|
86
|
+
// Skew between two hosts can make this negative. Dropped rather than clamped to zero: a zero is
|
|
87
|
+
// indistinguishable from a genuinely instant claim, and a p99 built from fabricated zeros reads
|
|
88
|
+
// healthy. The absence is the honest answer.
|
|
89
|
+
if (Number.isFinite(enqueued) && claimedAt >= enqueued)
|
|
90
|
+
metrics.claimLatencyMs.record(claimedAt - enqueued, { tenantId: job.tenantId });
|
|
91
|
+
}
|
|
92
|
+
await withSpan(deps.telemetry.tracer, BOUNDARY_SPANS.claim, {
|
|
93
|
+
// `consumer`, the other half of the producer span. A collector uses the pair to draw the queue hop.
|
|
94
|
+
kind: "consumer",
|
|
95
|
+
...(parent !== null ? { parent: job.traceparent } : {}),
|
|
96
|
+
attributes: {
|
|
97
|
+
tenantId: job.tenantId,
|
|
98
|
+
runId: job.runId,
|
|
99
|
+
// Whether the trace was actually continued. Without this, a propagation bug looks identical to a
|
|
100
|
+
// job that was enqueued before propagation existed, and both look like a working trace.
|
|
101
|
+
"queue.trace_continued": parent !== null,
|
|
102
|
+
...(workerId !== undefined ? { workerId } : {}),
|
|
103
|
+
},
|
|
104
|
+
}, async () => {
|
|
105
|
+
const logger = deps.telemetry.logger.child({ tenantId: job.tenantId, runId: job.runId });
|
|
106
|
+
logger.log("info", "run.claimed", { ...(workerId !== undefined ? { workerId } : {}) });
|
|
107
|
+
const startedAt = now();
|
|
108
|
+
try {
|
|
109
|
+
await handler(job);
|
|
110
|
+
metrics.runDurationMs.record(now() - startedAt, { tenantId: job.tenantId, outcome: "completed" });
|
|
111
|
+
metrics.runsTotal.record(1, { tenantId: job.tenantId, outcome: "completed" });
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const errorCode = errorCodeOf(error);
|
|
115
|
+
// Duration on the failure path too. A dashboard built only on successes shows latency improving
|
|
116
|
+
// as things break, because the slow runs are the ones that time out.
|
|
117
|
+
metrics.runDurationMs.record(now() - startedAt, { tenantId: job.tenantId, outcome: "failed" });
|
|
118
|
+
metrics.runsTotal.record(1, { tenantId: job.tenantId, outcome: "failed", errorCode });
|
|
119
|
+
logger.log("error", "run.failed", { errorCode });
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
stop: (graceMs) => inner.stop(graceMs),
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Time a model call — latency, outcome and the model, with no prompt anywhere near it.
|
|
130
|
+
*
|
|
131
|
+
* The attributes are the model id and the outcome. Not the prompt, not the response, not the token *contents* —
|
|
132
|
+
* the counts are on the usage ledger, which is the place designed to hold them and which is tenant-scoped.
|
|
133
|
+
*/
|
|
134
|
+
export const instrumentModelCall = async (deps, input, call) => {
|
|
135
|
+
const now = deps.now ?? (() => Date.now());
|
|
136
|
+
const startedAt = now();
|
|
137
|
+
return withSpan(deps.telemetry.tracer, BOUNDARY_SPANS.model, {
|
|
138
|
+
kind: "client",
|
|
139
|
+
attributes: {
|
|
140
|
+
tenantId: input.tenantId,
|
|
141
|
+
modelId: input.modelId,
|
|
142
|
+
...(input.runId !== undefined ? { runId: input.runId } : {}),
|
|
143
|
+
...(input.providerId !== undefined ? { providerId: input.providerId } : {}),
|
|
144
|
+
},
|
|
145
|
+
}, async () => {
|
|
146
|
+
const attrs = { tenantId: input.tenantId, modelId: input.modelId };
|
|
147
|
+
try {
|
|
148
|
+
const result = await call();
|
|
149
|
+
deps.metrics.modelLatencyMs.record(now() - startedAt, attrs);
|
|
150
|
+
deps.metrics.modelCallsTotal.record(1, { ...attrs, outcome: "ok" });
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
deps.metrics.modelLatencyMs.record(now() - startedAt, attrs);
|
|
155
|
+
deps.metrics.modelCallsTotal.record(1, { ...attrs, outcome: "error", errorCode: errorCodeOf(error) });
|
|
156
|
+
deps.telemetry.logger.log("warn", "model.failed", {
|
|
157
|
+
tenantId: input.tenantId,
|
|
158
|
+
modelId: input.modelId,
|
|
159
|
+
errorCode: errorCodeOf(error),
|
|
160
|
+
});
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
/** Time a tool call. Same shape, and the same deliberate absence of arguments and results. */
|
|
166
|
+
export const instrumentToolCall = async (deps, input, call) => {
|
|
167
|
+
const now = deps.now ?? (() => Date.now());
|
|
168
|
+
const startedAt = now();
|
|
169
|
+
return withSpan(deps.telemetry.tracer, BOUNDARY_SPANS.tool, {
|
|
170
|
+
kind: "internal",
|
|
171
|
+
attributes: {
|
|
172
|
+
tenantId: input.tenantId,
|
|
173
|
+
toolName: input.toolName,
|
|
174
|
+
...(input.runId !== undefined ? { runId: input.runId } : {}),
|
|
175
|
+
},
|
|
176
|
+
}, async () => {
|
|
177
|
+
const attrs = { tenantId: input.tenantId, toolName: input.toolName };
|
|
178
|
+
try {
|
|
179
|
+
const result = await call();
|
|
180
|
+
deps.metrics.toolLatencyMs.record(now() - startedAt, attrs);
|
|
181
|
+
deps.metrics.toolCallsTotal.record(1, { ...attrs, outcome: "ok" });
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
deps.metrics.toolLatencyMs.record(now() - startedAt, attrs);
|
|
186
|
+
deps.metrics.toolCallsTotal.record(1, { ...attrs, outcome: "error", errorCode: errorCodeOf(error) });
|
|
187
|
+
deps.telemetry.logger.log("warn", "tool.failed", {
|
|
188
|
+
tenantId: input.tenantId,
|
|
189
|
+
toolName: input.toolName,
|
|
190
|
+
errorCode: errorCodeOf(error),
|
|
191
|
+
});
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Record how long a run waited for a human.
|
|
198
|
+
*
|
|
199
|
+
* Not a wrapper, because the wait is not a function call: the run is *suspended*, the process may have exited,
|
|
200
|
+
* and the decision arrives in a different request entirely. So this is called when the decision lands, with both
|
|
201
|
+
* timestamps — the only shape that can measure a wait spanning a deploy.
|
|
202
|
+
*
|
|
203
|
+
* Often the longest span in a trace, and the one that most needs to be visibly *not* the platform's latency.
|
|
204
|
+
*/
|
|
205
|
+
export const recordApprovalWait = (deps, input) => {
|
|
206
|
+
const requested = Date.parse(input.requestedAt);
|
|
207
|
+
const decided = Date.parse(input.decidedAt);
|
|
208
|
+
if (Number.isFinite(requested) && Number.isFinite(decided) && decided >= requested)
|
|
209
|
+
deps.metrics.approvalWaitMs.record(decided - requested, { tenantId: input.tenantId });
|
|
210
|
+
const span = deps.telemetry.tracer.startSpan(BOUNDARY_SPANS.approvalWait, {
|
|
211
|
+
kind: "internal",
|
|
212
|
+
...(input.traceparent !== undefined ? { parent: input.traceparent } : {}),
|
|
213
|
+
attributes: {
|
|
214
|
+
tenantId: input.tenantId,
|
|
215
|
+
runId: input.runId,
|
|
216
|
+
interactionId: input.interactionId,
|
|
217
|
+
decision: input.decision,
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
span.setStatus("ok");
|
|
221
|
+
span.end();
|
|
222
|
+
deps.telemetry.logger.log("info", "approval.decided", {
|
|
223
|
+
tenantId: input.tenantId,
|
|
224
|
+
runId: input.runId,
|
|
225
|
+
interactionId: input.interactionId,
|
|
226
|
+
decision: input.decision,
|
|
227
|
+
...(Number.isFinite(requested) && Number.isFinite(decided) && decided >= requested
|
|
228
|
+
? { waitMs: decided - requested }
|
|
229
|
+
: {}),
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
//# sourceMappingURL=instrument.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every log line's name — the structural half of AC-5.
|
|
3
|
+
*
|
|
4
|
+
* A logger whose message is a `string` can be handed a prompt. Not by anyone careless: by someone debugging an
|
|
5
|
+
* incident at midnight who needs to see what the model was asked, ships it, and never removes it. A redaction
|
|
6
|
+
* denylist does not help, because the content is in the *message*, which a denylist cannot inspect without
|
|
7
|
+
* pattern-matching prose.
|
|
8
|
+
*
|
|
9
|
+
* So the message is a closed union of literals. A caller **cannot** put content in it — there is no string
|
|
10
|
+
* parameter to put it in. Adding a log line means adding a name here, which is a reviewed act in a file whose
|
|
11
|
+
* whole purpose is visible from its first line. That is the difference between a rule and a mechanism.
|
|
12
|
+
*
|
|
13
|
+
* Named after what happened, in past tense, so a log is readable as a sequence of facts.
|
|
14
|
+
*/
|
|
15
|
+
export declare const LOG_EVENTS: readonly ["request.received", "request.completed", "request.rejected", "run.admitted", "run.refused-quota", "run.refused-authorization", "run.enqueued", "run.enqueue-failed", "run.claimed", "run.claim-contended", "run.lease-expired", "run.reaped", "run.reap-failed", "run.started", "run.checkpointed", "run.completed", "run.failed", "run.cancelled", "run.retry-scheduled", "run.handler-threw", "model.called", "model.failed", "tool.called", "tool.failed", "tool.denied", "approval.requested", "approval.decided", "question.requested", "question.answered", "worker.started", "worker.draining", "worker.stopped", "telemetry.fields-dropped"];
|
|
16
|
+
export type LogEvent = (typeof LOG_EVENTS)[number];
|
|
17
|
+
//# sourceMappingURL=log-events.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every log line's name — the structural half of AC-5.
|
|
3
|
+
*
|
|
4
|
+
* A logger whose message is a `string` can be handed a prompt. Not by anyone careless: by someone debugging an
|
|
5
|
+
* incident at midnight who needs to see what the model was asked, ships it, and never removes it. A redaction
|
|
6
|
+
* denylist does not help, because the content is in the *message*, which a denylist cannot inspect without
|
|
7
|
+
* pattern-matching prose.
|
|
8
|
+
*
|
|
9
|
+
* So the message is a closed union of literals. A caller **cannot** put content in it — there is no string
|
|
10
|
+
* parameter to put it in. Adding a log line means adding a name here, which is a reviewed act in a file whose
|
|
11
|
+
* whole purpose is visible from its first line. That is the difference between a rule and a mechanism.
|
|
12
|
+
*
|
|
13
|
+
* Named after what happened, in past tense, so a log is readable as a sequence of facts.
|
|
14
|
+
*/
|
|
15
|
+
export const LOG_EVENTS = [
|
|
16
|
+
// Request and admission
|
|
17
|
+
"request.received",
|
|
18
|
+
"request.completed",
|
|
19
|
+
"request.rejected",
|
|
20
|
+
"run.admitted",
|
|
21
|
+
"run.refused-quota",
|
|
22
|
+
"run.refused-authorization",
|
|
23
|
+
// Queue
|
|
24
|
+
"run.enqueued",
|
|
25
|
+
"run.enqueue-failed",
|
|
26
|
+
"run.claimed",
|
|
27
|
+
"run.claim-contended",
|
|
28
|
+
"run.lease-expired",
|
|
29
|
+
"run.reaped",
|
|
30
|
+
"run.reap-failed",
|
|
31
|
+
// Execution
|
|
32
|
+
"run.started",
|
|
33
|
+
"run.checkpointed",
|
|
34
|
+
"run.completed",
|
|
35
|
+
"run.failed",
|
|
36
|
+
"run.cancelled",
|
|
37
|
+
"run.retry-scheduled",
|
|
38
|
+
"run.handler-threw",
|
|
39
|
+
// Model and tools
|
|
40
|
+
"model.called",
|
|
41
|
+
"model.failed",
|
|
42
|
+
"tool.called",
|
|
43
|
+
"tool.failed",
|
|
44
|
+
"tool.denied",
|
|
45
|
+
// Human in the loop
|
|
46
|
+
"approval.requested",
|
|
47
|
+
"approval.decided",
|
|
48
|
+
"question.requested",
|
|
49
|
+
"question.answered",
|
|
50
|
+
// Process lifecycle
|
|
51
|
+
"worker.started",
|
|
52
|
+
"worker.draining",
|
|
53
|
+
"worker.stopped",
|
|
54
|
+
// Telemetry's own faults. A dropped field must be visible, or redaction becomes a silent data loss that
|
|
55
|
+
// nobody notices until an incident needs the field that was being dropped.
|
|
56
|
+
"telemetry.fields-dropped",
|
|
57
|
+
];
|
|
58
|
+
//# sourceMappingURL=log-events.js.map
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The metric set — AC-3: "metrics answer queue health, latency and error rate without reading logs".
|
|
3
|
+
*
|
|
4
|
+
* That criterion is a test of *completeness*, so the instruments are declared as data and `createRunMetrics`
|
|
5
|
+
* builds a typed recorder for each. A metric name at a call site is a string nothing checks, and the failure is
|
|
6
|
+
* a dashboard panel that is empty because a name was misspelled once.
|
|
7
|
+
*
|
|
8
|
+
* Units are in the names (`_ms`, `_total`) as well as in the metadata, because a graph legend shows the name
|
|
9
|
+
* and not the unit, and "is 4000 seconds or milliseconds" is the wrong question to be asking during an incident.
|
|
10
|
+
*/
|
|
11
|
+
import type { Attributes, Meter, MetricRecorder } from "./index.js";
|
|
12
|
+
export type InstrumentKind = "counter" | "histogram" | "gauge";
|
|
13
|
+
export type InstrumentSpec = {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly kind: InstrumentKind;
|
|
16
|
+
readonly unit: string;
|
|
17
|
+
readonly description: string;
|
|
18
|
+
/**
|
|
19
|
+
* The operational question this exists to answer.
|
|
20
|
+
*
|
|
21
|
+
* Recorded per instrument because "enough to answer 'is it healthy'" is the acceptance criterion, and a metric
|
|
22
|
+
* with no question behind it is one nobody looks at — while a question with no metric is the gap this field
|
|
23
|
+
* makes visible.
|
|
24
|
+
*/
|
|
25
|
+
readonly answers: string;
|
|
26
|
+
};
|
|
27
|
+
export declare const RUN_INSTRUMENTS: {
|
|
28
|
+
readonly queueDepth: {
|
|
29
|
+
readonly name: "agentkit_queue_depth";
|
|
30
|
+
readonly kind: "gauge";
|
|
31
|
+
readonly unit: "{job}";
|
|
32
|
+
readonly description: "Jobs waiting on the run queue.";
|
|
33
|
+
readonly answers: "Is work arriving faster than it is being processed?";
|
|
34
|
+
};
|
|
35
|
+
readonly claimLatencyMs: {
|
|
36
|
+
readonly name: "agentkit_claim_latency_ms";
|
|
37
|
+
readonly kind: "histogram";
|
|
38
|
+
readonly unit: "ms";
|
|
39
|
+
readonly description: "Time from enqueue to a worker claiming the run.";
|
|
40
|
+
readonly answers: "How long does a user wait before anything starts happening?";
|
|
41
|
+
};
|
|
42
|
+
readonly runDurationMs: {
|
|
43
|
+
readonly name: "agentkit_run_duration_ms";
|
|
44
|
+
readonly kind: "histogram";
|
|
45
|
+
readonly unit: "ms";
|
|
46
|
+
readonly description: "Wall-clock time from claim to a terminal run state.";
|
|
47
|
+
readonly answers: "Are runs getting slower?";
|
|
48
|
+
};
|
|
49
|
+
readonly runsTotal: {
|
|
50
|
+
readonly name: "agentkit_runs_total";
|
|
51
|
+
readonly kind: "counter";
|
|
52
|
+
readonly unit: "{run}";
|
|
53
|
+
readonly description: "Runs reaching a terminal state, by outcome.";
|
|
54
|
+
readonly answers: "What fraction of runs fail, and is that changing?";
|
|
55
|
+
};
|
|
56
|
+
readonly modelLatencyMs: {
|
|
57
|
+
readonly name: "agentkit_model_latency_ms";
|
|
58
|
+
readonly kind: "histogram";
|
|
59
|
+
readonly unit: "ms";
|
|
60
|
+
readonly description: "Duration of one model call.";
|
|
61
|
+
readonly answers: "Is a slow run the provider's fault or ours?";
|
|
62
|
+
};
|
|
63
|
+
readonly modelCallsTotal: {
|
|
64
|
+
readonly name: "agentkit_model_calls_total";
|
|
65
|
+
readonly kind: "counter";
|
|
66
|
+
readonly unit: "{call}";
|
|
67
|
+
readonly description: "Model calls, by outcome and model.";
|
|
68
|
+
readonly answers: "What is the model error rate, per model?";
|
|
69
|
+
};
|
|
70
|
+
readonly toolCallsTotal: {
|
|
71
|
+
readonly name: "agentkit_tool_calls_total";
|
|
72
|
+
readonly kind: "counter";
|
|
73
|
+
readonly unit: "{call}";
|
|
74
|
+
readonly description: "Tool calls, by outcome and tool.";
|
|
75
|
+
readonly answers: "Which tool is failing, and how often?";
|
|
76
|
+
};
|
|
77
|
+
readonly toolLatencyMs: {
|
|
78
|
+
readonly name: "agentkit_tool_latency_ms";
|
|
79
|
+
readonly kind: "histogram";
|
|
80
|
+
readonly unit: "ms";
|
|
81
|
+
readonly description: "Duration of one tool call.";
|
|
82
|
+
readonly answers: "Is a tool the reason runs are slow?";
|
|
83
|
+
};
|
|
84
|
+
readonly approvalWaitMs: {
|
|
85
|
+
readonly name: "agentkit_approval_wait_ms";
|
|
86
|
+
readonly kind: "histogram";
|
|
87
|
+
readonly unit: "ms";
|
|
88
|
+
readonly description: "Time a run spent waiting for a human decision.";
|
|
89
|
+
readonly answers: "Are approvals the bottleneck rather than the platform?";
|
|
90
|
+
};
|
|
91
|
+
readonly retriesTotal: {
|
|
92
|
+
readonly name: "agentkit_retries_total";
|
|
93
|
+
readonly kind: "counter";
|
|
94
|
+
readonly unit: "{retry}";
|
|
95
|
+
readonly description: "Retry attempts, by reason.";
|
|
96
|
+
readonly answers: "Is a provider degrading before it starts failing outright?";
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
export type RunInstrumentKey = keyof typeof RUN_INSTRUMENTS;
|
|
100
|
+
export type RunMetrics = Readonly<Record<RunInstrumentKey, MetricRecorder>>;
|
|
101
|
+
/**
|
|
102
|
+
* Build every instrument once.
|
|
103
|
+
*
|
|
104
|
+
* Once, at wiring time, rather than per call: an OTel meter deduplicates by name, but a port implementation need
|
|
105
|
+
* not, and creating an instrument inside a hot path is how a metrics backend acquires a million series.
|
|
106
|
+
*/
|
|
107
|
+
export declare const createRunMetrics: (meter: Meter) => RunMetrics;
|
|
108
|
+
/**
|
|
109
|
+
* The attribute set a metric may carry.
|
|
110
|
+
*
|
|
111
|
+
* Bounded on purpose, and this is the important part: every metric attribute multiplies the series count, and
|
|
112
|
+
* an unbounded one — a run id, a conversation id, a user id — is how a metrics bill becomes the largest line in
|
|
113
|
+
* an infrastructure budget. Ids belong on **spans and logs**, which are sampled and indexed, never on metrics.
|
|
114
|
+
*/
|
|
115
|
+
export declare const METRIC_ATTRIBUTE_ALLOWLIST: readonly string[];
|
|
116
|
+
/**
|
|
117
|
+
* Drop unbounded attributes before recording.
|
|
118
|
+
*
|
|
119
|
+
* Applied by the adapter, not trusted to call sites. `runId` on a latency histogram is one line of code and one
|
|
120
|
+
* series per run — it looks like helpful detail in review and is a cardinality incident in production.
|
|
121
|
+
*/
|
|
122
|
+
export declare const boundMetricAttributes: (attributes: Attributes | undefined) => Attributes;
|
|
123
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The metric set — AC-3: "metrics answer queue health, latency and error rate without reading logs".
|
|
3
|
+
*
|
|
4
|
+
* That criterion is a test of *completeness*, so the instruments are declared as data and `createRunMetrics`
|
|
5
|
+
* builds a typed recorder for each. A metric name at a call site is a string nothing checks, and the failure is
|
|
6
|
+
* a dashboard panel that is empty because a name was misspelled once.
|
|
7
|
+
*
|
|
8
|
+
* Units are in the names (`_ms`, `_total`) as well as in the metadata, because a graph legend shows the name
|
|
9
|
+
* and not the unit, and "is 4000 seconds or milliseconds" is the wrong question to be asking during an incident.
|
|
10
|
+
*/
|
|
11
|
+
export const RUN_INSTRUMENTS = {
|
|
12
|
+
queueDepth: {
|
|
13
|
+
name: "agentkit_queue_depth",
|
|
14
|
+
kind: "gauge",
|
|
15
|
+
unit: "{job}",
|
|
16
|
+
description: "Jobs waiting on the run queue.",
|
|
17
|
+
answers: "Is work arriving faster than it is being processed?",
|
|
18
|
+
},
|
|
19
|
+
claimLatencyMs: {
|
|
20
|
+
name: "agentkit_claim_latency_ms",
|
|
21
|
+
kind: "histogram",
|
|
22
|
+
unit: "ms",
|
|
23
|
+
description: "Time from enqueue to a worker claiming the run.",
|
|
24
|
+
answers: "How long does a user wait before anything starts happening?",
|
|
25
|
+
},
|
|
26
|
+
runDurationMs: {
|
|
27
|
+
name: "agentkit_run_duration_ms",
|
|
28
|
+
kind: "histogram",
|
|
29
|
+
unit: "ms",
|
|
30
|
+
description: "Wall-clock time from claim to a terminal run state.",
|
|
31
|
+
answers: "Are runs getting slower?",
|
|
32
|
+
},
|
|
33
|
+
runsTotal: {
|
|
34
|
+
name: "agentkit_runs_total",
|
|
35
|
+
kind: "counter",
|
|
36
|
+
unit: "{run}",
|
|
37
|
+
description: "Runs reaching a terminal state, by outcome.",
|
|
38
|
+
answers: "What fraction of runs fail, and is that changing?",
|
|
39
|
+
},
|
|
40
|
+
modelLatencyMs: {
|
|
41
|
+
name: "agentkit_model_latency_ms",
|
|
42
|
+
kind: "histogram",
|
|
43
|
+
unit: "ms",
|
|
44
|
+
description: "Duration of one model call.",
|
|
45
|
+
answers: "Is a slow run the provider's fault or ours?",
|
|
46
|
+
},
|
|
47
|
+
modelCallsTotal: {
|
|
48
|
+
name: "agentkit_model_calls_total",
|
|
49
|
+
kind: "counter",
|
|
50
|
+
unit: "{call}",
|
|
51
|
+
description: "Model calls, by outcome and model.",
|
|
52
|
+
answers: "What is the model error rate, per model?",
|
|
53
|
+
},
|
|
54
|
+
toolCallsTotal: {
|
|
55
|
+
name: "agentkit_tool_calls_total",
|
|
56
|
+
kind: "counter",
|
|
57
|
+
unit: "{call}",
|
|
58
|
+
description: "Tool calls, by outcome and tool.",
|
|
59
|
+
answers: "Which tool is failing, and how often?",
|
|
60
|
+
},
|
|
61
|
+
toolLatencyMs: {
|
|
62
|
+
name: "agentkit_tool_latency_ms",
|
|
63
|
+
kind: "histogram",
|
|
64
|
+
unit: "ms",
|
|
65
|
+
description: "Duration of one tool call.",
|
|
66
|
+
answers: "Is a tool the reason runs are slow?",
|
|
67
|
+
},
|
|
68
|
+
approvalWaitMs: {
|
|
69
|
+
name: "agentkit_approval_wait_ms",
|
|
70
|
+
kind: "histogram",
|
|
71
|
+
unit: "ms",
|
|
72
|
+
description: "Time a run spent waiting for a human decision.",
|
|
73
|
+
answers: "Are approvals the bottleneck rather than the platform?",
|
|
74
|
+
},
|
|
75
|
+
retriesTotal: {
|
|
76
|
+
name: "agentkit_retries_total",
|
|
77
|
+
kind: "counter",
|
|
78
|
+
unit: "{retry}",
|
|
79
|
+
description: "Retry attempts, by reason.",
|
|
80
|
+
answers: "Is a provider degrading before it starts failing outright?",
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Build every instrument once.
|
|
85
|
+
*
|
|
86
|
+
* Once, at wiring time, rather than per call: an OTel meter deduplicates by name, but a port implementation need
|
|
87
|
+
* not, and creating an instrument inside a hot path is how a metrics backend acquires a million series.
|
|
88
|
+
*/
|
|
89
|
+
export const createRunMetrics = (meter) => {
|
|
90
|
+
const build = (spec) => {
|
|
91
|
+
const options = { unit: spec.unit, description: spec.description };
|
|
92
|
+
if (spec.kind === "counter")
|
|
93
|
+
return meter.counter(spec.name, options);
|
|
94
|
+
if (spec.kind === "histogram")
|
|
95
|
+
return meter.histogram(spec.name, options);
|
|
96
|
+
return meter.gauge(spec.name, options);
|
|
97
|
+
};
|
|
98
|
+
const out = {};
|
|
99
|
+
for (const [key, spec] of Object.entries(RUN_INSTRUMENTS))
|
|
100
|
+
out[key] = build(spec);
|
|
101
|
+
return out;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* The attribute set a metric may carry.
|
|
105
|
+
*
|
|
106
|
+
* Bounded on purpose, and this is the important part: every metric attribute multiplies the series count, and
|
|
107
|
+
* an unbounded one — a run id, a conversation id, a user id — is how a metrics bill becomes the largest line in
|
|
108
|
+
* an infrastructure budget. Ids belong on **spans and logs**, which are sampled and indexed, never on metrics.
|
|
109
|
+
*/
|
|
110
|
+
export const METRIC_ATTRIBUTE_ALLOWLIST = [
|
|
111
|
+
"tenantId",
|
|
112
|
+
"outcome",
|
|
113
|
+
"errorCode",
|
|
114
|
+
"toolName",
|
|
115
|
+
"modelId",
|
|
116
|
+
"providerId",
|
|
117
|
+
"reason",
|
|
118
|
+
];
|
|
119
|
+
const METRIC_ALLOWED = new Set(METRIC_ATTRIBUTE_ALLOWLIST);
|
|
120
|
+
/**
|
|
121
|
+
* Drop unbounded attributes before recording.
|
|
122
|
+
*
|
|
123
|
+
* Applied by the adapter, not trusted to call sites. `runId` on a latency histogram is one line of code and one
|
|
124
|
+
* series per run — it looks like helpful detail in review and is a cardinality incident in production.
|
|
125
|
+
*/
|
|
126
|
+
export const boundMetricAttributes = (attributes) => {
|
|
127
|
+
if (attributes === undefined)
|
|
128
|
+
return {};
|
|
129
|
+
const out = {};
|
|
130
|
+
for (const [key, value] of Object.entries(attributes))
|
|
131
|
+
if (METRIC_ALLOWED.has(key))
|
|
132
|
+
out[key] = value;
|
|
133
|
+
return out;
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telemetry that does nothing, and the in-memory telemetry the tests assert on.
|
|
3
|
+
*
|
|
4
|
+
* `NOOP_TELEMETRY` is why no call site has an `if (telemetry)`. Optional-and-checked would be checked in
|
|
5
|
+
* nineteen places and forgotten in the twentieth, and the forgotten one is a crash rather than a missing span.
|
|
6
|
+
*
|
|
7
|
+
* `createRecordingTelemetry` is the same port over arrays. It is what makes AC-1, AC-2 and AC-5 assertable
|
|
8
|
+
* without a collector: the redaction test needs to see the *actual bytes* a sink would write, and a mock that
|
|
9
|
+
* captured the call arguments would prove the caller's intent rather than the output.
|
|
10
|
+
*/
|
|
11
|
+
import type { Attributes, LogRecord, Span, SpanStatus, Telemetry, TelemetryContext } from "./index.js";
|
|
12
|
+
export declare const NOOP_TELEMETRY: Telemetry;
|
|
13
|
+
export type RecordedSpan = {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly kind: string;
|
|
16
|
+
readonly traceId: string;
|
|
17
|
+
readonly spanId: string;
|
|
18
|
+
readonly parentSpanId: string | null;
|
|
19
|
+
attributes: Record<string, string | number | boolean>;
|
|
20
|
+
status: SpanStatus;
|
|
21
|
+
errorCode: string | null;
|
|
22
|
+
ended: boolean;
|
|
23
|
+
};
|
|
24
|
+
export type RecordedMetric = {
|
|
25
|
+
readonly instrument: string;
|
|
26
|
+
readonly value: number;
|
|
27
|
+
readonly attributes: Attributes;
|
|
28
|
+
};
|
|
29
|
+
export type RecordingTelemetry = Telemetry & {
|
|
30
|
+
readonly spans: readonly RecordedSpan[];
|
|
31
|
+
readonly metrics: readonly RecordedMetric[];
|
|
32
|
+
readonly logs: readonly LogRecord[];
|
|
33
|
+
/** The bytes a sink would write. The redaction test asserts on these, not on the records. */
|
|
34
|
+
readonly lines: readonly string[];
|
|
35
|
+
};
|
|
36
|
+
export declare const createRecordingTelemetry: (base?: TelemetryContext, now?: () => string) => RecordingTelemetry;
|
|
37
|
+
/** The traceparent for a span, for putting into a job payload or an outbound header. */
|
|
38
|
+
export declare const traceparentOf: (span: Span) => string;
|
|
39
|
+
//# sourceMappingURL=noop.d.ts.map
|