@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,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The flow interpreter — REQ-038 (#187), REQ-037 (#186).
|
|
3
|
+
*
|
|
4
|
+
* A pure function. `advance` takes a definition, an execution and the outcome of whatever was asked for last, and
|
|
5
|
+
* returns the next execution plus **one effect** for the caller to perform. It performs nothing itself: no agent
|
|
6
|
+
* call, no tool call, no clock read, no store write.
|
|
7
|
+
*
|
|
8
|
+
* That is not stylistic. Everything both REQs ask for is a consequence:
|
|
9
|
+
*
|
|
10
|
+
* - **Durable resume** — the returned execution *is* the position. A host persists it and, after a restart, calls
|
|
11
|
+
* `advance` with the same execution and gets the same effect. There is no interpreter instance to rebuild.
|
|
12
|
+
* - **Idempotency across a resume** — the effect's key is derived from `(executionId, step, attempt)`, so the
|
|
13
|
+
* same step asked for twice produces the same key and the idempotency store answers with the first result.
|
|
14
|
+
* #187 AC-9 is that derivation, not a rule anyone has to remember.
|
|
15
|
+
* - **Budgets** — checked before the effect is produced, so an over-budget flow performs nothing at all rather
|
|
16
|
+
* than spending and then noticing.
|
|
17
|
+
* - **Tests** — feeding outcomes to a function and asserting the next state needs no agent, no database, no clock.
|
|
18
|
+
*
|
|
19
|
+
* The clock is an argument for the same reason: a pure function that reads `Date.now()` is not pure, and a wall
|
|
20
|
+
* clock ceiling that cannot be tested without waiting is a ceiling nobody tests.
|
|
21
|
+
*/
|
|
22
|
+
import type { FlowDefinition, FlowExecution } from "./index.js";
|
|
23
|
+
/**
|
|
24
|
+
* One unit of work the host performs and reports back.
|
|
25
|
+
*
|
|
26
|
+
* Data, not a callback. A callback would put the host's control flow inside the interpreter's, which is exactly
|
|
27
|
+
* what makes a host-written workflow undurable in the first place — the thing this module exists to replace.
|
|
28
|
+
*/
|
|
29
|
+
export type FlowEffect = {
|
|
30
|
+
readonly kind: "run-agent";
|
|
31
|
+
readonly agentId: string;
|
|
32
|
+
readonly prompt: string;
|
|
33
|
+
readonly instructions?: string;
|
|
34
|
+
readonly idempotencyKey: string;
|
|
35
|
+
/**
|
|
36
|
+
* What the flow has left to spend — #202 AC-3.
|
|
37
|
+
*
|
|
38
|
+
* Handed over rather than left for the handler to work out, and re-derived on every step because the
|
|
39
|
+
* remainder changes. A child run given its own independent ceiling is a member that can outspend the team,
|
|
40
|
+
* which is #186 AC-4 defeated by the composition that was supposed to honour it.
|
|
41
|
+
*/
|
|
42
|
+
readonly budgetRemaining: {
|
|
43
|
+
readonly steps: number;
|
|
44
|
+
readonly costMinorUnits?: number;
|
|
45
|
+
readonly wallClockMs?: number;
|
|
46
|
+
};
|
|
47
|
+
/** Which team member this step is, so the child run and its usage carry the attribution. */
|
|
48
|
+
readonly member?: string;
|
|
49
|
+
} | {
|
|
50
|
+
readonly kind: "run-team";
|
|
51
|
+
readonly teamId: string;
|
|
52
|
+
readonly prompt: string;
|
|
53
|
+
readonly idempotencyKey: string;
|
|
54
|
+
} | {
|
|
55
|
+
readonly kind: "call-tool";
|
|
56
|
+
readonly tool: string;
|
|
57
|
+
readonly input: Readonly<Record<string, unknown>>;
|
|
58
|
+
readonly idempotencyKey: string;
|
|
59
|
+
} | {
|
|
60
|
+
readonly kind: "ask-human";
|
|
61
|
+
readonly question: string;
|
|
62
|
+
readonly options?: readonly string[];
|
|
63
|
+
} | {
|
|
64
|
+
readonly kind: "sleep";
|
|
65
|
+
readonly untilMs: number;
|
|
66
|
+
} | {
|
|
67
|
+
readonly kind: "await-signal";
|
|
68
|
+
readonly signal: string;
|
|
69
|
+
} | {
|
|
70
|
+
readonly kind: "run-subflow";
|
|
71
|
+
readonly flowId: string;
|
|
72
|
+
readonly depth: number;
|
|
73
|
+
readonly idempotencyKey: string;
|
|
74
|
+
}
|
|
75
|
+
/** Nothing more to do. `status` says why. */
|
|
76
|
+
| {
|
|
77
|
+
readonly kind: "settled";
|
|
78
|
+
};
|
|
79
|
+
/** What the host reports back. `advance` is called again with it. */
|
|
80
|
+
export type StepOutcome = {
|
|
81
|
+
readonly kind: "ok";
|
|
82
|
+
readonly value?: unknown;
|
|
83
|
+
readonly costMinorUnits?: number;
|
|
84
|
+
} | {
|
|
85
|
+
readonly kind: "failed";
|
|
86
|
+
readonly error: string;
|
|
87
|
+
readonly costMinorUnits?: number;
|
|
88
|
+
}
|
|
89
|
+
/** A checkpoint was raised and is now parked. Carries what to resume on. */
|
|
90
|
+
| {
|
|
91
|
+
readonly kind: "parked";
|
|
92
|
+
readonly interactionId: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* An agent step became a child run, which is now queued — #202.
|
|
96
|
+
*
|
|
97
|
+
* Distinct from `parked` because what resumes it is different: a person answers a `parked` step, and a *run*
|
|
98
|
+
* finishing resumes this one. Collapsing them would mean the runner could not tell which of the two it was
|
|
99
|
+
* waiting for, and the poll-on-resume path needs to know there is a run to look at.
|
|
100
|
+
*/
|
|
101
|
+
| {
|
|
102
|
+
readonly kind: "parked-on-run";
|
|
103
|
+
readonly runId: string;
|
|
104
|
+
readonly member?: string;
|
|
105
|
+
}
|
|
106
|
+
/** A human answered, or a signal arrived. */
|
|
107
|
+
| {
|
|
108
|
+
readonly kind: "resumed";
|
|
109
|
+
readonly value?: unknown;
|
|
110
|
+
};
|
|
111
|
+
export type AdvanceInput = {
|
|
112
|
+
readonly definition: FlowDefinition;
|
|
113
|
+
readonly execution: FlowExecution;
|
|
114
|
+
/** Absent on the first call, and on a call that only wants the current effect back (a resume after a restart). */
|
|
115
|
+
readonly outcome?: StepOutcome;
|
|
116
|
+
readonly nowMs: number;
|
|
117
|
+
readonly nowIso: string;
|
|
118
|
+
};
|
|
119
|
+
export type AdvanceResult = {
|
|
120
|
+
readonly execution: FlowExecution;
|
|
121
|
+
readonly effect: FlowEffect;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Read a `$.a.b` path out of the state.
|
|
125
|
+
*
|
|
126
|
+
* A path walk, not an expression evaluator. The definitions will eventually be authored in a UI by someone who is
|
|
127
|
+
* not an engineer (REQ-042), and an expression language in stored data is an interpreter for whatever they typed.
|
|
128
|
+
*/
|
|
129
|
+
export declare const readPath: (state: Readonly<Record<string, unknown>>, path: string) => unknown;
|
|
130
|
+
/** Substitute `{{$.a.b}}` in a string from state. Absent paths become empty, never the literal `{{…}}`. */
|
|
131
|
+
export declare const interpolate: (template: string, state: Readonly<Record<string, unknown>>) => string;
|
|
132
|
+
export declare const advance: (input: AdvanceInput) => AdvanceResult;
|
|
133
|
+
/** A fresh execution, at the start step, with nothing spent. */
|
|
134
|
+
export declare const beginExecution: (input: {
|
|
135
|
+
readonly id: string;
|
|
136
|
+
readonly definition: FlowDefinition;
|
|
137
|
+
readonly tenantId: FlowExecution["tenantId"];
|
|
138
|
+
readonly runId: FlowExecution["runId"];
|
|
139
|
+
readonly principalId: FlowExecution["principalId"];
|
|
140
|
+
readonly conversationId?: FlowExecution["conversationId"];
|
|
141
|
+
readonly state?: Readonly<Record<string, unknown>>;
|
|
142
|
+
readonly depth?: number;
|
|
143
|
+
readonly nowMs: number;
|
|
144
|
+
readonly nowIso: string;
|
|
145
|
+
}) => FlowExecution;
|
|
146
|
+
//# sourceMappingURL=interpreter.d.ts.map
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The flow interpreter — REQ-038 (#187), REQ-037 (#186).
|
|
3
|
+
*
|
|
4
|
+
* A pure function. `advance` takes a definition, an execution and the outcome of whatever was asked for last, and
|
|
5
|
+
* returns the next execution plus **one effect** for the caller to perform. It performs nothing itself: no agent
|
|
6
|
+
* call, no tool call, no clock read, no store write.
|
|
7
|
+
*
|
|
8
|
+
* That is not stylistic. Everything both REQs ask for is a consequence:
|
|
9
|
+
*
|
|
10
|
+
* - **Durable resume** — the returned execution *is* the position. A host persists it and, after a restart, calls
|
|
11
|
+
* `advance` with the same execution and gets the same effect. There is no interpreter instance to rebuild.
|
|
12
|
+
* - **Idempotency across a resume** — the effect's key is derived from `(executionId, step, attempt)`, so the
|
|
13
|
+
* same step asked for twice produces the same key and the idempotency store answers with the first result.
|
|
14
|
+
* #187 AC-9 is that derivation, not a rule anyone has to remember.
|
|
15
|
+
* - **Budgets** — checked before the effect is produced, so an over-budget flow performs nothing at all rather
|
|
16
|
+
* than spending and then noticing.
|
|
17
|
+
* - **Tests** — feeding outcomes to a function and asserting the next state needs no agent, no database, no clock.
|
|
18
|
+
*
|
|
19
|
+
* The clock is an argument for the same reason: a pure function that reads `Date.now()` is not pure, and a wall
|
|
20
|
+
* clock ceiling that cannot be tested without waiting is a ceiling nobody tests.
|
|
21
|
+
*/
|
|
22
|
+
import { AgentPlatformError } from "../core/errors.js";
|
|
23
|
+
import { DEFAULT_FAILURE_POLICY, DEFAULT_MAX_DEPTH } from "./index.js";
|
|
24
|
+
// ---------------------------------------------------------------------------------------------------
|
|
25
|
+
// State access
|
|
26
|
+
// ---------------------------------------------------------------------------------------------------
|
|
27
|
+
/**
|
|
28
|
+
* Read a `$.a.b` path out of the state.
|
|
29
|
+
*
|
|
30
|
+
* A path walk, not an expression evaluator. The definitions will eventually be authored in a UI by someone who is
|
|
31
|
+
* not an engineer (REQ-042), and an expression language in stored data is an interpreter for whatever they typed.
|
|
32
|
+
*/
|
|
33
|
+
export const readPath = (state, path) => {
|
|
34
|
+
const segments = path.replace(/^\$\.?/, "").split(".").filter(Boolean);
|
|
35
|
+
let current = state;
|
|
36
|
+
for (const segment of segments) {
|
|
37
|
+
if (current === null || current === undefined || typeof current !== "object")
|
|
38
|
+
return undefined;
|
|
39
|
+
current = current[segment];
|
|
40
|
+
}
|
|
41
|
+
return current;
|
|
42
|
+
};
|
|
43
|
+
/** Substitute `{{$.a.b}}` in a string from state. Absent paths become empty, never the literal `{{…}}`. */
|
|
44
|
+
export const interpolate = (template, state) => template.replace(/\{\{([^}]+)\}\}/g, (_match, path) => {
|
|
45
|
+
const value = readPath(state, path.trim());
|
|
46
|
+
if (value === undefined || value === null)
|
|
47
|
+
return "";
|
|
48
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
49
|
+
});
|
|
50
|
+
/** Resolve `{{…}}` inside a tool's input, at any depth. */
|
|
51
|
+
const resolveInput = (input, state) => {
|
|
52
|
+
if (typeof input === "string") {
|
|
53
|
+
// A whole-value reference returns the *value*, not its JSON text: `{"id": "{{$.order.id}}"}` on a number
|
|
54
|
+
// should send a number, and stringifying it silently changes the tool's input type.
|
|
55
|
+
const whole = /^\{\{([^}]+)\}\}$/.exec(input.trim());
|
|
56
|
+
if (whole !== null)
|
|
57
|
+
return readPath(state, whole[1].trim());
|
|
58
|
+
return interpolate(input, state);
|
|
59
|
+
}
|
|
60
|
+
if (Array.isArray(input))
|
|
61
|
+
return input.map((item) => resolveInput(item, state));
|
|
62
|
+
if (input !== null && typeof input === "object") {
|
|
63
|
+
return Object.fromEntries(Object.entries(input).map(([k, v]) => [k, resolveInput(v, state)]));
|
|
64
|
+
}
|
|
65
|
+
return input;
|
|
66
|
+
};
|
|
67
|
+
const matches = (state, branchCase) => {
|
|
68
|
+
const actual = readPath(state, branchCase.path);
|
|
69
|
+
switch (branchCase.operator) {
|
|
70
|
+
case "exists":
|
|
71
|
+
return actual !== undefined && actual !== null;
|
|
72
|
+
case "absent":
|
|
73
|
+
return actual === undefined || actual === null;
|
|
74
|
+
case "equals":
|
|
75
|
+
return actual === branchCase.value;
|
|
76
|
+
case "not-equals":
|
|
77
|
+
return actual !== branchCase.value;
|
|
78
|
+
case "greater-than":
|
|
79
|
+
return typeof actual === "number" && typeof branchCase.value === "number" && actual > branchCase.value;
|
|
80
|
+
case "less-than":
|
|
81
|
+
return typeof actual === "number" && typeof branchCase.value === "number" && actual < branchCase.value;
|
|
82
|
+
case "contains":
|
|
83
|
+
// Strings and arrays, because both are things a person means by "contains" and refusing one would be a
|
|
84
|
+
// definition that fails at run time for a reason the author cannot see.
|
|
85
|
+
if (typeof actual === "string")
|
|
86
|
+
return actual.includes(String(branchCase.value));
|
|
87
|
+
if (Array.isArray(actual))
|
|
88
|
+
return actual.includes(branchCase.value);
|
|
89
|
+
return false;
|
|
90
|
+
default:
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
// ---------------------------------------------------------------------------------------------------
|
|
95
|
+
// advance
|
|
96
|
+
// ---------------------------------------------------------------------------------------------------
|
|
97
|
+
const stepNamed = (definition, name) => definition.steps.find((step) => step.name === name);
|
|
98
|
+
const terminal = (execution, status, nowIso, detail) => ({
|
|
99
|
+
execution: {
|
|
100
|
+
...execution,
|
|
101
|
+
status,
|
|
102
|
+
currentStep: null,
|
|
103
|
+
finishedAt: nowIso,
|
|
104
|
+
...(detail === undefined ? {} : { detail }),
|
|
105
|
+
},
|
|
106
|
+
effect: { kind: "settled" },
|
|
107
|
+
});
|
|
108
|
+
/** `(executionId, step, attempt)` — the same three on a resume, which is what makes a replay a no-op. */
|
|
109
|
+
const keyFor = (execution, step, attempt) => `flow:${execution.id}:${step}:${attempt}`;
|
|
110
|
+
const record = (execution, entry) => [...execution.history, entry];
|
|
111
|
+
export const advance = (input) => {
|
|
112
|
+
const { definition, execution, outcome, nowMs, nowIso } = input;
|
|
113
|
+
if (execution.status === "completed" || execution.status === "failed" || execution.status === "cancelled") {
|
|
114
|
+
// Idempotent: advancing a settled execution returns it unchanged rather than throwing. A worker that
|
|
115
|
+
// re-delivers a job for a finished flow is a normal event, not an error.
|
|
116
|
+
return { execution, effect: { kind: "settled" } };
|
|
117
|
+
}
|
|
118
|
+
const currentName = execution.currentStep ?? definition.start;
|
|
119
|
+
const step = stepNamed(definition, currentName);
|
|
120
|
+
if (step === undefined) {
|
|
121
|
+
// A definition that names a step it does not contain. Failing the execution with the name is the only useful
|
|
122
|
+
// answer; guessing the next step would run something nobody wrote.
|
|
123
|
+
return terminal(execution, "failed", nowIso, `step "${currentName}" is not defined in flow ${definition.id}`);
|
|
124
|
+
}
|
|
125
|
+
// -------------------------------------------------------------------------------------------------
|
|
126
|
+
// An outcome for the step we last asked about
|
|
127
|
+
// -------------------------------------------------------------------------------------------------
|
|
128
|
+
if (outcome !== undefined) {
|
|
129
|
+
if (outcome.kind === "parked") {
|
|
130
|
+
return {
|
|
131
|
+
execution: {
|
|
132
|
+
...execution,
|
|
133
|
+
status: "waiting",
|
|
134
|
+
waitingFor: { kind: "human", interactionId: outcome.interactionId },
|
|
135
|
+
},
|
|
136
|
+
effect: { kind: "settled" },
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (outcome.kind === "parked-on-run") {
|
|
140
|
+
return {
|
|
141
|
+
execution: {
|
|
142
|
+
...execution,
|
|
143
|
+
status: "waiting",
|
|
144
|
+
waitingFor: {
|
|
145
|
+
kind: "run",
|
|
146
|
+
runId: outcome.runId,
|
|
147
|
+
...(outcome.member === undefined ? {} : { member: outcome.member }),
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
effect: { kind: "settled" },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (outcome.kind === "failed") {
|
|
154
|
+
const policy = step.onFailure ?? DEFAULT_FAILURE_POLICY;
|
|
155
|
+
return handleFailure({ definition, execution, step, policy, error: outcome.error, nowIso, cost: outcome.costMinorUnits ?? 0 });
|
|
156
|
+
}
|
|
157
|
+
// `ok` or `resumed`: write the result, charge the spend, move on.
|
|
158
|
+
const value = outcome.kind === "ok" ? outcome.value : outcome.value;
|
|
159
|
+
const cost = (outcome.kind === "ok" ? outcome.costMinorUnits : undefined) ?? 0;
|
|
160
|
+
const state = step.assignTo === undefined ? execution.state : { ...execution.state, [step.assignTo]: value };
|
|
161
|
+
const next = "next" in step ? step.next : undefined;
|
|
162
|
+
const advanced = {
|
|
163
|
+
...execution,
|
|
164
|
+
state,
|
|
165
|
+
status: "running",
|
|
166
|
+
attempt: 0,
|
|
167
|
+
spend: { ...execution.spend, steps: execution.spend.steps + 1, costMinorUnits: execution.spend.costMinorUnits + cost },
|
|
168
|
+
history: record(execution, {
|
|
169
|
+
step: step.name,
|
|
170
|
+
kind: step.kind,
|
|
171
|
+
startedAt: nowIso,
|
|
172
|
+
finishedAt: nowIso,
|
|
173
|
+
outcome: "ok",
|
|
174
|
+
attempt: execution.attempt,
|
|
175
|
+
...(cost === 0 ? {} : { costMinorUnits: cost }),
|
|
176
|
+
}),
|
|
177
|
+
currentStep: next ?? null,
|
|
178
|
+
};
|
|
179
|
+
// `waitingFor` is cleared by omission rather than set to undefined, so a serialised execution does not carry
|
|
180
|
+
// a null field that a reader has to interpret.
|
|
181
|
+
const { waitingFor: _cleared, ...withoutWait } = advanced;
|
|
182
|
+
void _cleared;
|
|
183
|
+
if (next === undefined)
|
|
184
|
+
return terminal(withoutWait, "completed", nowIso);
|
|
185
|
+
return advance({ definition, execution: withoutWait, nowMs, nowIso });
|
|
186
|
+
}
|
|
187
|
+
// -------------------------------------------------------------------------------------------------
|
|
188
|
+
// No outcome: produce the effect for the current step
|
|
189
|
+
// -------------------------------------------------------------------------------------------------
|
|
190
|
+
/**
|
|
191
|
+
* Budgets **before** the effect, so an over-budget flow performs nothing.
|
|
192
|
+
*
|
|
193
|
+
* Checked here rather than after a step returns, because after is too late: the money is spent and the external
|
|
194
|
+
* write has happened. A ceiling that stops the *next* step is the only kind that stops anything.
|
|
195
|
+
*/
|
|
196
|
+
/**
|
|
197
|
+
* Not applied to `done`, for the same reason `done` consumes no budget: a ceiling stops *work*, and finishing
|
|
198
|
+
* is not work. Gating it meant a flow that did exactly its allowance of work then failed on the marker — so
|
|
199
|
+
* "maxSteps: 3" actually meant two steps and a marker, which is not what anyone writing it believes.
|
|
200
|
+
*/
|
|
201
|
+
const overBudget = step.kind === "done" ? null : budgetExceeded(definition, execution, nowMs);
|
|
202
|
+
if (overBudget !== null)
|
|
203
|
+
return terminal(execution, "failed", nowIso, overBudget);
|
|
204
|
+
if ((execution.depth ?? 0) > (definition.maxDepth ?? DEFAULT_MAX_DEPTH)) {
|
|
205
|
+
// A → B → A terminates here, and the message names the depth rather than the cycle: the chain is only known
|
|
206
|
+
// at run time, because a subflow reference is resolved then.
|
|
207
|
+
return terminal(execution, "failed", nowIso, `nesting depth ${execution.depth} exceeds the flow's limit of ${definition.maxDepth ?? DEFAULT_MAX_DEPTH}`);
|
|
208
|
+
}
|
|
209
|
+
const key = keyFor(execution, step.name, execution.attempt);
|
|
210
|
+
const running = { ...execution, status: "running", currentStep: step.name };
|
|
211
|
+
switch (step.kind) {
|
|
212
|
+
case "agent":
|
|
213
|
+
return {
|
|
214
|
+
execution: running,
|
|
215
|
+
effect: {
|
|
216
|
+
kind: "run-agent",
|
|
217
|
+
agentId: String(step.agentId),
|
|
218
|
+
prompt: interpolate(step.prompt, execution.state),
|
|
219
|
+
...(step.instructions === undefined ? {} : { instructions: step.instructions }),
|
|
220
|
+
idempotencyKey: key,
|
|
221
|
+
budgetRemaining: remaining(definition, execution, nowMs),
|
|
222
|
+
// The step's name is the member's name in a compiled team, which is what carries attribution through
|
|
223
|
+
// to the child run and its usage rows.
|
|
224
|
+
member: step.name,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
case "team":
|
|
228
|
+
return {
|
|
229
|
+
execution: running,
|
|
230
|
+
effect: { kind: "run-team", teamId: step.teamId, prompt: interpolate(step.prompt, execution.state), idempotencyKey: key },
|
|
231
|
+
};
|
|
232
|
+
case "tool":
|
|
233
|
+
return {
|
|
234
|
+
execution: running,
|
|
235
|
+
effect: {
|
|
236
|
+
kind: "call-tool",
|
|
237
|
+
tool: step.tool,
|
|
238
|
+
input: resolveInput(step.input, execution.state),
|
|
239
|
+
idempotencyKey: key,
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
case "branch": {
|
|
243
|
+
/**
|
|
244
|
+
* Evaluated here rather than as an effect: a branch performs nothing, so asking the host to "do" it would
|
|
245
|
+
* be a round trip for a comparison. It still costs a step, because a loop of branches is a loop.
|
|
246
|
+
*/
|
|
247
|
+
const taken = step.cases.find((branchCase) => matches(execution.state, branchCase));
|
|
248
|
+
const next = taken?.next ?? step.otherwise;
|
|
249
|
+
const withStep = {
|
|
250
|
+
...running,
|
|
251
|
+
spend: { ...execution.spend, steps: execution.spend.steps + 1 },
|
|
252
|
+
history: record(execution, {
|
|
253
|
+
step: step.name,
|
|
254
|
+
kind: "branch",
|
|
255
|
+
startedAt: nowIso,
|
|
256
|
+
finishedAt: nowIso,
|
|
257
|
+
outcome: "ok",
|
|
258
|
+
attempt: execution.attempt,
|
|
259
|
+
}),
|
|
260
|
+
currentStep: next ?? null,
|
|
261
|
+
attempt: 0,
|
|
262
|
+
};
|
|
263
|
+
if (next === undefined) {
|
|
264
|
+
// No matching case and no `otherwise`. Completing would be guessing that falling through was intended.
|
|
265
|
+
return terminal(withStep, "failed", nowIso, `no branch matched at "${step.name}" and no otherwise is defined`);
|
|
266
|
+
}
|
|
267
|
+
return advance({ definition, execution: withStep, nowMs, nowIso });
|
|
268
|
+
}
|
|
269
|
+
case "wait":
|
|
270
|
+
if (step.forSignal !== undefined) {
|
|
271
|
+
return {
|
|
272
|
+
execution: { ...running, status: "waiting", waitingFor: { kind: "signal", signal: step.forSignal } },
|
|
273
|
+
effect: { kind: "await-signal", signal: step.forSignal },
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
execution: {
|
|
278
|
+
...running,
|
|
279
|
+
status: "waiting",
|
|
280
|
+
waitingFor: { kind: "time", untilMs: nowMs + (step.forMs ?? 0) },
|
|
281
|
+
},
|
|
282
|
+
effect: { kind: "sleep", untilMs: nowMs + (step.forMs ?? 0) },
|
|
283
|
+
};
|
|
284
|
+
case "checkpoint":
|
|
285
|
+
return {
|
|
286
|
+
execution: running,
|
|
287
|
+
effect: {
|
|
288
|
+
kind: "ask-human",
|
|
289
|
+
question: interpolate(step.question, execution.state),
|
|
290
|
+
...(step.options === undefined ? {} : { options: step.options }),
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
case "subflow":
|
|
294
|
+
return {
|
|
295
|
+
execution: running,
|
|
296
|
+
effect: { kind: "run-subflow", flowId: step.flowId, depth: (execution.depth ?? 0) + 1, idempotencyKey: key },
|
|
297
|
+
};
|
|
298
|
+
case "done":
|
|
299
|
+
/**
|
|
300
|
+
* Does **not** consume a step of budget, and that is a correctness fix rather than a nicety.
|
|
301
|
+
*
|
|
302
|
+
* A budget is about work, and `done` performs none — it is a terminal marker. Counting it meant a flow
|
|
303
|
+
* whose ceiling exactly matched its work always failed at the last step: a sequential team with three
|
|
304
|
+
* members and `maxSteps: 3` ran all three and then died on the marker, with "step budget exhausted" as the
|
|
305
|
+
* reason. That reads as a runtime problem and is a definition arithmetic problem, and nobody would find it
|
|
306
|
+
* by reading either.
|
|
307
|
+
*
|
|
308
|
+
* A `branch` still counts, because a branch can loop and a loop of branches is a loop. A `done` cannot.
|
|
309
|
+
*/
|
|
310
|
+
return terminal(running, "completed", nowIso, step.outcome);
|
|
311
|
+
default: {
|
|
312
|
+
// Exhaustiveness, as a value rather than a comment: adding a step kind without handling it fails to compile.
|
|
313
|
+
const unreachable = step;
|
|
314
|
+
throw new AgentPlatformError({
|
|
315
|
+
code: "internal",
|
|
316
|
+
message: `unhandled step kind: ${JSON.stringify(unreachable)}`,
|
|
317
|
+
retryable: false,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* What is left, not what was allowed.
|
|
324
|
+
*
|
|
325
|
+
* The child run's ceiling is derived from this, so a member's limits shrink as the flow spends. Handing it the
|
|
326
|
+
* flow's *original* budget would let each member spend the whole thing.
|
|
327
|
+
*/
|
|
328
|
+
const remaining = (definition, execution, nowMs) => {
|
|
329
|
+
const { budget } = definition;
|
|
330
|
+
return {
|
|
331
|
+
steps: Math.max(0, budget.maxSteps - execution.spend.steps),
|
|
332
|
+
...(budget.maxCostMinorUnits === undefined
|
|
333
|
+
? {}
|
|
334
|
+
: { costMinorUnits: Math.max(0, budget.maxCostMinorUnits - execution.spend.costMinorUnits) }),
|
|
335
|
+
...(budget.maxWallClockMs === undefined
|
|
336
|
+
? {}
|
|
337
|
+
: { wallClockMs: Math.max(0, budget.maxWallClockMs - (nowMs - execution.spend.startedAtMs)) }),
|
|
338
|
+
};
|
|
339
|
+
};
|
|
340
|
+
const budgetExceeded = (definition, execution, nowMs) => {
|
|
341
|
+
const { budget } = definition;
|
|
342
|
+
if (execution.spend.steps >= budget.maxSteps) {
|
|
343
|
+
return `step budget exhausted: ${execution.spend.steps} of ${budget.maxSteps}`;
|
|
344
|
+
}
|
|
345
|
+
if (budget.maxCostMinorUnits !== undefined && execution.spend.costMinorUnits >= budget.maxCostMinorUnits) {
|
|
346
|
+
return `cost budget exhausted: ${execution.spend.costMinorUnits} of ${budget.maxCostMinorUnits}`;
|
|
347
|
+
}
|
|
348
|
+
if (budget.maxWallClockMs !== undefined && nowMs - execution.spend.startedAtMs >= budget.maxWallClockMs) {
|
|
349
|
+
// The one a step and cost ceiling both miss: a flow parked on a webhook consumes neither.
|
|
350
|
+
return `wall-clock budget exhausted: ${nowMs - execution.spend.startedAtMs}ms of ${budget.maxWallClockMs}ms`;
|
|
351
|
+
}
|
|
352
|
+
return null;
|
|
353
|
+
};
|
|
354
|
+
const handleFailure = (input) => {
|
|
355
|
+
const { definition, execution, step, policy, error, nowIso, cost } = input;
|
|
356
|
+
const spend = {
|
|
357
|
+
...execution.spend,
|
|
358
|
+
steps: execution.spend.steps + 1,
|
|
359
|
+
// A failed step that spent money still spent it. Not charging for failures is how a retrying flow costs more
|
|
360
|
+
// than its ceiling allows.
|
|
361
|
+
costMinorUnits: execution.spend.costMinorUnits + cost,
|
|
362
|
+
};
|
|
363
|
+
const history = record(execution, {
|
|
364
|
+
step: step.name,
|
|
365
|
+
kind: step.kind,
|
|
366
|
+
startedAt: nowIso,
|
|
367
|
+
finishedAt: nowIso,
|
|
368
|
+
outcome: "failed",
|
|
369
|
+
attempt: execution.attempt,
|
|
370
|
+
error,
|
|
371
|
+
...(cost === 0 ? {} : { costMinorUnits: cost }),
|
|
372
|
+
});
|
|
373
|
+
switch (policy.action) {
|
|
374
|
+
case "retry": {
|
|
375
|
+
const attempts = policy.maxAttempts ?? 3;
|
|
376
|
+
if (execution.attempt + 1 >= attempts) {
|
|
377
|
+
return terminal({ ...execution, spend, history }, "failed", nowIso, `${step.name} failed after ${attempts} attempts: ${error}`);
|
|
378
|
+
}
|
|
379
|
+
// The attempt number is in the execution, so a retry policy survives a restart — and it is in the
|
|
380
|
+
// idempotency key, so a retry is genuinely a new attempt rather than a replay of the failed one.
|
|
381
|
+
return {
|
|
382
|
+
execution: { ...execution, spend, history, attempt: execution.attempt + 1, status: "running" },
|
|
383
|
+
effect: { kind: "sleep", untilMs: 0 },
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
case "skip": {
|
|
387
|
+
const next = "next" in step ? step.next : undefined;
|
|
388
|
+
const skipped = { ...execution, spend, history, attempt: 0, currentStep: next ?? null, status: "running" };
|
|
389
|
+
if (next === undefined)
|
|
390
|
+
return terminal(skipped, "completed", nowIso, `${step.name} was skipped after failing`);
|
|
391
|
+
return { execution: skipped, effect: { kind: "settled" } };
|
|
392
|
+
}
|
|
393
|
+
case "escalate":
|
|
394
|
+
/**
|
|
395
|
+
* Handed to the enclosing scope rather than decided here.
|
|
396
|
+
*
|
|
397
|
+
* The execution fails, and `detail` says it escalated — so a team's manager or a parent flow can act on it.
|
|
398
|
+
* Without this, the vocabulary is "retry or die", and a manager-led team cannot react to a member failing,
|
|
399
|
+
* which is most of the reason to have a manager.
|
|
400
|
+
*/
|
|
401
|
+
return terminal({ ...execution, spend, history }, "failed", nowIso, `escalated from ${step.name}: ${error}`);
|
|
402
|
+
case "fail":
|
|
403
|
+
default:
|
|
404
|
+
return terminal({ ...execution, spend, history }, "failed", nowIso, `${step.name} failed: ${error}`);
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
/** A fresh execution, at the start step, with nothing spent. */
|
|
408
|
+
export const beginExecution = (input) => ({
|
|
409
|
+
id: input.id,
|
|
410
|
+
tenantId: input.tenantId,
|
|
411
|
+
flowId: input.definition.id,
|
|
412
|
+
// Pinned here, once. Editing the flow afterwards does not change this execution.
|
|
413
|
+
flowVersion: input.definition.version,
|
|
414
|
+
runId: input.runId,
|
|
415
|
+
principalId: input.principalId,
|
|
416
|
+
...(input.conversationId === undefined ? {} : { conversationId: input.conversationId }),
|
|
417
|
+
status: "running",
|
|
418
|
+
currentStep: input.definition.start,
|
|
419
|
+
state: input.state ?? {},
|
|
420
|
+
spend: { steps: 0, costMinorUnits: 0, startedAtMs: input.nowMs },
|
|
421
|
+
history: [],
|
|
422
|
+
attempt: 0,
|
|
423
|
+
depth: input.depth ?? 0,
|
|
424
|
+
startedAt: input.nowIso,
|
|
425
|
+
});
|
|
426
|
+
//# sourceMappingURL=interpreter.js.map
|