@theoremjs/agents 0.0.0-stage → 0.3.1
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 +21 -0
- package/README.md +545 -2
- package/esm/_dnt.polyfills.d.ts +66 -0
- package/esm/_dnt.polyfills.js +51 -0
- package/esm/mod.d.ts +79 -0
- package/esm/mod.js +49 -0
- package/esm/package.json +3 -0
- package/esm/src/cli/bin.d.ts +7 -0
- package/esm/src/cli/bin.js +9 -0
- package/esm/src/cli/commands/bench.d.ts +6 -0
- package/esm/src/cli/commands/bench.js +480 -0
- package/esm/src/cli/commands/detect-compile.d.ts +17 -0
- package/esm/src/cli/commands/detect-compile.js +22 -0
- package/esm/src/cli/commands/eval.d.ts +37 -0
- package/esm/src/cli/commands/eval.js +190 -0
- package/esm/src/cli/commands/fuzz-canary.d.ts +15 -0
- package/esm/src/cli/commands/fuzz-canary.js +241 -0
- package/esm/src/cli/commands/fuzz-guardrails.d.ts +2 -0
- package/esm/src/cli/commands/fuzz-guardrails.js +5 -0
- package/esm/src/cli/commands/profile.d.ts +2 -0
- package/esm/src/cli/commands/profile.js +80 -0
- package/esm/src/cli/commands/run.d.ts +13 -0
- package/esm/src/cli/commands/run.js +45 -0
- package/esm/src/cli/commands/test.d.ts +26 -0
- package/esm/src/cli/commands/test.js +143 -0
- package/esm/src/cli/event-log.d.ts +15 -0
- package/esm/src/cli/event-log.js +136 -0
- package/esm/src/cli/index.d.ts +10 -0
- package/esm/src/cli/index.js +267 -0
- package/esm/src/cli/matrix/fixtures.d.ts +10 -0
- package/esm/src/cli/matrix/fixtures.js +67 -0
- package/esm/src/cli/matrix/synthesizer.d.ts +18 -0
- package/esm/src/cli/matrix/synthesizer.js +103 -0
- package/esm/src/evals/attachments.d.ts +5 -0
- package/esm/src/evals/attachments.js +55 -0
- package/esm/src/evals/breakdown.d.ts +30 -0
- package/esm/src/evals/breakdown.js +69 -0
- package/esm/src/evals/graders/answer.d.ts +16 -0
- package/esm/src/evals/graders/answer.js +134 -0
- package/esm/src/evals/graders/code.d.ts +40 -0
- package/esm/src/evals/graders/code.js +184 -0
- package/esm/src/evals/graders/judge.d.ts +24 -0
- package/esm/src/evals/graders/judge.js +367 -0
- package/esm/src/evals/graders/latency.d.ts +6 -0
- package/esm/src/evals/graders/latency.js +82 -0
- package/esm/src/evals/graders/live.d.ts +14 -0
- package/esm/src/evals/graders/live.js +42 -0
- package/esm/src/evals/graders/media.d.ts +18 -0
- package/esm/src/evals/graders/media.js +91 -0
- package/esm/src/evals/graders/shared.d.ts +11 -0
- package/esm/src/evals/graders/shared.js +62 -0
- package/esm/src/evals/graders/transcript.d.ts +19 -0
- package/esm/src/evals/graders/transcript.js +202 -0
- package/esm/src/evals/mod.d.ts +34 -0
- package/esm/src/evals/mod.js +22 -0
- package/esm/src/evals/record.d.ts +41 -0
- package/esm/src/evals/record.js +107 -0
- package/esm/src/evals/rubrics/catalog.d.ts +148 -0
- package/esm/src/evals/rubrics/catalog.js +846 -0
- package/esm/src/evals/rubrics/mod.d.ts +24 -0
- package/esm/src/evals/rubrics/mod.js +55 -0
- package/esm/src/evals/rubrics/mustache.d.ts +23 -0
- package/esm/src/evals/rubrics/mustache.js +154 -0
- package/esm/src/evals/rubrics/types.d.ts +54 -0
- package/esm/src/evals/rubrics/types.js +74 -0
- package/esm/src/evals/run.d.ts +75 -0
- package/esm/src/evals/run.js +418 -0
- package/esm/src/evals/suite.d.ts +28 -0
- package/esm/src/evals/suite.js +97 -0
- package/esm/src/evals/summary.d.ts +35 -0
- package/esm/src/evals/summary.js +31 -0
- package/esm/src/evals/trial.d.ts +14 -0
- package/esm/src/evals/trial.js +135 -0
- package/esm/src/evals/types.d.ts +169 -0
- package/esm/src/evals/types.js +113 -0
- package/esm/src/evals/verdict.d.ts +23 -0
- package/esm/src/evals/verdict.js +48 -0
- package/esm/src/guardrails/boundaries.d.ts +41 -0
- package/esm/src/guardrails/boundaries.js +103 -0
- package/esm/src/guardrails/canary-gate.d.ts +22 -0
- package/esm/src/guardrails/canary-gate.js +40 -0
- package/esm/src/guardrails/canary.d.ts +146 -0
- package/esm/src/guardrails/canary.js +974 -0
- package/esm/src/guardrails/compile-egress.d.ts +12 -0
- package/esm/src/guardrails/compile-egress.js +12 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +24 -0
- package/esm/src/guardrails/corpus/canary-egress-attacks.js +305 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +5 -0
- package/esm/src/guardrails/corpus/fuzz-inbound.js +199 -0
- package/esm/src/guardrails/corpus/inbound-payloads.d.ts +5 -0
- package/esm/src/guardrails/corpus/inbound-payloads.js +120 -0
- package/esm/src/guardrails/corpus/live-attacks.d.ts +15 -0
- package/esm/src/guardrails/corpus/live-attacks.js +229 -0
- package/esm/src/guardrails/corpus/mod.d.ts +7 -0
- package/esm/src/guardrails/corpus/mod.js +4 -0
- package/esm/src/guardrails/corpus/secrets.d.ts +12 -0
- package/esm/src/guardrails/corpus/secrets.js +16 -0
- package/esm/src/guardrails/corpus/strings.d.ts +23 -0
- package/esm/src/guardrails/corpus/strings.js +29 -0
- package/esm/src/guardrails/corpus/types.d.ts +36 -0
- package/esm/src/guardrails/corpus/types.js +1 -0
- package/esm/src/guardrails/credential-rules.d.ts +36 -0
- package/esm/src/guardrails/credential-rules.js +3001 -0
- package/esm/src/guardrails/credential-scan.d.ts +45 -0
- package/esm/src/guardrails/credential-scan.js +172 -0
- package/esm/src/guardrails/destinations.d.ts +45 -0
- package/esm/src/guardrails/destinations.js +234 -0
- package/esm/src/guardrails/detect-at.d.ts +107 -0
- package/esm/src/guardrails/detect-at.js +434 -0
- package/esm/src/guardrails/detect-reply.d.ts +61 -0
- package/esm/src/guardrails/detect-reply.js +95 -0
- package/esm/src/guardrails/detect-stream.d.ts +33 -0
- package/esm/src/guardrails/detect-stream.js +77 -0
- package/esm/src/guardrails/detectors.d.ts +216 -0
- package/esm/src/guardrails/detectors.js +503 -0
- package/esm/src/guardrails/egress-automata.d.ts +12 -0
- package/esm/src/guardrails/egress-automata.js +25582 -0
- package/esm/src/guardrails/egress-compiler.d.ts +55 -0
- package/esm/src/guardrails/egress-compiler.js +431 -0
- package/esm/src/guardrails/egress-patterns.d.ts +26 -0
- package/esm/src/guardrails/egress-patterns.js +36 -0
- package/esm/src/guardrails/egress-stream.d.ts +123 -0
- package/esm/src/guardrails/egress-stream.js +774 -0
- package/esm/src/guardrails/egress-urls.d.ts +86 -0
- package/esm/src/guardrails/egress-urls.js +909 -0
- package/esm/src/guardrails/egress.d.ts +46 -0
- package/esm/src/guardrails/egress.js +124 -0
- package/esm/src/guardrails/error.d.ts +31 -0
- package/esm/src/guardrails/error.js +96 -0
- package/esm/src/guardrails/event-schemas.d.ts +100 -0
- package/esm/src/guardrails/event-schemas.js +48 -0
- package/esm/src/guardrails/events.d.ts +13 -0
- package/esm/src/guardrails/events.js +45 -0
- package/esm/src/guardrails/hints.d.ts +14 -0
- package/esm/src/guardrails/hints.js +57 -0
- package/esm/src/guardrails/hits.d.ts +14 -0
- package/esm/src/guardrails/hits.js +30 -0
- package/esm/src/guardrails/host-patterns.d.ts +85 -0
- package/esm/src/guardrails/host-patterns.js +173 -0
- package/esm/src/guardrails/injection-patterns.d.ts +20 -0
- package/esm/src/guardrails/injection-patterns.js +126 -0
- package/esm/src/guardrails/injection-reversed.d.ts +9 -0
- package/esm/src/guardrails/injection-reversed.js +49 -0
- package/esm/src/guardrails/injection.d.ts +30 -0
- package/esm/src/guardrails/injection.js +460 -0
- package/esm/src/guardrails/labels.d.ts +40 -0
- package/esm/src/guardrails/labels.js +874 -0
- package/esm/src/guardrails/live-outbound-gate.d.ts +55 -0
- package/esm/src/guardrails/live-outbound-gate.js +298 -0
- package/esm/src/guardrails/mod.d.ts +54 -0
- package/esm/src/guardrails/mod.js +34 -0
- package/esm/src/guardrails/network.d.ts +46 -0
- package/esm/src/guardrails/network.js +330 -0
- package/esm/src/guardrails/normalize.d.ts +20 -0
- package/esm/src/guardrails/normalize.js +165 -0
- package/esm/src/guardrails/policy.d.ts +4 -0
- package/esm/src/guardrails/policy.js +21 -0
- package/esm/src/guardrails/progressive-yield.d.ts +110 -0
- package/esm/src/guardrails/progressive-yield.js +281 -0
- package/esm/src/guardrails/prompt-echo.d.ts +40 -0
- package/esm/src/guardrails/prompt-echo.js +306 -0
- package/esm/src/guardrails/quota.d.ts +21 -0
- package/esm/src/guardrails/quota.js +80 -0
- package/esm/src/guardrails/rules.d.ts +47 -0
- package/esm/src/guardrails/rules.js +47 -0
- package/esm/src/guardrails/sanitize.d.ts +40 -0
- package/esm/src/guardrails/sanitize.js +160 -0
- package/esm/src/guardrails/sensitive-groups.d.ts +21 -0
- package/esm/src/guardrails/sensitive-groups.js +12 -0
- package/esm/src/guardrails/sensitive.d.ts +22 -0
- package/esm/src/guardrails/sensitive.js +234 -0
- package/esm/src/guardrails/serialize.d.ts +15 -0
- package/esm/src/guardrails/serialize.js +37 -0
- package/esm/src/guardrails/testing.d.ts +11 -0
- package/esm/src/guardrails/testing.js +10 -0
- package/esm/src/guardrails/theorem-error.d.ts +21 -0
- package/esm/src/guardrails/theorem-error.js +54 -0
- package/esm/src/guardrails/thought-guard.d.ts +76 -0
- package/esm/src/guardrails/thought-guard.js +664 -0
- package/esm/src/guardrails/tool-directives.d.ts +38 -0
- package/esm/src/guardrails/tool-directives.js +195 -0
- package/esm/src/guardrails/tool-leak.d.ts +26 -0
- package/esm/src/guardrails/tool-leak.js +45 -0
- package/esm/src/guardrails/tool-result.d.ts +92 -0
- package/esm/src/guardrails/tool-result.js +272 -0
- package/esm/src/guardrails/types.d.ts +250 -0
- package/esm/src/guardrails/types.js +58 -0
- package/esm/src/host/client-turn.d.ts +36 -0
- package/esm/src/host/client-turn.js +80 -0
- package/esm/src/host/mint-trace.d.ts +29 -0
- package/esm/src/host/mint-trace.js +60 -0
- package/esm/src/host/mod.d.ts +13 -0
- package/esm/src/host/mod.js +11 -0
- package/esm/src/host/readStreamingJsonStringField.d.ts +2 -0
- package/esm/src/host/readStreamingJsonStringField.js +27 -0
- package/esm/src/host/reply.d.ts +13 -0
- package/esm/src/host/reply.js +41 -0
- package/esm/src/interface/blocks.d.ts +11 -0
- package/esm/src/interface/blocks.js +283 -0
- package/esm/src/interface/composer-actions.d.ts +24 -0
- package/esm/src/interface/composer-actions.js +33 -0
- package/esm/src/interface/draft.d.ts +20 -0
- package/esm/src/interface/draft.js +39 -0
- package/esm/src/interface/from-profile.d.ts +24 -0
- package/esm/src/interface/from-profile.js +179 -0
- package/esm/src/interface/history.d.ts +46 -0
- package/esm/src/interface/history.js +278 -0
- package/esm/src/interface/inputs.d.ts +11 -0
- package/esm/src/interface/inputs.js +67 -0
- package/esm/src/interface/mod.d.ts +33 -0
- package/esm/src/interface/mod.js +22 -0
- package/esm/src/interface/models.d.ts +26 -0
- package/esm/src/interface/models.js +50 -0
- package/esm/src/interface/pending.d.ts +57 -0
- package/esm/src/interface/pending.js +152 -0
- package/esm/src/interface/profile-interface.d.ts +4 -0
- package/esm/src/interface/profile-interface.js +237 -0
- package/esm/src/interface/session.d.ts +71 -0
- package/esm/src/interface/session.js +128 -0
- package/esm/src/interface/stop.d.ts +13 -0
- package/esm/src/interface/stop.js +28 -0
- package/esm/src/interface/tool-calls.d.ts +32 -0
- package/esm/src/interface/tool-calls.js +127 -0
- package/esm/src/interface/tool-invoke.d.ts +6 -0
- package/esm/src/interface/tool-invoke.js +22 -0
- package/esm/src/interface/tool-media.d.ts +11 -0
- package/esm/src/interface/tool-media.js +113 -0
- package/esm/src/interface/types.d.ts +278 -0
- package/esm/src/interface/types.js +5 -0
- package/esm/src/kernel/api-catalog.d.ts +85 -0
- package/esm/src/kernel/api-catalog.js +229 -0
- package/esm/src/kernel/auth/credential-source.d.ts +23 -0
- package/esm/src/kernel/auth/credential-source.js +11 -0
- package/esm/src/kernel/auth/crypto.d.ts +56 -0
- package/esm/src/kernel/auth/crypto.js +163 -0
- package/esm/src/kernel/auth/mod.d.ts +5 -0
- package/esm/src/kernel/auth/mod.js +5 -0
- package/esm/src/kernel/auth/oauth.d.ts +18 -0
- package/esm/src/kernel/auth/oauth.js +439 -0
- package/esm/src/kernel/auth/scope-refusal.d.ts +9 -0
- package/esm/src/kernel/auth/scope-refusal.js +8 -0
- package/esm/src/kernel/auth/typed-secret.d.ts +13 -0
- package/esm/src/kernel/auth/typed-secret.js +23 -0
- package/esm/src/kernel/auth/types.d.ts +152 -0
- package/esm/src/kernel/auth/types.js +1 -0
- package/esm/src/kernel/checkpoint.d.ts +43 -0
- package/esm/src/kernel/checkpoint.js +58 -0
- package/esm/src/kernel/default-scope.d.ts +72 -0
- package/esm/src/kernel/default-scope.js +121 -0
- package/esm/src/kernel/engine/call-trace.d.ts +19 -0
- package/esm/src/kernel/engine/call-trace.js +29 -0
- package/esm/src/kernel/engine/compaction.d.ts +71 -0
- package/esm/src/kernel/engine/compaction.js +203 -0
- package/esm/src/kernel/engine/decision.d.ts +43 -0
- package/esm/src/kernel/engine/decision.js +439 -0
- package/esm/src/kernel/engine/embedding.d.ts +8 -0
- package/esm/src/kernel/engine/embedding.js +118 -0
- package/esm/src/kernel/engine/hash.d.ts +6 -0
- package/esm/src/kernel/engine/hash.js +22 -0
- package/esm/src/kernel/engine/live-inbound.d.ts +19 -0
- package/esm/src/kernel/engine/live-inbound.js +44 -0
- package/esm/src/kernel/engine/live-ingress.d.ts +15 -0
- package/esm/src/kernel/engine/live-ingress.js +42 -0
- package/esm/src/kernel/engine/record.d.ts +3 -0
- package/esm/src/kernel/engine/record.js +11 -0
- package/esm/src/kernel/engine/repair.d.ts +7 -0
- package/esm/src/kernel/engine/repair.js +36 -0
- package/esm/src/kernel/engine/runner/gates.d.ts +5 -0
- package/esm/src/kernel/engine/runner/gates.js +508 -0
- package/esm/src/kernel/engine/runner/mod.d.ts +29 -0
- package/esm/src/kernel/engine/runner/mod.js +625 -0
- package/esm/src/kernel/engine/runner/nested.d.ts +32 -0
- package/esm/src/kernel/engine/runner/nested.js +134 -0
- package/esm/src/kernel/engine/runner/schema-validation.d.ts +35 -0
- package/esm/src/kernel/engine/runner/schema-validation.js +179 -0
- package/esm/src/kernel/engine/runner/stages.d.ts +28 -0
- package/esm/src/kernel/engine/runner/stages.js +41 -0
- package/esm/src/kernel/engine/runner/state.d.ts +97 -0
- package/esm/src/kernel/engine/runner/state.js +50 -0
- package/esm/src/kernel/engine/runner/step-order.d.ts +27 -0
- package/esm/src/kernel/engine/runner/step-order.js +165 -0
- package/esm/src/kernel/engine/runner/steps.d.ts +14 -0
- package/esm/src/kernel/engine/runner/steps.js +519 -0
- package/esm/src/kernel/engine/runner/stream.d.ts +71 -0
- package/esm/src/kernel/engine/runner/stream.js +376 -0
- package/esm/src/kernel/engine/runner/usage.d.ts +45 -0
- package/esm/src/kernel/engine/runner/usage.js +148 -0
- package/esm/src/kernel/engine/session/mod.d.ts +33 -0
- package/esm/src/kernel/engine/session/mod.js +1182 -0
- package/esm/src/kernel/engine/session/session-trace.d.ts +115 -0
- package/esm/src/kernel/engine/session/session-trace.js +500 -0
- package/esm/src/kernel/engine/token-estimate.d.ts +23 -0
- package/esm/src/kernel/engine/token-estimate.js +38 -0
- package/esm/src/kernel/engine/tool-trace.d.ts +58 -0
- package/esm/src/kernel/engine/tool-trace.js +132 -0
- package/esm/src/kernel/engine/tree.d.ts +2 -0
- package/esm/src/kernel/engine/tree.js +17 -0
- package/esm/src/kernel/engine/turn-trace.d.ts +119 -0
- package/esm/src/kernel/engine/turn-trace.js +855 -0
- package/esm/src/kernel/engine/usage.d.ts +36 -0
- package/esm/src/kernel/engine/usage.js +151 -0
- package/esm/src/kernel/interaction-parts.d.ts +13 -0
- package/esm/src/kernel/interaction-parts.js +40 -0
- package/esm/src/kernel/json.d.ts +10 -0
- package/esm/src/kernel/json.js +3 -0
- package/esm/src/kernel/mod.d.ts +74 -0
- package/esm/src/kernel/mod.js +45 -0
- package/esm/src/kernel/profile-graph.d.ts +145 -0
- package/esm/src/kernel/profile-graph.js +158 -0
- package/esm/src/kernel/profile-presence.d.ts +18 -0
- package/esm/src/kernel/profile-presence.js +158 -0
- package/esm/src/kernel/profile-scope.d.ts +40 -0
- package/esm/src/kernel/profile-scope.js +274 -0
- package/esm/src/kernel/protocols/binding.d.ts +65 -0
- package/esm/src/kernel/protocols/binding.js +125 -0
- package/esm/src/kernel/protocols/limits.d.ts +15 -0
- package/esm/src/kernel/protocols/limits.js +28 -0
- package/esm/src/kernel/protocols/mod.d.ts +5 -0
- package/esm/src/kernel/protocols/mod.js +5 -0
- package/esm/src/kernel/protocols/prices.d.ts +36 -0
- package/esm/src/kernel/protocols/prices.js +37 -0
- package/esm/src/kernel/protocols/protocol.d.ts +253 -0
- package/esm/src/kernel/protocols/protocol.js +253 -0
- package/esm/src/kernel/protocols/provider.d.ts +108 -0
- package/esm/src/kernel/protocols/provider.js +143 -0
- package/esm/src/kernel/protocols/runtime.d.ts +55 -0
- package/esm/src/kernel/protocols/runtime.js +534 -0
- package/esm/src/kernel/provider-budget.d.ts +32 -0
- package/esm/src/kernel/provider-budget.js +262 -0
- package/esm/src/kernel/provider-dispatch.d.ts +13 -0
- package/esm/src/kernel/provider-dispatch.js +36 -0
- package/esm/src/kernel/provider-live.d.ts +7 -0
- package/esm/src/kernel/provider-live.js +189 -0
- package/esm/src/kernel/provider-runtime.d.ts +57 -0
- package/esm/src/kernel/provider-runtime.js +524 -0
- package/esm/src/kernel/registry/attachments.d.ts +46 -0
- package/esm/src/kernel/registry/attachments.js +220 -0
- package/esm/src/kernel/registry/catalog.d.ts +25 -0
- package/esm/src/kernel/registry/catalog.js +85 -0
- package/esm/src/kernel/registry/embedding-rules.d.ts +28 -0
- package/esm/src/kernel/registry/embedding-rules.js +66 -0
- package/esm/src/kernel/registry/ingress.d.ts +12 -0
- package/esm/src/kernel/registry/ingress.js +179 -0
- package/esm/src/kernel/registry/kernel-registry.d.ts +15 -0
- package/esm/src/kernel/registry/kernel-registry.js +15 -0
- package/esm/src/kernel/registry/profile-outputs.d.ts +3 -0
- package/esm/src/kernel/registry/profile-outputs.js +10 -0
- package/esm/src/kernel/registry/profiles.d.ts +116 -0
- package/esm/src/kernel/registry/profiles.js +773 -0
- package/esm/src/kernel/registry/provider-request.d.ts +6 -0
- package/esm/src/kernel/registry/provider-request.js +34 -0
- package/esm/src/kernel/registry/resolve.d.ts +17 -0
- package/esm/src/kernel/registry/resolve.js +238 -0
- package/esm/src/kernel/registry/schemas.d.ts +12 -0
- package/esm/src/kernel/registry/schemas.js +27 -0
- package/esm/src/kernel/registry/slot-fill.d.ts +7 -0
- package/esm/src/kernel/registry/slot-fill.js +22 -0
- package/esm/src/kernel/registry/sole-model.d.ts +2 -0
- package/esm/src/kernel/registry/sole-model.js +4 -0
- package/esm/src/kernel/registry/system-prompt.d.ts +8 -0
- package/esm/src/kernel/registry/system-prompt.js +32 -0
- package/esm/src/kernel/registry/system-role.d.ts +3 -0
- package/esm/src/kernel/registry/system-role.js +9 -0
- package/esm/src/kernel/registry/vault.d.ts +14 -0
- package/esm/src/kernel/registry/vault.js +1 -0
- package/esm/src/kernel/schema.d.ts +226 -0
- package/esm/src/kernel/schema.js +895 -0
- package/esm/src/kernel/scope.d.ts +29 -0
- package/esm/src/kernel/scope.js +45 -0
- package/esm/src/kernel/stages.d.ts +176 -0
- package/esm/src/kernel/stages.js +431 -0
- package/esm/src/kernel/stop.d.ts +116 -0
- package/esm/src/kernel/stop.js +137 -0
- package/esm/src/kernel/system-parts.d.ts +26 -0
- package/esm/src/kernel/system-parts.js +98 -0
- package/esm/src/kernel/tools/activity-label.d.ts +22 -0
- package/esm/src/kernel/tools/activity-label.js +435 -0
- package/esm/src/kernel/tools/agent.d.ts +66 -0
- package/esm/src/kernel/tools/agent.js +212 -0
- package/esm/src/kernel/tools/coordinate.d.ts +4 -0
- package/esm/src/kernel/tools/coordinate.js +165 -0
- package/esm/src/kernel/tools/events.d.ts +66 -0
- package/esm/src/kernel/tools/events.js +184 -0
- package/esm/src/kernel/tools/execute.d.ts +100 -0
- package/esm/src/kernel/tools/execute.js +1173 -0
- package/esm/src/kernel/tools/gate-answer.d.ts +60 -0
- package/esm/src/kernel/tools/gate-answer.js +65 -0
- package/esm/src/kernel/tools/handler-events.d.ts +45 -0
- package/esm/src/kernel/tools/handler-events.js +106 -0
- package/esm/src/kernel/tools/harness.d.ts +17 -0
- package/esm/src/kernel/tools/harness.js +43 -0
- package/esm/src/kernel/tools/http-limit.d.ts +27 -0
- package/esm/src/kernel/tools/http-limit.js +85 -0
- package/esm/src/kernel/tools/invoke.d.ts +32 -0
- package/esm/src/kernel/tools/invoke.js +282 -0
- package/esm/src/kernel/tools/mcp-sessions.d.ts +23 -0
- package/esm/src/kernel/tools/mcp-sessions.js +69 -0
- package/esm/src/kernel/tools/mod.d.ts +10 -0
- package/esm/src/kernel/tools/mod.js +7 -0
- package/esm/src/kernel/tools/model-text.d.ts +16 -0
- package/esm/src/kernel/tools/model-text.js +31 -0
- package/esm/src/kernel/tools/operation.d.ts +76 -0
- package/esm/src/kernel/tools/operation.js +100 -0
- package/esm/src/kernel/tools/page-answer.d.ts +8 -0
- package/esm/src/kernel/tools/page-answer.js +19 -0
- package/esm/src/kernel/tools/permission.d.ts +15 -0
- package/esm/src/kernel/tools/permission.js +38 -0
- package/esm/src/kernel/tools/project.d.ts +11 -0
- package/esm/src/kernel/tools/project.js +55 -0
- package/esm/src/kernel/tools/registry.d.ts +27 -0
- package/esm/src/kernel/tools/registry.js +143 -0
- package/esm/src/kernel/tools/remote.d.ts +144 -0
- package/esm/src/kernel/tools/remote.js +1125 -0
- package/esm/src/kernel/tools/resolve.d.ts +31 -0
- package/esm/src/kernel/tools/resolve.js +267 -0
- package/esm/src/kernel/tools/run.d.ts +28 -0
- package/esm/src/kernel/tools/run.js +86 -0
- package/esm/src/kernel/tools/schema.d.ts +10 -0
- package/esm/src/kernel/tools/schema.js +95 -0
- package/esm/src/kernel/tools/signed-in-fetch.d.ts +19 -0
- package/esm/src/kernel/tools/signed-in-fetch.js +45 -0
- package/esm/src/kernel/tools/stage-run.d.ts +116 -0
- package/esm/src/kernel/tools/stage-run.js +198 -0
- package/esm/src/kernel/tools/types.d.ts +497 -0
- package/esm/src/kernel/tools/types.js +1 -0
- package/esm/src/kernel/tools/unchecked-output.d.ts +14 -0
- package/esm/src/kernel/tools/unchecked-output.js +18 -0
- package/esm/src/kernel/turn-events.d.ts +776 -0
- package/esm/src/kernel/turn-events.js +543 -0
- package/esm/src/kernel/types.d.ts +1025 -0
- package/esm/src/kernel/types.js +1 -0
- package/esm/src/kernel/util/base64.d.ts +4 -0
- package/esm/src/kernel/util/base64.js +15 -0
- package/esm/src/kernel/util/exact-type.d.ts +31 -0
- package/esm/src/kernel/util/exact-type.js +1 -0
- package/esm/src/kernel/util/find-last.d.ts +3 -0
- package/esm/src/kernel/util/find-last.js +9 -0
- package/esm/src/kernel/util/mime.d.ts +2 -0
- package/esm/src/kernel/util/mime.js +4 -0
- package/esm/src/kernel/util/partial-json.d.ts +23 -0
- package/esm/src/kernel/util/partial-json.js +128 -0
- package/esm/src/kernel/util/record.d.ts +2 -0
- package/esm/src/kernel/util/record.js +4 -0
- package/esm/src/observability/destinations.d.ts +14 -0
- package/esm/src/observability/destinations.js +43 -0
- package/esm/src/observability/jsonl.d.ts +18 -0
- package/esm/src/observability/jsonl.js +110 -0
- package/esm/src/observability/mod.d.ts +22 -0
- package/esm/src/observability/mod.js +16 -0
- package/esm/src/observability/openinference.d.ts +11 -0
- package/esm/src/observability/openinference.js +205 -0
- package/esm/src/observability/otlp.d.ts +71 -0
- package/esm/src/observability/otlp.js +98 -0
- package/esm/src/observability/phoenix.d.ts +30 -0
- package/esm/src/observability/phoenix.js +71 -0
- package/esm/src/observability/policy.d.ts +17 -0
- package/esm/src/observability/policy.js +73 -0
- package/esm/src/observability/resolve-policy.d.ts +8 -0
- package/esm/src/observability/resolve-policy.js +64 -0
- package/esm/src/observability/spans.d.ts +22 -0
- package/esm/src/observability/spans.js +72 -0
- package/esm/src/observability/trace-catalog.d.ts +128 -0
- package/esm/src/observability/trace-catalog.js +906 -0
- package/esm/src/observability/trace-record.d.ts +23 -0
- package/esm/src/observability/trace-record.js +228 -0
- package/esm/src/observability/trace-schema.d.ts +57 -0
- package/esm/src/observability/trace-schema.js +59 -0
- package/esm/src/observability/trace-sink.d.ts +14 -0
- package/esm/src/observability/trace-sink.js +3 -0
- package/esm/src/observability/trace-span.d.ts +98 -0
- package/esm/src/observability/trace-span.js +201 -0
- package/esm/src/observability/trace.d.ts +13 -0
- package/esm/src/observability/trace.js +37 -0
- package/esm/src/observability/types.d.ts +109 -0
- package/esm/src/observability/types.js +3 -0
- package/esm/src/presets/google/speech-voices.d.ts +9 -0
- package/esm/src/presets/google/speech-voices.js +38 -0
- package/esm/src/presets/google-limits.d.ts +13 -0
- package/esm/src/presets/google-limits.js +18 -0
- package/esm/src/presets/google.d.ts +119 -0
- package/esm/src/presets/google.js +197 -0
- package/esm/src/presets/local.d.ts +26 -0
- package/esm/src/presets/local.js +30 -0
- package/esm/src/presets/mod.d.ts +11 -0
- package/esm/src/presets/mod.js +9 -0
- package/esm/src/presets/openrouter.d.ts +26 -0
- package/esm/src/presets/openrouter.js +31 -0
- package/esm/src/providers/embedding-limits.d.ts +7 -0
- package/esm/src/providers/embedding-limits.js +12 -0
- package/esm/src/providers/gemini/embedding-protocol.d.ts +8 -0
- package/esm/src/providers/gemini/embedding-protocol.js +125 -0
- package/esm/src/providers/gemini/interactions-protocol.d.ts +8 -0
- package/esm/src/providers/gemini/interactions-protocol.js +105 -0
- package/esm/src/providers/gemini/live-protocol.d.ts +3 -0
- package/esm/src/providers/gemini/live-protocol.js +191 -0
- package/esm/src/providers/gemini/prices.d.ts +3 -0
- package/esm/src/providers/gemini/prices.js +63 -0
- package/esm/src/providers/gemini/provider.d.ts +7 -0
- package/esm/src/providers/gemini/provider.js +39 -0
- package/esm/src/providers/google/api-error.d.ts +8 -0
- package/esm/src/providers/google/api-error.js +123 -0
- package/esm/src/providers/google/builtins.d.ts +14 -0
- package/esm/src/providers/google/builtins.js +57 -0
- package/esm/src/providers/google/grounding.d.ts +5 -0
- package/esm/src/providers/google/grounding.js +226 -0
- package/esm/src/providers/google/interactions/framing.d.ts +27 -0
- package/esm/src/providers/google/interactions/framing.js +304 -0
- package/esm/src/providers/google/interactions/steps.d.ts +12 -0
- package/esm/src/providers/google/interactions/steps.js +200 -0
- package/esm/src/providers/google/interactions/stream.d.ts +23 -0
- package/esm/src/providers/google/interactions/stream.js +230 -0
- package/esm/src/providers/google/live/framing.d.ts +61 -0
- package/esm/src/providers/google/live/framing.js +597 -0
- package/esm/src/providers/google/live/stream.d.ts +13 -0
- package/esm/src/providers/google/live/stream.js +57 -0
- package/esm/src/providers/google/thinking.d.ts +3 -0
- package/esm/src/providers/google/thinking.js +8 -0
- package/esm/src/providers/google/urls.d.ts +4 -0
- package/esm/src/providers/google/urls.js +4 -0
- package/esm/src/providers/google/usage.d.ts +7 -0
- package/esm/src/providers/google/usage.js +49 -0
- package/esm/src/providers/live.d.ts +6 -0
- package/esm/src/providers/live.js +6 -0
- package/esm/src/providers/local/embedding-protocol.d.ts +7 -0
- package/esm/src/providers/local/embedding-protocol.js +60 -0
- package/esm/src/providers/local/provider.d.ts +7 -0
- package/esm/src/providers/local/provider.js +28 -0
- package/esm/src/providers/mod.d.ts +32 -0
- package/esm/src/providers/mod.js +23 -0
- package/esm/src/providers/open-ai/protocol.d.ts +3 -0
- package/esm/src/providers/open-ai/protocol.js +189 -0
- package/esm/src/providers/openrouter/embedding-protocol.d.ts +6 -0
- package/esm/src/providers/openrouter/embedding-protocol.js +86 -0
- package/esm/src/providers/openrouter/image.d.ts +15 -0
- package/esm/src/providers/openrouter/image.js +34 -0
- package/esm/src/providers/openrouter/images-protocol.d.ts +3 -0
- package/esm/src/providers/openrouter/images-protocol.js +82 -0
- package/esm/src/providers/openrouter/openai/compat.d.ts +19 -0
- package/esm/src/providers/openrouter/openai/compat.js +162 -0
- package/esm/src/providers/openrouter/openai/image-payload.d.ts +9 -0
- package/esm/src/providers/openrouter/openai/image-payload.js +115 -0
- package/esm/src/providers/openrouter/openai/reasoning-state.d.ts +2 -0
- package/esm/src/providers/openrouter/openai/reasoning-state.js +31 -0
- package/esm/src/providers/openrouter/openai/usage.d.ts +8 -0
- package/esm/src/providers/openrouter/openai/usage.js +56 -0
- package/esm/src/providers/openrouter/protocol.d.ts +8 -0
- package/esm/src/providers/openrouter/protocol.js +171 -0
- package/esm/src/providers/openrouter/provider.d.ts +10 -0
- package/esm/src/providers/openrouter/provider.js +50 -0
- package/esm/src/providers/shared/carried-tool-files.d.ts +7 -0
- package/esm/src/providers/shared/carried-tool-files.js +43 -0
- package/esm/src/providers/shared/pcm.d.ts +14 -0
- package/esm/src/providers/shared/pcm.js +78 -0
- package/esm/src/providers/shared/protocol-events.d.ts +8 -0
- package/esm/src/providers/shared/protocol-events.js +20 -0
- package/esm/src/providers/shared/response-identity.d.ts +11 -0
- package/esm/src/providers/shared/response-identity.js +12 -0
- package/esm/src/providers/shared/retry.d.ts +17 -0
- package/esm/src/providers/shared/retry.js +90 -0
- package/esm/src/providers/shared/sse.d.ts +6 -0
- package/esm/src/providers/shared/sse.js +93 -0
- package/esm/src/providers/shared/structured-output.d.ts +4 -0
- package/esm/src/providers/shared/structured-output.js +11 -0
- package/esm/src/providers/shared/tool-args.d.ts +24 -0
- package/esm/src/providers/shared/tool-args.js +88 -0
- package/esm/src/providers/shared/tool-draft.d.ts +16 -0
- package/esm/src/providers/shared/tool-draft.js +14 -0
- package/esm/src/providers/shared/upstream-tap.d.ts +8 -0
- package/esm/src/providers/shared/upstream-tap.js +85 -0
- package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
- package/esm/src/providers/shared/upstream-tape.js +88 -0
- package/esm/src/providers/system-one/protocol.d.ts +8 -0
- package/esm/src/providers/system-one/protocol.js +93 -0
- package/esm/src/providers/types.d.ts +51 -0
- package/esm/src/providers/types.js +2 -0
- package/esm/src/providers/typesafe/provider.d.ts +7 -0
- package/esm/src/providers/typesafe/provider.js +24 -0
- package/esm/src/surface/formats.d.ts +46 -0
- package/esm/src/surface/formats.js +178 -0
- package/esm/src/surface/mod.d.ts +16 -0
- package/esm/src/surface/mod.js +12 -0
- package/esm/src/surface/runtime.d.ts +44 -0
- package/esm/src/surface/runtime.js +441 -0
- package/esm/src/surface/tools.d.ts +9 -0
- package/esm/src/surface/tools.js +62 -0
- package/esm/src/surface/types.d.ts +110 -0
- package/esm/src/surface/types.js +4 -0
- package/package.json +100 -4
|
@@ -0,0 +1,1182 @@
|
|
|
1
|
+
import { canaryNote, contextNote, USER_CLOSE, USER_OPEN, userDataNote, } from '../../../guardrails/canary.js';
|
|
2
|
+
import { addGivenDestinations, addHistoryDestinations, addRequestDestinations, addResultDestinations, turnDestinations, } from '../../../guardrails/destinations.js';
|
|
3
|
+
import { addHistoryUrls, addRequestUrls, addSeenUrls, givenUrlSets, } from '../../../guardrails/egress-urls.js';
|
|
4
|
+
import { describeError, errorKind, TheoremError, throwIfAborted, toErrorEvent, withPublicLabels, } from '../../../guardrails/error.js';
|
|
5
|
+
import { projectGuardrailTurnEvent } from '../../../guardrails/events.js';
|
|
6
|
+
import { labelText } from '../../../guardrails/labels.js';
|
|
7
|
+
import { abortLiveOutboundTurn, createLiveOutboundGateSession, finalizeLiveOutboundTurn, processLiveOutboundBatch, } from '../../../guardrails/live-outbound-gate.js';
|
|
8
|
+
import { sanitizeTurnRequestWithEvents } from '../../../guardrails/sanitize.js';
|
|
9
|
+
import { recordTaint } from '../../../guardrails/tool-result.js';
|
|
10
|
+
import { resolveObservabilityPolicy } from '../../../observability/resolve-policy.js';
|
|
11
|
+
import { liveToolReadBack } from '../../../providers/live.js';
|
|
12
|
+
import { memoryCredentialSource } from '../../auth/credential-source.js';
|
|
13
|
+
import { historyHash } from '../../checkpoint.js';
|
|
14
|
+
import { openRegisteredLiveSession } from '../../provider-live.js';
|
|
15
|
+
import { assertHistoryKept } from '../../provider-runtime.js';
|
|
16
|
+
import { assertTurnContext } from '../../registry/ingress.js';
|
|
17
|
+
import { providerCompleteRequest } from '../../registry/provider-request.js';
|
|
18
|
+
import { resolveTurnInRegistry } from '../../registry/resolve.js';
|
|
19
|
+
import { fillSlots } from '../../registry/slot-fill.js';
|
|
20
|
+
import { DEFAULT_RESUMED_AFTER_MS } from '../../schema.js';
|
|
21
|
+
import { injectedStageEvent, runStage, stageEventFields, } from '../../stages.js';
|
|
22
|
+
import { profileAllowsInject, stageAbortStop } from '../../stop.js';
|
|
23
|
+
import { bindSystem } from '../../system-parts.js';
|
|
24
|
+
import { failedActivity, failureEvent } from '../../tools/events.js';
|
|
25
|
+
import { executeRegisteredTool, lapsedGateFailure, shownToolArguments, } from '../../tools/execute.js';
|
|
26
|
+
import { answerGatedCall, gateExpired, resolveGateTtlMs, resumeForAnswer, } from '../../tools/gate-answer.js';
|
|
27
|
+
import { formatToolFailureForModel, formatToolResult } from '../../tools/model-text.js';
|
|
28
|
+
import { ownToolsOf } from '../../tools/project.js';
|
|
29
|
+
import { cloneTurnToolSnapshot } from '../../tools/resolve.js';
|
|
30
|
+
import { turnDoneOf } from '../../turn-events.js';
|
|
31
|
+
import { prepareLiveContext, prepareLiveInboundText } from '../live-inbound.js';
|
|
32
|
+
import { assertLiveIngress } from '../live-ingress.js';
|
|
33
|
+
import { sessionAgentCaller } from '../runner/mod.js';
|
|
34
|
+
import { startLiveTrace } from './session-trace.js';
|
|
35
|
+
function pageAnswerForLiveRequest(page, execution) {
|
|
36
|
+
return page ?? (execution ? undefined : { unanswered: true });
|
|
37
|
+
}
|
|
38
|
+
function isUnresolvedOperationError(error) {
|
|
39
|
+
if (!(error instanceof TheoremError) || !error.copy)
|
|
40
|
+
return false;
|
|
41
|
+
return 'key' in error.copy
|
|
42
|
+
? error.copy.key === 'tool.execution_uncertain'
|
|
43
|
+
: error.copy.some((copy) => copy.key === 'tool.execution_uncertain');
|
|
44
|
+
}
|
|
45
|
+
function isReleased(held) {
|
|
46
|
+
return typeof held.state === 'object' && held.state.released === true;
|
|
47
|
+
}
|
|
48
|
+
function isSettledPhase(tool) {
|
|
49
|
+
return tool.phase === 'complete' || tool.phase === 'error' || tool.phase === 'gate';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The provider's close after it warned of it (`goAway`): the session's last
|
|
53
|
+
* event, not a failure. Every close fact rides along for the builder; the raw
|
|
54
|
+
* close text is `errorInternal`, which `forClient` strips.
|
|
55
|
+
*/
|
|
56
|
+
function sessionEndedEvent(closed, goAway, labels) {
|
|
57
|
+
const internal = closed.error ? describeError(closed.error) : closed.reason;
|
|
58
|
+
return {
|
|
59
|
+
type: 'session',
|
|
60
|
+
session: {
|
|
61
|
+
kind: 'ended',
|
|
62
|
+
...(goAway.timeLeftMs !== undefined ? { timeLeftMs: goAway.timeLeftMs } : {}),
|
|
63
|
+
message: labelText('live.session_ended', {}, labels),
|
|
64
|
+
ended: {
|
|
65
|
+
cause: 'go_away',
|
|
66
|
+
code: closed.code,
|
|
67
|
+
closedAfterMs: goAway.closedAfterMs,
|
|
68
|
+
...(closed.error ? { errorKind: errorKind(closed.error) } : {}),
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
...(internal ? { errorInternal: internal } : {}),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** Live ingress takes text: an inject's texts, or `undefined` when any message is not plain text. */
|
|
75
|
+
function liveInjectTexts(messages) {
|
|
76
|
+
const texts = [];
|
|
77
|
+
for (const msg of messages) {
|
|
78
|
+
const text = msg.role === 'tool' || msg.parts?.length ? undefined : msg.content?.trim();
|
|
79
|
+
if (!text)
|
|
80
|
+
return undefined;
|
|
81
|
+
texts.push(text);
|
|
82
|
+
}
|
|
83
|
+
return texts;
|
|
84
|
+
}
|
|
85
|
+
function assertLiveProfile(profile) {
|
|
86
|
+
if (profile.type !== 'live') {
|
|
87
|
+
throw new TheoremError('request', `runSession requires profile.type 'live' (got '${profile.type}' for ${profile.id})`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Accept a registry-resolved snapshot from the process that owns the tool
|
|
92
|
+
* registry. The profile's `tools.allow` stays the ceiling: a custom tool id
|
|
93
|
+
* outside it is refused rather than declared.
|
|
94
|
+
*/
|
|
95
|
+
function sessionSnapshotWithinAllow(profile, snapshot) {
|
|
96
|
+
const allow = new Set(profile.tools.allow);
|
|
97
|
+
const builtins = new Set(snapshot.builtins);
|
|
98
|
+
const outside = new Set();
|
|
99
|
+
for (const id of [...snapshot.gated, ...snapshot.visible, ...snapshot.executable]) {
|
|
100
|
+
if (!allow.has(id) && !builtins.has(id)) {
|
|
101
|
+
outside.add(id);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (outside.size > 0) {
|
|
105
|
+
// labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
106
|
+
const detail = `session snapshot declares tools outside tools.allow: ${[...outside].join(', ')}`;
|
|
107
|
+
throw new TheoremError('config', `Profile ${profile.id}: ${detail}`);
|
|
108
|
+
}
|
|
109
|
+
return cloneTurnToolSnapshot(snapshot);
|
|
110
|
+
}
|
|
111
|
+
function toTurnRequest(req) {
|
|
112
|
+
return {
|
|
113
|
+
profile: req.profile,
|
|
114
|
+
providerState: req.providerState,
|
|
115
|
+
system: req.system,
|
|
116
|
+
builtInArgs: req.builtInArgs,
|
|
117
|
+
path: req.path,
|
|
118
|
+
sessionPermissions: req.sessionPermissions,
|
|
119
|
+
signal: req.signal,
|
|
120
|
+
metadata: req.metadata,
|
|
121
|
+
host: req.host,
|
|
122
|
+
input: {
|
|
123
|
+
text: '',
|
|
124
|
+
history: req.history,
|
|
125
|
+
slots: req.slots,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The prompt the session opens with: the greeting on a new call, the resume prompt when the
|
|
131
|
+
* caller was away long enough to notice. It is the profile's own copy, so no boundary reads it.
|
|
132
|
+
*/
|
|
133
|
+
function openingPrompt(profile, req, slots) {
|
|
134
|
+
const { greeting, resumed } = profile.live;
|
|
135
|
+
if (!req.providerState && !req.history?.length) {
|
|
136
|
+
return greeting ? fillSlots(profile, greeting, slots, 'live.greeting') : undefined;
|
|
137
|
+
}
|
|
138
|
+
if (!resumed || (req.awayMs ?? 0) < (resumed.afterMs ?? DEFAULT_RESUMED_AFTER_MS)) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
return fillSlots(profile, resumed.prompt, slots, 'live.resumed.prompt');
|
|
142
|
+
}
|
|
143
|
+
/** Refuses context the profile's `inputs.context` does not take. */
|
|
144
|
+
function assertSessionContext(profile, context) {
|
|
145
|
+
assertTurnContext(profile, { profile: profile.id, input: { context } });
|
|
146
|
+
}
|
|
147
|
+
function applyVoiceOverride(generation, voice) {
|
|
148
|
+
if (!voice || !generation.live) {
|
|
149
|
+
return generation;
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
...generation,
|
|
153
|
+
live: {
|
|
154
|
+
...generation.live,
|
|
155
|
+
voice,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function applyInitialInput(generation, input) {
|
|
160
|
+
if (!input || input.length === 0) {
|
|
161
|
+
return generation;
|
|
162
|
+
}
|
|
163
|
+
return { ...generation, input };
|
|
164
|
+
}
|
|
165
|
+
async function applyOutbound(gate, events, turnPhase, onWithhold) {
|
|
166
|
+
if (turnPhase === 'abort') {
|
|
167
|
+
abortLiveOutboundTurn(gate);
|
|
168
|
+
return events.filter((ev) => ev.type === 'tool' ||
|
|
169
|
+
ev.type === 'session' ||
|
|
170
|
+
(ev.type === 'done' && ev.interrupted === true));
|
|
171
|
+
}
|
|
172
|
+
const batch = await processLiveOutboundBatch(gate, events);
|
|
173
|
+
if (batch.action === 'withhold') {
|
|
174
|
+
onWithhold();
|
|
175
|
+
return [
|
|
176
|
+
...(batch.events ?? []).filter((event) => !isTurnComplete(event) && event.type !== 'provider_checkpoint'),
|
|
177
|
+
toErrorEvent(batch.error),
|
|
178
|
+
];
|
|
179
|
+
}
|
|
180
|
+
const out = batch.action === 'emit' ? batch.events : [];
|
|
181
|
+
if (turnPhase !== 'complete')
|
|
182
|
+
return out;
|
|
183
|
+
// why: The response is whole only once its verdict is in: `turn_complete` follows what the
|
|
184
|
+
// verdict released, and a withheld response ends on its error.
|
|
185
|
+
const said = out.filter((event) => !isTurnComplete(event));
|
|
186
|
+
const finalized = await finalizeLiveOutboundTurn(gate);
|
|
187
|
+
if (finalized.action === 'withhold') {
|
|
188
|
+
onWithhold();
|
|
189
|
+
return [
|
|
190
|
+
...said.filter((event) => event.type !== 'provider_checkpoint'),
|
|
191
|
+
...(finalized.events ?? []),
|
|
192
|
+
toErrorEvent(finalized.error),
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
return [
|
|
196
|
+
...said,
|
|
197
|
+
...(finalized.action === 'emit' ? finalized.events : []),
|
|
198
|
+
...out.filter(isTurnComplete),
|
|
199
|
+
{ type: 'done', stop: { kind: 'completed' } },
|
|
200
|
+
];
|
|
201
|
+
}
|
|
202
|
+
function isTurnComplete(event) {
|
|
203
|
+
return event.type === 'session' && event.session.kind === 'turn_complete';
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* The output a settled Live call sends upstream, or `undefined` when nothing
|
|
207
|
+
* is sent (a held gate, or no result). The model reads it as the `functionResponse`:
|
|
208
|
+
* the same guarded text a turn sends, never the tool's raw output.
|
|
209
|
+
*/
|
|
210
|
+
function liveToolOutput(s) {
|
|
211
|
+
if (s.gated)
|
|
212
|
+
return undefined;
|
|
213
|
+
const result = s.modelResult ?? (s.failure ? formatToolFailureForModel(s.failure) : undefined);
|
|
214
|
+
return result ? formatToolResult(result) : undefined;
|
|
215
|
+
}
|
|
216
|
+
/** What the model reads back from a Live call: the `functionResponse.response` sent. */
|
|
217
|
+
function liveReadBack(s) {
|
|
218
|
+
const output = liveToolOutput(s);
|
|
219
|
+
return output === undefined
|
|
220
|
+
? undefined
|
|
221
|
+
: { text: liveToolReadBack(output), parts: s.modelResult?.parts };
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* A provider batch as the host receives it: a call's `done` becomes the
|
|
225
|
+
* host's (`turnDoneOf`); `response` stays with the trace, which read the batch.
|
|
226
|
+
*/
|
|
227
|
+
function hostEventsOf(events, snapshot) {
|
|
228
|
+
return events.flatMap((ev) => {
|
|
229
|
+
if (ev.type === 'response' || ev.type === 'reasoning')
|
|
230
|
+
return [];
|
|
231
|
+
if (ev.type !== 'done')
|
|
232
|
+
return [ev];
|
|
233
|
+
const { type: _done, ...done } = ev;
|
|
234
|
+
return [turnDoneOf(done, snapshot)];
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function* drainPendingHostEvents(pendingHostEvents) {
|
|
238
|
+
while (pendingHostEvents.length > 0) {
|
|
239
|
+
const pending = pendingHostEvents.shift();
|
|
240
|
+
if (pending)
|
|
241
|
+
yield pending;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function boundaryDoneEvents(doneBatch, turnPhase, interrupted) {
|
|
245
|
+
if (doneBatch.length > 0)
|
|
246
|
+
return doneBatch;
|
|
247
|
+
if (turnPhase === 'abort' || interrupted) {
|
|
248
|
+
return [{ type: 'done', stop: { kind: 'interrupted' }, interrupted: true }];
|
|
249
|
+
}
|
|
250
|
+
return [{ type: 'done', stop: { kind: 'completed' } }];
|
|
251
|
+
}
|
|
252
|
+
function recordLiveHistory(events, recordUserText, recordAssistantText) {
|
|
253
|
+
for (const event of events) {
|
|
254
|
+
if (event.type === 'text')
|
|
255
|
+
recordAssistantText(event.text);
|
|
256
|
+
if (event.type === 'evidence' &&
|
|
257
|
+
event.text &&
|
|
258
|
+
(event.evidence.kind === 'input_transcription' ||
|
|
259
|
+
event.evidence.kind === 'output_transcription') &&
|
|
260
|
+
!event.evidence.interim) {
|
|
261
|
+
if (event.evidence.kind === 'input_transcription')
|
|
262
|
+
recordUserText(event.text);
|
|
263
|
+
if (event.evidence.kind === 'output_transcription' &&
|
|
264
|
+
!events.some((item) => item.type === 'text'))
|
|
265
|
+
recordAssistantText(event.text);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async function sealLiveCheckpoints(events, history, calls, keepsHistory) {
|
|
270
|
+
const sealed = [];
|
|
271
|
+
for (const event of events) {
|
|
272
|
+
if (!keepsHistory && event.type === 'provider_checkpoint')
|
|
273
|
+
continue;
|
|
274
|
+
if (!keepsHistory && event.type === 'done') {
|
|
275
|
+
const { providerState: _dropped, ...standsAlone } = event;
|
|
276
|
+
sealed.push(standsAlone);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if ((event.type === 'provider_checkpoint' || event.type === 'done') && event.providerState) {
|
|
280
|
+
const covered = [...history];
|
|
281
|
+
for (const [callId, held] of calls) {
|
|
282
|
+
if (covered.some((message) => message.tool_calls?.some((call) => call.id === callId)))
|
|
283
|
+
continue;
|
|
284
|
+
covered.push({
|
|
285
|
+
role: 'assistant',
|
|
286
|
+
tool_calls: [
|
|
287
|
+
{
|
|
288
|
+
id: callId,
|
|
289
|
+
type: 'function',
|
|
290
|
+
function: { name: held.name, arguments: JSON.stringify(held.arguments) },
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
sealed.push({
|
|
296
|
+
...event,
|
|
297
|
+
...(event.type === 'provider_checkpoint' ? { history: structuredClone(covered) } : {}),
|
|
298
|
+
providerState: {
|
|
299
|
+
...event.providerState,
|
|
300
|
+
coveredHistoryLength: covered.length,
|
|
301
|
+
coveredHistoryHash: await historyHash(covered),
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
else
|
|
306
|
+
sealed.push(event);
|
|
307
|
+
}
|
|
308
|
+
return sealed;
|
|
309
|
+
}
|
|
310
|
+
function* yieldLiveNonDoneEvents(gated, includeMatch) {
|
|
311
|
+
const doneBatch = gated.filter((ev) => ev.type === 'done');
|
|
312
|
+
for (const ev of gated) {
|
|
313
|
+
if (ev.type === 'done')
|
|
314
|
+
continue;
|
|
315
|
+
yield projectGuardrailTurnEvent(ev, includeMatch ?? false);
|
|
316
|
+
}
|
|
317
|
+
return doneBatch;
|
|
318
|
+
}
|
|
319
|
+
function buildLiveSession(args) {
|
|
320
|
+
const { profile, canary, connection, gate, givenUrls, destinations, signal, onStage, host: sessionHost, resolveHost, path, snapshot, trace, gateTtlMs, signInGate, } = args;
|
|
321
|
+
/** Grows as the user approves `session_consent` tools. */
|
|
322
|
+
let sessionPermissions = args.sessionPermissions;
|
|
323
|
+
/** Where keys typed at sign-in gates go when a call brings no source of its own. */
|
|
324
|
+
const sessionCredentials = args.credentials ?? memoryCredentialSource();
|
|
325
|
+
const calls = new Map();
|
|
326
|
+
let closed = false;
|
|
327
|
+
let withholdClose = false;
|
|
328
|
+
const pendingHostEvents = [];
|
|
329
|
+
/** Wakes the host's stream when the session queues an event while the model is silent. */
|
|
330
|
+
let wakeHost;
|
|
331
|
+
const includeMatch = resolveObservabilityPolicy(profile.observability).include
|
|
332
|
+
.guardrailMatchPreview;
|
|
333
|
+
let cycle = 'idle';
|
|
334
|
+
let cycleStep = 0;
|
|
335
|
+
/** What this cycle's tool results read from outside the host; a cycle starts clean, as a turn does. */
|
|
336
|
+
let cycleTaint;
|
|
337
|
+
const history = args.historySeed?.length
|
|
338
|
+
? structuredClone(args.historySeed)
|
|
339
|
+
: [];
|
|
340
|
+
/** Serialize stage + ingress so concurrent send* cannot interleave cycles. */
|
|
341
|
+
let ingressChain = Promise.resolve();
|
|
342
|
+
const recordUserText = (text) => {
|
|
343
|
+
const trimmed = text.trim();
|
|
344
|
+
if (!trimmed)
|
|
345
|
+
return;
|
|
346
|
+
history.push({ role: 'user', content: trimmed });
|
|
347
|
+
addSeenUrls(givenUrls.request, trimmed);
|
|
348
|
+
addGivenDestinations(destinations, trimmed);
|
|
349
|
+
};
|
|
350
|
+
const recordAssistantText = (text) => {
|
|
351
|
+
if (!text)
|
|
352
|
+
return;
|
|
353
|
+
const last = history.at(-1);
|
|
354
|
+
if (last?.role === 'assistant' && typeof last.content === 'string' && !last.tool_calls) {
|
|
355
|
+
last.content = `${last.content}${text}`;
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
history.push({ role: 'assistant', content: text });
|
|
359
|
+
};
|
|
360
|
+
const recordToolSettle = (tool, readBack, parts) => {
|
|
361
|
+
history.push({
|
|
362
|
+
role: 'assistant',
|
|
363
|
+
tool_calls: [
|
|
364
|
+
{
|
|
365
|
+
id: tool.callId,
|
|
366
|
+
type: 'function',
|
|
367
|
+
function: {
|
|
368
|
+
name: tool.name,
|
|
369
|
+
arguments: JSON.stringify(tool.arguments),
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
}, {
|
|
374
|
+
role: 'tool',
|
|
375
|
+
tool_call_id: tool.callId,
|
|
376
|
+
name: tool.name,
|
|
377
|
+
content: readBack,
|
|
378
|
+
...(parts?.length ? { parts } : {}),
|
|
379
|
+
});
|
|
380
|
+
addSeenUrls(givenUrls.tools, readBack);
|
|
381
|
+
};
|
|
382
|
+
const enqueuePending = (ev) => {
|
|
383
|
+
pendingHostEvents.push(projectGuardrailTurnEvent(ev, includeMatch));
|
|
384
|
+
const wake = wakeHost;
|
|
385
|
+
wakeHost = undefined;
|
|
386
|
+
wake?.();
|
|
387
|
+
};
|
|
388
|
+
for (const ev of args.requestEvents)
|
|
389
|
+
enqueuePending(ev);
|
|
390
|
+
const hostEventQueued = () => pendingHostEvents.length > 0
|
|
391
|
+
? Promise.resolve('queued')
|
|
392
|
+
: new Promise((resolve) => {
|
|
393
|
+
wakeHost = () => resolve('queued');
|
|
394
|
+
});
|
|
395
|
+
const assertOpen = () => {
|
|
396
|
+
if (closed) {
|
|
397
|
+
throw new TheoremError('request', 'Live session is closed'); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
const sendInput = (input) => {
|
|
401
|
+
assertOpen();
|
|
402
|
+
connection.sendInput(input);
|
|
403
|
+
};
|
|
404
|
+
const answerModel = (callId, held, readBack, parts) => {
|
|
405
|
+
recordToolSettle({ name: held.name, callId, arguments: held.arguments }, readBack, parts);
|
|
406
|
+
assertOpen();
|
|
407
|
+
connection.sendToolResponse(callId, held.name, readBack, parts);
|
|
408
|
+
};
|
|
409
|
+
const settleHeld = (callId, held, readBack, parts) => {
|
|
410
|
+
clearTimeout(held.lapse);
|
|
411
|
+
calls.delete(callId);
|
|
412
|
+
answerModel(callId, held, readBack, parts);
|
|
413
|
+
};
|
|
414
|
+
const settleHeldFailure = (callId, held, failure) => {
|
|
415
|
+
const readBack = formatToolResult(formatToolFailureForModel(failure));
|
|
416
|
+
enqueuePending(failureEvent({
|
|
417
|
+
name: held.name,
|
|
418
|
+
callId,
|
|
419
|
+
operationId: held.operationId,
|
|
420
|
+
activityFailed: failedActivity(held.name, profile.labels),
|
|
421
|
+
}, failure, readBack));
|
|
422
|
+
settleHeld(callId, held, readBack);
|
|
423
|
+
};
|
|
424
|
+
/** A released call nobody answered in time: the model reads that its sign-in expired. */
|
|
425
|
+
const lapseReleased = (callId, held) => {
|
|
426
|
+
if (closed || calls.get(callId) !== held || typeof held.state !== 'object')
|
|
427
|
+
return;
|
|
428
|
+
const { gate } = held.state;
|
|
429
|
+
const failure = lapsedGateFailure(held.name, gate.kind === 'auth' ? gate.authChallenge.service : undefined, profile.labels);
|
|
430
|
+
settleHeldFailure(callId, held, failure);
|
|
431
|
+
};
|
|
432
|
+
/** A closed session answers nothing more. */
|
|
433
|
+
const stopLapses = () => {
|
|
434
|
+
for (const held of calls.values())
|
|
435
|
+
clearTimeout(held.lapse);
|
|
436
|
+
};
|
|
437
|
+
/**
|
|
438
|
+
* A call that ran into `gate`. A sign-in gate this session answers at once tells the model its
|
|
439
|
+
* pending note and releases the call; a call already released stays released. Either waits
|
|
440
|
+
* `gateTtlMs` for its outcome. False: the call is not released, and the caller holds it.
|
|
441
|
+
*/
|
|
442
|
+
const releaseGated = (callId, held, wasReleased, gate, readBack) => {
|
|
443
|
+
const answersNow = signInGate === 'answer' && gate.kind === 'auth' && readBack !== undefined;
|
|
444
|
+
if (!answersNow && !wasReleased)
|
|
445
|
+
return false;
|
|
446
|
+
if (answersNow)
|
|
447
|
+
answerModel(callId, held, readBack);
|
|
448
|
+
clearTimeout(held.lapse);
|
|
449
|
+
held.state = { gate, createdAt: Date.now(), released: true };
|
|
450
|
+
held.lapse = setTimeout(() => lapseReleased(callId, held), gateTtlMs);
|
|
451
|
+
return true;
|
|
452
|
+
};
|
|
453
|
+
/**
|
|
454
|
+
* Hold each call the model makes. A call the provider already failed
|
|
455
|
+
* (malformed arguments) is settled here: the model reads its failure. A
|
|
456
|
+
* provider cancel lets go of a call that is not running.
|
|
457
|
+
*/
|
|
458
|
+
const holdCalls = (events) => {
|
|
459
|
+
for (const ev of events) {
|
|
460
|
+
if (ev.type !== 'tool')
|
|
461
|
+
continue;
|
|
462
|
+
const tool = ev.tool;
|
|
463
|
+
if (tool.phase === undefined) {
|
|
464
|
+
calls.set(tool.callId, {
|
|
465
|
+
name: tool.name,
|
|
466
|
+
arguments: tool.arguments,
|
|
467
|
+
operationId: tool.operationId ?? crypto.randomUUID(),
|
|
468
|
+
state: 'open',
|
|
469
|
+
});
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const held = calls.get(tool.callId);
|
|
473
|
+
if (!held)
|
|
474
|
+
continue;
|
|
475
|
+
if (tool.phase === 'error' && tool.readBack !== undefined) {
|
|
476
|
+
settleHeld(tool.callId, held, tool.readBack);
|
|
477
|
+
}
|
|
478
|
+
else if (tool.phase === 'cancel' &&
|
|
479
|
+
held.state !== 'running' &&
|
|
480
|
+
held.state !== 'uncertain') {
|
|
481
|
+
calls.delete(tool.callId);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
const closeSocket = (code, reason, initiator) => {
|
|
486
|
+
trace.socketClosed(code, reason, initiator);
|
|
487
|
+
connection.close(code, reason);
|
|
488
|
+
};
|
|
489
|
+
/** A `done` names the response span it ends. */
|
|
490
|
+
const stampDone = (ev) => {
|
|
491
|
+
if (ev.type !== 'done')
|
|
492
|
+
return ev;
|
|
493
|
+
const traceparent = trace.responseTraceparent();
|
|
494
|
+
return traceparent ? { ...ev, traceparent } : ev;
|
|
495
|
+
};
|
|
496
|
+
const runCycleStage = async (stage, extra) => {
|
|
497
|
+
const gen = runStage({
|
|
498
|
+
...extra,
|
|
499
|
+
stage,
|
|
500
|
+
step: Math.max(1, cycleStep),
|
|
501
|
+
history,
|
|
502
|
+
handlers: onStage ? [onStage] : [],
|
|
503
|
+
guardrails: profile.guardrails,
|
|
504
|
+
injectAllowed: profileAllowsInject(profile),
|
|
505
|
+
host: sessionHost,
|
|
506
|
+
signal,
|
|
507
|
+
span: trace.root,
|
|
508
|
+
});
|
|
509
|
+
let result = await gen.next();
|
|
510
|
+
while (!result.done) {
|
|
511
|
+
enqueuePending(result.value);
|
|
512
|
+
result = await gen.next();
|
|
513
|
+
}
|
|
514
|
+
return {
|
|
515
|
+
abort: result.value.abort,
|
|
516
|
+
inject: result.value.inject,
|
|
517
|
+
};
|
|
518
|
+
};
|
|
519
|
+
/** The text to send, or `undefined` when a match blocks it: the message does not reach the model. */
|
|
520
|
+
const readLiveText = (text) => {
|
|
521
|
+
const start = performance.now();
|
|
522
|
+
const prepared = prepareLiveInboundText(profile, text);
|
|
523
|
+
trace.inboundCheck(performance.now() - start, prepared.guardrail);
|
|
524
|
+
if (prepared.guardrail)
|
|
525
|
+
enqueuePending(prepared.guardrail);
|
|
526
|
+
return prepared.text;
|
|
527
|
+
};
|
|
528
|
+
/** Context is background: it joins no history and opens no cycle, and its addresses count as given. */
|
|
529
|
+
const landContext = (context) => {
|
|
530
|
+
assertSessionContext(profile, context);
|
|
531
|
+
const start = performance.now();
|
|
532
|
+
const prepared = prepareLiveContext(profile, context);
|
|
533
|
+
trace.inboundCheck(performance.now() - start, prepared.guardrails[0]);
|
|
534
|
+
for (const guardrail of prepared.guardrails)
|
|
535
|
+
enqueuePending(guardrail);
|
|
536
|
+
for (const text of prepared.texts) {
|
|
537
|
+
addSeenUrls(givenUrls.request, text);
|
|
538
|
+
addGivenDestinations(destinations, text);
|
|
539
|
+
assertOpen();
|
|
540
|
+
connection.sendContext(text);
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
const ingestPreparedLiveText = (text) => {
|
|
544
|
+
const safe = readLiveText(text);
|
|
545
|
+
if (safe === undefined)
|
|
546
|
+
return;
|
|
547
|
+
recordUserText(safe.slice(USER_OPEN.length + 1, -(USER_CLOSE.length + 1)));
|
|
548
|
+
sendInput({ type: 'text', text: safe });
|
|
549
|
+
};
|
|
550
|
+
/**
|
|
551
|
+
* Write each inject whole into the open cycle (no re-entry into pre_turn),
|
|
552
|
+
* then record the named ones that landed. An inject live cannot write as
|
|
553
|
+
* text is refused whole with a stage warning, never written in part.
|
|
554
|
+
*/
|
|
555
|
+
const landInject = (stage, units, extra) => {
|
|
556
|
+
const landed = [];
|
|
557
|
+
for (const unit of units) {
|
|
558
|
+
const texts = liveInjectTexts(unit.messages);
|
|
559
|
+
if (!texts) {
|
|
560
|
+
enqueuePending({
|
|
561
|
+
...stageEventFields(stage, extra),
|
|
562
|
+
stageWarnings: [
|
|
563
|
+
{
|
|
564
|
+
code: 'inject_invalid_messages',
|
|
565
|
+
field: 'inject',
|
|
566
|
+
message: 'live ingress takes plain-text inject messages only; inject refused', // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
567
|
+
},
|
|
568
|
+
],
|
|
569
|
+
});
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
for (const text of texts) {
|
|
573
|
+
assertLiveIngress(profile, 'text');
|
|
574
|
+
ingestPreparedLiveText(text);
|
|
575
|
+
}
|
|
576
|
+
landed.push(unit);
|
|
577
|
+
}
|
|
578
|
+
const event = injectedStageEvent(stage, landed, extra);
|
|
579
|
+
if (event)
|
|
580
|
+
enqueuePending(event);
|
|
581
|
+
};
|
|
582
|
+
/** A stage's `abort` ends the open cycle: a cancelled `done`, then `post_turn`. */
|
|
583
|
+
const cancelCycle = async (abort) => {
|
|
584
|
+
const stop = stageAbortStop(abort);
|
|
585
|
+
// why: Idle first, so a boundary the provider sends meanwhile does not end the cycle again.
|
|
586
|
+
cycle = 'idle';
|
|
587
|
+
enqueuePending({ type: 'done', stop, interrupted: true });
|
|
588
|
+
await runCycleStage('post_turn', { stop });
|
|
589
|
+
};
|
|
590
|
+
const openCycleIfNeeded = async () => {
|
|
591
|
+
if (cycle === 'open')
|
|
592
|
+
return { aborted: false };
|
|
593
|
+
cycle = 'open';
|
|
594
|
+
cycleStep += 1;
|
|
595
|
+
cycleTaint = undefined;
|
|
596
|
+
destinations.remote.clear();
|
|
597
|
+
const pre = await runCycleStage('pre_turn');
|
|
598
|
+
if (pre.abort) {
|
|
599
|
+
await cancelCycle(pre.abort);
|
|
600
|
+
return { aborted: true };
|
|
601
|
+
}
|
|
602
|
+
landInject('pre_turn', pre.inject);
|
|
603
|
+
return { aborted: false };
|
|
604
|
+
};
|
|
605
|
+
const endCycleAroundDone = async function* (doneEvents) {
|
|
606
|
+
if (cycle !== 'open') {
|
|
607
|
+
for (const ev of doneEvents)
|
|
608
|
+
yield ev;
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const before = await runCycleStage('before_end', {
|
|
612
|
+
stop: doneEvents.find((e) => e.type === 'done')?.stop,
|
|
613
|
+
});
|
|
614
|
+
if (before.abort) {
|
|
615
|
+
yield { type: 'done', stop: { kind: 'cancelled' }, interrupted: true };
|
|
616
|
+
await runCycleStage('post_turn', { stop: { kind: 'cancelled' } });
|
|
617
|
+
cycle = 'idle';
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
landInject('before_end', before.inject);
|
|
621
|
+
for (const ev of doneEvents) {
|
|
622
|
+
yield ev;
|
|
623
|
+
}
|
|
624
|
+
const terminal = doneEvents.find((e) => e.type === 'done');
|
|
625
|
+
await runCycleStage('post_turn', { stop: terminal?.stop });
|
|
626
|
+
cycle = 'idle';
|
|
627
|
+
};
|
|
628
|
+
const withIngress = (fn) => {
|
|
629
|
+
const flush = async () => {
|
|
630
|
+
await fn();
|
|
631
|
+
await connection.flush?.();
|
|
632
|
+
};
|
|
633
|
+
const next = ingressChain.then(flush, flush);
|
|
634
|
+
ingressChain = next.then(() => undefined, () => undefined);
|
|
635
|
+
return next;
|
|
636
|
+
};
|
|
637
|
+
/**
|
|
638
|
+
* Run a held call through the registry, with stages. A gate holds it for its
|
|
639
|
+
* decision; any other settlement answers the model and lets it go.
|
|
640
|
+
*/
|
|
641
|
+
const runHeldCore = async (callId, held, input, resume, per) => {
|
|
642
|
+
const waiting = held.state;
|
|
643
|
+
const wasReleased = isReleased(held);
|
|
644
|
+
held.state = 'running';
|
|
645
|
+
const host = per.host ?? sessionHost;
|
|
646
|
+
const record = trace.toolRecord(callId);
|
|
647
|
+
let dispatched = false;
|
|
648
|
+
const exec = executeRegisteredTool({
|
|
649
|
+
onDispatch: () => {
|
|
650
|
+
dispatched = true;
|
|
651
|
+
},
|
|
652
|
+
tools: args.tools,
|
|
653
|
+
agents: args.agents,
|
|
654
|
+
execution: args.execution,
|
|
655
|
+
profile,
|
|
656
|
+
name: held.name,
|
|
657
|
+
input,
|
|
658
|
+
callId,
|
|
659
|
+
ctx: {
|
|
660
|
+
operationId: held.operationId,
|
|
661
|
+
sessionPermissions,
|
|
662
|
+
credentials: per.credentials ?? sessionCredentials,
|
|
663
|
+
resolveHost,
|
|
664
|
+
path,
|
|
665
|
+
signal,
|
|
666
|
+
resume,
|
|
667
|
+
host,
|
|
668
|
+
// why: A call opens no second line to the page, so a call the page did not answer is not held for it.
|
|
669
|
+
page: pageAnswerForLiveRequest(per.page, args.pageExecution),
|
|
670
|
+
pageExecution: args.pageExecution,
|
|
671
|
+
turn: { step: Math.max(1, cycleStep), taint: cycleTaint, destinations },
|
|
672
|
+
},
|
|
673
|
+
snapshot,
|
|
674
|
+
stages: {
|
|
675
|
+
handlers: onStage ? [onStage] : [],
|
|
676
|
+
profile,
|
|
677
|
+
step: Math.max(1, cycleStep),
|
|
678
|
+
history: () => history,
|
|
679
|
+
injectAllowed: profileAllowsInject(profile),
|
|
680
|
+
host,
|
|
681
|
+
signal,
|
|
682
|
+
},
|
|
683
|
+
scope: gate.context,
|
|
684
|
+
openSpan: record.open,
|
|
685
|
+
readBack: liveReadBack,
|
|
686
|
+
});
|
|
687
|
+
let s;
|
|
688
|
+
try {
|
|
689
|
+
while (true) {
|
|
690
|
+
const next = await exec.next();
|
|
691
|
+
if (next.done) {
|
|
692
|
+
s = next.value;
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
enqueuePending(next.value);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
catch (err) {
|
|
699
|
+
// invariant: A dispatched call with no outcome cannot regain approval authority.
|
|
700
|
+
const unresolved = dispatched || isUnresolvedOperationError(err);
|
|
701
|
+
held.state = unresolved ? 'uncertain' : waiting;
|
|
702
|
+
if (unresolved)
|
|
703
|
+
throw new TheoremError('unavailable', labelText('tool.execution_uncertain', {}, profile.labels), {
|
|
704
|
+
copy: { key: 'tool.execution_uncertain' },
|
|
705
|
+
cause: err,
|
|
706
|
+
});
|
|
707
|
+
throw err;
|
|
708
|
+
}
|
|
709
|
+
finally {
|
|
710
|
+
record.finish();
|
|
711
|
+
}
|
|
712
|
+
if (s.gated) {
|
|
713
|
+
if (!releaseGated(callId, held, wasReleased, s.gated, s.gateReadBack)) {
|
|
714
|
+
held.state = { gate: s.gated, createdAt: Date.now() };
|
|
715
|
+
}
|
|
716
|
+
return { gated: s.gated };
|
|
717
|
+
}
|
|
718
|
+
if (s.aborted) {
|
|
719
|
+
// why: A stage stopped the call. The model still reads an answer, or it
|
|
720
|
+
// waits for one; then the open cycle ends cancelled, as a turn does.
|
|
721
|
+
const failure = s.callNotStarted
|
|
722
|
+
? {
|
|
723
|
+
code: 'cancelled',
|
|
724
|
+
kind: 'cancelled',
|
|
725
|
+
message: labelText('session.tool_aborted', { tool: held.name }, profile.labels),
|
|
726
|
+
}
|
|
727
|
+
: s.failure;
|
|
728
|
+
if (s.callNotStarted && failure) {
|
|
729
|
+
settleHeldFailure(callId, held, failure);
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
const output = liveToolOutput(s);
|
|
733
|
+
if (output === undefined)
|
|
734
|
+
calls.delete(callId);
|
|
735
|
+
else
|
|
736
|
+
settleHeld(callId, held, output, s.modelResult?.parts);
|
|
737
|
+
}
|
|
738
|
+
const { aborted } = s;
|
|
739
|
+
await withIngress(async () => {
|
|
740
|
+
if (cycle === 'open')
|
|
741
|
+
await cancelCycle(aborted);
|
|
742
|
+
});
|
|
743
|
+
return {
|
|
744
|
+
outputRaw: s.outputRaw,
|
|
745
|
+
outputModel: s.modelResult,
|
|
746
|
+
failure,
|
|
747
|
+
awaiting: s.awaiting,
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
if (s.modelResult?.provenance) {
|
|
751
|
+
cycleTaint = recordTaint(cycleTaint, s.modelResult.provenance, s.modelResult.suspicious);
|
|
752
|
+
addResultDestinations(destinations, s.modelResult, s.modelResult.provenance);
|
|
753
|
+
}
|
|
754
|
+
const { pendingInject } = s;
|
|
755
|
+
if (pendingInject) {
|
|
756
|
+
await withIngress(async () => {
|
|
757
|
+
const opened = await openCycleIfNeeded();
|
|
758
|
+
if (opened.aborted)
|
|
759
|
+
return;
|
|
760
|
+
landInject('post_tool', pendingInject, { callId, toolName: held.name });
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
const output = liveToolOutput(s);
|
|
764
|
+
if (output === undefined)
|
|
765
|
+
calls.delete(callId);
|
|
766
|
+
else
|
|
767
|
+
settleHeld(callId, held, output, s.modelResult?.parts);
|
|
768
|
+
return {
|
|
769
|
+
outputRaw: s.outputRaw,
|
|
770
|
+
outputModel: s.modelResult,
|
|
771
|
+
failure: s.failure,
|
|
772
|
+
awaiting: s.awaiting,
|
|
773
|
+
};
|
|
774
|
+
};
|
|
775
|
+
const runHeld = async (...input) => {
|
|
776
|
+
const result = await runHeldCore(...input);
|
|
777
|
+
await connection.flush?.();
|
|
778
|
+
return result;
|
|
779
|
+
};
|
|
780
|
+
const gateExpiredError = (callId) =>
|
|
781
|
+
// labels-exempt: developer contract / internal diagnostic — the user reads session.gate_expired
|
|
782
|
+
new TheoremError('request', `gate ${callId} expired`, {
|
|
783
|
+
copy: { key: 'session.gate_expired' },
|
|
784
|
+
});
|
|
785
|
+
const gateLapsed = (held) => typeof held.state === 'object' && gateExpired(held.state.createdAt, Date.now(), gateTtlMs);
|
|
786
|
+
/**
|
|
787
|
+
* The held call `callId`, when it can run now. A gate that waited past
|
|
788
|
+
* `gateTtlMs` is settled (a held one as abandoned, a released one as lapsed;
|
|
789
|
+
* the model reads that), then refused.
|
|
790
|
+
*/
|
|
791
|
+
const takeHeld = async (callId, operationId) => {
|
|
792
|
+
const held = calls.get(callId);
|
|
793
|
+
if (!held || held.state === 'running') {
|
|
794
|
+
throw new TheoremError('request', `call ${callId} is not waiting to run`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
795
|
+
}
|
|
796
|
+
if (operationId !== undefined && operationId !== held.operationId) {
|
|
797
|
+
throw new TheoremError('request', labelText('session.gate_expired', {}, profile.labels), {
|
|
798
|
+
copy: { key: 'session.gate_expired' },
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
if (held.state === 'uncertain')
|
|
802
|
+
throw new TheoremError('unavailable', labelText('tool.execution_uncertain', {}, profile.labels), {
|
|
803
|
+
copy: { key: 'tool.execution_uncertain' },
|
|
804
|
+
});
|
|
805
|
+
if (gateLapsed(held)) {
|
|
806
|
+
if (isReleased(held))
|
|
807
|
+
lapseReleased(callId, held);
|
|
808
|
+
else
|
|
809
|
+
await runHeld(callId, held, held.arguments, resumeForAnswer({ decision: 'abandon' }), {});
|
|
810
|
+
throw gateExpiredError(callId);
|
|
811
|
+
}
|
|
812
|
+
return held;
|
|
813
|
+
};
|
|
814
|
+
/** The model's call as the host is shown it: its arguments as the tool gets them. */
|
|
815
|
+
const asToolGetsIt = (event) => {
|
|
816
|
+
if (event.type !== 'tool' || event.tool.phase !== undefined)
|
|
817
|
+
return event;
|
|
818
|
+
const { name, arguments: input } = event.tool;
|
|
819
|
+
const shown = shownToolArguments({
|
|
820
|
+
tools: args.tools,
|
|
821
|
+
profile,
|
|
822
|
+
name,
|
|
823
|
+
input,
|
|
824
|
+
scope: gate.context,
|
|
825
|
+
});
|
|
826
|
+
return { ...event, tool: { ...event.tool, arguments: shown } };
|
|
827
|
+
};
|
|
828
|
+
const deliverBatch = async function* (item) {
|
|
829
|
+
const identities = new Map();
|
|
830
|
+
const events = item.events.map((event) => {
|
|
831
|
+
if (event.type !== 'tool')
|
|
832
|
+
return event;
|
|
833
|
+
const { tool } = event;
|
|
834
|
+
if (tool.phase === undefined) {
|
|
835
|
+
if (calls.has(tool.callId) || identities.has(tool.callId)) {
|
|
836
|
+
throw new TheoremError('bad_response', labelText('provider.duplicate_call', {}, profile.labels), { cause: { code: 'duplicate_pending_call' } });
|
|
837
|
+
}
|
|
838
|
+
identities.set(tool.callId, crypto.randomUUID());
|
|
839
|
+
}
|
|
840
|
+
const operationId = identities.get(tool.callId) ?? calls.get(tool.callId)?.operationId;
|
|
841
|
+
return { ...event, tool: { ...tool, operationId } };
|
|
842
|
+
});
|
|
843
|
+
holdCalls(events);
|
|
844
|
+
// why: Usage is held per response and emitted once, reported or estimated, by `settle`.
|
|
845
|
+
const hostEvents = hostEventsOf(events.filter((ev) => ev.type !== 'tokens'), snapshot).map(asToolGetsIt);
|
|
846
|
+
const gateStart = performance.now();
|
|
847
|
+
const gated = await applyOutbound(gate, hostEvents, item.turnPhase, () => {
|
|
848
|
+
withholdClose = true;
|
|
849
|
+
});
|
|
850
|
+
// why: An abort only drops what the gate held; there is no check to time.
|
|
851
|
+
if (item.turnPhase !== 'abort')
|
|
852
|
+
trace.outboundTime(performance.now() - gateStart);
|
|
853
|
+
for (const ev of gated) {
|
|
854
|
+
if (ev.type === 'guardrail')
|
|
855
|
+
trace.outbound(ev);
|
|
856
|
+
}
|
|
857
|
+
recordLiveHistory(gated, recordUserText, recordAssistantText);
|
|
858
|
+
const sealed = await sealLiveCheckpoints(gated, history, calls, profile.providerContinuation?.history !== 'stateless');
|
|
859
|
+
const doneBatch = yield* yieldLiveNonDoneEvents(sealed, includeMatch);
|
|
860
|
+
const tokens = await trace.settle();
|
|
861
|
+
if (tokens)
|
|
862
|
+
yield tokens;
|
|
863
|
+
const interrupted = doneBatch.some((ev) => ev.type === 'done' && ev.interrupted);
|
|
864
|
+
const completeBoundary = item.turnPhase === 'complete' || item.turnPhase === 'abort' || doneBatch.length > 0;
|
|
865
|
+
// why: Boundary batches (`interactionStatus: IDLE`, or bare `turnComplete` when the
|
|
866
|
+
// provider sends no status) often have no folded `done` — still end the cycle.
|
|
867
|
+
if (completeBoundary && cycle === 'open') {
|
|
868
|
+
const boundaryDone = boundaryDoneEvents(doneBatch, item.turnPhase, interrupted);
|
|
869
|
+
for await (const ev of endCycleAroundDone(boundaryDone)) {
|
|
870
|
+
yield projectGuardrailTurnEvent(stampDone(ev), includeMatch);
|
|
871
|
+
}
|
|
872
|
+
yield* drainPendingHostEvents(pendingHostEvents);
|
|
873
|
+
}
|
|
874
|
+
else if (doneBatch.length > 0) {
|
|
875
|
+
for (const ev of doneBatch) {
|
|
876
|
+
yield projectGuardrailTurnEvent(stampDone(ev), includeMatch);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
const streamToHost = async function* () {
|
|
881
|
+
// why: Who closes the socket when the loop ends: the host, unless THEOREM stops it.
|
|
882
|
+
let closer = 'host';
|
|
883
|
+
let thrown;
|
|
884
|
+
const provider = connection.batches();
|
|
885
|
+
// why: The batch being awaited; it stays pending across wakes, so no frame is read twice.
|
|
886
|
+
let nextBatch;
|
|
887
|
+
try {
|
|
888
|
+
while (true) {
|
|
889
|
+
nextBatch ??= provider.next();
|
|
890
|
+
// why: A tool the host ran, or a stage the session fired, reaches the host
|
|
891
|
+
// now: a model waiting on a call's result sends nothing meanwhile.
|
|
892
|
+
const woke = await Promise.race([nextBatch, hostEventQueued()]);
|
|
893
|
+
throwIfAborted(signal);
|
|
894
|
+
if (woke === 'queued') {
|
|
895
|
+
yield* drainPendingHostEvents(pendingHostEvents);
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
nextBatch = undefined;
|
|
899
|
+
if (woke.done)
|
|
900
|
+
break;
|
|
901
|
+
const item = woke.value;
|
|
902
|
+
trace.receive(item);
|
|
903
|
+
yield* drainPendingHostEvents(pendingHostEvents);
|
|
904
|
+
if (item.type === 'closed') {
|
|
905
|
+
if (item.goAway)
|
|
906
|
+
yield sessionEndedEvent(item, item.goAway, profile.labels);
|
|
907
|
+
else if (item.error)
|
|
908
|
+
yield toErrorEvent(item.error);
|
|
909
|
+
break;
|
|
910
|
+
}
|
|
911
|
+
if (item.type === 'error') {
|
|
912
|
+
closer = 'theorem';
|
|
913
|
+
yield toErrorEvent(item.error);
|
|
914
|
+
break;
|
|
915
|
+
}
|
|
916
|
+
if (item.type === 'row') {
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
yield* deliverBatch(item);
|
|
920
|
+
if (withholdClose) {
|
|
921
|
+
closer = 'theorem';
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
yield* drainPendingHostEvents(pendingHostEvents);
|
|
926
|
+
}
|
|
927
|
+
catch (err) {
|
|
928
|
+
thrown = err;
|
|
929
|
+
throw err;
|
|
930
|
+
}
|
|
931
|
+
finally {
|
|
932
|
+
closed = true;
|
|
933
|
+
stopLapses();
|
|
934
|
+
if (withholdClose)
|
|
935
|
+
closeSocket(1011, 'guardrail withheld', closer);
|
|
936
|
+
else
|
|
937
|
+
closeSocket(1000, 'session-closed', closer);
|
|
938
|
+
// why: The socket is closed, so a batch still awaited resolves and the provider can finish.
|
|
939
|
+
await provider.return(undefined);
|
|
940
|
+
await trace.close({ thrown });
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
const session = {
|
|
944
|
+
profileId: profile.id,
|
|
945
|
+
canary,
|
|
946
|
+
async *events() {
|
|
947
|
+
for await (const raw of streamToHost()) {
|
|
948
|
+
const event = withPublicLabels(raw, profile.labels);
|
|
949
|
+
trace.delivered(event);
|
|
950
|
+
yield event;
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
sendAudio(audio) {
|
|
954
|
+
return withIngress(async () => {
|
|
955
|
+
if (!audio.data)
|
|
956
|
+
return;
|
|
957
|
+
assertLiveIngress(profile, 'audio');
|
|
958
|
+
const opened = await openCycleIfNeeded();
|
|
959
|
+
if (opened.aborted)
|
|
960
|
+
return;
|
|
961
|
+
sendInput({ type: 'audio', mimeType: audio.mimeType, data: audio.data });
|
|
962
|
+
});
|
|
963
|
+
},
|
|
964
|
+
sendVideo(video) {
|
|
965
|
+
return withIngress(async () => {
|
|
966
|
+
assertLiveIngress(profile, 'video');
|
|
967
|
+
const opened = await openCycleIfNeeded();
|
|
968
|
+
if (opened.aborted)
|
|
969
|
+
return;
|
|
970
|
+
sendInput({ type: 'video', mimeType: video.mimeType, data: video.data });
|
|
971
|
+
});
|
|
972
|
+
},
|
|
973
|
+
sendText(text) {
|
|
974
|
+
return withIngress(async () => {
|
|
975
|
+
assertLiveIngress(profile, 'text');
|
|
976
|
+
const opened = await openCycleIfNeeded();
|
|
977
|
+
if (opened.aborted)
|
|
978
|
+
return;
|
|
979
|
+
ingestPreparedLiveText(text);
|
|
980
|
+
});
|
|
981
|
+
},
|
|
982
|
+
sendContext(context) {
|
|
983
|
+
return withIngress(() => {
|
|
984
|
+
landContext(context);
|
|
985
|
+
return Promise.resolve();
|
|
986
|
+
});
|
|
987
|
+
},
|
|
988
|
+
async executeTool({ callId, operationId, decision, input, secret, credentials, host, page, }) {
|
|
989
|
+
assertOpen();
|
|
990
|
+
const held = await takeHeld(callId, operationId);
|
|
991
|
+
const gate = typeof held.state === 'object' ? held.state.gate : undefined;
|
|
992
|
+
if (!gate) {
|
|
993
|
+
if (decision !== undefined || input !== undefined || secret !== undefined) {
|
|
994
|
+
throw new TheoremError('request', `call ${callId} is not waiting on a gate; it takes no decision, input, or secret`);
|
|
995
|
+
}
|
|
996
|
+
return await runHeld(callId, held, held.arguments, undefined, { credentials, host, page });
|
|
997
|
+
}
|
|
998
|
+
if (decision === undefined) {
|
|
999
|
+
throw new TheoremError('request', `call ${callId} is waiting on a gate; answer it with a decision`);
|
|
1000
|
+
}
|
|
1001
|
+
const answered = answerGatedCall({ callId, decision, input, secret }, {
|
|
1002
|
+
name: held.name,
|
|
1003
|
+
arguments: gate.preparedArguments ?? held.arguments,
|
|
1004
|
+
permission: gate.permission,
|
|
1005
|
+
gateKind: gate.kind,
|
|
1006
|
+
...(gate.kind === 'auth' ? { auth: gate.authChallenge } : {}),
|
|
1007
|
+
}, sessionPermissions ?? []);
|
|
1008
|
+
if (answered.typed) {
|
|
1009
|
+
await (credentials ?? sessionCredentials).set(answered.typed.slot, answered.typed.credential);
|
|
1010
|
+
}
|
|
1011
|
+
sessionPermissions = answered.sessionPermissions;
|
|
1012
|
+
return await runHeld(callId, held, answered.input, answered.resume, {
|
|
1013
|
+
credentials,
|
|
1014
|
+
host,
|
|
1015
|
+
page,
|
|
1016
|
+
});
|
|
1017
|
+
},
|
|
1018
|
+
answerToolCall({ callId, events }) {
|
|
1019
|
+
assertOpen();
|
|
1020
|
+
const held = calls.get(callId);
|
|
1021
|
+
if (!held || (held.state !== 'open' && !isReleased(held))) {
|
|
1022
|
+
throw new TheoremError('request', `call ${callId} is not waiting for an answer`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
1023
|
+
}
|
|
1024
|
+
if (gateLapsed(held)) {
|
|
1025
|
+
lapseReleased(callId, held);
|
|
1026
|
+
throw gateExpiredError(callId);
|
|
1027
|
+
}
|
|
1028
|
+
let settled;
|
|
1029
|
+
for (const ev of events) {
|
|
1030
|
+
if (ev.type !== 'tool')
|
|
1031
|
+
continue;
|
|
1032
|
+
if (ev.tool.phase === undefined || ev.tool.callId !== callId) {
|
|
1033
|
+
throw new TheoremError('request', `the run answering call ${callId} carries a call of its own (${ev.tool.callId})`);
|
|
1034
|
+
}
|
|
1035
|
+
if (ev.tool.operationId !== held.operationId || ev.tool.name !== held.name) {
|
|
1036
|
+
throw new TheoremError('request', labelText('session.gate_expired', {}, profile.labels), {
|
|
1037
|
+
copy: { key: 'session.gate_expired' },
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
if (isSettledPhase(ev.tool))
|
|
1041
|
+
settled = ev.tool;
|
|
1042
|
+
}
|
|
1043
|
+
if (!settled) {
|
|
1044
|
+
throw new TheoremError('request', `the run answering call ${callId} settled nothing`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
1045
|
+
}
|
|
1046
|
+
const forward = () => {
|
|
1047
|
+
for (const ev of events) {
|
|
1048
|
+
if (ev.type !== 'done')
|
|
1049
|
+
enqueuePending(ev);
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
if (settled.phase === 'gate') {
|
|
1053
|
+
forward();
|
|
1054
|
+
releaseGated(callId, held, isReleased(held), settled.gate, settled.readBack);
|
|
1055
|
+
return { gated: settled.gate };
|
|
1056
|
+
}
|
|
1057
|
+
const { readBack } = settled;
|
|
1058
|
+
if (readBack === undefined) {
|
|
1059
|
+
throw new TheoremError('request', `the run answering call ${callId} carries no readBack`); // labels-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
|
|
1060
|
+
}
|
|
1061
|
+
forward();
|
|
1062
|
+
settleHeld(callId, held, readBack);
|
|
1063
|
+
return settled.phase === 'complete'
|
|
1064
|
+
? { outputRaw: settled.output, ...(settled.awaiting ? { awaiting: true } : {}) }
|
|
1065
|
+
: { failure: settled.failure };
|
|
1066
|
+
},
|
|
1067
|
+
async close(reason = 'session-closed') {
|
|
1068
|
+
if (!closed) {
|
|
1069
|
+
closed = true;
|
|
1070
|
+
stopLapses();
|
|
1071
|
+
closeSocket(1000, reason, 'host');
|
|
1072
|
+
}
|
|
1073
|
+
// why: The session record is sealed here; frames the host reads after closing are not in it.
|
|
1074
|
+
await connection.flush?.();
|
|
1075
|
+
return trace.close({});
|
|
1076
|
+
},
|
|
1077
|
+
};
|
|
1078
|
+
const { initialContext, opening } = args;
|
|
1079
|
+
if (args.openInitialCycle || initialContext || opening) {
|
|
1080
|
+
void withIngress(async () => {
|
|
1081
|
+
if (initialContext)
|
|
1082
|
+
landContext(initialContext);
|
|
1083
|
+
if (!args.openInitialCycle && !opening)
|
|
1084
|
+
return;
|
|
1085
|
+
const opened = await openCycleIfNeeded();
|
|
1086
|
+
if (opening && !opened.aborted) {
|
|
1087
|
+
sendInput({ type: 'text', text: opening });
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
return session;
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* The session record is written however the session ends, including when opening fails. An unknown
|
|
1095
|
+
* profile fails to open and is recorded under the standard observability policy.
|
|
1096
|
+
*/
|
|
1097
|
+
export async function runSessionInRegistry(registry, req, options, sinkOverride) {
|
|
1098
|
+
const trace = startLiveTrace(req, registry.profiles.find(req.profile), sinkOverride);
|
|
1099
|
+
try {
|
|
1100
|
+
return await openTracedSession(registry, req, options, trace);
|
|
1101
|
+
}
|
|
1102
|
+
catch (err) {
|
|
1103
|
+
await trace.close({ thrown: err });
|
|
1104
|
+
throw err;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
async function openTracedSession(registry, req, options, trace) {
|
|
1108
|
+
const gateTtlMs = resolveGateTtlMs('runSession', options.gateTtlMs);
|
|
1109
|
+
const turnReq = toTurnRequest(req);
|
|
1110
|
+
const { request: safe, events: requestEvents, refusal, } = sanitizeTurnRequestWithEvents(turnReq, registry.profiles.get(turnReq.profile));
|
|
1111
|
+
if (refusal)
|
|
1112
|
+
throw refusal;
|
|
1113
|
+
throwIfAborted(safe.signal);
|
|
1114
|
+
const { profile, generation: gen0 } = resolveTurnInRegistry(registry, safe);
|
|
1115
|
+
assertLiveProfile(profile);
|
|
1116
|
+
if (req.context)
|
|
1117
|
+
assertSessionContext(profile, req.context);
|
|
1118
|
+
if (req.snapshot) {
|
|
1119
|
+
gen0.tools = sessionSnapshotWithinAllow(profile, req.snapshot);
|
|
1120
|
+
}
|
|
1121
|
+
gen0.builtins = gen0.tools.builtins;
|
|
1122
|
+
let generation = applyVoiceOverride(gen0, req.voice);
|
|
1123
|
+
const hasInitialInput = Boolean(req.input && req.input.length > 0);
|
|
1124
|
+
generation = applyInitialInput(generation, req.input);
|
|
1125
|
+
const system = bindSystem(generation.resolvedSystem, [
|
|
1126
|
+
canaryNote(generation.canary, profile.labels),
|
|
1127
|
+
userDataNote(profile.labels),
|
|
1128
|
+
profile.inputs?.context ? contextNote(profile.labels) : '',
|
|
1129
|
+
]);
|
|
1130
|
+
const completeReq = {
|
|
1131
|
+
...providerCompleteRequest(registry.tools, generation, system.text),
|
|
1132
|
+
signal: safe.signal,
|
|
1133
|
+
tapUpstream: trace.sent,
|
|
1134
|
+
};
|
|
1135
|
+
const binding = profile.models[generation.model];
|
|
1136
|
+
trace.bind({
|
|
1137
|
+
request: completeReq,
|
|
1138
|
+
generation,
|
|
1139
|
+
binding,
|
|
1140
|
+
system: system.text,
|
|
1141
|
+
canary: generation.canary,
|
|
1142
|
+
});
|
|
1143
|
+
const givenUrls = givenUrlSets();
|
|
1144
|
+
addRequestUrls(givenUrls, completeReq);
|
|
1145
|
+
addHistoryUrls(givenUrls, req.history ?? []);
|
|
1146
|
+
const destinations = turnDestinations();
|
|
1147
|
+
addRequestDestinations(destinations, completeReq, givenUrls.own);
|
|
1148
|
+
addHistoryDestinations(destinations, req.history ?? [], givenUrls.own);
|
|
1149
|
+
const gate = await createLiveOutboundGateSession(profile, generation.canary || undefined, system.private, givenUrls, ownToolsOf(registry.tools, profile));
|
|
1150
|
+
assertHistoryKept(profile.providerContinuation, req.providerState, req.history);
|
|
1151
|
+
const definition = registry.providers.require(binding.provider, binding.apiId);
|
|
1152
|
+
const connection = await openRegisteredLiveSession(definition, { ...binding }, completeReq, options, req.providerState, profile.providerContinuation);
|
|
1153
|
+
trace.setup(connection.setup);
|
|
1154
|
+
return buildLiveSession({
|
|
1155
|
+
execution: options.execution,
|
|
1156
|
+
pageExecution: options.pageExecution,
|
|
1157
|
+
tools: registry.tools,
|
|
1158
|
+
profile,
|
|
1159
|
+
canary: generation.canary,
|
|
1160
|
+
connection,
|
|
1161
|
+
gate,
|
|
1162
|
+
givenUrls,
|
|
1163
|
+
destinations,
|
|
1164
|
+
signal: safe.signal,
|
|
1165
|
+
onStage: req.onStage,
|
|
1166
|
+
host: req.host,
|
|
1167
|
+
credentials: req.credentials,
|
|
1168
|
+
resolveHost: req.resolveHost,
|
|
1169
|
+
sessionPermissions: req.sessionPermissions,
|
|
1170
|
+
path: req.path,
|
|
1171
|
+
snapshot: gen0.tools,
|
|
1172
|
+
historySeed: req.history,
|
|
1173
|
+
openInitialCycle: hasInitialInput,
|
|
1174
|
+
requestEvents,
|
|
1175
|
+
initialContext: req.context,
|
|
1176
|
+
opening: hasInitialInput ? undefined : openingPrompt(profile, req, safe.input?.slots),
|
|
1177
|
+
trace,
|
|
1178
|
+
agents: sessionAgentCaller(registry, req, options, trace.agentCanaries),
|
|
1179
|
+
gateTtlMs,
|
|
1180
|
+
signInGate: options.signInGate ?? 'hold',
|
|
1181
|
+
});
|
|
1182
|
+
}
|