@pikku/core 0.12.84 → 0.12.88
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/CHANGELOG.md +503 -0
- package/dist/bootstrap-compat/root.d.ts +20 -0
- package/dist/classification/column-form.d.ts +32 -0
- package/dist/classification/column-form.js +42 -0
- package/dist/classification/data-classification.d.ts +64 -0
- package/dist/classification/index.d.ts +13 -0
- package/dist/classification/index.js +2 -0
- package/dist/classification/secret-value.d.ts +56 -0
- package/dist/classification/secret-value.js +46 -0
- package/dist/crypto-utils.d.ts +1 -1
- package/dist/dev/hot-reload.d.ts +1 -1
- package/dist/dev/hot-reload.js +1 -1
- package/dist/ecosystem/agent-scorer.d.ts +1 -1
- package/dist/ecosystem/agent.d.ts +4 -1
- package/dist/ecosystem/channel/local.d.ts +2 -0
- package/dist/ecosystem/channel/local.js +2 -0
- package/dist/ecosystem/channel/serverless.d.ts +9 -0
- package/dist/ecosystem/channel/serverless.js +1 -0
- package/dist/ecosystem/channel.d.ts +3 -0
- package/dist/ecosystem/channel.js +1 -0
- package/dist/ecosystem/cli.d.ts +1 -1
- package/dist/ecosystem/cli.js +1 -1
- package/dist/ecosystem/credential.d.ts +1 -0
- package/dist/ecosystem/crypto-utils.d.ts +8 -0
- package/dist/ecosystem/crypto-utils.js +1 -0
- package/dist/ecosystem/dev.d.ts +1 -0
- package/dist/ecosystem/dev.js +1 -0
- package/{src/ecosystem/testing.ts → dist/ecosystem/errors.d.ts} +3 -3
- package/dist/ecosystem/errors.js +2 -0
- package/dist/ecosystem/function.d.ts +1 -1
- package/dist/ecosystem/function.js +1 -1
- package/dist/ecosystem/http.d.ts +3 -1
- package/dist/ecosystem/http.js +3 -1
- package/dist/ecosystem/mcp.d.ts +1 -1
- package/dist/ecosystem/mcp.js +1 -1
- package/dist/ecosystem/middleware.d.ts +2 -1
- package/dist/ecosystem/middleware.js +2 -1
- package/dist/ecosystem/persona.d.ts +4 -2
- package/dist/ecosystem/persona.js +2 -1
- package/dist/ecosystem/queue.d.ts +4 -2
- package/dist/ecosystem/queue.js +3 -1
- package/dist/ecosystem/role.d.ts +1 -0
- package/dist/ecosystem/rpc.d.ts +2 -0
- package/dist/ecosystem/rpc.js +1 -0
- package/dist/ecosystem/scenario.d.ts +8 -2
- package/dist/ecosystem/scenario.js +5 -1
- package/dist/ecosystem/scheduler.d.ts +2 -1
- package/dist/ecosystem/scheduler.js +2 -1
- package/dist/ecosystem/schema.d.ts +2 -1
- package/dist/ecosystem/schema.js +1 -1
- package/dist/ecosystem/scope.d.ts +2 -0
- package/dist/ecosystem/scope.js +1 -0
- package/dist/ecosystem/secret.d.ts +2 -0
- package/dist/ecosystem/secret.js +1 -0
- package/dist/ecosystem/services/local-content.d.ts +2 -1
- package/dist/ecosystem/services/local-content.js +1 -1
- package/dist/ecosystem/services/local-meta.d.ts +1 -0
- package/dist/ecosystem/services/local-meta.js +1 -0
- package/dist/ecosystem/services.d.ts +11 -3
- package/dist/ecosystem/services.js +7 -1
- package/dist/ecosystem/trigger.d.ts +1 -0
- package/dist/ecosystem/trigger.js +1 -1
- package/dist/ecosystem/types.d.ts +6 -3
- package/dist/ecosystem/types.js +3 -2
- package/dist/ecosystem/variable.d.ts +1 -0
- package/dist/ecosystem/virtual-user.d.ts +3 -0
- package/dist/ecosystem/virtual-user.js +3 -0
- package/dist/ecosystem/workflow.d.ts +2 -0
- package/dist/ecosystem/workflow.js +2 -0
- package/dist/errors/index.d.ts +8 -0
- package/dist/errors/index.js +1 -0
- package/dist/errors/serialized-error.d.ts +7 -0
- package/dist/function/function-meta.types.d.ts +139 -0
- package/dist/function/function-runner.d.ts +3 -2
- package/dist/function/function-runner.js +31 -11
- package/dist/function/functions.types.d.ts +10 -2
- package/dist/function/index.d.ts +8 -2
- package/dist/function/index.js +4 -1
- package/dist/function/pikku-request.d.ts +8 -0
- package/dist/function/pikku-request.js +15 -0
- package/dist/middleware/auth-apikey.d.ts +2 -10
- package/dist/middleware/auth-apikey.js +1 -1
- package/dist/middleware/auth-bearer.d.ts +2 -10
- package/dist/middleware/auth-bearer.js +1 -1
- package/dist/middleware/auth-cookie.d.ts +2 -10
- package/dist/middleware/auth-cookie.js +1 -1
- package/dist/middleware/cors.d.ts +5 -11
- package/dist/middleware/cors.js +13 -6
- package/dist/middleware/index.d.ts +3 -0
- package/dist/middleware/index.js +1 -0
- package/dist/middleware/middleware-factories.d.ts +9 -0
- package/dist/middleware/middleware-factories.js +19 -0
- package/dist/middleware/middleware.types.d.ts +27 -0
- package/dist/middleware/middleware.types.js +1 -0
- package/dist/middleware/remote-auth.d.ts +2 -2
- package/dist/middleware/remote-auth.js +1 -1
- package/dist/middleware/telemetry.d.ts +4 -20
- package/dist/middleware/telemetry.js +1 -1
- package/dist/middleware-runner.d.ts +2 -1
- package/dist/pikku-state.d.ts +2 -1
- package/dist/services/audit-service.d.ts +1 -1
- package/dist/services/credential-wire-service.js +1 -1
- package/dist/services/email-service.d.ts +1 -1
- package/dist/services/file-scenario-run-store.d.ts +58 -0
- package/dist/services/file-scenario-run-store.js +214 -0
- package/dist/services/in-memory-workflow-service.d.ts +1 -1
- package/dist/services/index.d.ts +5 -1
- package/dist/services/index.js +3 -0
- package/dist/services/local-secrets.d.ts +1 -1
- package/dist/services/local-secrets.js +1 -1
- package/dist/services/logger.d.ts +7 -7
- package/dist/services/meta-service.d.ts +2 -1
- package/dist/services/scoped-secret-service.d.ts +1 -1
- package/dist/services/secret-service.d.ts +1 -1
- package/dist/services/typed-secret-service.d.ts +16 -2
- package/dist/services/typed-secret-service.js +5 -0
- package/dist/services/webhook-service.d.ts +1 -1
- package/dist/services/workflow-service.d.ts +2 -1
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +1 -0
- package/dist/types/core.types.d.ts +30 -180
- package/dist/types/core.types.js +1 -19
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +1 -0
- package/dist/types/state.types.d.ts +3 -1
- package/dist/utils.d.ts +13 -0
- package/dist/utils.js +2 -0
- package/dist/wirings/addon/addon-runner.d.ts +17 -0
- package/dist/wirings/addon/addon-runner.js +150 -0
- package/dist/wirings/addon/index.d.ts +6 -0
- package/dist/wirings/addon/index.js +3 -0
- package/dist/wirings/addon/remote-addon-auth.d.ts +16 -0
- package/dist/wirings/addon/remote-addon-auth.js +39 -0
- package/dist/wirings/addon/wire-addon.d.ts +80 -0
- package/dist/wirings/addon/wire-addon.js +120 -0
- package/dist/wirings/addon/wire-remote-addon.d.ts +22 -0
- package/dist/wirings/addon/wire-remote-addon.js +11 -0
- package/dist/wirings/agent/agent-memory.d.ts +11 -0
- package/dist/wirings/agent/agent-memory.js +17 -2
- package/dist/wirings/agent/agent-prepare.d.ts +3 -3
- package/dist/wirings/agent/agent-prepare.js +1 -1
- package/dist/wirings/agent/agent-runner.js +2 -3
- package/dist/wirings/agent/agent-stream.js +117 -93
- package/dist/wirings/agent/agent-utils.d.ts +13 -0
- package/dist/wirings/agent/agent-utils.js +16 -0
- package/dist/wirings/agent/agent.types.d.ts +12 -10
- package/dist/wirings/agent/index.d.ts +1 -1
- package/dist/wirings/agent/voice-input.js +1 -1
- package/dist/wirings/agent/voice-output.js +1 -1
- package/dist/wirings/agent-scorer/agent-scorer-judge.d.ts +13 -0
- package/dist/wirings/agent-scorer/agent-scorer-judge.js +52 -0
- package/dist/wirings/agent-scorer/agent-scorer.d.ts +7 -1
- package/dist/wirings/agent-scorer/agent-scorer.js +1 -0
- package/dist/wirings/agent-scorer/agent-scorer.types.d.ts +20 -0
- package/dist/wirings/agent-scorer/index.d.ts +1 -1
- package/dist/wirings/channel/channel-common.d.ts +2 -1
- package/dist/wirings/channel/channel-middleware-runner.d.ts +2 -1
- package/dist/wirings/channel/channel.types.d.ts +1 -1
- package/dist/wirings/channel/serverless/index.d.ts +1 -0
- package/dist/wirings/cli/channel/index.d.ts +1 -1
- package/dist/wirings/cli/cli-runner.d.ts +2 -1
- package/dist/wirings/cli/cli-runner.js +4 -1
- package/dist/wirings/cli/cli.types.d.ts +21 -2
- package/dist/wirings/cli/command-parser.js +157 -23
- package/dist/wirings/gateway/gateway.types.d.ts +2 -1
- package/dist/wirings/http/http-runner.d.ts +1 -1
- package/dist/wirings/http/http-runner.js +1 -1
- package/dist/wirings/http/http.types.d.ts +2 -2
- package/dist/wirings/http/index.d.ts +1 -0
- package/dist/wirings/http/index.js +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.js +1 -1
- package/dist/wirings/http/pikku-fetch-http-response.js +2 -2
- package/dist/wirings/mcp/mcp.types.d.ts +1 -1
- package/dist/wirings/persona/index.d.ts +1 -1
- package/dist/wirings/persona/index.js +1 -1
- package/dist/wirings/persona/persona.types.d.ts +14 -0
- package/dist/wirings/queue/queue.types.d.ts +1 -1
- package/dist/wirings/queue/signed-queue-service.d.ts +1 -1
- package/dist/wirings/rpc/index.d.ts +0 -6
- package/dist/wirings/rpc/index.js +0 -3
- package/dist/wirings/rpc/remote-addon-auth.d.ts +1 -1
- package/dist/wirings/rpc/remote-addon-auth.js +1 -1
- package/dist/wirings/rpc/rpc-runner.js +2 -2
- package/dist/wirings/rpc/wire-addon.d.ts +1 -1
- package/dist/wirings/scheduler/scheduler.types.d.ts +2 -1
- package/dist/wirings/scope/index.d.ts +1 -0
- package/dist/wirings/scope/index.js +1 -0
- package/dist/wirings/secret/secret.types.d.ts +10 -0
- package/dist/wirings/secret/validate-secret-definitions.js +2 -0
- package/dist/wirings/variable/validate-variable-definitions.js +2 -0
- package/dist/wirings/variable/variable.types.d.ts +15 -0
- package/dist/wirings/virtual-user/index.d.ts +1 -1
- package/dist/wirings/virtual-user/index.js +1 -1
- package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +1 -1
- package/dist/wirings/virtual-user/virtual-user-derive.d.ts +1 -1
- package/dist/wirings/workflow/graph/graph-node.d.ts +9 -2
- package/dist/wirings/workflow/graph/graph-node.js +2 -0
- package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -0
- package/dist/wirings/workflow/graph/graph-runner.js +283 -31
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +16 -1
- package/dist/wirings/workflow/index.d.ts +2 -0
- package/dist/wirings/workflow/index.js +1 -0
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +15 -5
- package/dist/wirings/workflow/pikku-scenario-service.js +44 -16
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +22 -7
- package/dist/wirings/workflow/pikku-workflow-service.js +28 -29
- package/dist/wirings/workflow/run-timeline.d.ts +1 -1
- package/dist/wirings/workflow/scenario-run.types.d.ts +171 -0
- package/dist/wirings/workflow/scenario-run.types.js +1 -0
- package/dist/wirings/workflow/scenario-step-guards.d.ts +1 -2
- package/dist/wirings/workflow/scenario-step-guards.js +0 -8
- package/dist/wirings/workflow/scenario-step.types.d.ts +27 -12
- package/dist/wirings/workflow/workflow-approval.js +2 -1
- package/dist/wirings/workflow/workflow-run-engine.types.d.ts +2 -1
- package/dist/wirings/workflow/workflow-step-claim.d.ts +16 -0
- package/dist/wirings/workflow/workflow-step-claim.js +27 -0
- package/dist/wirings/workflow/workflow.types.d.ts +2 -1
- package/knowledge/decisions/index.md +2 -0
- package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +1 -1
- package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +1 -1
- package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-and-not-a-queued-job.md +2 -2
- package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +6 -6
- package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +1 -1
- package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +1 -1
- package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +1 -1
- package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +1 -1
- package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +1 -1
- package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +1 -1
- package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +1 -1
- package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +1 -1
- package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +1 -1
- package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +1 -1
- package/knowledge/decisions/internals/core-column-form-is-an-axis-of-its-own.md +4 -4
- package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +1 -1
- package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +3 -3
- package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +2 -2
- package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +2 -2
- package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +1 -1
- package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +1 -1
- package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +3 -3
- package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +1 -1
- package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +4 -4
- package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +1 -1
- package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +1 -1
- package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +1 -1
- package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +1 -1
- package/knowledge/decisions/internals/index.md +4 -2
- package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +1 -1
- package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +1 -1
- package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +1 -1
- package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +1 -1
- package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +1 -1
- package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +2 -2
- package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +5 -5
- package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +1 -1
- package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +1 -1
- package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +43 -19
- package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +1 -1
- package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +1 -1
- package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +3 -3
- package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +1 -1
- package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +1 -1
- package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +1 -1
- package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +1 -1
- package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +1 -1
- package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +1 -1
- package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +1 -1
- package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +1 -1
- package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +1 -1
- package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +9 -5
- package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +1 -1
- package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +2 -2
- package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +1 -1
- package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +1 -1
- package/knowledge/decisions/security/a-scaffold-flag-says-a-surface-exists-not-who-may-call-it.md +47 -0
- package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +1 -1
- package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +2 -3
- package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +1 -1
- package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +1 -1
- package/knowledge/decisions/security/agent-gate-requires-a-session-only-when-auth-is-true.md +1 -1
- package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +3 -3
- package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +4 -3
- package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +1 -1
- package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +2 -2
- package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +1 -1
- package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +2 -2
- package/knowledge/decisions/security/index.md +4 -1
- package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +1 -1
- package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +3 -3
- package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +1 -1
- package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +8 -2
- package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +1 -1
- package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +2 -2
- package/knowledge/index.md +3 -1
- package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +1 -1
- package/knowledge/questions/index.md +2 -0
- package/package.json +14 -13
- package/run-tests.sh +0 -0
- package/scripts/generate-api-report.d.mts +1 -1
- package/scripts/generate-api-report.mjs +67 -60
- package/scripts/generate-api-report.mts +11 -45
- package/src/api-report.test.ts +70 -1
- package/src/app-alias-surface.test.ts +179 -0
- package/src/app-leaf-surface.test.ts +217 -0
- package/src/bootstrap-compat/root.ts +20 -0
- package/src/{column-form.test.ts → classification/column-form.test.ts} +2 -2
- package/src/{data-classification.ts → classification/data-classification.ts} +1 -5
- package/src/classification/index.ts +38 -0
- package/src/{secret-value.test.ts → classification/secret-value.test.ts} +2 -2
- package/src/{secret-value.ts → classification/secret-value.ts} +1 -6
- package/src/crypto-utils.ts +1 -1
- package/src/dev/hot-reload.ts +1 -1
- package/src/dev/reload-meta.test.ts +1 -1
- package/src/errors/index.ts +9 -0
- package/src/errors/serialized-error.ts +9 -0
- package/src/factory-functions.test.ts +4 -2
- package/src/function/function-meta.types.ts +151 -0
- package/src/function/function-runner.test.ts +4 -2
- package/src/function/function-runner.ts +47 -14
- package/src/function/functions.types.ts +11 -7
- package/src/function/index.ts +24 -5
- package/src/function/list.types.ts +1 -3
- package/src/middleware/auth-apikey.ts +4 -2
- package/src/middleware/auth-bearer.test.ts +1 -1
- package/src/middleware/auth-bearer.ts +4 -2
- package/src/middleware/auth-cookie.ts +4 -1
- package/src/middleware/cors.test.ts +126 -5
- package/src/middleware/cors.ts +19 -8
- package/src/middleware/index.ts +16 -0
- package/src/middleware/middleware-factories.ts +62 -0
- package/src/middleware/middleware.types.ts +61 -0
- package/src/middleware/remote-auth.test.ts +1 -1
- package/src/middleware/remote-auth.ts +1 -1
- package/src/middleware/telemetry.ts +4 -2
- package/src/middleware-runner.test.ts +2 -3
- package/src/middleware-runner.ts +2 -2
- package/src/no-root-barrel.test.ts +114 -0
- package/src/permissions.ts +2 -4
- package/src/pikku-state.ts +3 -1
- package/src/public-surface.json +56 -241
- package/src/public-surface.json.README +3 -3
- package/src/public-surface.test.ts +5 -5
- package/src/remote.test.ts +1 -1
- package/src/removed-legacy-exports.test.ts +5 -1
- package/src/services/audit-service.ts +1 -1
- package/src/services/credential-wire-service.ts +1 -1
- package/src/services/email-service.ts +2 -4
- package/src/services/file-scenario-run-store.test.ts +349 -0
- package/src/services/file-scenario-run-store.ts +282 -0
- package/src/services/in-memory-workflow-service.ts +1 -1
- package/src/services/index.ts +13 -1
- package/src/services/local-content-request-handler.ts +1 -2
- package/src/services/local-secrets.ts +1 -1
- package/src/services/logger.ts +7 -7
- package/src/services/meta-service.ts +2 -2
- package/src/services/queue-webhook-service.test.ts +1 -1
- package/src/services/scoped-secret-service.ts +1 -1
- package/src/services/secret-service.ts +1 -1
- package/src/services/typed-secret-service.test.ts +52 -0
- package/src/services/typed-secret-service.ts +26 -2
- package/src/services/webhook-service.ts +1 -1
- package/src/services/workflow-service.ts +2 -1
- package/src/template-alias-surface.test.ts +240 -0
- package/src/testing/index.ts +1 -0
- package/src/types/core.types.ts +33 -285
- package/src/types/index.ts +25 -0
- package/src/types/state.types.ts +6 -5
- package/src/types/typed-credentials.test.ts +72 -0
- package/src/utils.ts +34 -0
- package/src/wirings/{rpc → addon}/addon-scopes.test.ts +1 -1
- package/src/wirings/addon/index.ts +12 -0
- package/src/wirings/{rpc → addon}/remote-addon-auth.ts +4 -1
- package/src/wirings/{rpc → addon}/wire-addon.ts +37 -2
- package/src/wirings/agent/agent-memory.test.ts +73 -0
- package/src/wirings/agent/agent-memory.ts +16 -2
- package/src/wirings/agent/agent-middleware.types.test.ts +41 -0
- package/src/wirings/agent/agent-prepare.ts +8 -5
- package/src/wirings/agent/agent-runner.ts +2 -3
- package/src/wirings/agent/agent-stream-delegate.test.ts +381 -0
- package/src/wirings/agent/agent-stream.ts +160 -81
- package/src/wirings/agent/agent-utils.ts +19 -0
- package/src/wirings/agent/agent.types.ts +12 -11
- package/src/wirings/agent/index.ts +1 -0
- package/src/wirings/agent/voice-input.ts +1 -1
- package/src/wirings/agent/voice-output.test.ts +2 -4
- package/src/wirings/agent/voice-output.ts +1 -1
- package/src/wirings/agent-scorer/agent-scorer-judge.test.ts +145 -0
- package/src/wirings/agent-scorer/agent-scorer-judge.ts +60 -0
- package/src/wirings/agent-scorer/agent-scorer.ts +8 -0
- package/src/wirings/agent-scorer/agent-scorer.types.ts +21 -0
- package/src/wirings/agent-scorer/index.ts +1 -0
- package/src/wirings/channel/channel-common.ts +4 -2
- package/src/wirings/channel/channel-handler.ts +2 -5
- package/src/wirings/channel/channel-middleware-runner.ts +1 -1
- package/src/wirings/channel/channel-rpc.types.ts +1 -5
- package/src/wirings/channel/channel.types.ts +1 -2
- package/src/wirings/channel/local/local-channel-runner.test.ts +1 -2
- package/src/wirings/channel/serverless/index.ts +1 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +2 -2
- package/src/wirings/cli/channel/index.ts +1 -3
- package/src/wirings/cli/cli-runner.test.ts +1 -1
- package/src/wirings/cli/cli-runner.ts +11 -7
- package/src/wirings/cli/cli.types.ts +24 -3
- package/src/wirings/cli/command-parser.test.ts +388 -1
- package/src/wirings/cli/command-parser.ts +189 -29
- package/src/wirings/gateway/gateway-runner.test.ts +1 -2
- package/src/wirings/gateway/gateway-runner.ts +3 -6
- package/src/wirings/gateway/gateway.types.ts +5 -4
- package/src/wirings/http/http-routes.test.ts +1 -2
- package/src/wirings/http/http-runner-addon-ref.test.ts +238 -0
- package/src/wirings/http/http-runner.test.ts +3 -8
- package/src/wirings/http/http-runner.ts +9 -3
- package/src/wirings/http/http.types.ts +3 -12
- package/src/wirings/http/index.ts +1 -0
- package/src/wirings/http/pikku-fetch-http-request.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-response.ts +3 -2
- package/src/wirings/mcp/mcp-runner.test.ts +1 -1
- package/src/wirings/mcp/mcp.types.ts +1 -2
- package/src/wirings/persona/index.ts +1 -4
- package/src/wirings/persona/persona.types.ts +14 -0
- package/src/wirings/queue/queue-identity.test.ts +1 -1
- package/src/wirings/queue/queue.types.ts +2 -6
- package/src/wirings/queue/signed-queue-service.ts +1 -1
- package/src/wirings/rpc/index.ts +0 -12
- package/src/wirings/rpc/rpc-runner.test.ts +4 -4
- package/src/wirings/rpc/rpc-runner.ts +3 -3
- package/src/wirings/scheduler/scheduler.types.ts +2 -5
- package/src/wirings/scope/index.ts +1 -0
- package/src/wirings/secret/secret.types.ts +10 -0
- package/src/wirings/secret/validate-secret-definitions.test.ts +23 -0
- package/src/wirings/secret/validate-secret-definitions.ts +2 -0
- package/src/wirings/variable/validate-variable-definitions.test.ts +24 -0
- package/src/wirings/variable/validate-variable-definitions.ts +2 -0
- package/src/wirings/variable/variable.types.ts +15 -0
- package/src/wirings/virtual-user/index.ts +1 -4
- package/src/wirings/virtual-user/prepare-virtual-user-run.ts +1 -1
- package/src/wirings/virtual-user/virtual-user-derive.test.ts +1 -1
- package/src/wirings/virtual-user/virtual-user-derive.ts +1 -1
- package/src/wirings/virtual-user/virtual-user.types.ts +1 -5
- package/src/wirings/workflow/graph/graph-foreach-queued.test.ts +569 -0
- package/src/wirings/workflow/graph/graph-foreach.test.ts +626 -0
- package/src/wirings/workflow/graph/graph-node.ts +16 -1
- package/src/wirings/workflow/graph/graph-runner.ts +387 -61
- package/src/wirings/workflow/graph/workflow-graph.types.ts +28 -3
- package/src/wirings/workflow/index.ts +8 -0
- package/src/wirings/workflow/pikku-scenario-service.ts +83 -20
- package/src/wirings/workflow/pikku-workflow-service.ts +37 -35
- package/src/wirings/workflow/run-timeline.ts +1 -1
- package/src/wirings/workflow/scenario-expectations.test.ts +28 -13
- package/src/wirings/workflow/scenario-run.types.ts +186 -0
- package/src/wirings/workflow/scenario-step-guards.ts +1 -14
- package/src/wirings/workflow/scenario-step.test.ts +32 -7
- package/src/wirings/workflow/scenario-step.types.ts +27 -12
- package/src/wirings/workflow/workflow-approval-policy.test.ts +117 -47
- package/src/wirings/workflow/workflow-approval.ts +2 -1
- package/src/wirings/workflow/workflow-run-engine.types.ts +2 -1
- package/src/wirings/workflow/workflow-step-claim.ts +46 -0
- package/src/wirings/workflow/workflow.types.ts +4 -12
- package/src/wirings-stay-decoupled.test.ts +6 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/tsconfig.type-tests.json +4 -2
- package/dist/ecosystem/ai-agent.d.ts +0 -18
- package/dist/ecosystem/ai-agent.js +0 -6
- package/dist/ecosystem/ai-scorer.d.ts +0 -12
- package/dist/ecosystem/ai-scorer.js +0 -5
- package/dist/services/ai-agent-runner-service.d.ts +0 -220
- package/dist/services/ai-run-state-service.d.ts +0 -29
- package/dist/services/ai-storage-service.d.ts +0 -18
- package/dist/services/in-memory-ai-run-state-service.d.ts +0 -19
- package/dist/services/in-memory-ai-run-state-service.js +0 -54
- package/dist/testing/service-tests/ai-storage-service-tests.d.ts +0 -3
- package/dist/testing/service-tests/ai-storage-service-tests.js +0 -302
- package/dist/wirings/ai-agent/agent-rpc.d.ts +0 -15
- package/dist/wirings/ai-agent/agent-rpc.js +0 -53
- package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -79
- package/dist/wirings/ai-agent/ai-agent-agui.js +0 -327
- package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +0 -58
- package/dist/wirings/ai-agent/ai-agent-finalize.js +0 -138
- package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +0 -35
- package/dist/wirings/ai-agent/ai-agent-helpers.js +0 -47
- package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +0 -153
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +0 -257
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +0 -42
- package/dist/wirings/ai-agent/ai-agent-memory.js +0 -333
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -16
- package/dist/wirings/ai-agent/ai-agent-model-config.js +0 -51
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +0 -117
- package/dist/wirings/ai-agent/ai-agent-prepare.js +0 -728
- package/dist/wirings/ai-agent/ai-agent-registry.d.ts +0 -16
- package/dist/wirings/ai-agent/ai-agent-registry.js +0 -51
- package/dist/wirings/ai-agent/ai-agent-runner.d.ts +0 -7
- package/dist/wirings/ai-agent/ai-agent-runner.js +0 -580
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +0 -41
- package/dist/wirings/ai-agent/ai-agent-stream.js +0 -1029
- package/dist/wirings/ai-agent/ai-agent-turn.d.ts +0 -57
- package/dist/wirings/ai-agent/ai-agent-turn.js +0 -82
- package/dist/wirings/ai-agent/ai-agent-utils.d.ts +0 -1
- package/dist/wirings/ai-agent/ai-agent-utils.js +0 -1
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +0 -625
- package/dist/wirings/ai-agent/index.d.ts +0 -12
- package/dist/wirings/ai-agent/index.js +0 -10
- package/dist/wirings/ai-agent/voice-input.d.ts +0 -69
- package/dist/wirings/ai-agent/voice-input.js +0 -150
- package/dist/wirings/ai-agent/voice-output.d.ts +0 -85
- package/dist/wirings/ai-agent/voice-output.js +0 -196
- package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +0 -26
- package/dist/wirings/ai-scorer/ai-scorer-grade.js +0 -33
- package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +0 -17
- package/dist/wirings/ai-scorer/ai-scorer-judge.js +0 -92
- package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +0 -15
- package/dist/wirings/ai-scorer/ai-scorer-live.js +0 -38
- package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +0 -18
- package/dist/wirings/ai-scorer/ai-scorer-registry.js +0 -46
- package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +0 -8
- package/dist/wirings/ai-scorer/ai-scorer-sampling.js +0 -31
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +0 -10
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +0 -40
- package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +0 -15
- package/dist/wirings/ai-scorer/ai-scorer-worker.js +0 -58
- package/dist/wirings/ai-scorer/ai-scorer.d.ts +0 -39
- package/dist/wirings/ai-scorer/ai-scorer.js +0 -40
- package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +0 -90
- package/dist/wirings/ai-scorer/ai-scorer.types.js +0 -4
- package/dist/wirings/ai-scorer/index.d.ts +0 -6
- package/dist/wirings/ai-scorer/index.js +0 -5
- package/src/ecosystem/agent-scorer.ts +0 -24
- package/src/ecosystem/agent.ts +0 -42
- package/src/ecosystem/channel/local.ts +0 -1
- package/src/ecosystem/channel.ts +0 -22
- package/src/ecosystem/cli/channel.ts +0 -25
- package/src/ecosystem/cli.ts +0 -21
- package/src/ecosystem/credential.ts +0 -10
- package/src/ecosystem/dev.ts +0 -12
- package/src/ecosystem/function.ts +0 -28
- package/src/ecosystem/gateway.ts +0 -4
- package/src/ecosystem/hmac.ts +0 -1
- package/src/ecosystem/http.ts +0 -22
- package/src/ecosystem/mcp.ts +0 -17
- package/src/ecosystem/middleware.ts +0 -27
- package/src/ecosystem/node-host-resolver.ts +0 -1
- package/src/ecosystem/node.ts +0 -5
- package/src/ecosystem/oauth2.ts +0 -4
- package/src/ecosystem/persona.ts +0 -30
- package/src/ecosystem/queue.ts +0 -23
- package/src/ecosystem/remote.ts +0 -9
- package/src/ecosystem/role.ts +0 -13
- package/src/ecosystem/rpc.ts +0 -7
- package/src/ecosystem/safe-fetch.ts +0 -8
- package/src/ecosystem/scenario.ts +0 -32
- package/src/ecosystem/scheduler.ts +0 -5
- package/src/ecosystem/schema.ts +0 -1
- package/src/ecosystem/scope.ts +0 -14
- package/src/ecosystem/secret.ts +0 -15
- package/src/ecosystem/services/istanbul-coverage.ts +0 -1
- package/src/ecosystem/services/local-content-request-handler.ts +0 -16
- package/src/ecosystem/services/local-content.ts +0 -1
- package/src/ecosystem/services/v8-coverage.ts +0 -1
- package/src/ecosystem/services.ts +0 -82
- package/src/ecosystem/trigger.ts +0 -7
- package/src/ecosystem/types.ts +0 -34
- package/src/ecosystem/variable.ts +0 -14
- package/src/ecosystem/virtual-user.ts +0 -48
- package/src/ecosystem/workflow.ts +0 -68
- package/src/ecosystem.ts +0 -40
- package/src/index.ts +0 -244
- /package/dist/{services/ai-agent-runner-service.js → bootstrap-compat/root.js} +0 -0
- /package/dist/{services/ai-run-state-service.js → classification/data-classification.js} +0 -0
- /package/dist/{services/ai-storage-service.js → errors/serialized-error.js} +0 -0
- /package/dist/{wirings/ai-agent/ai-agent.types.js → function/function-meta.types.js} +0 -0
- /package/src/{column-form.ts → classification/column-form.ts} +0 -0
- /package/src/{pikku-request.test.ts → function/pikku-request.test.ts} +0 -0
- /package/src/{pikku-request.ts → function/pikku-request.ts} +0 -0
- /package/src/wirings/{rpc → addon}/addon-auth-tags.test.ts +0 -0
- /package/src/wirings/{rpc → addon}/addon-runner.ts +0 -0
- /package/src/wirings/{rpc → addon}/addon-secrets.test.ts +0 -0
- /package/src/wirings/{rpc → addon}/wire-addon.test.ts +0 -0
- /package/src/wirings/{rpc → addon}/wire-remote-addon.ts +0 -0
|
@@ -1,1029 +0,0 @@
|
|
|
1
|
-
import { finalizeAgentRun, lastUserMessageText, } from './ai-agent-finalize.js';
|
|
2
|
-
import { pikkuState, getSingletonServices } from '../../pikku-state.js';
|
|
3
|
-
import { applyInputMiddleware } from './ai-agent-turn.js';
|
|
4
|
-
import { AIProviderNotConfiguredError } from '../../errors/errors.js';
|
|
5
|
-
import { randomUUID } from './ai-agent-utils.js';
|
|
6
|
-
import { SPOKEN_TRANSCRIPT } from './voice-input.js';
|
|
7
|
-
import { combineChannelMiddleware, wrapChannelWithMiddleware, } from '../channel/channel-middleware-runner.js';
|
|
8
|
-
import { resolveMemoryServices, loadContextMessages, trimMessages, getWorkingMemoryMiddleware, } from './ai-agent-memory.js';
|
|
9
|
-
import { prepareAgentRun, resolveAgent, buildInstructions, buildToolDefs, createScopedChannel, resolveOwnerResourceId, agentSessionScope, assertResourceOwner, assertResourcePrincipalOwner, assertAgentAuthorized, ToolApprovalRequired, ToolCredentialRequired, APPROVAL_REQUIRED, CREDENTIAL_REQUIRED, } from './ai-agent-prepare.js';
|
|
10
|
-
import { resolveModelConfig } from './ai-agent-model-config.js';
|
|
11
|
-
import { getInFlightTools, isAbortError, persistOrphanedToolResults, registerInterruptibleRun, signalRunInterrupt, trackInterruptNote, trackToolExecution, } from './ai-agent-interrupt.js';
|
|
12
|
-
function createPersistingChannel(parent, storage, threadId, logger) {
|
|
13
|
-
let fullText = '';
|
|
14
|
-
let stepText = '';
|
|
15
|
-
let stepGenerativeUI = null;
|
|
16
|
-
let stepToolCalls = [];
|
|
17
|
-
let stepToolResults = [];
|
|
18
|
-
const totalUsage = {
|
|
19
|
-
inputTokens: 0,
|
|
20
|
-
outputTokens: 0,
|
|
21
|
-
};
|
|
22
|
-
// Survives the per-step flush below, which clears its own buffers: the run
|
|
23
|
-
// record needs every call the run made, not just the last step's.
|
|
24
|
-
const runToolCalls = [];
|
|
25
|
-
const flushStep = async (opts) => {
|
|
26
|
-
if (!storage)
|
|
27
|
-
return;
|
|
28
|
-
const text = stepText;
|
|
29
|
-
const generativeUI = stepGenerativeUI;
|
|
30
|
-
const calls = stepToolCalls;
|
|
31
|
-
const results = stepToolResults;
|
|
32
|
-
stepText = '';
|
|
33
|
-
stepGenerativeUI = null;
|
|
34
|
-
stepToolCalls = [];
|
|
35
|
-
stepToolResults = [];
|
|
36
|
-
if (text || generativeUI != null || calls.length > 0) {
|
|
37
|
-
const assistantMsg = {
|
|
38
|
-
id: randomUUID(),
|
|
39
|
-
role: 'assistant',
|
|
40
|
-
content: generativeUI != null
|
|
41
|
-
? [
|
|
42
|
-
...(text ? [{ type: 'text', text }] : []),
|
|
43
|
-
{ type: 'generative-ui', spec: generativeUI },
|
|
44
|
-
]
|
|
45
|
-
: text || undefined,
|
|
46
|
-
toolCalls: calls.length > 0 ? calls : undefined,
|
|
47
|
-
...(opts?.interrupted ? { interrupted: true } : {}),
|
|
48
|
-
createdAt: new Date(),
|
|
49
|
-
};
|
|
50
|
-
const messages = [assistantMsg];
|
|
51
|
-
if (results.length > 0) {
|
|
52
|
-
messages.push({
|
|
53
|
-
id: randomUUID(),
|
|
54
|
-
role: 'tool',
|
|
55
|
-
toolResults: results,
|
|
56
|
-
createdAt: new Date(),
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
await storage.saveMessages(threadId, messages);
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
const flushDetached = () => {
|
|
63
|
-
void flushStep().catch((error) => {
|
|
64
|
-
logger?.error('Failed to persist agent messages', {
|
|
65
|
-
threadId,
|
|
66
|
-
error,
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
};
|
|
70
|
-
const runToolCallIndex = new Map();
|
|
71
|
-
const channel = {
|
|
72
|
-
channelId: parent.channelId,
|
|
73
|
-
openingData: parent.openingData,
|
|
74
|
-
get state() {
|
|
75
|
-
return parent.state;
|
|
76
|
-
},
|
|
77
|
-
get fullText() {
|
|
78
|
-
return fullText;
|
|
79
|
-
},
|
|
80
|
-
get totalUsage() {
|
|
81
|
-
return totalUsage;
|
|
82
|
-
},
|
|
83
|
-
get runToolCalls() {
|
|
84
|
-
return runToolCalls;
|
|
85
|
-
},
|
|
86
|
-
flush: flushStep,
|
|
87
|
-
close: () => parent.close(),
|
|
88
|
-
sendBinary: (data) => parent.sendBinary(data),
|
|
89
|
-
send: (event) => {
|
|
90
|
-
// Accumulated whether or not storage is configured: `fullText` is what
|
|
91
|
-
// the client was streamed, and an interrupted run has to be able to
|
|
92
|
-
// report the fragment it got through even with persistence turned off.
|
|
93
|
-
if (event.type === 'text-delta')
|
|
94
|
-
fullText += event.text;
|
|
95
|
-
if (event.type === 'tool-call') {
|
|
96
|
-
runToolCallIndex.set(event.toolCallId, runToolCalls.length);
|
|
97
|
-
runToolCalls.push({
|
|
98
|
-
name: event.toolName,
|
|
99
|
-
args: event.args,
|
|
100
|
-
result: '',
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
if (event.type === 'tool-result') {
|
|
104
|
-
const index = runToolCallIndex.get(event.toolCallId);
|
|
105
|
-
const result = typeof event.result === 'string'
|
|
106
|
-
? event.result
|
|
107
|
-
: JSON.stringify(event.result);
|
|
108
|
-
const call = index === undefined ? undefined : runToolCalls[index];
|
|
109
|
-
if (call) {
|
|
110
|
-
call.result = result;
|
|
111
|
-
if (event.error)
|
|
112
|
-
call.error = event.error;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
if (storage) {
|
|
116
|
-
switch (event.type) {
|
|
117
|
-
case 'text-delta':
|
|
118
|
-
stepText += event.text;
|
|
119
|
-
break;
|
|
120
|
-
case 'tool-call':
|
|
121
|
-
stepToolCalls.push({
|
|
122
|
-
id: event.toolCallId,
|
|
123
|
-
name: event.toolName,
|
|
124
|
-
args: event.args,
|
|
125
|
-
});
|
|
126
|
-
break;
|
|
127
|
-
case 'tool-result':
|
|
128
|
-
stepToolResults.push({
|
|
129
|
-
id: event.toolCallId,
|
|
130
|
-
name: event.toolName,
|
|
131
|
-
result: typeof event.result === 'string'
|
|
132
|
-
? event.result
|
|
133
|
-
: JSON.stringify(event.result),
|
|
134
|
-
...(event.error ? { error: event.error } : {}),
|
|
135
|
-
});
|
|
136
|
-
break;
|
|
137
|
-
case 'generative-ui':
|
|
138
|
-
stepGenerativeUI = event.spec;
|
|
139
|
-
break;
|
|
140
|
-
case 'usage':
|
|
141
|
-
totalUsage.inputTokens += event.tokens.input;
|
|
142
|
-
totalUsage.outputTokens += event.tokens.output;
|
|
143
|
-
if (event.model)
|
|
144
|
-
totalUsage.model = event.model;
|
|
145
|
-
flushDetached();
|
|
146
|
-
break;
|
|
147
|
-
case 'done':
|
|
148
|
-
flushDetached();
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
parent.send(event);
|
|
153
|
-
},
|
|
154
|
-
setState: (s) => parent.setState(s),
|
|
155
|
-
getState: () => parent.getState(),
|
|
156
|
-
clearState: () => parent.clearState(),
|
|
157
|
-
remote: (funcName, data) => parent.remote(funcName, data),
|
|
158
|
-
};
|
|
159
|
-
return channel;
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Agents already warned about, so a per-request hook does not become a
|
|
163
|
-
* per-request log line.
|
|
164
|
-
*/
|
|
165
|
-
const warnedUnstreamedOutputHooks = new Set();
|
|
166
|
-
/**
|
|
167
|
-
* `modifyOutput` does not run on a streamed run at all. Nothing here could act
|
|
168
|
-
* on what it returns — the text has already reached the client, and
|
|
169
|
-
* `createPersistingChannel` flushes each step to storage as it goes, so by the
|
|
170
|
-
* time the run ends the transcript is already written.
|
|
171
|
-
*
|
|
172
|
-
* Rewriting on this path belongs to `modifyOutputStream`, which genuinely
|
|
173
|
-
* works: the stream middleware wraps the persisting channel, so what is stored
|
|
174
|
-
* and accumulated is already what the client was sent. A middleware that
|
|
175
|
-
* rewrites in `modifyOutput` only — a redaction hook, typically — is therefore
|
|
176
|
-
* silently ineffective when the agent is streamed, and is told so once.
|
|
177
|
-
*/
|
|
178
|
-
const warnUnstreamedOutputHooks = (agentName, aiMiddlewares, logger) => {
|
|
179
|
-
if (warnedUnstreamedOutputHooks.has(agentName))
|
|
180
|
-
return;
|
|
181
|
-
const unstreamed = aiMiddlewares.some((mw) => mw.modifyOutput && !mw.modifyOutputStream);
|
|
182
|
-
if (!unstreamed)
|
|
183
|
-
return;
|
|
184
|
-
warnedUnstreamedOutputHooks.add(agentName);
|
|
185
|
-
logger?.warn(`Agent '${agentName}' has AI middleware with modifyOutput but no modifyOutputStream — modifyOutput does not apply to streamed runs. Implement modifyOutputStream to affect a streamed reply.`);
|
|
186
|
-
};
|
|
187
|
-
async function postStreamCleanup(persistingChannel, aiRunState, runId, run) {
|
|
188
|
-
await finalizeAgentRun(aiRunState, {
|
|
189
|
-
runId,
|
|
190
|
-
agentName: run.agentName,
|
|
191
|
-
threadId: run.threadId,
|
|
192
|
-
resourceId: run.resourceId,
|
|
193
|
-
input: run.input,
|
|
194
|
-
// Already what the client received: the stream middleware wraps the
|
|
195
|
-
// persisting channel, so both were accumulated post-rewrite.
|
|
196
|
-
text: persistingChannel.fullText,
|
|
197
|
-
steps: [
|
|
198
|
-
{
|
|
199
|
-
usage: persistingChannel.totalUsage,
|
|
200
|
-
toolCalls: persistingChannel.runToolCalls,
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
usage: persistingChannel.totalUsage,
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
async function runStreamStepLoop(params) {
|
|
207
|
-
const { agent, runnerParams, maxSteps, agentRunner, streamChannel, channel, aiMiddlewares, } = params;
|
|
208
|
-
const singletonServices = getSingletonServices();
|
|
209
|
-
for (let step = 0; step < maxSteps; step++) {
|
|
210
|
-
if (agent.prepareStep) {
|
|
211
|
-
let stopped = false;
|
|
212
|
-
await agent.prepareStep({
|
|
213
|
-
stepNumber: step,
|
|
214
|
-
messages: runnerParams.messages,
|
|
215
|
-
tools: runnerParams.tools,
|
|
216
|
-
toolChoice: runnerParams.toolChoice,
|
|
217
|
-
model: runnerParams.model,
|
|
218
|
-
stop: () => {
|
|
219
|
-
stopped = true;
|
|
220
|
-
},
|
|
221
|
-
});
|
|
222
|
-
if (stopped)
|
|
223
|
-
break;
|
|
224
|
-
}
|
|
225
|
-
channel.send({ type: 'step-start', stepNumber: step });
|
|
226
|
-
const stepResult = await agentRunner.stream(runnerParams, streamChannel);
|
|
227
|
-
for (const mw of aiMiddlewares) {
|
|
228
|
-
if (mw.afterStep) {
|
|
229
|
-
await mw.afterStep(singletonServices, {
|
|
230
|
-
stepNumber: step,
|
|
231
|
-
text: stepResult.text,
|
|
232
|
-
toolCalls: stepResult.toolCalls,
|
|
233
|
-
toolResults: stepResult.toolResults,
|
|
234
|
-
usage: stepResult.usage,
|
|
235
|
-
finishReason: stepResult.finishReason,
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (stepResult.toolCalls.length === 0)
|
|
240
|
-
break;
|
|
241
|
-
const approvalsNeeded = checkForApprovals(stepResult, runnerParams.tools);
|
|
242
|
-
if (approvalsNeeded.length > 0) {
|
|
243
|
-
for (const approval of approvalsNeeded) {
|
|
244
|
-
const toolDef = runnerParams.tools.find((t) => t.name === approval.toolName);
|
|
245
|
-
if (toolDef?.approvalDescriptionFn && !approval.reason) {
|
|
246
|
-
try {
|
|
247
|
-
approval.reason = await toolDef.approvalDescriptionFn(approval.args);
|
|
248
|
-
}
|
|
249
|
-
catch { }
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return { outcome: 'approval', approvals: approvalsNeeded };
|
|
253
|
-
}
|
|
254
|
-
const credentialRequests = checkForCredentialRequests(stepResult);
|
|
255
|
-
if (credentialRequests.length > 0) {
|
|
256
|
-
appendStepMessages(runnerParams, stepResult);
|
|
257
|
-
return { outcome: 'credential', credentialRequests };
|
|
258
|
-
}
|
|
259
|
-
appendStepMessages(runnerParams, stepResult);
|
|
260
|
-
}
|
|
261
|
-
return { outcome: 'done' };
|
|
262
|
-
}
|
|
263
|
-
export function checkForApprovals(stepResult, tools) {
|
|
264
|
-
const approvals = [];
|
|
265
|
-
for (const tc of stepResult.toolCalls) {
|
|
266
|
-
const toolDef = tools.find((t) => t.name === tc.toolName);
|
|
267
|
-
if (toolDef?.needsApproval) {
|
|
268
|
-
approvals.push(new ToolApprovalRequired(tc.toolCallId, tc.toolName, tc.args));
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
if (!toolDef?.forwardsApproval) {
|
|
272
|
-
continue;
|
|
273
|
-
}
|
|
274
|
-
const tr = stepResult.toolResults.find((r) => r.toolCallId === tc.toolCallId);
|
|
275
|
-
if (tr?.result &&
|
|
276
|
-
typeof tr.result === 'object' &&
|
|
277
|
-
APPROVAL_REQUIRED in tr.result) {
|
|
278
|
-
const r = tr.result;
|
|
279
|
-
if (r.subApprovals?.length) {
|
|
280
|
-
for (const sub of r.subApprovals) {
|
|
281
|
-
approvals.push(new ToolApprovalRequired(sub.toolCallId, r.toolName, r.args, sub.reason, sub.toolName, sub.args, r.agentRunId));
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
else {
|
|
285
|
-
approvals.push(new ToolApprovalRequired(tc.toolCallId, r.toolName, r.args, r.reason, r.displayToolName, r.displayArgs, r.agentRunId));
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return approvals;
|
|
290
|
-
}
|
|
291
|
-
export function checkForCredentialRequests(stepResult) {
|
|
292
|
-
const requests = [];
|
|
293
|
-
for (const tr of stepResult.toolResults) {
|
|
294
|
-
// knowledge: decisions/security/ai-agent-credential-requests-are-symbol-branded.md
|
|
295
|
-
if (tr.result &&
|
|
296
|
-
typeof tr.result === 'object' &&
|
|
297
|
-
CREDENTIAL_REQUIRED in tr.result) {
|
|
298
|
-
const r = tr.result;
|
|
299
|
-
const tc = stepResult.toolCalls.find((t) => t.toolCallId === tr.toolCallId);
|
|
300
|
-
requests.push(new ToolCredentialRequired(tr.toolCallId, tc?.toolName ?? 'unknown', tc?.args ?? {}, r.credentialName, r.credentialType, r.connectUrl));
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return requests;
|
|
304
|
-
}
|
|
305
|
-
export function appendStepMessages(runnerParams, stepResult) {
|
|
306
|
-
const structuredOutput = stepResult.object && typeof stepResult.object === 'object'
|
|
307
|
-
? stepResult.object
|
|
308
|
-
: null;
|
|
309
|
-
const assistantContent = structuredOutput?.ui != null
|
|
310
|
-
? [
|
|
311
|
-
...(stepResult.text
|
|
312
|
-
? [{ type: 'text', text: stepResult.text }]
|
|
313
|
-
: []),
|
|
314
|
-
{ type: 'generative-ui', spec: structuredOutput.ui },
|
|
315
|
-
]
|
|
316
|
-
: stepResult.text || undefined;
|
|
317
|
-
const assistantMsg = {
|
|
318
|
-
id: randomUUID(),
|
|
319
|
-
role: 'assistant',
|
|
320
|
-
content: assistantContent,
|
|
321
|
-
toolCalls: stepResult.toolCalls.length > 0
|
|
322
|
-
? stepResult.toolCalls.map((tc) => ({
|
|
323
|
-
id: tc.toolCallId,
|
|
324
|
-
name: tc.toolName,
|
|
325
|
-
args: tc.args,
|
|
326
|
-
}))
|
|
327
|
-
: undefined,
|
|
328
|
-
...(stepResult.reasoningContent
|
|
329
|
-
? { reasoningContent: stepResult.reasoningContent }
|
|
330
|
-
: {}),
|
|
331
|
-
createdAt: new Date(),
|
|
332
|
-
};
|
|
333
|
-
runnerParams.messages.push(assistantMsg);
|
|
334
|
-
if (stepResult.toolResults.length > 0) {
|
|
335
|
-
const toolMsg = {
|
|
336
|
-
id: randomUUID(),
|
|
337
|
-
role: 'tool',
|
|
338
|
-
toolResults: stepResult.toolResults.map((tr) => ({
|
|
339
|
-
id: tr.toolCallId,
|
|
340
|
-
name: tr.toolName,
|
|
341
|
-
result: typeof tr.result === 'string' ? tr.result : JSON.stringify(tr.result),
|
|
342
|
-
})),
|
|
343
|
-
createdAt: new Date(),
|
|
344
|
-
};
|
|
345
|
-
runnerParams.messages.push(toolMsg);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
function handleApprovals(approvals, runId, channel, aiRunState, persistingChannel) {
|
|
349
|
-
return (async () => {
|
|
350
|
-
await persistingChannel.flush();
|
|
351
|
-
const pendingApprovals = approvals.map((err) => err.agentRunId
|
|
352
|
-
? {
|
|
353
|
-
type: 'agent-call',
|
|
354
|
-
toolCallId: err.toolCallId,
|
|
355
|
-
agentName: err.toolName,
|
|
356
|
-
agentRunId: err.agentRunId,
|
|
357
|
-
displayToolName: err.displayToolName ?? err.toolName,
|
|
358
|
-
displayArgs: err.displayArgs ?? err.args,
|
|
359
|
-
}
|
|
360
|
-
: {
|
|
361
|
-
type: 'tool-call',
|
|
362
|
-
toolCallId: err.toolCallId,
|
|
363
|
-
toolName: err.toolName,
|
|
364
|
-
args: err.args,
|
|
365
|
-
});
|
|
366
|
-
await aiRunState.updateRun(runId, {
|
|
367
|
-
status: 'suspended',
|
|
368
|
-
suspendReason: 'approval',
|
|
369
|
-
pendingApprovals,
|
|
370
|
-
});
|
|
371
|
-
for (const err of approvals) {
|
|
372
|
-
const approvalEvent = {
|
|
373
|
-
type: 'approval-request',
|
|
374
|
-
toolCallId: err.toolCallId,
|
|
375
|
-
toolName: err.displayToolName ?? err.toolName,
|
|
376
|
-
args: err.displayArgs ?? err.args,
|
|
377
|
-
reason: err.reason,
|
|
378
|
-
runId,
|
|
379
|
-
};
|
|
380
|
-
channel.send(approvalEvent);
|
|
381
|
-
}
|
|
382
|
-
channel.send({ type: 'done' });
|
|
383
|
-
channel.close();
|
|
384
|
-
})();
|
|
385
|
-
}
|
|
386
|
-
function handleCredentialRequests(requests, runId, channel, aiRunState, persistingChannel) {
|
|
387
|
-
return (async () => {
|
|
388
|
-
await persistingChannel.flush();
|
|
389
|
-
const pendingApprovals = requests.map((req) => ({
|
|
390
|
-
type: 'credential-request',
|
|
391
|
-
toolCallId: req.toolCallId,
|
|
392
|
-
toolName: req.toolName,
|
|
393
|
-
args: req.args,
|
|
394
|
-
credentialName: req.credentialName,
|
|
395
|
-
credentialType: req.credentialType,
|
|
396
|
-
connectUrl: req.connectUrl,
|
|
397
|
-
}));
|
|
398
|
-
await aiRunState.updateRun(runId, {
|
|
399
|
-
status: 'suspended',
|
|
400
|
-
suspendReason: 'credential',
|
|
401
|
-
pendingApprovals,
|
|
402
|
-
});
|
|
403
|
-
for (const req of requests) {
|
|
404
|
-
channel.send({
|
|
405
|
-
type: 'credential-request',
|
|
406
|
-
toolCallId: req.toolCallId,
|
|
407
|
-
toolName: req.toolName,
|
|
408
|
-
args: req.args,
|
|
409
|
-
credentialName: req.credentialName,
|
|
410
|
-
credentialType: req.credentialType,
|
|
411
|
-
connectUrl: req.connectUrl,
|
|
412
|
-
runId,
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
channel.send({ type: 'done' });
|
|
416
|
-
channel.close();
|
|
417
|
-
})();
|
|
418
|
-
}
|
|
419
|
-
export async function streamAIAgent(agentName, input, channel, params, agentSessionMap, options) {
|
|
420
|
-
const sessionMap = agentSessionMap ?? new Map();
|
|
421
|
-
const normalizedInput = {
|
|
422
|
-
...input,
|
|
423
|
-
resourceId: resolveOwnerResourceId(params, agentSessionScope(agentName), input.resourceId),
|
|
424
|
-
};
|
|
425
|
-
const streamContext = { channel, options };
|
|
426
|
-
const { agent, packageName, resolvedName, agentRunner, storage, memoryConfig, threadId, userMessage, runnerParams, maxSteps, missingRpcs, workingMemorySchemaName, } = await prepareAgentRun(agentName, normalizedInput, params, sessionMap, streamContext);
|
|
427
|
-
runnerParams.agentId = agentName;
|
|
428
|
-
const singletonServices = getSingletonServices();
|
|
429
|
-
const { aiRunState } = singletonServices;
|
|
430
|
-
if (!aiRunState) {
|
|
431
|
-
throw new Error('AIRunStateService not available in singletonServices');
|
|
432
|
-
}
|
|
433
|
-
if (missingRpcs.length > 0) {
|
|
434
|
-
await aiRunState.createRun({
|
|
435
|
-
agentName,
|
|
436
|
-
threadId,
|
|
437
|
-
resourceId: normalizedInput.resourceId,
|
|
438
|
-
status: 'suspended',
|
|
439
|
-
suspendReason: 'rpc-missing',
|
|
440
|
-
missingRpcs,
|
|
441
|
-
usage: { inputTokens: 0, outputTokens: 0, model: agent.model },
|
|
442
|
-
createdAt: new Date(),
|
|
443
|
-
updatedAt: new Date(),
|
|
444
|
-
});
|
|
445
|
-
channel.send({ type: 'suspended', reason: 'rpc-missing', missingRpcs });
|
|
446
|
-
channel.send({ type: 'done' });
|
|
447
|
-
return '';
|
|
448
|
-
}
|
|
449
|
-
const aiMiddlewares = [
|
|
450
|
-
...getWorkingMemoryMiddleware(memoryConfig, storage, {
|
|
451
|
-
threadId,
|
|
452
|
-
workingMemorySchemaName,
|
|
453
|
-
logger: singletonServices.logger,
|
|
454
|
-
schemaService: singletonServices.schema,
|
|
455
|
-
}),
|
|
456
|
-
...(agent.aiMiddleware ?? []),
|
|
457
|
-
];
|
|
458
|
-
// One bag per run, shared by every middleware — see PikkuAIMiddlewareHooks.
|
|
459
|
-
const sharedNotes = {};
|
|
460
|
-
const { messages: modifiedMessages, instructions: modifiedInstructions } = await applyInputMiddleware(aiMiddlewares, singletonServices, {
|
|
461
|
-
messages: runnerParams.messages,
|
|
462
|
-
instructions: runnerParams.instructions,
|
|
463
|
-
}, sharedNotes);
|
|
464
|
-
runnerParams.messages = modifiedMessages;
|
|
465
|
-
runnerParams.instructions = modifiedInstructions;
|
|
466
|
-
// knowledge: decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md
|
|
467
|
-
const transcript = sharedNotes[SPOKEN_TRANSCRIPT];
|
|
468
|
-
if (typeof transcript === 'string') {
|
|
469
|
-
channel.send({ type: 'transcript', text: transcript });
|
|
470
|
-
}
|
|
471
|
-
// knowledge: decisions/internals/thread-history-records-the-transcript-not-the-audio.md
|
|
472
|
-
const lastModified = modifiedMessages[modifiedMessages.length - 1];
|
|
473
|
-
const persistedUserMessage = lastModified?.id === userMessage.id ? lastModified : userMessage;
|
|
474
|
-
const runId = await aiRunState.createRun({
|
|
475
|
-
agentName,
|
|
476
|
-
threadId,
|
|
477
|
-
resourceId: normalizedInput.resourceId,
|
|
478
|
-
status: 'running',
|
|
479
|
-
usage: { inputTokens: 0, outputTokens: 0, model: agent.model },
|
|
480
|
-
createdAt: new Date(),
|
|
481
|
-
updatedAt: new Date(),
|
|
482
|
-
});
|
|
483
|
-
options?.onRunCreated?.(runId);
|
|
484
|
-
// Registered before the first model call and released in `finally`, so the
|
|
485
|
-
// window in which an interrupt can land is exactly the window in which there
|
|
486
|
-
// is something to interrupt.
|
|
487
|
-
const interruptHandle = registerInterruptibleRun(runId);
|
|
488
|
-
runnerParams.abortSignal = interruptHandle.signal;
|
|
489
|
-
runnerParams.tools = trackToolExecution(runnerParams.tools, interruptHandle);
|
|
490
|
-
if (storage) {
|
|
491
|
-
await storage.saveMessages(threadId, [persistedUserMessage]);
|
|
492
|
-
}
|
|
493
|
-
warnUnstreamedOutputHooks(agentName, aiMiddlewares, singletonServices.logger);
|
|
494
|
-
const streamMiddleware = aiMiddlewares
|
|
495
|
-
.filter((mw) => mw.modifyOutputStream)
|
|
496
|
-
.map((mw) => {
|
|
497
|
-
const state = {};
|
|
498
|
-
const allEvents = [];
|
|
499
|
-
return async (services, event, next) => {
|
|
500
|
-
allEvents.push(event);
|
|
501
|
-
const result = await mw.modifyOutputStream(services, {
|
|
502
|
-
event,
|
|
503
|
-
allEvents,
|
|
504
|
-
state,
|
|
505
|
-
shared: sharedNotes,
|
|
506
|
-
// Sends downstream directly, so a hook can hand back the fast event
|
|
507
|
-
// now and push the slow one when it is ready.
|
|
508
|
-
emit: next,
|
|
509
|
-
signal: interruptHandle.signal,
|
|
510
|
-
});
|
|
511
|
-
if (result == null)
|
|
512
|
-
return;
|
|
513
|
-
if (Array.isArray(result)) {
|
|
514
|
-
for (const r of result)
|
|
515
|
-
await next(r);
|
|
516
|
-
}
|
|
517
|
-
else {
|
|
518
|
-
await next(result);
|
|
519
|
-
}
|
|
520
|
-
};
|
|
521
|
-
});
|
|
522
|
-
const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta');
|
|
523
|
-
const meta = agentsMeta[resolvedName];
|
|
524
|
-
const allChannelMiddleware = combineChannelMiddleware('agent', `stream:${agentName}`, {
|
|
525
|
-
wireInheritedChannelMiddleware: meta?.channelMiddleware,
|
|
526
|
-
wireChannelMiddleware: [
|
|
527
|
-
...(agent.channelMiddleware ?? []),
|
|
528
|
-
...streamMiddleware,
|
|
529
|
-
],
|
|
530
|
-
});
|
|
531
|
-
const persistingChannel = createPersistingChannel(channel, storage, threadId, singletonServices.logger);
|
|
532
|
-
const wrappedChannel = allChannelMiddleware.length > 0
|
|
533
|
-
? wrapChannelWithMiddleware({ channel: persistingChannel }, singletonServices, allChannelMiddleware).channel
|
|
534
|
-
: persistingChannel;
|
|
535
|
-
const credentialFilteredChannel = {
|
|
536
|
-
...wrappedChannel,
|
|
537
|
-
// knowledge: decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md
|
|
538
|
-
send: (event) => {
|
|
539
|
-
if (event.type === 'tool-result' &&
|
|
540
|
-
event.result !== null &&
|
|
541
|
-
typeof event.result === 'object' &&
|
|
542
|
-
'__credentialRequired' in event.result) {
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
return wrappedChannel.send(event);
|
|
546
|
-
},
|
|
547
|
-
};
|
|
548
|
-
const isDelegateMode = agent.agentMode !== 'supervise' && meta?.agents?.length;
|
|
549
|
-
const delegateState = { delegated: false };
|
|
550
|
-
if (isDelegateMode) {
|
|
551
|
-
streamContext.delegateState = delegateState;
|
|
552
|
-
}
|
|
553
|
-
const outputChannel = isDelegateMode
|
|
554
|
-
? {
|
|
555
|
-
...credentialFilteredChannel,
|
|
556
|
-
send: (event) => {
|
|
557
|
-
if (delegateState.delegated &&
|
|
558
|
-
(event.type === 'text-delta' || event.type === 'reasoning-delta'))
|
|
559
|
-
return;
|
|
560
|
-
return credentialFilteredChannel.send(event);
|
|
561
|
-
},
|
|
562
|
-
delegateState,
|
|
563
|
-
}
|
|
564
|
-
: credentialFilteredChannel;
|
|
565
|
-
try {
|
|
566
|
-
const loopResult = await runStreamStepLoop({
|
|
567
|
-
agent,
|
|
568
|
-
runnerParams,
|
|
569
|
-
maxSteps,
|
|
570
|
-
agentRunner,
|
|
571
|
-
streamChannel: outputChannel,
|
|
572
|
-
persistingChannel,
|
|
573
|
-
channel,
|
|
574
|
-
aiMiddlewares,
|
|
575
|
-
});
|
|
576
|
-
if (loopResult.outcome === 'approval') {
|
|
577
|
-
await handleApprovals(loopResult.approvals, runId, channel, aiRunState, persistingChannel);
|
|
578
|
-
return persistingChannel.fullText;
|
|
579
|
-
}
|
|
580
|
-
if (loopResult.outcome === 'credential') {
|
|
581
|
-
await handleCredentialRequests(loopResult.credentialRequests, runId, channel, aiRunState, persistingChannel);
|
|
582
|
-
return persistingChannel.fullText;
|
|
583
|
-
}
|
|
584
|
-
await postStreamCleanup(persistingChannel, aiRunState, runId, {
|
|
585
|
-
agentName,
|
|
586
|
-
threadId,
|
|
587
|
-
resourceId: input.resourceId,
|
|
588
|
-
input: lastUserMessageText(runnerParams.messages),
|
|
589
|
-
});
|
|
590
|
-
// knowledge: decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md
|
|
591
|
-
await outputChannel.send({ type: 'done' });
|
|
592
|
-
channel.close();
|
|
593
|
-
return persistingChannel.fullText;
|
|
594
|
-
}
|
|
595
|
-
catch (err) {
|
|
596
|
-
// An interrupt is not a failure: the truncated text is real output the user
|
|
597
|
-
// already heard part of, so it is persisted and marked rather than dropped.
|
|
598
|
-
const interruption = interruptHandle.interruption;
|
|
599
|
-
if (interruption || isAbortError(err)) {
|
|
600
|
-
await persistingChannel.flush({ interrupted: true });
|
|
601
|
-
await aiRunState.updateRun(runId, { status: 'interrupted' });
|
|
602
|
-
// Deliberately not awaited. A tool still running must not hold the stream
|
|
603
|
-
// open — the whole point of barge-in is that the agent stops now — so the
|
|
604
|
-
// note lands later and the next run on this thread waits for it instead.
|
|
605
|
-
if (storage) {
|
|
606
|
-
trackInterruptNote(threadId, persistOrphanedToolResults(interruptHandle, storage, threadId));
|
|
607
|
-
}
|
|
608
|
-
channel.send({
|
|
609
|
-
type: 'interrupted',
|
|
610
|
-
runId,
|
|
611
|
-
text: persistingChannel.fullText,
|
|
612
|
-
reason: interruption?.reason ?? 'user',
|
|
613
|
-
});
|
|
614
|
-
channel.send({ type: 'done' });
|
|
615
|
-
channel.close();
|
|
616
|
-
return persistingChannel.fullText;
|
|
617
|
-
}
|
|
618
|
-
for (const mw of aiMiddlewares) {
|
|
619
|
-
if (mw.onError) {
|
|
620
|
-
try {
|
|
621
|
-
await mw.onError(singletonServices, {
|
|
622
|
-
error: err instanceof Error ? err : new Error(String(err)),
|
|
623
|
-
stepNumber: -1,
|
|
624
|
-
messages: runnerParams.messages,
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
catch { }
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
await aiRunState.updateRun(runId, {
|
|
631
|
-
status: 'failed',
|
|
632
|
-
errorMessage: err instanceof Error ? err.message : String(err),
|
|
633
|
-
});
|
|
634
|
-
channel.send({
|
|
635
|
-
type: 'error',
|
|
636
|
-
message: err instanceof Error ? err.message : String(err),
|
|
637
|
-
});
|
|
638
|
-
channel.send({ type: 'done' });
|
|
639
|
-
channel.close();
|
|
640
|
-
return persistingChannel.fullText;
|
|
641
|
-
}
|
|
642
|
-
finally {
|
|
643
|
-
interruptHandle.release();
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
/**
|
|
647
|
-
* Stop an in-flight run on behalf of a caller, after checking the run is theirs.
|
|
648
|
-
*
|
|
649
|
-
* Unlike {@link resumeAIAgent} this needs no channel: it does not continue the
|
|
650
|
-
* stream, it ends one. The `interrupted` event and the truncated message are
|
|
651
|
-
* emitted by the original {@link streamAIAgent} call on its own channel, so this
|
|
652
|
-
* can be reached over a plain RPC while the stream is held open elsewhere —
|
|
653
|
-
* which is exactly the shape a voice barge-in needs.
|
|
654
|
-
*
|
|
655
|
-
* Ownership is the whole gate, deliberately without {@link assertAgentAuthorized}.
|
|
656
|
-
* Resuming re-enters the agent and approves a tool call, so a revoked grant must
|
|
657
|
-
* block it; stopping is the opposite — if a caller's access to an agent was
|
|
658
|
-
* revoked mid-run, being able to stop their own run is harmless and arguably
|
|
659
|
-
* what you want.
|
|
660
|
-
*
|
|
661
|
-
* Returns `false` when there was nothing to stop. Racing a run that finishes on
|
|
662
|
-
* its own is the normal case in voice, so it is not an error.
|
|
663
|
-
*/
|
|
664
|
-
export async function interruptAIAgent(input, params) {
|
|
665
|
-
const { aiRunState, logger } = getSingletonServices();
|
|
666
|
-
if (!aiRunState) {
|
|
667
|
-
throw new Error('AIRunStateService not available in singletonServices');
|
|
668
|
-
}
|
|
669
|
-
const run = await aiRunState.getRun(input.runId);
|
|
670
|
-
if (!run) {
|
|
671
|
-
throw new Error(`No run found for runId ${input.runId}`);
|
|
672
|
-
}
|
|
673
|
-
assertResourceOwner(resolveOwnerResourceId(params, agentSessionScope(run.agentName), run.resourceId), run.resourceId, 'run');
|
|
674
|
-
const stopped = signalRunInterrupt(input.runId, {
|
|
675
|
-
reason: input.reason ?? 'user',
|
|
676
|
-
});
|
|
677
|
-
// knowledge: decisions/internals/an-agent-run-owned-by-another-instance-says-so.md
|
|
678
|
-
if (!stopped && run.status === 'running') {
|
|
679
|
-
logger?.warn(`Could not interrupt run ${input.runId}: it is running in another process. ` +
|
|
680
|
-
'Interrupts are process-local; fan them out over eventHub to support multiple instances.');
|
|
681
|
-
}
|
|
682
|
-
// Reported after the abort so it names what is *still* running: aborting the
|
|
683
|
-
// model call does nothing to a tool already executing.
|
|
684
|
-
return { stopped, inFlightTools: getInFlightTools(input.runId) };
|
|
685
|
-
}
|
|
686
|
-
export async function resumeAIAgent(input, channel, params, options) {
|
|
687
|
-
const singletonServices = getSingletonServices();
|
|
688
|
-
const { aiRunState } = singletonServices;
|
|
689
|
-
if (!aiRunState) {
|
|
690
|
-
throw new Error('AIRunStateService not available in singletonServices');
|
|
691
|
-
}
|
|
692
|
-
const run = await aiRunState.getRun(input.runId);
|
|
693
|
-
if (!run) {
|
|
694
|
-
throw new Error(`No run found for runId ${input.runId}`);
|
|
695
|
-
}
|
|
696
|
-
assertResourcePrincipalOwner(params, agentSessionScope(run.agentName), run.resourceId, 'run');
|
|
697
|
-
const pending = run.pendingApprovals?.find((p) => p.toolCallId === input.toolCallId);
|
|
698
|
-
if (!pending) {
|
|
699
|
-
throw new Error(`No pending approval for toolCallId ${input.toolCallId} on run ${input.runId}`);
|
|
700
|
-
}
|
|
701
|
-
const { agent, packageName, resolvedName } = resolveAgent(run.agentName);
|
|
702
|
-
await assertAgentAuthorized(agent, params, packageName);
|
|
703
|
-
// The read above is not a claim — concurrent resumes all see the same pending
|
|
704
|
-
// approval. `resolveApproval` is the claim, and the loser must not go on to
|
|
705
|
-
// run the tool a second time.
|
|
706
|
-
const claimed = await aiRunState.resolveApproval(input.toolCallId, input.approved ? 'approved' : 'denied');
|
|
707
|
-
if (!claimed) {
|
|
708
|
-
throw new Error(`Approval for toolCallId ${input.toolCallId} was already resolved by another caller`);
|
|
709
|
-
}
|
|
710
|
-
const { storage } = resolveMemoryServices(agent, singletonServices);
|
|
711
|
-
const memoryConfig = agent.memory;
|
|
712
|
-
const agentRunner = singletonServices.aiAgentRunner;
|
|
713
|
-
if (!agentRunner) {
|
|
714
|
-
throw new AIProviderNotConfiguredError();
|
|
715
|
-
}
|
|
716
|
-
if (!input.approved) {
|
|
717
|
-
if (pending.type === 'agent-call') {
|
|
718
|
-
await aiRunState.updateRun(pending.agentRunId, { status: 'failed' });
|
|
719
|
-
}
|
|
720
|
-
const denialResult = 'The user explicitly declined this action. Inform them that it was declined and do not retry.';
|
|
721
|
-
if (storage) {
|
|
722
|
-
await storage.saveMessages(run.threadId, [
|
|
723
|
-
{
|
|
724
|
-
id: randomUUID(),
|
|
725
|
-
role: 'tool',
|
|
726
|
-
toolResults: [
|
|
727
|
-
{
|
|
728
|
-
id: input.toolCallId,
|
|
729
|
-
name: pending.type === 'tool-call' ||
|
|
730
|
-
pending.type === 'credential-request'
|
|
731
|
-
? pending.toolName
|
|
732
|
-
: pending.agentName,
|
|
733
|
-
result: denialResult,
|
|
734
|
-
},
|
|
735
|
-
],
|
|
736
|
-
createdAt: new Date(),
|
|
737
|
-
},
|
|
738
|
-
]);
|
|
739
|
-
}
|
|
740
|
-
channel.send({
|
|
741
|
-
type: 'tool-result',
|
|
742
|
-
toolCallId: input.toolCallId,
|
|
743
|
-
toolName: pending.type === 'tool-call' || pending.type === 'credential-request'
|
|
744
|
-
? pending.toolName
|
|
745
|
-
: pending.agentName,
|
|
746
|
-
result: denialResult,
|
|
747
|
-
});
|
|
748
|
-
const updatedRun = await aiRunState.getRun(run.runId);
|
|
749
|
-
const remaining = updatedRun?.pendingApprovals ?? [];
|
|
750
|
-
if (remaining.length > 0) {
|
|
751
|
-
channel.send({ type: 'done' });
|
|
752
|
-
channel.close();
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
755
|
-
await aiRunState.updateRun(run.runId, { status: 'running' });
|
|
756
|
-
await continueAfterToolResult(run, agent, packageName, resolvedName, storage, memoryConfig, agentRunner, channel, params, aiRunState, options);
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
if (pending.type === 'agent-call') {
|
|
760
|
-
const subRun = await aiRunState.getRun(pending.agentRunId);
|
|
761
|
-
if (!subRun) {
|
|
762
|
-
throw new Error(`Sub-agent run not found: ${pending.agentRunId}`);
|
|
763
|
-
}
|
|
764
|
-
const subPending = subRun.pendingApprovals?.find((p) => p.toolCallId === input.toolCallId) ??
|
|
765
|
-
subRun.pendingApprovals?.[0];
|
|
766
|
-
if (!subPending) {
|
|
767
|
-
throw new Error(`No pending approval on sub-agent run ${pending.agentRunId}`);
|
|
768
|
-
}
|
|
769
|
-
const subChannel = createScopedChannel(channel, subRun.agentName, 'resume');
|
|
770
|
-
channel.send({
|
|
771
|
-
type: 'agent-call',
|
|
772
|
-
agentName: subRun.agentName,
|
|
773
|
-
session: 'resume',
|
|
774
|
-
input: null,
|
|
775
|
-
});
|
|
776
|
-
await resumeAIAgent({
|
|
777
|
-
runId: pending.agentRunId,
|
|
778
|
-
toolCallId: subPending.toolCallId,
|
|
779
|
-
approved: true,
|
|
780
|
-
}, subChannel, params, options);
|
|
781
|
-
channel.send({
|
|
782
|
-
type: 'agent-result',
|
|
783
|
-
agentName: subRun.agentName,
|
|
784
|
-
session: 'resume',
|
|
785
|
-
result: null,
|
|
786
|
-
});
|
|
787
|
-
if (storage) {
|
|
788
|
-
await storage.saveMessages(run.threadId, [
|
|
789
|
-
{
|
|
790
|
-
id: randomUUID(),
|
|
791
|
-
role: 'tool',
|
|
792
|
-
toolResults: [
|
|
793
|
-
{
|
|
794
|
-
id: input.toolCallId,
|
|
795
|
-
name: pending.agentName,
|
|
796
|
-
result: 'Sub-agent completed successfully',
|
|
797
|
-
},
|
|
798
|
-
],
|
|
799
|
-
createdAt: new Date(),
|
|
800
|
-
},
|
|
801
|
-
]);
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
else {
|
|
805
|
-
const streamContext = {
|
|
806
|
-
channel,
|
|
807
|
-
options: { ...options, requiresToolApproval: false },
|
|
808
|
-
};
|
|
809
|
-
const aiMiddlewaresForResume = agent.aiMiddleware ?? [];
|
|
810
|
-
const { tools } = await buildToolDefs(params, new Map(), run.resourceId, resolvedName, packageName, streamContext, aiMiddlewaresForResume);
|
|
811
|
-
const matchingTool = tools.find((t) => t.name === pending.toolName);
|
|
812
|
-
if (!matchingTool) {
|
|
813
|
-
throw new Error(`Tool "${pending.toolName}" not found in agent definition`);
|
|
814
|
-
}
|
|
815
|
-
const toolArgs = typeof pending.args === 'string' ? JSON.parse(pending.args) : pending.args;
|
|
816
|
-
let toolResult;
|
|
817
|
-
let toolError;
|
|
818
|
-
try {
|
|
819
|
-
toolResult = await matchingTool.execute(toolArgs);
|
|
820
|
-
}
|
|
821
|
-
catch (execErr) {
|
|
822
|
-
if (execErr?.payload?.error === 'missing_credential') {
|
|
823
|
-
toolResult = execErr.payload;
|
|
824
|
-
toolError = 'missing_credential';
|
|
825
|
-
}
|
|
826
|
-
else {
|
|
827
|
-
toolError = execErr instanceof Error ? execErr.message : String(execErr);
|
|
828
|
-
toolResult = `Error: ${toolError}`;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
const resultStr = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult);
|
|
832
|
-
if (storage) {
|
|
833
|
-
await storage.saveMessages(run.threadId, [
|
|
834
|
-
{
|
|
835
|
-
id: randomUUID(),
|
|
836
|
-
role: 'tool',
|
|
837
|
-
toolResults: [
|
|
838
|
-
{
|
|
839
|
-
id: input.toolCallId,
|
|
840
|
-
name: pending.toolName,
|
|
841
|
-
result: resultStr,
|
|
842
|
-
},
|
|
843
|
-
],
|
|
844
|
-
createdAt: new Date(),
|
|
845
|
-
},
|
|
846
|
-
]);
|
|
847
|
-
}
|
|
848
|
-
channel.send({
|
|
849
|
-
type: 'tool-result',
|
|
850
|
-
toolCallId: input.toolCallId,
|
|
851
|
-
toolName: pending.toolName,
|
|
852
|
-
result: toolResult,
|
|
853
|
-
...(toolError ? { error: toolError } : {}),
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
const updatedRun = await aiRunState.getRun(run.runId);
|
|
857
|
-
const remaining = updatedRun?.pendingApprovals ?? [];
|
|
858
|
-
if (remaining.length > 0) {
|
|
859
|
-
channel.send({ type: 'done' });
|
|
860
|
-
channel.close();
|
|
861
|
-
return;
|
|
862
|
-
}
|
|
863
|
-
await aiRunState.updateRun(run.runId, { status: 'running' });
|
|
864
|
-
await continueAfterToolResult(run, agent, packageName, resolvedName, storage, memoryConfig, agentRunner, channel, params, aiRunState, options);
|
|
865
|
-
}
|
|
866
|
-
async function continueAfterToolResult(run, agent, packageName, resolvedName, storage, memoryConfig, agentRunner, channel, params, aiRunState, options) {
|
|
867
|
-
const singletonServices = getSingletonServices();
|
|
868
|
-
const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta');
|
|
869
|
-
const meta = agentsMeta[resolvedName];
|
|
870
|
-
const workingMemorySchemaName = meta?.workingMemorySchema ?? null;
|
|
871
|
-
const messages = storage
|
|
872
|
-
? await storage.getMessages(run.threadId, {
|
|
873
|
-
lastN: memoryConfig?.lastMessages ?? 20,
|
|
874
|
-
})
|
|
875
|
-
: [];
|
|
876
|
-
const workingMemoryJsonSchema = workingMemorySchemaName
|
|
877
|
-
? pikkuState(packageName, 'misc', 'schemas').get(workingMemorySchemaName)
|
|
878
|
-
: undefined;
|
|
879
|
-
const contextMessages = await loadContextMessages(memoryConfig, storage, { message: '', threadId: run.threadId, resourceId: run.resourceId }, workingMemoryJsonSchema);
|
|
880
|
-
const allMessages = [...contextMessages, ...messages];
|
|
881
|
-
const trimmedMessages = trimMessages(allMessages);
|
|
882
|
-
const instructions = await buildInstructions(resolvedName, packageName);
|
|
883
|
-
const aiMiddlewares = [
|
|
884
|
-
...getWorkingMemoryMiddleware(memoryConfig, storage, {
|
|
885
|
-
threadId: run.threadId,
|
|
886
|
-
workingMemorySchemaName,
|
|
887
|
-
logger: singletonServices.logger,
|
|
888
|
-
schemaService: singletonServices.schema,
|
|
889
|
-
}),
|
|
890
|
-
...(agent.aiMiddleware ?? []),
|
|
891
|
-
];
|
|
892
|
-
// One bag per run, shared by every middleware — see PikkuAIMiddlewareHooks.
|
|
893
|
-
const sharedNotes = {};
|
|
894
|
-
const { messages: modifiedMessages, instructions: modifiedInstructions } = await applyInputMiddleware(aiMiddlewares, singletonServices, { messages: trimmedMessages, instructions: instructions }, sharedNotes);
|
|
895
|
-
// knowledge: decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md
|
|
896
|
-
const interruptHandle = registerInterruptibleRun(run.runId);
|
|
897
|
-
warnUnstreamedOutputHooks(run.agentName, aiMiddlewares, singletonServices.logger);
|
|
898
|
-
const streamMiddleware = aiMiddlewares
|
|
899
|
-
.filter((mw) => mw.modifyOutputStream)
|
|
900
|
-
.map((mw) => {
|
|
901
|
-
const state = {};
|
|
902
|
-
const allEvents = [];
|
|
903
|
-
return async (services, event, next) => {
|
|
904
|
-
allEvents.push(event);
|
|
905
|
-
const result = await mw.modifyOutputStream(services, {
|
|
906
|
-
event,
|
|
907
|
-
allEvents,
|
|
908
|
-
state,
|
|
909
|
-
shared: sharedNotes,
|
|
910
|
-
// Sends downstream directly, so a hook can hand back the fast event
|
|
911
|
-
// now and push the slow one when it is ready.
|
|
912
|
-
emit: next,
|
|
913
|
-
signal: interruptHandle.signal,
|
|
914
|
-
});
|
|
915
|
-
if (result == null)
|
|
916
|
-
return;
|
|
917
|
-
if (Array.isArray(result)) {
|
|
918
|
-
for (const r of result)
|
|
919
|
-
await next(r);
|
|
920
|
-
}
|
|
921
|
-
else {
|
|
922
|
-
await next(result);
|
|
923
|
-
}
|
|
924
|
-
};
|
|
925
|
-
});
|
|
926
|
-
const allChannelMiddleware = combineChannelMiddleware('agent', `stream:${run.agentName}`, {
|
|
927
|
-
wireInheritedChannelMiddleware: meta?.channelMiddleware,
|
|
928
|
-
wireChannelMiddleware: [
|
|
929
|
-
...(agent.channelMiddleware ?? []),
|
|
930
|
-
...streamMiddleware,
|
|
931
|
-
],
|
|
932
|
-
});
|
|
933
|
-
const persistingChannel = createPersistingChannel(channel, storage, run.threadId, singletonServices.logger);
|
|
934
|
-
const wrappedChannel = allChannelMiddleware.length > 0
|
|
935
|
-
? wrapChannelWithMiddleware({ channel: persistingChannel }, singletonServices, allChannelMiddleware).channel
|
|
936
|
-
: persistingChannel;
|
|
937
|
-
const streamContext = { channel, options };
|
|
938
|
-
const resumeTools = (await buildToolDefs(params, new Map(), run.resourceId, resolvedName, packageName, streamContext, aiMiddlewares)).tools;
|
|
939
|
-
const resolved = resolveModelConfig(resolvedName, agent);
|
|
940
|
-
const maxSteps = resolved.maxSteps ?? 10;
|
|
941
|
-
const runnerParams = {
|
|
942
|
-
model: resolved.model,
|
|
943
|
-
temperature: resolved.temperature,
|
|
944
|
-
instructions: modifiedInstructions,
|
|
945
|
-
messages: modifiedMessages,
|
|
946
|
-
tools: resumeTools,
|
|
947
|
-
maxSteps: 1,
|
|
948
|
-
toolChoice: (agent.toolChoice ?? 'auto'),
|
|
949
|
-
providerOptions: agent.providerOptions,
|
|
950
|
-
outputSchema: meta?.outputSchema
|
|
951
|
-
? pikkuState(packageName, 'misc', 'schemas').get(meta.outputSchema)
|
|
952
|
-
: undefined,
|
|
953
|
-
};
|
|
954
|
-
runnerParams.abortSignal = interruptHandle.signal;
|
|
955
|
-
runnerParams.tools = trackToolExecution(runnerParams.tools, interruptHandle);
|
|
956
|
-
try {
|
|
957
|
-
const loopResult = await runStreamStepLoop({
|
|
958
|
-
agent,
|
|
959
|
-
runnerParams,
|
|
960
|
-
maxSteps,
|
|
961
|
-
agentRunner,
|
|
962
|
-
streamChannel: wrappedChannel,
|
|
963
|
-
persistingChannel,
|
|
964
|
-
channel,
|
|
965
|
-
aiMiddlewares,
|
|
966
|
-
});
|
|
967
|
-
if (loopResult.outcome === 'approval') {
|
|
968
|
-
await handleApprovals(loopResult.approvals, run.runId, channel, aiRunState, persistingChannel);
|
|
969
|
-
return;
|
|
970
|
-
}
|
|
971
|
-
if (loopResult.outcome === 'credential') {
|
|
972
|
-
await handleCredentialRequests(loopResult.credentialRequests, run.runId, channel, aiRunState, persistingChannel);
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
await postStreamCleanup(persistingChannel, aiRunState, run.runId, {
|
|
976
|
-
...run,
|
|
977
|
-
input: lastUserMessageText(runnerParams.messages),
|
|
978
|
-
});
|
|
979
|
-
// knowledge: decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md
|
|
980
|
-
await wrappedChannel.send({ type: 'done' });
|
|
981
|
-
channel.close();
|
|
982
|
-
}
|
|
983
|
-
catch (err) {
|
|
984
|
-
// Same reasoning as the first turn: an interrupt is not a failure, and the
|
|
985
|
-
// part of the reply the user already heard is real output.
|
|
986
|
-
const interruption = interruptHandle.interruption;
|
|
987
|
-
if (interruption || isAbortError(err)) {
|
|
988
|
-
await persistingChannel.flush({ interrupted: true });
|
|
989
|
-
await aiRunState.updateRun(run.runId, { status: 'interrupted' });
|
|
990
|
-
if (storage) {
|
|
991
|
-
trackInterruptNote(run.threadId, persistOrphanedToolResults(interruptHandle, storage, run.threadId));
|
|
992
|
-
}
|
|
993
|
-
channel.send({
|
|
994
|
-
type: 'interrupted',
|
|
995
|
-
runId: run.runId,
|
|
996
|
-
text: persistingChannel.fullText,
|
|
997
|
-
reason: interruption?.reason ?? 'user',
|
|
998
|
-
});
|
|
999
|
-
channel.send({ type: 'done' });
|
|
1000
|
-
channel.close();
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
for (const mw of aiMiddlewares) {
|
|
1004
|
-
if (mw.onError) {
|
|
1005
|
-
try {
|
|
1006
|
-
await mw.onError(singletonServices, {
|
|
1007
|
-
error: err instanceof Error ? err : new Error(String(err)),
|
|
1008
|
-
stepNumber: -1,
|
|
1009
|
-
messages: runnerParams.messages,
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
catch { }
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
await aiRunState.updateRun(run.runId, {
|
|
1016
|
-
status: 'failed',
|
|
1017
|
-
errorMessage: err instanceof Error ? err.message : String(err),
|
|
1018
|
-
});
|
|
1019
|
-
channel.send({
|
|
1020
|
-
type: 'error',
|
|
1021
|
-
message: err instanceof Error ? err.message : String(err),
|
|
1022
|
-
});
|
|
1023
|
-
channel.send({ type: 'done' });
|
|
1024
|
-
channel.close();
|
|
1025
|
-
}
|
|
1026
|
-
finally {
|
|
1027
|
-
interruptHandle.release();
|
|
1028
|
-
}
|
|
1029
|
-
}
|