@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,4 +1,6 @@
|
|
|
1
|
+
import { finalizeAgentRun, lastUserMessageText, } from './ai-agent-finalize.js';
|
|
1
2
|
import { pikkuState, getSingletonServices } from '../../pikku-state.js';
|
|
3
|
+
import { applyInputMiddleware } from './ai-agent-turn.js';
|
|
2
4
|
import { AIProviderNotConfiguredError } from '../../errors/errors.js';
|
|
3
5
|
import { randomUUID } from './ai-agent-utils.js';
|
|
4
6
|
import { SPOKEN_TRANSCRIPT } from './voice-input.js';
|
|
@@ -17,6 +19,9 @@ function createPersistingChannel(parent, storage, threadId, logger) {
|
|
|
17
19
|
inputTokens: 0,
|
|
18
20
|
outputTokens: 0,
|
|
19
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 = [];
|
|
20
25
|
const flushStep = async (opts) => {
|
|
21
26
|
if (!storage)
|
|
22
27
|
return;
|
|
@@ -62,6 +67,7 @@ function createPersistingChannel(parent, storage, threadId, logger) {
|
|
|
62
67
|
});
|
|
63
68
|
});
|
|
64
69
|
};
|
|
70
|
+
const runToolCallIndex = new Map();
|
|
65
71
|
const channel = {
|
|
66
72
|
channelId: parent.channelId,
|
|
67
73
|
openingData: parent.openingData,
|
|
@@ -74,6 +80,9 @@ function createPersistingChannel(parent, storage, threadId, logger) {
|
|
|
74
80
|
get totalUsage() {
|
|
75
81
|
return totalUsage;
|
|
76
82
|
},
|
|
83
|
+
get runToolCalls() {
|
|
84
|
+
return runToolCalls;
|
|
85
|
+
},
|
|
77
86
|
flush: flushStep,
|
|
78
87
|
close: () => parent.close(),
|
|
79
88
|
sendBinary: (data) => parent.sendBinary(data),
|
|
@@ -83,6 +92,26 @@ function createPersistingChannel(parent, storage, threadId, logger) {
|
|
|
83
92
|
// report the fragment it got through even with persistence turned off.
|
|
84
93
|
if (event.type === 'text-delta')
|
|
85
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
|
+
}
|
|
86
115
|
if (storage) {
|
|
87
116
|
switch (event.type) {
|
|
88
117
|
case 'text-delta':
|
|
@@ -102,6 +131,7 @@ function createPersistingChannel(parent, storage, threadId, logger) {
|
|
|
102
131
|
result: typeof event.result === 'string'
|
|
103
132
|
? event.result
|
|
104
133
|
: JSON.stringify(event.result),
|
|
134
|
+
...(event.error ? { error: event.error } : {}),
|
|
105
135
|
});
|
|
106
136
|
break;
|
|
107
137
|
case 'generative-ui':
|
|
@@ -128,36 +158,49 @@ function createPersistingChannel(parent, storage, threadId, logger) {
|
|
|
128
158
|
};
|
|
129
159
|
return channel;
|
|
130
160
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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,
|
|
161
204
|
});
|
|
162
205
|
}
|
|
163
206
|
async function runStreamStepLoop(params) {
|
|
@@ -414,36 +457,18 @@ export async function streamAIAgent(agentName, input, channel, params, agentSess
|
|
|
414
457
|
];
|
|
415
458
|
// One bag per run, shared by every middleware — see PikkuAIMiddlewareHooks.
|
|
416
459
|
const sharedNotes = {};
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
const result = await mw.modifyInput(singletonServices, {
|
|
422
|
-
messages: modifiedMessages,
|
|
423
|
-
instructions: modifiedInstructions,
|
|
424
|
-
shared: sharedNotes,
|
|
425
|
-
});
|
|
426
|
-
modifiedMessages = result.messages;
|
|
427
|
-
modifiedInstructions = result.instructions;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
460
|
+
const { messages: modifiedMessages, instructions: modifiedInstructions } = await applyInputMiddleware(aiMiddlewares, singletonServices, {
|
|
461
|
+
messages: runnerParams.messages,
|
|
462
|
+
instructions: runnerParams.instructions,
|
|
463
|
+
}, sharedNotes);
|
|
430
464
|
runnerParams.messages = modifiedMessages;
|
|
431
465
|
runnerParams.instructions = modifiedInstructions;
|
|
432
|
-
//
|
|
433
|
-
// has no idea what it said; until this arrives its own message is a blank
|
|
434
|
-
// bubble. Ahead of the run rather than alongside it because the answer starts
|
|
435
|
-
// streaming within a few hundred milliseconds, and a question that appears
|
|
436
|
-
// after its answer reads as the wrong question.
|
|
466
|
+
// knowledge: decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md
|
|
437
467
|
const transcript = sharedNotes[SPOKEN_TRANSCRIPT];
|
|
438
468
|
if (typeof transcript === 'string') {
|
|
439
469
|
channel.send({ type: 'transcript', text: transcript });
|
|
440
470
|
}
|
|
441
|
-
//
|
|
442
|
-
// spoken turn that is not what arrived over the wire: the wire carried a
|
|
443
|
-
// base64 audio blob, and persisting it would write megabytes of unreadable
|
|
444
|
-
// data into the history while losing the only readable record of the turn.
|
|
445
|
-
// Identity-checked rather than assumed — a middleware is free to rewrite the
|
|
446
|
-
// message list into something with no relation to the turn.
|
|
471
|
+
// knowledge: decisions/internals/thread-history-records-the-transcript-not-the-audio.md
|
|
447
472
|
const lastModified = modifiedMessages[modifiedMessages.length - 1];
|
|
448
473
|
const persistedUserMessage = lastModified?.id === userMessage.id ? lastModified : userMessage;
|
|
449
474
|
const runId = await aiRunState.createRun({
|
|
@@ -465,6 +490,7 @@ export async function streamAIAgent(agentName, input, channel, params, agentSess
|
|
|
465
490
|
if (storage) {
|
|
466
491
|
await storage.saveMessages(threadId, [persistedUserMessage]);
|
|
467
492
|
}
|
|
493
|
+
warnUnstreamedOutputHooks(agentName, aiMiddlewares, singletonServices.logger);
|
|
468
494
|
const streamMiddleware = aiMiddlewares
|
|
469
495
|
.filter((mw) => mw.modifyOutputStream)
|
|
470
496
|
.map((mw) => {
|
|
@@ -508,10 +534,7 @@ export async function streamAIAgent(agentName, input, channel, params, agentSess
|
|
|
508
534
|
: persistingChannel;
|
|
509
535
|
const credentialFilteredChannel = {
|
|
510
536
|
...wrappedChannel,
|
|
511
|
-
//
|
|
512
|
-
// swallowing the promise here makes every `await channel.send(...)` upstream
|
|
513
|
-
// a no-op — including the one that waits for a buffering hook to flush
|
|
514
|
-
// before the channel closes.
|
|
537
|
+
// knowledge: decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md
|
|
515
538
|
send: (event) => {
|
|
516
539
|
if (event.type === 'tool-result' &&
|
|
517
540
|
event.result !== null &&
|
|
@@ -558,12 +581,13 @@ export async function streamAIAgent(agentName, input, channel, params, agentSess
|
|
|
558
581
|
await handleCredentialRequests(loopResult.credentialRequests, runId, channel, aiRunState, persistingChannel);
|
|
559
582
|
return persistingChannel.fullText;
|
|
560
583
|
}
|
|
561
|
-
await postStreamCleanup(persistingChannel,
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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
|
|
567
591
|
await outputChannel.send({ type: 'done' });
|
|
568
592
|
channel.close();
|
|
569
593
|
return persistingChannel.fullText;
|
|
@@ -650,12 +674,7 @@ export async function interruptAIAgent(input, params) {
|
|
|
650
674
|
const stopped = signalRunInterrupt(input.runId, {
|
|
651
675
|
reason: input.reason ?? 'user',
|
|
652
676
|
});
|
|
653
|
-
//
|
|
654
|
-
// another instance. Returning a bare `false` there is indistinguishable from
|
|
655
|
-
// "it already finished", so the one deployment shape this registry does not
|
|
656
|
-
// cover fails silently — an agent that simply refuses to shut up, with nothing
|
|
657
|
-
// in the logs. Say so instead. See `signalRunInterrupt` for the fix (fan the
|
|
658
|
-
// interrupt out over eventHub so every instance tries locally).
|
|
677
|
+
// knowledge: decisions/internals/an-agent-run-owned-by-another-instance-says-so.md
|
|
659
678
|
if (!stopped && run.status === 'running') {
|
|
660
679
|
logger?.warn(`Could not interrupt run ${input.runId}: it is running in another process. ` +
|
|
661
680
|
'Interrupts are process-local; fan them out over eventHub to support multiple instances.');
|
|
@@ -681,7 +700,13 @@ export async function resumeAIAgent(input, channel, params, options) {
|
|
|
681
700
|
}
|
|
682
701
|
const { agent, packageName, resolvedName } = resolveAgent(run.agentName);
|
|
683
702
|
await assertAgentAuthorized(agent, params, packageName);
|
|
684
|
-
|
|
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
|
+
}
|
|
685
710
|
const { storage } = resolveMemoryServices(agent, singletonServices);
|
|
686
711
|
const memoryConfig = agent.memory;
|
|
687
712
|
const agentRunner = singletonServices.aiAgentRunner;
|
|
@@ -789,18 +814,19 @@ export async function resumeAIAgent(input, channel, params, options) {
|
|
|
789
814
|
}
|
|
790
815
|
const toolArgs = typeof pending.args === 'string' ? JSON.parse(pending.args) : pending.args;
|
|
791
816
|
let toolResult;
|
|
792
|
-
let
|
|
817
|
+
let toolError;
|
|
793
818
|
try {
|
|
794
819
|
toolResult = await matchingTool.execute(toolArgs);
|
|
795
820
|
}
|
|
796
821
|
catch (execErr) {
|
|
797
822
|
if (execErr?.payload?.error === 'missing_credential') {
|
|
798
823
|
toolResult = execErr.payload;
|
|
824
|
+
toolError = 'missing_credential';
|
|
799
825
|
}
|
|
800
826
|
else {
|
|
801
|
-
|
|
827
|
+
toolError = execErr instanceof Error ? execErr.message : String(execErr);
|
|
828
|
+
toolResult = `Error: ${toolError}`;
|
|
802
829
|
}
|
|
803
|
-
isError = true;
|
|
804
830
|
}
|
|
805
831
|
const resultStr = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult);
|
|
806
832
|
if (storage) {
|
|
@@ -824,7 +850,7 @@ export async function resumeAIAgent(input, channel, params, options) {
|
|
|
824
850
|
toolCallId: input.toolCallId,
|
|
825
851
|
toolName: pending.toolName,
|
|
826
852
|
result: toolResult,
|
|
827
|
-
...(
|
|
853
|
+
...(toolError ? { error: toolError } : {}),
|
|
828
854
|
});
|
|
829
855
|
}
|
|
830
856
|
const updatedRun = await aiRunState.getRun(run.runId);
|
|
@@ -865,24 +891,10 @@ async function continueAfterToolResult(run, agent, packageName, resolvedName, st
|
|
|
865
891
|
];
|
|
866
892
|
// One bag per run, shared by every middleware — see PikkuAIMiddlewareHooks.
|
|
867
893
|
const sharedNotes = {};
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
for (const mw of aiMiddlewares) {
|
|
871
|
-
if (mw.modifyInput) {
|
|
872
|
-
const result = await mw.modifyInput(singletonServices, {
|
|
873
|
-
messages: modifiedMessages,
|
|
874
|
-
instructions: modifiedInstructions,
|
|
875
|
-
shared: sharedNotes,
|
|
876
|
-
});
|
|
877
|
-
modifiedMessages = result.messages;
|
|
878
|
-
modifiedInstructions = result.instructions;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
// Resuming is as interruptible as the first turn. It is the same person
|
|
882
|
-
// listening to the same voice, and after an approval it is where most of the
|
|
883
|
-
// reply actually gets spoken — an approved delete is followed by the agent
|
|
884
|
-
// talking about it, and that is a normal thing to talk over.
|
|
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
|
|
885
896
|
const interruptHandle = registerInterruptibleRun(run.runId);
|
|
897
|
+
warnUnstreamedOutputHooks(run.agentName, aiMiddlewares, singletonServices.logger);
|
|
886
898
|
const streamMiddleware = aiMiddlewares
|
|
887
899
|
.filter((mw) => mw.modifyOutputStream)
|
|
888
900
|
.map((mw) => {
|
|
@@ -960,11 +972,11 @@ async function continueAfterToolResult(run, agent, packageName, resolvedName, st
|
|
|
960
972
|
await handleCredentialRequests(loopResult.credentialRequests, run.runId, channel, aiRunState, persistingChannel);
|
|
961
973
|
return;
|
|
962
974
|
}
|
|
963
|
-
await postStreamCleanup(persistingChannel,
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
//
|
|
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
|
|
968
980
|
await wrappedChannel.send({ type: 'done' });
|
|
969
981
|
channel.close();
|
|
970
982
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { CoreSingletonServices } from '../../types/core.types.js';
|
|
2
|
+
import type { PikkuAIMiddlewareHooks } from './ai-agent.types.js';
|
|
3
|
+
/** Exactly what `modifyInput` accepts and returns, taken from the hook itself. */
|
|
4
|
+
type TurnInput = Pick<Parameters<NonNullable<PikkuAIMiddlewareHooks['modifyInput']>>[1], 'messages' | 'instructions'>;
|
|
5
|
+
/**
|
|
6
|
+
* Run every `modifyInput` hook in order, threading each result into the next.
|
|
7
|
+
*
|
|
8
|
+
* Shared by the four places a turn starts — first turn and resume, on both the
|
|
9
|
+
* streaming and non-streaming paths. `sharedNotes` is one bag per run that the
|
|
10
|
+
* caller keeps, because hooks use it to pass things forward (`voiceInput`
|
|
11
|
+
* leaves the transcript there for `voiceOutput`).
|
|
12
|
+
*/
|
|
13
|
+
export declare const applyInputMiddleware: (aiMiddlewares: PikkuAIMiddlewareHooks[], singletonServices: CoreSingletonServices, input: TurnInput, sharedNotes: Record<string, unknown>) => Promise<TurnInput>;
|
|
14
|
+
/** The step fields both agent paths hand to `afterStep`, taken from the hook. */
|
|
15
|
+
type StepResult = Omit<Parameters<NonNullable<PikkuAIMiddlewareHooks['afterStep']>>[1], 'stepNumber'>;
|
|
16
|
+
/**
|
|
17
|
+
* Notify every `afterStep` hook. The payload is the step verbatim plus its
|
|
18
|
+
* ordinal — a hook that wants the run's totals accumulates them itself.
|
|
19
|
+
*/
|
|
20
|
+
export declare const notifyAfterStep: (aiMiddlewares: PikkuAIMiddlewareHooks[], singletonServices: CoreSingletonServices, stepNumber: number, stepResult: StepResult) => Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* The step shape kept on the run: each tool call paired with its result.
|
|
23
|
+
*
|
|
24
|
+
* A result is stringified unless it already is a string, because this is what
|
|
25
|
+
* gets persisted and read back as history — an object there is unreadable.
|
|
26
|
+
*/
|
|
27
|
+
export declare const toAccumulatedStep: (stepResult: StepResult) => {
|
|
28
|
+
usage: {
|
|
29
|
+
inputTokens: number;
|
|
30
|
+
outputTokens: number;
|
|
31
|
+
};
|
|
32
|
+
toolCalls: {
|
|
33
|
+
error?: string | undefined;
|
|
34
|
+
name: string;
|
|
35
|
+
args: Record<string, unknown>;
|
|
36
|
+
result: string;
|
|
37
|
+
}[];
|
|
38
|
+
};
|
|
39
|
+
type PendingApproval = {
|
|
40
|
+
toolName: string;
|
|
41
|
+
args: unknown;
|
|
42
|
+
reason?: string;
|
|
43
|
+
};
|
|
44
|
+
type ToolDef = {
|
|
45
|
+
name: string;
|
|
46
|
+
approvalDescriptionFn?: (args: any) => Promise<string> | string;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Fill in each approval's human-readable reason from the tool's own
|
|
50
|
+
* `approvalDescriptionFn`, where it did not supply one.
|
|
51
|
+
*
|
|
52
|
+
* A description that throws is swallowed: the approval still has to be raised,
|
|
53
|
+
* and a gate that disappears because its label failed to render is the worst
|
|
54
|
+
* available outcome.
|
|
55
|
+
*/
|
|
56
|
+
export declare const describeApprovals: (approvals: PendingApproval[], tools: ToolDef[]) => Promise<void>;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run every `modifyInput` hook in order, threading each result into the next.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the four places a turn starts — first turn and resume, on both the
|
|
5
|
+
* streaming and non-streaming paths. `sharedNotes` is one bag per run that the
|
|
6
|
+
* caller keeps, because hooks use it to pass things forward (`voiceInput`
|
|
7
|
+
* leaves the transcript there for `voiceOutput`).
|
|
8
|
+
*/
|
|
9
|
+
export const applyInputMiddleware = async (aiMiddlewares, singletonServices, input, sharedNotes) => {
|
|
10
|
+
let { messages, instructions } = input;
|
|
11
|
+
for (const mw of aiMiddlewares) {
|
|
12
|
+
if (!mw.modifyInput)
|
|
13
|
+
continue;
|
|
14
|
+
const result = await mw.modifyInput(singletonServices, {
|
|
15
|
+
messages,
|
|
16
|
+
instructions,
|
|
17
|
+
shared: sharedNotes,
|
|
18
|
+
});
|
|
19
|
+
messages = result.messages;
|
|
20
|
+
instructions = result.instructions;
|
|
21
|
+
}
|
|
22
|
+
return { messages, instructions };
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Notify every `afterStep` hook. The payload is the step verbatim plus its
|
|
26
|
+
* ordinal — a hook that wants the run's totals accumulates them itself.
|
|
27
|
+
*/
|
|
28
|
+
export const notifyAfterStep = async (aiMiddlewares, singletonServices, stepNumber, stepResult) => {
|
|
29
|
+
for (const mw of aiMiddlewares) {
|
|
30
|
+
if (!mw.afterStep)
|
|
31
|
+
continue;
|
|
32
|
+
await mw.afterStep(singletonServices, {
|
|
33
|
+
stepNumber,
|
|
34
|
+
text: stepResult.text,
|
|
35
|
+
toolCalls: stepResult.toolCalls,
|
|
36
|
+
toolResults: stepResult.toolResults,
|
|
37
|
+
usage: stepResult.usage,
|
|
38
|
+
finishReason: stepResult.finishReason,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The step shape kept on the run: each tool call paired with its result.
|
|
44
|
+
*
|
|
45
|
+
* A result is stringified unless it already is a string, because this is what
|
|
46
|
+
* gets persisted and read back as history — an object there is unreadable.
|
|
47
|
+
*/
|
|
48
|
+
export const toAccumulatedStep = (stepResult) => ({
|
|
49
|
+
usage: stepResult.usage,
|
|
50
|
+
toolCalls: stepResult.toolCalls.map((tc) => {
|
|
51
|
+
const tr = stepResult.toolResults.find((r) => r.toolCallId === tc.toolCallId);
|
|
52
|
+
return {
|
|
53
|
+
name: tc.toolName,
|
|
54
|
+
args: tc.args,
|
|
55
|
+
result: typeof tr?.result === 'string'
|
|
56
|
+
? tr.result
|
|
57
|
+
: JSON.stringify(tr?.result ?? ''),
|
|
58
|
+
...(tr?.error ? { error: tr.error } : {}),
|
|
59
|
+
};
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Fill in each approval's human-readable reason from the tool's own
|
|
64
|
+
* `approvalDescriptionFn`, where it did not supply one.
|
|
65
|
+
*
|
|
66
|
+
* A description that throws is swallowed: the approval still has to be raised,
|
|
67
|
+
* and a gate that disappears because its label failed to render is the worst
|
|
68
|
+
* available outcome.
|
|
69
|
+
*/
|
|
70
|
+
export const describeApprovals = async (approvals, tools) => {
|
|
71
|
+
for (const approval of approvals) {
|
|
72
|
+
if (approval.reason)
|
|
73
|
+
continue;
|
|
74
|
+
const toolDef = tools.find((t) => t.name === approval.toolName);
|
|
75
|
+
if (!toolDef?.approvalDescriptionFn)
|
|
76
|
+
continue;
|
|
77
|
+
try {
|
|
78
|
+
approval.reason = await toolDef.approvalDescriptionFn(approval.args);
|
|
79
|
+
}
|
|
80
|
+
catch { }
|
|
81
|
+
}
|
|
82
|
+
};
|
|
@@ -43,6 +43,13 @@ export interface AIToolResult {
|
|
|
43
43
|
id: string;
|
|
44
44
|
name: string;
|
|
45
45
|
result: string;
|
|
46
|
+
/**
|
|
47
|
+
* Set when the tool threw rather than returned. Carried separately from
|
|
48
|
+
* `result`, which is a rendered string by the time it is persisted — a tool
|
|
49
|
+
* may legitimately return text beginning `Error:`, so the prefix cannot be
|
|
50
|
+
* read as a failure signal.
|
|
51
|
+
*/
|
|
52
|
+
error?: string;
|
|
46
53
|
}
|
|
47
54
|
export interface AIMessage {
|
|
48
55
|
id: string;
|
|
@@ -79,6 +86,8 @@ export interface AIAgentStep {
|
|
|
79
86
|
name: string;
|
|
80
87
|
args: Record<string, unknown>;
|
|
81
88
|
result: string;
|
|
89
|
+
/** The failure message, when the tool threw rather than returned. */
|
|
90
|
+
error?: string;
|
|
82
91
|
}[];
|
|
83
92
|
}
|
|
84
93
|
export interface AIAgentInputAttachment {
|
|
@@ -216,6 +225,21 @@ export interface PikkuAIMiddlewareHooks<State extends Record<string, unknown> =
|
|
|
216
225
|
*/
|
|
217
226
|
signal?: AbortSignal;
|
|
218
227
|
}) => Promise<AIStreamEvent | AIStreamEvent[] | null> | AIStreamEvent | AIStreamEvent[] | null;
|
|
228
|
+
/**
|
|
229
|
+
* The last chance to rewrite what the run produced, before it is persisted
|
|
230
|
+
* and returned.
|
|
231
|
+
*
|
|
232
|
+
* It does **not** run on a streamed run: there the text has already reached
|
|
233
|
+
* the client and each step is flushed to storage as it goes, so nothing could
|
|
234
|
+
* act on what this returned. Use {@link modifyOutputStream} to rewrite a
|
|
235
|
+
* streamed reply — a middleware that implements only this one is warned about
|
|
236
|
+
* when an agent it is attached to streams.
|
|
237
|
+
*
|
|
238
|
+
* `toolCalls` is here so a redaction pass covers the whole run record rather
|
|
239
|
+
* than just the visible answer: the tool arguments and results are persisted
|
|
240
|
+
* and handed to anything that grades the run, and scrubbing the reply alone
|
|
241
|
+
* leaves them untouched.
|
|
242
|
+
*/
|
|
219
243
|
modifyOutput?: (services: Services, ctx: {
|
|
220
244
|
text: string;
|
|
221
245
|
messages: AIMessage[];
|
|
@@ -223,12 +247,15 @@ export interface PikkuAIMiddlewareHooks<State extends Record<string, unknown> =
|
|
|
223
247
|
inputTokens: number;
|
|
224
248
|
outputTokens: number;
|
|
225
249
|
};
|
|
250
|
+
toolCalls: NonNullable<AIAgentStep['toolCalls']>;
|
|
226
251
|
}) => Promise<{
|
|
227
252
|
text: string;
|
|
228
253
|
messages: AIMessage[];
|
|
254
|
+
toolCalls?: NonNullable<AIAgentStep['toolCalls']>;
|
|
229
255
|
}> | {
|
|
230
256
|
text: string;
|
|
231
257
|
messages: AIMessage[];
|
|
258
|
+
toolCalls?: NonNullable<AIAgentStep['toolCalls']>;
|
|
232
259
|
};
|
|
233
260
|
beforeToolCall?: (services: Services, ctx: {
|
|
234
261
|
toolName: string;
|
|
@@ -262,6 +289,8 @@ export interface PikkuAIMiddlewareHooks<State extends Record<string, unknown> =
|
|
|
262
289
|
toolCallId: string;
|
|
263
290
|
toolName: string;
|
|
264
291
|
result: unknown;
|
|
292
|
+
/** Set when the tool threw rather than returned. */
|
|
293
|
+
error?: string;
|
|
265
294
|
}[];
|
|
266
295
|
usage: {
|
|
267
296
|
inputTokens: number;
|
|
@@ -282,7 +311,7 @@ export type AIAgentMemoryConfig = {
|
|
|
282
311
|
lastMessages?: number;
|
|
283
312
|
workingMemory?: unknown;
|
|
284
313
|
};
|
|
285
|
-
export type CoreAIAgent<PikkuPermission = CorePikkuPermission<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>, Scope extends string = string> = {
|
|
314
|
+
export type CoreAIAgent<PikkuPermission = CorePikkuPermission<any, any>, PikkuMiddleware = CorePikkuMiddleware<any>, Scope extends string = string, Scorer extends string = string> = {
|
|
286
315
|
name: string;
|
|
287
316
|
description: string;
|
|
288
317
|
summary?: string;
|
|
@@ -308,6 +337,16 @@ export type CoreAIAgent<PikkuPermission = CorePikkuPermission<any, any>, PikkuMi
|
|
|
308
337
|
tools?: unknown[];
|
|
309
338
|
agents?: unknown[];
|
|
310
339
|
workflows?: unknown[];
|
|
340
|
+
/**
|
|
341
|
+
* Grades this agent's finished runs on live traffic, named by the generated
|
|
342
|
+
* `ScorerName` union rather than by `ref()` — a scorer is not a function, so
|
|
343
|
+
* there is nothing in the function map for a ref to resolve against.
|
|
344
|
+
*
|
|
345
|
+
* A reference-based judge listed here is never sampled: live traffic has no
|
|
346
|
+
* answer key. Scenarios name scorers directly and may grade with scorers an
|
|
347
|
+
* agent does not ship with.
|
|
348
|
+
*/
|
|
349
|
+
scorers?: Scorer[];
|
|
311
350
|
agentMode?: 'delegate' | 'supervise';
|
|
312
351
|
memory?: AIAgentMemoryConfig;
|
|
313
352
|
maxSteps?: number;
|
|
@@ -382,6 +421,12 @@ export type AIStreamEvent = {
|
|
|
382
421
|
toolCallId: string;
|
|
383
422
|
toolName: string;
|
|
384
423
|
result: unknown;
|
|
424
|
+
/**
|
|
425
|
+
* The failure message, set when the tool threw rather than returned.
|
|
426
|
+
* Carried explicitly because a tool may legitimately return text that
|
|
427
|
+
* reads like an error, so `result` cannot be matched on to tell.
|
|
428
|
+
*/
|
|
429
|
+
error?: string;
|
|
385
430
|
agent?: string;
|
|
386
431
|
session?: string;
|
|
387
432
|
} | {
|
|
@@ -400,7 +445,7 @@ export type AIStreamEvent = {
|
|
|
400
445
|
toolName: string;
|
|
401
446
|
args: unknown;
|
|
402
447
|
reason?: string;
|
|
403
|
-
runId
|
|
448
|
+
runId: string;
|
|
404
449
|
agent?: string;
|
|
405
450
|
session?: string;
|
|
406
451
|
} | {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export { agent, agentStream, agentResume, agentApprove, agentInterrupt, } from './ai-agent-helpers.js';
|
|
2
|
-
export { wrapChannelWithAGUI
|
|
2
|
+
export { wrapChannelWithAGUI } from './ai-agent-agui.js';
|
|
3
3
|
export { runAIAgent, resumeAIAgentSync } from './ai-agent-runner.js';
|
|
4
|
+
export { resolveModelAlias } from './ai-agent-model-config.js';
|
|
4
5
|
export { streamAIAgent, resumeAIAgent, interruptAIAgent, } from './ai-agent-stream.js';
|
|
5
|
-
export { voiceInput,
|
|
6
|
+
export { voiceInput, NoSpeechDetectedError, SPOKEN_TURN, SPOKEN_TRANSCRIPT, } from './voice-input.js';
|
|
6
7
|
export { voiceOutput, unspeakableScripts, voiceForText, type SpeakableScripts, } from './voice-output.js';
|
|
7
|
-
export { AgentInterruptedError,
|
|
8
|
-
export type { AgentInterruption, AgentInterruptResult, InterruptibleRunHandle,
|
|
9
|
-
export { type RunAIAgentParams, type StreamAIAgentOptions, ToolApprovalRequired, ToolCredentialRequired, canAccessThread, isOwnedByPrincipal,
|
|
10
|
-
export { addAIAgent,
|
|
11
|
-
export type { AIAgentInput,
|
|
8
|
+
export { AgentInterruptedError, signalRunInterrupt, } from './ai-agent-interrupt.js';
|
|
9
|
+
export type { AgentInterruption, AgentInterruptResult, InterruptibleRunHandle, } from './ai-agent-interrupt.js';
|
|
10
|
+
export { type RunAIAgentParams, type StreamAIAgentOptions, ToolApprovalRequired, ToolCredentialRequired, canAccessThread, isOwnedByPrincipal, threadOwnerConstraint, } from './ai-agent-prepare.js';
|
|
11
|
+
export { addAIAgent, } from './ai-agent-registry.js';
|
|
12
|
+
export type { AIAgentInput, AIAgentMeta, AIAgentMemoryConfig, AIAgentStep, AIContentPart, AgentRunRow, AgentRunService, AgentRunState, AIMessage, AIStreamChannel, AIStreamEvent, AIThread, CoreAIAgent, PendingApproval, PikkuAIMiddlewareHooks, } from './ai-agent.types.js';
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { agent, agentStream, agentResume, agentApprove, agentInterrupt, } from './ai-agent-helpers.js';
|
|
2
2
|
export { wrapChannelWithAGUI } from './ai-agent-agui.js';
|
|
3
3
|
export { runAIAgent, resumeAIAgentSync } from './ai-agent-runner.js';
|
|
4
|
+
export { resolveModelAlias } from './ai-agent-model-config.js';
|
|
4
5
|
export { streamAIAgent, resumeAIAgent, interruptAIAgent, } from './ai-agent-stream.js';
|
|
5
|
-
export { voiceInput,
|
|
6
|
+
export { voiceInput, NoSpeechDetectedError, SPOKEN_TURN, SPOKEN_TRANSCRIPT, } from './voice-input.js';
|
|
6
7
|
export { voiceOutput, unspeakableScripts, voiceForText, } from './voice-output.js';
|
|
7
|
-
export { AgentInterruptedError,
|
|
8
|
-
export { ToolApprovalRequired, ToolCredentialRequired, canAccessThread, isOwnedByPrincipal,
|
|
9
|
-
export { addAIAgent,
|
|
8
|
+
export { AgentInterruptedError, signalRunInterrupt, } from './ai-agent-interrupt.js';
|
|
9
|
+
export { ToolApprovalRequired, ToolCredentialRequired, canAccessThread, isOwnedByPrincipal, threadOwnerConstraint, } from './ai-agent-prepare.js';
|
|
10
|
+
export { addAIAgent, } from './ai-agent-registry.js';
|
|
@@ -135,12 +135,7 @@ export const voiceInput = (config) => pikkuAIMiddleware({
|
|
|
135
135
|
// to send, and a message with no content is not a question.
|
|
136
136
|
if (updatedContent.length === 0)
|
|
137
137
|
throw new NoSpeechDetectedError();
|
|
138
|
-
//
|
|
139
|
-
// reads as non-speech and still have content — an image with a silent
|
|
140
|
-
// caption clip — which leaves nothing above to throw. Recording `''` here
|
|
141
|
-
// would send a transcript event saying the user said nothing, and a client
|
|
142
|
-
// that tells "not transcribed yet" from "transcribed" by the key being
|
|
143
|
-
// absent would render the turn as an empty bubble rather than a pending one.
|
|
138
|
+
// knowledge: decisions/internals/an-empty-transcript-is-not-recorded.md
|
|
144
139
|
if (heard.length > 0) {
|
|
145
140
|
shared[SPOKEN_TRANSCRIPT] = heard.join(' ');
|
|
146
141
|
}
|