@pikku/core 0.12.79 → 0.12.82
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 +473 -0
- package/dist/dev/hot-reload.js +1 -6
- package/dist/ecosystem.d.ts +27 -0
- package/dist/ecosystem.js +26 -0
- package/dist/errors/error-handler.js +4 -2
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/function/function-runner.js +22 -47
- package/dist/function/functions.types.d.ts +9 -5
- package/dist/function/index.d.ts +1 -1
- package/dist/index.d.ts +14 -19
- package/dist/index.js +6 -11
- package/dist/middleware/auth-cookie.d.ts +0 -4
- package/dist/middleware/auth-cookie.js +38 -2
- package/dist/middleware/cors.js +1 -0
- package/dist/middleware/index.d.ts +1 -1
- package/dist/middleware/index.js +1 -1
- package/dist/middleware/remote-auth.js +14 -3
- package/dist/permissions.d.ts +8 -10
- package/dist/permissions.js +0 -11
- package/dist/pikku-state.js +16 -5
- package/dist/schema.js +35 -1
- package/dist/services/ai-agent-runner-service.d.ts +7 -0
- package/dist/services/ai-run-state-service.d.ts +17 -1
- package/dist/services/in-memory-ai-run-state-service.d.ts +6 -2
- package/dist/services/in-memory-ai-run-state-service.js +11 -1
- package/dist/services/in-memory-workflow-service.js +2 -0
- package/dist/services/index.d.ts +15 -15
- package/dist/services/index.js +5 -5
- package/dist/services/local-content-request-handler.d.ts +21 -0
- package/dist/services/local-content-request-handler.js +72 -53
- package/dist/services/local-content.d.ts +6 -0
- package/dist/services/local-content.js +14 -1
- package/dist/services/scoped-credential-service.d.ts +21 -0
- package/dist/services/scoped-credential-service.js +53 -0
- package/dist/services/workflow-service.d.ts +4 -2
- package/dist/testing/service-tests/agent-run-service-tests.d.ts +10 -0
- package/dist/testing/service-tests/agent-run-service-tests.js +72 -0
- package/dist/testing/service-tests/ai-storage-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/ai-storage-service-tests.js +302 -0
- package/dist/testing/service-tests/channel-store-tests.d.ts +3 -0
- package/dist/testing/service-tests/channel-store-tests.js +72 -0
- package/dist/testing/service-tests/credential-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/credential-service-tests.js +109 -0
- package/dist/testing/service-tests/deployment-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/deployment-service-tests.js +21 -0
- package/dist/testing/service-tests/event-hub-store-tests.d.ts +3 -0
- package/dist/testing/service-tests/event-hub-store-tests.js +34 -0
- package/dist/testing/service-tests/secret-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/secret-service-tests.js +80 -0
- package/dist/testing/service-tests/session-store-tests.d.ts +3 -0
- package/dist/testing/service-tests/session-store-tests.js +43 -0
- package/dist/testing/service-tests/workflow-run-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/workflow-run-service-tests.js +42 -0
- package/dist/testing/service-tests/workflow-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/workflow-service-tests.js +150 -0
- package/dist/testing/service-tests.d.ts +6 -0
- package/dist/testing/service-tests.js +26 -791
- package/dist/types/core.types.d.ts +11 -3
- package/dist/types/state.types.d.ts +14 -1
- package/dist/wirings/actor-flow/index.d.ts +1 -1
- package/dist/wirings/actor-flow/run-conversation.js +1 -6
- package/dist/wirings/ai-agent/agent-rpc.d.ts +15 -0
- package/dist/wirings/ai-agent/agent-rpc.js +53 -0
- package/dist/wirings/ai-agent/ai-agent-agui.js +1 -5
- package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +58 -0
- package/dist/wirings/ai-agent/ai-agent-finalize.js +138 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +1 -0
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +2 -8
- package/dist/wirings/ai-agent/ai-agent-memory.js +37 -18
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +7 -0
- package/dist/wirings/ai-agent/ai-agent-model-config.js +44 -1
- package/dist/wirings/ai-agent/ai-agent-prepare.js +5 -9
- package/dist/wirings/ai-agent/ai-agent-runner.js +89 -147
- package/dist/wirings/ai-agent/ai-agent-stream.js +109 -97
- package/dist/wirings/ai-agent/ai-agent-turn.d.ts +57 -0
- package/dist/wirings/ai-agent/ai-agent-turn.js +82 -0
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +47 -2
- package/dist/wirings/ai-agent/index.d.ts +8 -7
- package/dist/wirings/ai-agent/index.js +5 -4
- package/dist/wirings/ai-agent/voice-input.js +1 -6
- package/dist/wirings/ai-agent/voice-output.js +2 -12
- package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +26 -0
- package/dist/wirings/ai-scorer/ai-scorer-grade.js +33 -0
- package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +17 -0
- package/dist/wirings/ai-scorer/ai-scorer-judge.js +92 -0
- package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +15 -0
- package/dist/wirings/ai-scorer/ai-scorer-live.js +38 -0
- package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +18 -0
- package/dist/wirings/ai-scorer/ai-scorer-registry.js +46 -0
- package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +8 -0
- package/dist/wirings/ai-scorer/ai-scorer-sampling.js +31 -0
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +10 -0
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +40 -0
- package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +15 -0
- package/dist/wirings/ai-scorer/ai-scorer-worker.js +58 -0
- package/dist/wirings/ai-scorer/ai-scorer.d.ts +39 -0
- package/dist/wirings/ai-scorer/ai-scorer.js +40 -0
- package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +90 -0
- package/dist/wirings/ai-scorer/ai-scorer.types.js +4 -0
- package/dist/wirings/ai-scorer/index.d.ts +6 -0
- package/dist/wirings/ai-scorer/index.js +5 -0
- package/dist/wirings/channel/channel-common.js +1 -0
- package/dist/wirings/channel/channel-handler.js +3 -5
- package/dist/wirings/channel/channel-rpc-service.d.ts +0 -6
- package/dist/wirings/channel/channel-rpc-service.js +0 -8
- package/dist/wirings/channel/channel-rpc.types.d.ts +6 -0
- package/dist/wirings/channel/channel-rpc.types.js +8 -0
- package/dist/wirings/channel/channel-runner.d.ts +1 -3
- package/dist/wirings/channel/channel-runner.js +16 -8
- package/dist/wirings/channel/channel.types.d.ts +2 -0
- package/dist/wirings/channel/index.d.ts +5 -6
- package/dist/wirings/channel/index.js +3 -4
- package/dist/wirings/channel/local/local-channel-runner.js +8 -1
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +3 -0
- package/dist/wirings/cli/channel/cli-channel-runner.js +2 -0
- package/dist/wirings/cli/channel/cli-raw-channel-runner.js +9 -1
- package/dist/wirings/cli/channel/index.d.ts +1 -2
- package/dist/wirings/cli/channel/index.js +0 -1
- package/dist/wirings/cli/cli-runner.js +17 -3
- package/dist/wirings/cli/cli.types.d.ts +0 -8
- package/dist/wirings/cli/command-parser.js +13 -0
- package/dist/wirings/credential/index.d.ts +1 -1
- package/dist/wirings/gateway/gateway-runner.js +9 -2
- package/dist/wirings/gateway/index.d.ts +1 -1
- package/dist/wirings/http/http-routes.js +2 -0
- package/dist/wirings/http/http-runner.d.ts +0 -10
- package/dist/wirings/http/http-runner.js +14 -15
- package/dist/wirings/http/http.types.d.ts +0 -10
- package/dist/wirings/http/index.d.ts +2 -3
- package/dist/wirings/http/index.js +1 -1
- package/dist/wirings/mcp/index.d.ts +1 -1
- package/dist/wirings/mcp/mcp-runner.d.ts +15 -7
- package/dist/wirings/mcp/mcp-runner.js +18 -11
- package/dist/wirings/persona/index.d.ts +3 -4
- package/dist/wirings/persona/index.js +2 -3
- package/dist/wirings/queue/index.d.ts +1 -3
- package/dist/wirings/queue/index.js +1 -3
- package/dist/wirings/rpc/addon-runner.d.ts +4 -0
- package/dist/wirings/rpc/addon-runner.js +19 -3
- package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
- package/dist/wirings/rpc/rpc-runner.js +55 -86
- package/dist/wirings/rpc/rpc-types.d.ts +7 -0
- package/dist/wirings/rpc/wire-addon.d.ts +13 -0
- package/dist/wirings/rpc/wire-addon.js +4 -0
- package/dist/wirings/scheduler/index.d.ts +1 -1
- package/dist/wirings/secret/validate-secret-definitions.js +2 -2
- package/dist/wirings/trigger/index.d.ts +1 -1
- package/dist/wirings/trigger/pikku-trigger-service.d.ts +0 -4
- package/dist/wirings/trigger/trigger-runner.js +1 -0
- package/dist/wirings/virtual-user/index.d.ts +5 -6
- package/dist/wirings/virtual-user/index.js +2 -4
- package/dist/wirings/virtual-user/run-virtual-user.js +11 -11
- package/dist/wirings/virtual-user/virtual-user-derive.js +4 -25
- package/dist/wirings/virtual-user/virtual-user-dispositions.js +1 -4
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +100 -3
- package/dist/wirings/workflow/feature.js +7 -4
- package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -2
- package/dist/wirings/workflow/graph/graph-runner.js +8 -7
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -4
- package/dist/wirings/workflow/index.d.ts +12 -7
- package/dist/wirings/workflow/index.js +7 -3
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
- package/dist/wirings/workflow/pikku-scenario-service.js +64 -10
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -147
- package/dist/wirings/workflow/pikku-workflow-service.js +85 -493
- package/dist/wirings/workflow/scenario-step.types.d.ts +8 -0
- package/dist/wirings/workflow/workflow-approval-audit.d.ts +16 -0
- package/dist/wirings/workflow/workflow-approval-audit.js +40 -0
- package/dist/wirings/workflow/workflow-approval-policy.d.ts +20 -0
- package/dist/wirings/workflow/workflow-approval-policy.js +48 -0
- package/dist/wirings/workflow/workflow-approval.d.ts +67 -0
- package/dist/wirings/workflow/workflow-approval.js +177 -0
- package/dist/wirings/workflow/workflow-constants.d.ts +26 -0
- package/dist/wirings/workflow/workflow-constants.js +35 -0
- package/dist/wirings/workflow/workflow-errors.d.ts +58 -0
- package/dist/wirings/workflow/workflow-errors.js +112 -0
- package/dist/wirings/workflow/workflow-meta-resolver.d.ts +11 -0
- package/dist/wirings/workflow/workflow-meta-resolver.js +31 -0
- package/dist/wirings/workflow/workflow-queue-routing.d.ts +8 -0
- package/dist/wirings/workflow/workflow-queue-routing.js +38 -0
- package/dist/wirings/workflow/workflow-queue-wiring.d.ts +20 -0
- package/dist/wirings/workflow/workflow-queue-wiring.js +79 -0
- package/dist/wirings/workflow/workflow-recovery.d.ts +68 -0
- package/dist/wirings/workflow/workflow-recovery.js +101 -0
- package/dist/wirings/workflow/workflow-run-engine.types.d.ts +54 -0
- package/dist/wirings/workflow/workflow-run-ownership.d.ts +17 -0
- package/dist/wirings/workflow/workflow-run-ownership.js +30 -0
- package/dist/wirings/workflow/workflow-suspend.d.ts +12 -0
- package/dist/wirings/workflow/workflow-suspend.js +33 -0
- package/dist/wirings/workflow/workflow.types.d.ts +8 -1
- package/knowledge/decisions/internals/a-non-streaming-agent-run-registers-with-airunstate-too.md +22 -0
- package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +20 -0
- package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +21 -0
- package/knowledge/decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md +21 -0
- package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +46 -0
- package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +45 -0
- package/knowledge/decisions/internals/addon-pikku-meta-ships-at-the-package-root-or-under-dist.md +32 -0
- package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +21 -0
- package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +22 -0
- package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +39 -0
- package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +23 -0
- package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +25 -0
- package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +22 -0
- package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +20 -0
- package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +22 -0
- package/knowledge/decisions/internals/an-unref-d-timer-cannot-be-awaited-under-node-test.md +66 -0
- package/knowledge/decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md +33 -0
- package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +43 -0
- package/knowledge/decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md +26 -0
- package/knowledge/decisions/internals/index.md +42 -3
- package/knowledge/decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md +21 -0
- package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +24 -0
- package/knowledge/decisions/internals/side-effects-are-an-allowlist-not-a-boolean.md +34 -0
- package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +24 -0
- package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +22 -0
- package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +26 -0
- package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +43 -0
- package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +58 -0
- package/knowledge/decisions/internals/the-middleware-resolution-cache-is-deliberately-unbounded.md +40 -0
- package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +40 -0
- package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +28 -0
- package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +25 -0
- package/knowledge/decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md +21 -0
- package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +22 -0
- package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +25 -0
- package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +115 -0
- package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +21 -0
- package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +24 -0
- package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +35 -0
- package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +4 -2
- package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +37 -0
- package/knowledge/decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md +33 -0
- package/knowledge/decisions/security/a-permission-gets-a-wire-it-cannot-reply-on.md +33 -0
- package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +37 -0
- package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +28 -0
- package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +50 -0
- package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
- package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +59 -0
- package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
- package/knowledge/decisions/security/index.md +9 -0
- package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +35 -0
- package/knowledge/questions/index.md +2 -1
- package/knowledge/questions/unauthorized-channel-replies-escape-the-declared-out-type.md +44 -0
- package/package.json +15 -3
- package/scripts/generate-api-report.d.mts +1 -0
- package/scripts/generate-api-report.mjs +89 -0
- package/scripts/generate-api-report.mts +343 -0
- package/src/api-report.test.ts +69 -0
- package/src/column-form.ts +1 -4
- package/src/crypto-utils.test.ts +15 -2
- package/src/dev/hot-reload.ts +2 -7
- package/src/ecosystem.ts +40 -0
- package/src/errors/error-handler.ts +5 -3
- package/src/errors/error.test.ts +4 -1
- package/src/errors/index.ts +1 -1
- package/src/function/function-runner.test.ts +53 -1
- package/src/function/function-runner.ts +33 -48
- package/src/function/functions.types.ts +15 -9
- package/src/function/index.ts +0 -2
- package/src/handle-error.ts +1 -1
- package/src/index.ts +3 -57
- package/src/middleware/auth-cookie.test.ts +50 -0
- package/src/middleware/auth-cookie.ts +38 -2
- package/src/middleware/cors.ts +2 -1
- package/src/middleware/index.ts +1 -1
- package/src/middleware/remote-auth.test.ts +43 -0
- package/src/middleware/remote-auth.ts +17 -3
- package/src/middleware-runner.ts +4 -2
- package/src/no-any-casts.test.ts +57 -0
- package/src/permissions.test.ts +3 -1
- package/src/permissions.ts +22 -24
- package/src/pikku-state.ts +22 -6
- package/src/public-surface.json +554 -0
- package/src/public-surface.json.README +25 -0
- package/src/public-surface.test.ts +105 -0
- package/src/removed-legacy-exports.test.ts +62 -0
- package/src/schema.test.ts +78 -0
- package/src/schema.ts +36 -1
- package/src/services/ai-agent-runner-service.ts +12 -1
- package/src/services/ai-run-state-service.ts +18 -1
- package/src/services/audit-service.ts +2 -2
- package/src/services/in-memory-ai-run-state-service.ts +16 -2
- package/src/services/in-memory-workflow-service.ts +2 -0
- package/src/services/index.ts +4 -47
- package/src/services/local-content-request-handler.test.ts +43 -5
- package/src/services/local-content-request-handler.ts +103 -74
- package/src/services/local-content.ts +15 -1
- package/src/services/local-email-service.ts +5 -1
- package/src/services/scoped-credential-service.test.ts +86 -0
- package/src/services/scoped-credential-service.ts +63 -0
- package/src/services/system-role-guard.test.ts +4 -1
- package/src/services/workflow-service.ts +9 -2
- package/src/side-effects-are-declared.test.ts +84 -0
- package/src/source-files-stay-composable.test.ts +41 -0
- package/src/testing/service-tests/agent-run-service-tests.ts +98 -0
- package/src/testing/service-tests/ai-storage-service-tests.ts +379 -0
- package/src/testing/service-tests/channel-store-tests.ts +98 -0
- package/src/testing/service-tests/credential-service-tests.ts +143 -0
- package/src/testing/service-tests/deployment-service-tests.ts +33 -0
- package/src/testing/service-tests/event-hub-store-tests.ts +48 -0
- package/src/testing/service-tests/secret-service-tests.ts +105 -0
- package/src/testing/service-tests/session-store-tests.ts +62 -0
- package/src/testing/service-tests/workflow-run-service-tests.ts +59 -0
- package/src/testing/service-tests/workflow-service-tests.ts +308 -0
- package/src/testing/service-tests.ts +31 -1111
- package/src/types/core.types.ts +14 -3
- package/src/types/state.types.ts +17 -1
- package/src/wirings/actor-flow/index.ts +0 -3
- package/src/wirings/actor-flow/run-conversation.ts +1 -6
- package/src/wirings/ai-agent/agent-rpc.ts +120 -0
- package/src/wirings/ai-agent/ai-agent-agui.ts +1 -5
- package/src/wirings/ai-agent/ai-agent-finalize.test.ts +186 -0
- package/src/wirings/ai-agent/ai-agent-finalize.ts +197 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +2 -1
- package/src/wirings/ai-agent/ai-agent-interrupt.ts +1 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +62 -39
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +72 -3
- package/src/wirings/ai-agent/ai-agent-model-config.ts +49 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +12 -12
- package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +1 -0
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +82 -2
- package/src/wirings/ai-agent/ai-agent-runner.ts +120 -160
- package/src/wirings/ai-agent/ai-agent-stream-output-hooks.test.ts +353 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +152 -117
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
- package/src/wirings/ai-agent/ai-agent-turn.test.ts +67 -0
- package/src/wirings/ai-agent/ai-agent-turn.ts +122 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +65 -5
- package/src/wirings/ai-agent/index.ts +2 -16
- package/src/wirings/ai-agent/voice-input.ts +1 -6
- package/src/wirings/ai-agent/voice-output.ts +2 -12
- package/src/wirings/ai-scorer/ai-scorer-grade.test.ts +106 -0
- package/src/wirings/ai-scorer/ai-scorer-grade.ts +55 -0
- package/src/wirings/ai-scorer/ai-scorer-judge.test.ts +143 -0
- package/src/wirings/ai-scorer/ai-scorer-judge.ts +120 -0
- package/src/wirings/ai-scorer/ai-scorer-live.test.ts +174 -0
- package/src/wirings/ai-scorer/ai-scorer-live.ts +56 -0
- package/src/wirings/ai-scorer/ai-scorer-registry.ts +63 -0
- package/src/wirings/ai-scorer/ai-scorer-sampling.test.ts +34 -0
- package/src/wirings/ai-scorer/ai-scorer-sampling.ts +36 -0
- package/src/wirings/ai-scorer/ai-scorer-snapshots.test.ts +49 -0
- package/src/wirings/ai-scorer/ai-scorer-snapshots.ts +46 -0
- package/src/wirings/ai-scorer/ai-scorer-worker.test.ts +122 -0
- package/src/wirings/ai-scorer/ai-scorer-worker.ts +69 -0
- package/src/wirings/ai-scorer/ai-scorer.ts +76 -0
- package/src/wirings/ai-scorer/ai-scorer.types.ts +107 -0
- package/src/wirings/ai-scorer/index.ts +24 -0
- package/src/wirings/channel/channel-common.ts +5 -2
- package/src/wirings/channel/channel-handler-shapes.test.ts +72 -0
- package/src/wirings/channel/channel-handler.ts +7 -7
- package/src/wirings/channel/channel-rpc-service.ts +0 -14
- package/src/wirings/channel/channel-rpc.test.ts +25 -4
- package/src/wirings/channel/channel-rpc.types.ts +14 -0
- package/src/wirings/channel/channel-runner.ts +33 -20
- package/src/wirings/channel/channel.types.ts +2 -0
- package/src/wirings/channel/index.ts +1 -20
- package/src/wirings/channel/local/local-channel-runner.test.ts +68 -0
- package/src/wirings/channel/local/local-channel-runner.ts +9 -2
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +2 -1
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +6 -3
- package/src/wirings/cli/channel/cli-channel-runner.ts +3 -1
- package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +23 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +12 -1
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +4 -1
- package/src/wirings/cli/channel/index.ts +0 -7
- package/src/wirings/cli/cli-runner.test.ts +68 -0
- package/src/wirings/cli/cli-runner.ts +25 -5
- package/src/wirings/cli/cli.types.ts +0 -39
- package/src/wirings/cli/command-parser.test.ts +19 -0
- package/src/wirings/cli/command-parser.ts +17 -1
- package/src/wirings/credential/index.ts +0 -1
- package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
- package/src/wirings/gateway/gateway-runner.ts +27 -19
- package/src/wirings/gateway/index.ts +0 -3
- package/src/wirings/http/http-routes.ts +4 -1
- package/src/wirings/http/http-runner.test.ts +66 -0
- package/src/wirings/http/http-runner.ts +21 -28
- package/src/wirings/http/http.types.ts +0 -15
- package/src/wirings/http/index.ts +2 -8
- package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
- package/src/wirings/http/web-request.ts +1 -1
- package/src/wirings/mcp/index.ts +0 -1
- package/src/wirings/mcp/mcp-runner.test.ts +181 -0
- package/src/wirings/mcp/mcp-runner.ts +37 -21
- package/src/wirings/persona/index.ts +0 -8
- package/src/wirings/persona/persona-environments.test.ts +14 -3
- package/src/wirings/persona/persona.test.ts +13 -3
- package/src/wirings/persona/validate-personas.ts +5 -1
- package/src/wirings/queue/index.ts +0 -14
- package/src/wirings/queue/queue-runner.ts +1 -1
- package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
- package/src/wirings/rpc/addon-runner.ts +34 -3
- package/src/wirings/rpc/addon-secrets.test.ts +261 -0
- package/src/wirings/rpc/rpc-runner.test.ts +2 -0
- package/src/wirings/rpc/rpc-runner.ts +60 -136
- package/src/wirings/rpc/rpc-types.ts +11 -0
- package/src/wirings/rpc/wire-addon.ts +20 -1
- package/src/wirings/scheduler/index.ts +0 -1
- package/src/wirings/secret/validate-secret-definitions.test.ts +22 -0
- package/src/wirings/secret/validate-secret-definitions.ts +2 -2
- package/src/wirings/trigger/index.ts +0 -1
- package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
- package/src/wirings/trigger/trigger-runner.ts +7 -5
- package/src/wirings/virtual-user/index.ts +0 -16
- package/src/wirings/virtual-user/run-virtual-user.test.ts +18 -9
- package/src/wirings/virtual-user/run-virtual-user.ts +28 -15
- package/src/wirings/virtual-user/virtual-user-agents.test.ts +4 -1
- package/src/wirings/virtual-user/virtual-user-derive.ts +4 -25
- package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +7 -2
- package/src/wirings/virtual-user/virtual-user-dispositions.ts +1 -4
- package/src/wirings/virtual-user/virtual-user-intents.test.ts +13 -3
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +117 -4
- package/src/wirings/workflow/feature.ts +7 -4
- package/src/wirings/workflow/graph/graph-node.ts +1 -1
- package/src/wirings/workflow/graph/graph-runner.test.ts +76 -2
- package/src/wirings/workflow/graph/graph-runner.ts +13 -16
- package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -5
- package/src/wirings/workflow/index.ts +14 -24
- package/src/wirings/workflow/pikku-scenario-service.ts +101 -27
- package/src/wirings/workflow/pikku-workflow-service.test.ts +15 -14
- package/src/wirings/workflow/pikku-workflow-service.ts +214 -743
- package/src/wirings/workflow/scenario-expectations.test.ts +75 -0
- package/src/wirings/workflow/scenario-hooks.test.ts +52 -0
- package/src/wirings/workflow/scenario-step.types.ts +8 -0
- package/src/wirings/workflow/workflow-approval-audit.ts +47 -0
- package/src/wirings/workflow/workflow-approval-policy.test.ts +524 -0
- package/src/wirings/workflow/workflow-approval-policy.ts +68 -0
- package/src/wirings/workflow/workflow-approval.ts +289 -0
- package/src/wirings/workflow/workflow-child-run-session.test.ts +89 -0
- package/src/wirings/workflow/workflow-constants.ts +47 -0
- package/src/wirings/workflow/workflow-dispatch-durability.test.ts +1 -1
- package/src/wirings/workflow/workflow-errors.ts +128 -0
- package/src/wirings/workflow/workflow-inline-authority.test.ts +12 -6
- package/src/wirings/workflow/workflow-meta-resolver.ts +45 -0
- package/src/wirings/workflow/workflow-missing-meta.test.ts +92 -0
- package/src/wirings/workflow/workflow-queue-routing.ts +85 -0
- package/src/wirings/workflow/workflow-queue-wiring.ts +149 -0
- package/src/wirings/workflow/workflow-recovery.ts +162 -0
- package/src/wirings/workflow/workflow-retry-policy.test.ts +1 -1
- package/src/wirings/workflow/workflow-run-authority.test.ts +212 -0
- package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
- package/src/wirings/workflow/workflow-run-ownership.ts +37 -0
- package/src/wirings/workflow/workflow-suspend.ts +61 -0
- package/src/wirings/workflow/workflow.types.ts +7 -9
- package/src/wirings-stay-decoupled.test.ts +127 -0
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/internal.d.ts +0 -3
- package/dist/internal.js +0 -2
- package/dist/middleware/timeout.d.ts +0 -9
- package/dist/middleware/timeout.js +0 -15
- package/dist/pikku-response.d.ts +0 -6
- package/dist/pikku-response.js +0 -6
- package/dist/services/gopass-secrets.d.ts +0 -15
- package/dist/services/gopass-secrets.js +0 -76
- package/dist/services/http-scenario-actors.d.ts +0 -75
- package/dist/services/http-scenario-actors.js +0 -195
- package/dist/services/http-user-flow-actors.d.ts +0 -67
- package/dist/services/http-user-flow-actors.js +0 -193
- package/dist/services/scenario-actors-service.d.ts +0 -127
- package/dist/services/scenario-actors-service.js +0 -40
- package/dist/services/user-flow-actors-service.d.ts +0 -39
- package/dist/wirings/credential/wire-credential.d.ts +0 -48
- package/dist/wirings/credential/wire-credential.js +0 -47
- package/dist/wirings/oauth2/oauth2-client.d.ts +0 -47
- package/dist/wirings/oauth2/oauth2-client.js +0 -263
- package/dist/wirings/oauth2/oauth2-routes.d.ts +0 -35
- package/dist/wirings/oauth2/oauth2-routes.js +0 -146
- package/dist/wirings/scope/wire-scope.d.ts +0 -33
- package/dist/wirings/scope/wire-scope.js +0 -32
- package/dist/wirings/workflow/dsl/index.d.ts +0 -5
- package/dist/wirings/workflow/dsl/index.js +0 -4
- package/dist/wirings/workflow/graph/index.d.ts +0 -5
- package/dist/wirings/workflow/graph/index.js +0 -4
- package/src/internal.ts +0 -10
- /package/dist/{services/user-flow-actors-service.js → wirings/workflow/workflow-run-engine.types.js} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AIStreamChannel,
|
|
3
3
|
AIStreamEvent,
|
|
4
|
+
AIAgentStep,
|
|
4
5
|
AIMessage,
|
|
5
6
|
AIToolCall,
|
|
6
7
|
AIToolResult,
|
|
@@ -9,7 +10,12 @@ import type {
|
|
|
9
10
|
CoreAIAgent,
|
|
10
11
|
AIAgentMemoryConfig,
|
|
11
12
|
} from './ai-agent.types.js'
|
|
13
|
+
import {
|
|
14
|
+
finalizeAgentRun,
|
|
15
|
+
lastUserMessageText,
|
|
16
|
+
} from './ai-agent-finalize.js'
|
|
12
17
|
import { pikkuState, getSingletonServices } from '../../pikku-state.js'
|
|
18
|
+
import { applyInputMiddleware } from './ai-agent-turn.js'
|
|
13
19
|
import { AIProviderNotConfiguredError } from '../../errors/errors.js'
|
|
14
20
|
import { randomUUID } from './ai-agent-utils.js'
|
|
15
21
|
import { SPOKEN_TRANSCRIPT } from './voice-input.js'
|
|
@@ -67,6 +73,8 @@ type PersistingChannel = AIStreamChannel & {
|
|
|
67
73
|
fullText: string
|
|
68
74
|
flush: (opts?: { interrupted?: boolean }) => Promise<void>
|
|
69
75
|
totalUsage: { inputTokens: number; outputTokens: number; model?: string }
|
|
76
|
+
/** Every tool the run called, kept for the whole run rather than per step. */
|
|
77
|
+
runToolCalls: NonNullable<AIAgentStep['toolCalls']>
|
|
70
78
|
}
|
|
71
79
|
|
|
72
80
|
function createPersistingChannel(
|
|
@@ -88,6 +96,9 @@ function createPersistingChannel(
|
|
|
88
96
|
inputTokens: 0,
|
|
89
97
|
outputTokens: 0,
|
|
90
98
|
}
|
|
99
|
+
// Survives the per-step flush below, which clears its own buffers: the run
|
|
100
|
+
// record needs every call the run made, not just the last step's.
|
|
101
|
+
const runToolCalls: NonNullable<AIAgentStep['toolCalls']> = []
|
|
91
102
|
|
|
92
103
|
const flushStep = async (opts?: { interrupted?: boolean }) => {
|
|
93
104
|
if (!storage) return
|
|
@@ -136,6 +147,8 @@ function createPersistingChannel(
|
|
|
136
147
|
})
|
|
137
148
|
}
|
|
138
149
|
|
|
150
|
+
const runToolCallIndex = new Map<string, number>()
|
|
151
|
+
|
|
139
152
|
const channel: PersistingChannel = {
|
|
140
153
|
channelId: parent.channelId,
|
|
141
154
|
openingData: parent.openingData,
|
|
@@ -148,6 +161,9 @@ function createPersistingChannel(
|
|
|
148
161
|
get totalUsage() {
|
|
149
162
|
return totalUsage
|
|
150
163
|
},
|
|
164
|
+
get runToolCalls() {
|
|
165
|
+
return runToolCalls
|
|
166
|
+
},
|
|
151
167
|
flush: flushStep,
|
|
152
168
|
close: () => parent.close(),
|
|
153
169
|
sendBinary: (data) => parent.sendBinary(data),
|
|
@@ -156,6 +172,26 @@ function createPersistingChannel(
|
|
|
156
172
|
// the client was streamed, and an interrupted run has to be able to
|
|
157
173
|
// report the fragment it got through even with persistence turned off.
|
|
158
174
|
if (event.type === 'text-delta') fullText += event.text
|
|
175
|
+
if (event.type === 'tool-call') {
|
|
176
|
+
runToolCallIndex.set(event.toolCallId, runToolCalls.length)
|
|
177
|
+
runToolCalls.push({
|
|
178
|
+
name: event.toolName,
|
|
179
|
+
args: event.args as Record<string, unknown>,
|
|
180
|
+
result: '',
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
if (event.type === 'tool-result') {
|
|
184
|
+
const index = runToolCallIndex.get(event.toolCallId)
|
|
185
|
+
const result =
|
|
186
|
+
typeof event.result === 'string'
|
|
187
|
+
? event.result
|
|
188
|
+
: JSON.stringify(event.result)
|
|
189
|
+
const call = index === undefined ? undefined : runToolCalls[index]
|
|
190
|
+
if (call) {
|
|
191
|
+
call.result = result
|
|
192
|
+
if (event.error) call.error = event.error
|
|
193
|
+
}
|
|
194
|
+
}
|
|
159
195
|
if (storage) {
|
|
160
196
|
switch (event.type) {
|
|
161
197
|
case 'text-delta':
|
|
@@ -176,6 +212,7 @@ function createPersistingChannel(
|
|
|
176
212
|
typeof event.result === 'string'
|
|
177
213
|
? event.result
|
|
178
214
|
: JSON.stringify(event.result),
|
|
215
|
+
...(event.error ? { error: event.error } : {}),
|
|
179
216
|
})
|
|
180
217
|
break
|
|
181
218
|
case 'generative-ui':
|
|
@@ -202,44 +239,67 @@ function createPersistingChannel(
|
|
|
202
239
|
return channel
|
|
203
240
|
}
|
|
204
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Agents already warned about, so a per-request hook does not become a
|
|
244
|
+
* per-request log line.
|
|
245
|
+
*/
|
|
246
|
+
const warnedUnstreamedOutputHooks = new Set<string>()
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* `modifyOutput` does not run on a streamed run at all. Nothing here could act
|
|
250
|
+
* on what it returns — the text has already reached the client, and
|
|
251
|
+
* `createPersistingChannel` flushes each step to storage as it goes, so by the
|
|
252
|
+
* time the run ends the transcript is already written.
|
|
253
|
+
*
|
|
254
|
+
* Rewriting on this path belongs to `modifyOutputStream`, which genuinely
|
|
255
|
+
* works: the stream middleware wraps the persisting channel, so what is stored
|
|
256
|
+
* and accumulated is already what the client was sent. A middleware that
|
|
257
|
+
* rewrites in `modifyOutput` only — a redaction hook, typically — is therefore
|
|
258
|
+
* silently ineffective when the agent is streamed, and is told so once.
|
|
259
|
+
*/
|
|
260
|
+
const warnUnstreamedOutputHooks = (
|
|
261
|
+
agentName: string,
|
|
262
|
+
aiMiddlewares: PikkuAIMiddlewareHooks[],
|
|
263
|
+
logger?: { warn: (...args: any[]) => void }
|
|
264
|
+
) => {
|
|
265
|
+
if (warnedUnstreamedOutputHooks.has(agentName)) return
|
|
266
|
+
const unstreamed = aiMiddlewares.some(
|
|
267
|
+
(mw) => mw.modifyOutput && !mw.modifyOutputStream
|
|
268
|
+
)
|
|
269
|
+
if (!unstreamed) return
|
|
270
|
+
warnedUnstreamedOutputHooks.add(agentName)
|
|
271
|
+
logger?.warn(
|
|
272
|
+
`Agent '${agentName}' has AI middleware with modifyOutput but no modifyOutputStream — modifyOutput does not apply to streamed runs. Implement modifyOutputStream to affect a streamed reply.`
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
|
|
205
276
|
async function postStreamCleanup(
|
|
206
277
|
persistingChannel: PersistingChannel,
|
|
207
|
-
aiMiddlewares: PikkuAIMiddlewareHooks[],
|
|
208
|
-
singletonServices: any,
|
|
209
|
-
messages: AIMessage[],
|
|
210
278
|
aiRunState: AIRunStateService,
|
|
211
|
-
runId: string
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const mw = aiMiddlewares[i]
|
|
218
|
-
if (mw.modifyOutput) {
|
|
219
|
-
const result = await mw.modifyOutput(singletonServices, {
|
|
220
|
-
text: outputText,
|
|
221
|
-
messages: outputMessages,
|
|
222
|
-
usage: {
|
|
223
|
-
inputTokens: usage.inputTokens,
|
|
224
|
-
outputTokens: usage.outputTokens,
|
|
225
|
-
},
|
|
226
|
-
})
|
|
227
|
-
outputText = result.text
|
|
228
|
-
outputMessages = result.messages
|
|
229
|
-
}
|
|
279
|
+
runId: string,
|
|
280
|
+
run: {
|
|
281
|
+
agentName: string
|
|
282
|
+
threadId: string
|
|
283
|
+
resourceId?: string
|
|
284
|
+
input: string
|
|
230
285
|
}
|
|
231
|
-
|
|
232
|
-
await aiRunState
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
286
|
+
): Promise<void> {
|
|
287
|
+
await finalizeAgentRun(aiRunState, {
|
|
288
|
+
runId,
|
|
289
|
+
agentName: run.agentName,
|
|
290
|
+
threadId: run.threadId,
|
|
291
|
+
resourceId: run.resourceId,
|
|
292
|
+
input: run.input,
|
|
293
|
+
// Already what the client received: the stream middleware wraps the
|
|
294
|
+
// persisting channel, so both were accumulated post-rewrite.
|
|
295
|
+
text: persistingChannel.fullText,
|
|
296
|
+
steps: [
|
|
297
|
+
{
|
|
298
|
+
usage: persistingChannel.totalUsage,
|
|
299
|
+
toolCalls: persistingChannel.runToolCalls,
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
usage: persistingChannel.totalUsage,
|
|
243
303
|
})
|
|
244
304
|
}
|
|
245
305
|
|
|
@@ -676,38 +736,26 @@ export async function streamAIAgent(
|
|
|
676
736
|
// One bag per run, shared by every middleware — see PikkuAIMiddlewareHooks.
|
|
677
737
|
const sharedNotes: Record<string, unknown> = {}
|
|
678
738
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
messages:
|
|
685
|
-
instructions:
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
modifiedInstructions = result.instructions
|
|
690
|
-
}
|
|
691
|
-
}
|
|
739
|
+
const { messages: modifiedMessages, instructions: modifiedInstructions } =
|
|
740
|
+
await applyInputMiddleware(
|
|
741
|
+
aiMiddlewares,
|
|
742
|
+
singletonServices,
|
|
743
|
+
{
|
|
744
|
+
messages: runnerParams.messages,
|
|
745
|
+
instructions: runnerParams.instructions,
|
|
746
|
+
},
|
|
747
|
+
sharedNotes
|
|
748
|
+
)
|
|
692
749
|
runnerParams.messages = modifiedMessages
|
|
693
750
|
runnerParams.instructions = modifiedInstructions
|
|
694
751
|
|
|
695
|
-
//
|
|
696
|
-
// has no idea what it said; until this arrives its own message is a blank
|
|
697
|
-
// bubble. Ahead of the run rather than alongside it because the answer starts
|
|
698
|
-
// streaming within a few hundred milliseconds, and a question that appears
|
|
699
|
-
// after its answer reads as the wrong question.
|
|
752
|
+
// knowledge: decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md
|
|
700
753
|
const transcript = sharedNotes[SPOKEN_TRANSCRIPT]
|
|
701
754
|
if (typeof transcript === 'string') {
|
|
702
755
|
channel.send({ type: 'transcript', text: transcript })
|
|
703
756
|
}
|
|
704
757
|
|
|
705
|
-
//
|
|
706
|
-
// spoken turn that is not what arrived over the wire: the wire carried a
|
|
707
|
-
// base64 audio blob, and persisting it would write megabytes of unreadable
|
|
708
|
-
// data into the history while losing the only readable record of the turn.
|
|
709
|
-
// Identity-checked rather than assumed — a middleware is free to rewrite the
|
|
710
|
-
// message list into something with no relation to the turn.
|
|
758
|
+
// knowledge: decisions/internals/thread-history-records-the-transcript-not-the-audio.md
|
|
711
759
|
const lastModified = modifiedMessages[modifiedMessages.length - 1]
|
|
712
760
|
const persistedUserMessage =
|
|
713
761
|
lastModified?.id === userMessage.id ? lastModified : userMessage
|
|
@@ -734,6 +782,8 @@ export async function streamAIAgent(
|
|
|
734
782
|
await storage.saveMessages(threadId, [persistedUserMessage])
|
|
735
783
|
}
|
|
736
784
|
|
|
785
|
+
warnUnstreamedOutputHooks(agentName, aiMiddlewares, singletonServices.logger)
|
|
786
|
+
|
|
737
787
|
const streamMiddleware = aiMiddlewares
|
|
738
788
|
.filter((mw) => mw.modifyOutputStream)
|
|
739
789
|
.map((mw) => {
|
|
@@ -768,7 +818,7 @@ export async function streamAIAgent(
|
|
|
768
818
|
{
|
|
769
819
|
wireInheritedChannelMiddleware: meta?.channelMiddleware,
|
|
770
820
|
wireChannelMiddleware: [
|
|
771
|
-
...(
|
|
821
|
+
...(agent.channelMiddleware ?? []),
|
|
772
822
|
...streamMiddleware,
|
|
773
823
|
],
|
|
774
824
|
}
|
|
@@ -792,10 +842,7 @@ export async function streamAIAgent(
|
|
|
792
842
|
|
|
793
843
|
const credentialFilteredChannel: AIStreamChannel = {
|
|
794
844
|
...wrappedChannel,
|
|
795
|
-
//
|
|
796
|
-
// swallowing the promise here makes every `await channel.send(...)` upstream
|
|
797
|
-
// a no-op — including the one that waits for a buffering hook to flush
|
|
798
|
-
// before the channel closes.
|
|
845
|
+
// knowledge: decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md
|
|
799
846
|
send: (event: AIStreamEvent) => {
|
|
800
847
|
if (
|
|
801
848
|
event.type === 'tool-result' &&
|
|
@@ -863,20 +910,14 @@ export async function streamAIAgent(
|
|
|
863
910
|
return persistingChannel.fullText
|
|
864
911
|
}
|
|
865
912
|
|
|
866
|
-
await postStreamCleanup(
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
runnerParams.messages,
|
|
871
|
-
|
|
872
|
-
runId
|
|
873
|
-
)
|
|
913
|
+
await postStreamCleanup(persistingChannel, aiRunState, runId, {
|
|
914
|
+
agentName,
|
|
915
|
+
threadId,
|
|
916
|
+
resourceId: input.resourceId,
|
|
917
|
+
input: lastUserMessageText(runnerParams.messages),
|
|
918
|
+
})
|
|
874
919
|
|
|
875
|
-
//
|
|
876
|
-
// `done` is the only signal a stream hook gets that the reply is over, and
|
|
877
|
-
// the ones that buffer need it: `voiceOutput` speaks a trailing fragment
|
|
878
|
-
// that never reached a full stop, and waits for audio it has already paid
|
|
879
|
-
// to synthesize. Sent raw, that work is discarded by the `close()` below.
|
|
920
|
+
// knowledge: decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md
|
|
880
921
|
await outputChannel.send({ type: 'done' })
|
|
881
922
|
channel.close()
|
|
882
923
|
return persistingChannel.fullText
|
|
@@ -979,12 +1020,7 @@ export async function interruptAIAgent(
|
|
|
979
1020
|
reason: input.reason ?? 'user',
|
|
980
1021
|
})
|
|
981
1022
|
|
|
982
|
-
//
|
|
983
|
-
// another instance. Returning a bare `false` there is indistinguishable from
|
|
984
|
-
// "it already finished", so the one deployment shape this registry does not
|
|
985
|
-
// cover fails silently — an agent that simply refuses to shut up, with nothing
|
|
986
|
-
// in the logs. Say so instead. See `signalRunInterrupt` for the fix (fan the
|
|
987
|
-
// interrupt out over eventHub so every instance tries locally).
|
|
1023
|
+
// knowledge: decisions/internals/an-agent-run-owned-by-another-instance-says-so.md
|
|
988
1024
|
if (!stopped && run.status === 'running') {
|
|
989
1025
|
logger?.warn(
|
|
990
1026
|
`Could not interrupt run ${input.runId}: it is running in another process. ` +
|
|
@@ -1037,10 +1073,18 @@ export async function resumeAIAgent(
|
|
|
1037
1073
|
|
|
1038
1074
|
await assertAgentAuthorized(agent, params, packageName)
|
|
1039
1075
|
|
|
1040
|
-
|
|
1076
|
+
// The read above is not a claim — concurrent resumes all see the same pending
|
|
1077
|
+
// approval. `resolveApproval` is the claim, and the loser must not go on to
|
|
1078
|
+
// run the tool a second time.
|
|
1079
|
+
const claimed = await aiRunState.resolveApproval(
|
|
1041
1080
|
input.toolCallId,
|
|
1042
1081
|
input.approved ? 'approved' : 'denied'
|
|
1043
1082
|
)
|
|
1083
|
+
if (!claimed) {
|
|
1084
|
+
throw new Error(
|
|
1085
|
+
`Approval for toolCallId ${input.toolCallId} was already resolved by another caller`
|
|
1086
|
+
)
|
|
1087
|
+
}
|
|
1044
1088
|
|
|
1045
1089
|
const { storage } = resolveMemoryServices(agent, singletonServices)
|
|
1046
1090
|
const memoryConfig = agent.memory
|
|
@@ -1199,16 +1243,17 @@ export async function resumeAIAgent(
|
|
|
1199
1243
|
typeof pending.args === 'string' ? JSON.parse(pending.args) : pending.args
|
|
1200
1244
|
|
|
1201
1245
|
let toolResult: unknown
|
|
1202
|
-
let
|
|
1246
|
+
let toolError: string | undefined
|
|
1203
1247
|
try {
|
|
1204
1248
|
toolResult = await matchingTool.execute(toolArgs)
|
|
1205
1249
|
} catch (execErr: any) {
|
|
1206
1250
|
if (execErr?.payload?.error === 'missing_credential') {
|
|
1207
1251
|
toolResult = execErr.payload
|
|
1252
|
+
toolError = 'missing_credential'
|
|
1208
1253
|
} else {
|
|
1209
|
-
|
|
1254
|
+
toolError = execErr instanceof Error ? execErr.message : String(execErr)
|
|
1255
|
+
toolResult = `Error: ${toolError}`
|
|
1210
1256
|
}
|
|
1211
|
-
isError = true
|
|
1212
1257
|
}
|
|
1213
1258
|
|
|
1214
1259
|
const resultStr =
|
|
@@ -1235,7 +1280,7 @@ export async function resumeAIAgent(
|
|
|
1235
1280
|
toolCallId: input.toolCallId,
|
|
1236
1281
|
toolName: pending.toolName,
|
|
1237
1282
|
result: toolResult,
|
|
1238
|
-
...(
|
|
1283
|
+
...(toolError ? { error: toolError } : {}),
|
|
1239
1284
|
})
|
|
1240
1285
|
}
|
|
1241
1286
|
|
|
@@ -1317,26 +1362,23 @@ async function continueAfterToolResult(
|
|
|
1317
1362
|
// One bag per run, shared by every middleware — see PikkuAIMiddlewareHooks.
|
|
1318
1363
|
const sharedNotes: Record<string, unknown> = {}
|
|
1319
1364
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
shared: sharedNotes,
|
|
1328
|
-
})
|
|
1329
|
-
modifiedMessages = result.messages
|
|
1330
|
-
modifiedInstructions = result.instructions
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1365
|
+
const { messages: modifiedMessages, instructions: modifiedInstructions } =
|
|
1366
|
+
await applyInputMiddleware(
|
|
1367
|
+
aiMiddlewares,
|
|
1368
|
+
singletonServices,
|
|
1369
|
+
{ messages: trimmedMessages, instructions: instructions },
|
|
1370
|
+
sharedNotes
|
|
1371
|
+
)
|
|
1333
1372
|
|
|
1334
|
-
//
|
|
1335
|
-
// listening to the same voice, and after an approval it is where most of the
|
|
1336
|
-
// reply actually gets spoken — an approved delete is followed by the agent
|
|
1337
|
-
// talking about it, and that is a normal thing to talk over.
|
|
1373
|
+
// knowledge: decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md
|
|
1338
1374
|
const interruptHandle = registerInterruptibleRun(run.runId)
|
|
1339
1375
|
|
|
1376
|
+
warnUnstreamedOutputHooks(
|
|
1377
|
+
run.agentName,
|
|
1378
|
+
aiMiddlewares,
|
|
1379
|
+
singletonServices.logger
|
|
1380
|
+
)
|
|
1381
|
+
|
|
1340
1382
|
const streamMiddleware = aiMiddlewares
|
|
1341
1383
|
.filter((mw) => mw.modifyOutputStream)
|
|
1342
1384
|
.map((mw) => {
|
|
@@ -1369,7 +1411,7 @@ async function continueAfterToolResult(
|
|
|
1369
1411
|
{
|
|
1370
1412
|
wireInheritedChannelMiddleware: meta?.channelMiddleware,
|
|
1371
1413
|
wireChannelMiddleware: [
|
|
1372
|
-
...(
|
|
1414
|
+
...(agent.channelMiddleware ?? []),
|
|
1373
1415
|
...streamMiddleware,
|
|
1374
1416
|
],
|
|
1375
1417
|
}
|
|
@@ -1458,19 +1500,12 @@ async function continueAfterToolResult(
|
|
|
1458
1500
|
return
|
|
1459
1501
|
}
|
|
1460
1502
|
|
|
1461
|
-
await postStreamCleanup(
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
runnerParams.messages,
|
|
1466
|
-
aiRunState,
|
|
1467
|
-
run.runId
|
|
1468
|
-
)
|
|
1503
|
+
await postStreamCleanup(persistingChannel, aiRunState, run.runId, {
|
|
1504
|
+
...run,
|
|
1505
|
+
input: lastUserMessageText(runnerParams.messages),
|
|
1506
|
+
})
|
|
1469
1507
|
|
|
1470
|
-
//
|
|
1471
|
-
// matters more here. After an approval, most of what gets spoken is the
|
|
1472
|
-
// agent talking about what it just did, so this is the half of the reply a
|
|
1473
|
-
// dropped flush would silence.
|
|
1508
|
+
// knowledge: decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md
|
|
1474
1509
|
await wrappedChannel.send({ type: 'done' })
|
|
1475
1510
|
channel.close()
|
|
1476
1511
|
} catch (err) {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { toAccumulatedStep } from './ai-agent-turn.js'
|
|
5
|
+
import type { AIAgentStepResult } from '../../services/ai-agent-runner-service.js'
|
|
6
|
+
|
|
7
|
+
const stepResult = (
|
|
8
|
+
overrides?: Partial<AIAgentStepResult>
|
|
9
|
+
): AIAgentStepResult => ({
|
|
10
|
+
text: '',
|
|
11
|
+
toolCalls: [],
|
|
12
|
+
toolResults: [],
|
|
13
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
14
|
+
finishReason: 'stop',
|
|
15
|
+
...overrides,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe('toAccumulatedStep', () => {
|
|
19
|
+
test('carries a tool failure as its own field, not only as rendered text', () => {
|
|
20
|
+
const step = toAccumulatedStep(
|
|
21
|
+
stepResult({
|
|
22
|
+
toolCalls: [
|
|
23
|
+
{ toolCallId: 'call-1', toolName: 'lookupOrder', args: { id: 7 } },
|
|
24
|
+
],
|
|
25
|
+
toolResults: [
|
|
26
|
+
{
|
|
27
|
+
toolCallId: 'call-1',
|
|
28
|
+
toolName: 'lookupOrder',
|
|
29
|
+
result: 'Error: order service unreachable',
|
|
30
|
+
error: 'order service unreachable',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
})
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
assert.deepEqual(step.toolCalls, [
|
|
37
|
+
{
|
|
38
|
+
name: 'lookupOrder',
|
|
39
|
+
args: { id: 7 },
|
|
40
|
+
result: 'Error: order service unreachable',
|
|
41
|
+
error: 'order service unreachable',
|
|
42
|
+
},
|
|
43
|
+
])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('leaves error unset on a tool that returned normally, even if it says Error', () => {
|
|
47
|
+
// A tool is allowed to return the word "Error" — which is exactly why
|
|
48
|
+
// "did this fail" cannot be answered by matching on the result text.
|
|
49
|
+
const step = toAccumulatedStep(
|
|
50
|
+
stepResult({
|
|
51
|
+
toolCalls: [
|
|
52
|
+
{ toolCallId: 'call-1', toolName: 'searchLogs', args: { q: 'x' } },
|
|
53
|
+
],
|
|
54
|
+
toolResults: [
|
|
55
|
+
{
|
|
56
|
+
toolCallId: 'call-1',
|
|
57
|
+
toolName: 'searchLogs',
|
|
58
|
+
result: 'Error: connection refused (1 match)',
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
})
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
assert.equal(step.toolCalls[0].error, undefined)
|
|
65
|
+
assert.ok(!('error' in step.toolCalls[0]))
|
|
66
|
+
})
|
|
67
|
+
})
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { CoreSingletonServices } from '../../types/core.types.js'
|
|
2
|
+
import type { PikkuAIMiddlewareHooks } from './ai-agent.types.js'
|
|
3
|
+
|
|
4
|
+
/** Exactly what `modifyInput` accepts and returns, taken from the hook itself. */
|
|
5
|
+
type TurnInput = Pick<
|
|
6
|
+
Parameters<NonNullable<PikkuAIMiddlewareHooks['modifyInput']>>[1],
|
|
7
|
+
'messages' | 'instructions'
|
|
8
|
+
>
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Run every `modifyInput` hook in order, threading each result into the next.
|
|
12
|
+
*
|
|
13
|
+
* Shared by the four places a turn starts — first turn and resume, on both the
|
|
14
|
+
* streaming and non-streaming paths. `sharedNotes` is one bag per run that the
|
|
15
|
+
* caller keeps, because hooks use it to pass things forward (`voiceInput`
|
|
16
|
+
* leaves the transcript there for `voiceOutput`).
|
|
17
|
+
*/
|
|
18
|
+
export const applyInputMiddleware = async (
|
|
19
|
+
aiMiddlewares: PikkuAIMiddlewareHooks[],
|
|
20
|
+
singletonServices: CoreSingletonServices,
|
|
21
|
+
input: TurnInput,
|
|
22
|
+
sharedNotes: Record<string, unknown>
|
|
23
|
+
): Promise<TurnInput> => {
|
|
24
|
+
let { messages, instructions } = input
|
|
25
|
+
for (const mw of aiMiddlewares) {
|
|
26
|
+
if (!mw.modifyInput) continue
|
|
27
|
+
const result = await mw.modifyInput(singletonServices, {
|
|
28
|
+
messages,
|
|
29
|
+
instructions,
|
|
30
|
+
shared: sharedNotes,
|
|
31
|
+
})
|
|
32
|
+
messages = result.messages
|
|
33
|
+
instructions = result.instructions
|
|
34
|
+
}
|
|
35
|
+
return { messages, instructions }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The step fields both agent paths hand to `afterStep`, taken from the hook. */
|
|
39
|
+
type StepResult = Omit<
|
|
40
|
+
Parameters<NonNullable<PikkuAIMiddlewareHooks['afterStep']>>[1],
|
|
41
|
+
'stepNumber'
|
|
42
|
+
>
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Notify every `afterStep` hook. The payload is the step verbatim plus its
|
|
46
|
+
* ordinal — a hook that wants the run's totals accumulates them itself.
|
|
47
|
+
*/
|
|
48
|
+
export const notifyAfterStep = async (
|
|
49
|
+
aiMiddlewares: PikkuAIMiddlewareHooks[],
|
|
50
|
+
singletonServices: CoreSingletonServices,
|
|
51
|
+
stepNumber: number,
|
|
52
|
+
stepResult: StepResult
|
|
53
|
+
): Promise<void> => {
|
|
54
|
+
for (const mw of aiMiddlewares) {
|
|
55
|
+
if (!mw.afterStep) continue
|
|
56
|
+
await mw.afterStep(singletonServices, {
|
|
57
|
+
stepNumber,
|
|
58
|
+
text: stepResult.text,
|
|
59
|
+
toolCalls: stepResult.toolCalls,
|
|
60
|
+
toolResults: stepResult.toolResults,
|
|
61
|
+
usage: stepResult.usage,
|
|
62
|
+
finishReason: stepResult.finishReason,
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The step shape kept on the run: each tool call paired with its result.
|
|
69
|
+
*
|
|
70
|
+
* A result is stringified unless it already is a string, because this is what
|
|
71
|
+
* gets persisted and read back as history — an object there is unreadable.
|
|
72
|
+
*/
|
|
73
|
+
export const toAccumulatedStep = (stepResult: StepResult) => ({
|
|
74
|
+
usage: stepResult.usage,
|
|
75
|
+
toolCalls: stepResult.toolCalls.map((tc) => {
|
|
76
|
+
const tr = stepResult.toolResults.find(
|
|
77
|
+
(r) => r.toolCallId === tc.toolCallId
|
|
78
|
+
)
|
|
79
|
+
return {
|
|
80
|
+
name: tc.toolName,
|
|
81
|
+
args: tc.args as Record<string, unknown>,
|
|
82
|
+
result:
|
|
83
|
+
typeof tr?.result === 'string'
|
|
84
|
+
? tr.result
|
|
85
|
+
: JSON.stringify(tr?.result ?? ''),
|
|
86
|
+
...(tr?.error ? { error: tr.error } : {}),
|
|
87
|
+
}
|
|
88
|
+
}),
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
type PendingApproval = {
|
|
92
|
+
toolName: string
|
|
93
|
+
args: unknown
|
|
94
|
+
reason?: string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
type ToolDef = {
|
|
98
|
+
name: string
|
|
99
|
+
approvalDescriptionFn?: (args: any) => Promise<string> | string
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Fill in each approval's human-readable reason from the tool's own
|
|
104
|
+
* `approvalDescriptionFn`, where it did not supply one.
|
|
105
|
+
*
|
|
106
|
+
* A description that throws is swallowed: the approval still has to be raised,
|
|
107
|
+
* and a gate that disappears because its label failed to render is the worst
|
|
108
|
+
* available outcome.
|
|
109
|
+
*/
|
|
110
|
+
export const describeApprovals = async (
|
|
111
|
+
approvals: PendingApproval[],
|
|
112
|
+
tools: ToolDef[]
|
|
113
|
+
): Promise<void> => {
|
|
114
|
+
for (const approval of approvals) {
|
|
115
|
+
if (approval.reason) continue
|
|
116
|
+
const toolDef = tools.find((t) => t.name === approval.toolName)
|
|
117
|
+
if (!toolDef?.approvalDescriptionFn) continue
|
|
118
|
+
try {
|
|
119
|
+
approval.reason = await toolDef.approvalDescriptionFn(approval.args)
|
|
120
|
+
} catch {}
|
|
121
|
+
}
|
|
122
|
+
}
|