@pikku/core 0.12.78 → 0.12.80
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 +234 -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/function/function-runner.js +20 -42
- package/dist/function/functions.types.d.ts +9 -5
- package/dist/index.d.ts +4 -9
- package/dist/index.js +3 -8
- 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 +12 -5
- package/dist/schema.js +35 -1
- package/dist/services/ai-run-state-service.d.ts +7 -1
- package/dist/services/in-memory-ai-run-state-service.d.ts +1 -1
- package/dist/services/in-memory-ai-run-state-service.js +2 -1
- package/dist/services/in-memory-workflow-service.d.ts +10 -0
- package/dist/services/in-memory-workflow-service.js +25 -0
- 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/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 +226 -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 -1
- package/dist/types/state.types.d.ts +1 -1
- package/dist/utils/node-host-resolver.d.ts +12 -0
- package/dist/utils/node-host-resolver.js +16 -0
- package/dist/utils/safe-fetch.d.ts +18 -0
- package/dist/utils/safe-fetch.js +167 -29
- 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-memory.js +3 -1
- package/dist/wirings/ai-agent/ai-agent-prepare.js +3 -9
- package/dist/wirings/ai-agent/ai-agent-runner.js +28 -107
- package/dist/wirings/ai-agent/ai-agent-stream.js +20 -61
- package/dist/wirings/ai-agent/ai-agent-turn.d.ts +56 -0
- package/dist/wirings/ai-agent/ai-agent-turn.js +81 -0
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +1 -1
- package/dist/wirings/ai-agent/voice-input.js +1 -6
- package/dist/wirings/ai-agent/voice-output.js +2 -12
- 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/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/cli-runner.js +4 -2
- package/dist/wirings/cli/cli.types.d.ts +0 -8
- package/dist/wirings/cli/command-parser.js +13 -0
- package/dist/wirings/gateway/gateway-runner.js +41 -5
- 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 +6 -13
- package/dist/wirings/http/http.types.d.ts +0 -10
- package/dist/wirings/http/index.d.ts +1 -1
- package/dist/wirings/http/index.js +1 -1
- package/dist/wirings/mcp/mcp-runner.d.ts +0 -7
- package/dist/wirings/mcp/mcp-runner.js +0 -6
- package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
- package/dist/wirings/rpc/rpc-runner.js +53 -86
- package/dist/wirings/rpc/rpc-types.d.ts +3 -0
- package/dist/wirings/secret/validate-secret-definitions.js +2 -0
- 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/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 +29 -2
- 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 +7 -2
- package/dist/wirings/workflow/index.js +5 -1
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
- package/dist/wirings/workflow/pikku-scenario-service.js +38 -10
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +44 -111
- package/dist/wirings/workflow/pikku-workflow-service.js +86 -404
- package/dist/wirings/workflow/workflow-approval.d.ts +39 -0
- package/dist/wirings/workflow/workflow-approval.js +114 -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-engine.types.js +1 -0
- package/dist/wirings/workflow/workflow-run-ownership.d.ts +16 -0
- package/dist/wirings/workflow/workflow-run-ownership.js +29 -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 +7 -0
- 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/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-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 +38 -26
- 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/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-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 +34 -0
- package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
- package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
- package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +13 -3
- package/knowledge/decisions/security/index.md +7 -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 +14 -3
- package/scripts/generate-api-report.d.mts +1 -0
- package/scripts/generate-api-report.mjs +89 -0
- package/scripts/generate-api-report.mts +218 -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/function/function-runner.test.ts +1 -1
- package/src/function/function-runner.ts +28 -39
- package/src/function/functions.types.ts +15 -9
- package/src/handle-error.ts +1 -1
- package/src/index.ts +3 -22
- 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 +17 -6
- package/src/public-surface.json +578 -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-run-state-service.ts +7 -1
- package/src/services/audit-service.ts +2 -2
- package/src/services/in-memory-ai-run-state-service.ts +3 -2
- package/src/services/in-memory-workflow-service.ts +25 -0
- package/src/services/index.ts +1 -4
- 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/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 +286 -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 +15 -1
- package/src/types/state.types.ts +1 -1
- package/src/utils/node-host-resolver.ts +20 -0
- package/src/utils/safe-fetch.test.ts +143 -1
- package/src/utils/safe-fetch.ts +200 -25
- 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-interrupt.test.ts +2 -1
- package/src/wirings/ai-agent/ai-agent-memory.ts +8 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +10 -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 +49 -120
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +36 -63
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
- package/src/wirings/ai-agent/ai-agent-turn.ts +121 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +1 -1
- package/src/wirings/ai-agent/voice-input.ts +1 -6
- package/src/wirings/ai-agent/voice-output.ts +2 -12
- 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/local/local-channel-runner.ts +1 -1
- 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-client-runner.ts +4 -1
- package/src/wirings/cli/cli-runner.ts +7 -4
- 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/gateway/gateway-authorization.test.ts +131 -0
- package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
- package/src/wirings/gateway/gateway-runner.ts +62 -22
- package/src/wirings/http/http-routes.ts +4 -1
- package/src/wirings/http/http-runner.ts +11 -26
- package/src/wirings/http/http.types.ts +0 -15
- package/src/wirings/http/index.ts +1 -7
- package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
- package/src/wirings/http/web-request.ts +1 -1
- package/src/wirings/mcp/mcp-runner.ts +2 -16
- 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/queue-runner.ts +1 -1
- package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
- package/src/wirings/rpc/rpc-runner.ts +58 -136
- package/src/wirings/rpc/rpc-types.ts +7 -0
- package/src/wirings/rpc/wire-addon.ts +3 -1
- package/src/wirings/secret/validate-secret-definitions.test.ts +69 -0
- package/src/wirings/secret/validate-secret-definitions.ts +2 -0
- package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
- package/src/wirings/trigger/trigger-runner.ts +7 -5
- 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 +35 -2
- 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 +4 -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 +12 -4
- package/src/wirings/workflow/pikku-scenario-service.ts +54 -25
- package/src/wirings/workflow/pikku-workflow-service.test.ts +2 -2
- package/src/wirings/workflow/pikku-workflow-service.ts +208 -630
- package/src/wirings/workflow/scenario-hooks.test.ts +51 -0
- package/src/wirings/workflow/workflow-approval.ts +185 -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-dispatch-relay.test.ts +128 -0
- 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 +215 -0
- package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
- package/src/wirings/workflow/workflow-run-ownership.ts +36 -0
- package/src/wirings/workflow/workflow-suspend.ts +61 -0
- package/src/wirings/workflow/workflow.types.ts +7 -0
- package/src/wirings-stay-decoupled.test.ts +123 -0
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/src/internal.ts +0 -10
|
@@ -233,6 +233,11 @@ export interface CoreUserSession {
|
|
|
233
233
|
* Populated by whoever builds the session — core reads them, never fetches.
|
|
234
234
|
*/
|
|
235
235
|
scopes?: string[];
|
|
236
|
+
/**
|
|
237
|
+
* Restricts the session to functions declared `readonly`. The function runner
|
|
238
|
+
* throws `ReadonlySessionError` for anything else.
|
|
239
|
+
*/
|
|
240
|
+
readonly?: boolean;
|
|
236
241
|
}
|
|
237
242
|
/**
|
|
238
243
|
* Kept structural so core stays independent of any one auth package —
|
|
@@ -299,12 +304,17 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
|
|
|
299
304
|
*/
|
|
300
305
|
auth?: () => Promise<AuthInstance>;
|
|
301
306
|
}
|
|
302
|
-
export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boolean = false, UserSession extends CoreUserSession = CoreUserSession, TypedRPC extends PikkuRPC = PikkuRPC, IsChannel extends true | null = null, MCPTools extends string | never = never, TypedWorkflow extends PikkuWorkflowWire | never = PikkuWorkflowWire, TriggerOutput = unknown, TypedScenario extends PikkuScenarioWire | never = PikkuScenarioWire
|
|
307
|
+
export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boolean = false, UserSession extends CoreUserSession = CoreUserSession, TypedRPC extends PikkuRPC = PikkuRPC, IsChannel extends true | null = null, MCPTools extends string | never = never, TypedWorkflow extends PikkuWorkflowWire | never = PikkuWorkflowWire, TriggerOutput = unknown, TypedScenario extends PikkuScenarioWire<any> | never = PikkuScenarioWire<any>, TypedActors extends ScenarioPersonas = ScenarioPersonas> = {
|
|
303
308
|
/** Always present — lazily initialised on first access for every function invocation */
|
|
304
309
|
rpc: TypedRPC;
|
|
305
310
|
} & Partial<{
|
|
306
311
|
wireType: PikkuWiringTypes;
|
|
307
312
|
wireId: string;
|
|
313
|
+
/**
|
|
314
|
+
* A logger scoped to this invocation, when a host attaches one. Core never
|
|
315
|
+
* sets it; services that log fall back to the singleton logger.
|
|
316
|
+
*/
|
|
317
|
+
logger: Logger;
|
|
308
318
|
/** Trace ID for distributed tracing — propagated across remote RPC calls via x-trace-id header */
|
|
309
319
|
traceId: string;
|
|
310
320
|
functionId: string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type HostResolver } from './safe-fetch.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a hostname through the platform resolver, returning every address it
|
|
4
|
+
* points at so `safeFetch` can reject a public name aimed at an internal one.
|
|
5
|
+
*
|
|
6
|
+
* This module is Node-only and is never imported by core itself — Workers has no
|
|
7
|
+
* DNS API, and a static `node:dns` import in the shared path would break that
|
|
8
|
+
* build.
|
|
9
|
+
*/
|
|
10
|
+
export declare const nodeHostResolver: HostResolver;
|
|
11
|
+
/** Installs {@link nodeHostResolver} as the default for every `safeFetch`. */
|
|
12
|
+
export declare const installNodeHostResolver: () => void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { lookup } from 'node:dns/promises';
|
|
2
|
+
import { setDefaultHostResolver } from './safe-fetch.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves a hostname through the platform resolver, returning every address it
|
|
5
|
+
* points at so `safeFetch` can reject a public name aimed at an internal one.
|
|
6
|
+
*
|
|
7
|
+
* This module is Node-only and is never imported by core itself — Workers has no
|
|
8
|
+
* DNS API, and a static `node:dns` import in the shared path would break that
|
|
9
|
+
* build.
|
|
10
|
+
*/
|
|
11
|
+
export const nodeHostResolver = async (hostname) => {
|
|
12
|
+
const results = await lookup(hostname, { all: true, verbatim: true });
|
|
13
|
+
return results.map(({ address }) => address);
|
|
14
|
+
};
|
|
15
|
+
/** Installs {@link nodeHostResolver} as the default for every `safeFetch`. */
|
|
16
|
+
export const installNodeHostResolver = () => setDefaultHostResolver(nodeHostResolver);
|
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
* private IP.
|
|
5
5
|
*/
|
|
6
6
|
export declare function isPrivateHost(hostname: string): boolean;
|
|
7
|
+
/** Resolves a hostname to the IP addresses it points at. */
|
|
8
|
+
export type HostResolver = (hostname: string) => Promise<string[]>;
|
|
9
|
+
/**
|
|
10
|
+
* Installs the resolver {@link safeFetch} uses when a call passes no
|
|
11
|
+
* `resolveHost` of its own.
|
|
12
|
+
*
|
|
13
|
+
* Core cannot resolve DNS itself — Workers has no DNS API — so without a
|
|
14
|
+
* resolver the guard is literal-only and a public name pointing at
|
|
15
|
+
* `169.254.169.254` passes. Node runtimes install
|
|
16
|
+
* `nodeHostResolver` from `@pikku/core/node-host-resolver` at startup.
|
|
17
|
+
*/
|
|
18
|
+
export declare function setDefaultHostResolver(resolver: HostResolver | undefined): void;
|
|
7
19
|
export interface SafeFetchOptions {
|
|
8
20
|
/**
|
|
9
21
|
* When set, the host of every hop must appear in this allowlist. When omitted,
|
|
@@ -12,6 +24,12 @@ export interface SafeFetchOptions {
|
|
|
12
24
|
allowedHosts?: string[];
|
|
13
25
|
/** Maximum redirect hops to follow (each re-validated). Defaults to 3. */
|
|
14
26
|
maxRedirects?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Resolves a hostname so a *public* name pointing at a private address is
|
|
29
|
+
* refused. Defaults to whatever {@link setDefaultHostResolver} installed;
|
|
30
|
+
* pass `null` to opt a call out of resolution entirely.
|
|
31
|
+
*/
|
|
32
|
+
resolveHost?: HostResolver | null;
|
|
15
33
|
}
|
|
16
34
|
export declare function assertFetchableUrl(url: string, options?: SafeFetchOptions): URL;
|
|
17
35
|
/**
|
package/dist/utils/safe-fetch.js
CHANGED
|
@@ -34,6 +34,120 @@ function parseIPv4Octets(host) {
|
|
|
34
34
|
}
|
|
35
35
|
return octets;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* IPv4 blocks that must never be reachable from user-supplied URLs: private,
|
|
39
|
+
* loopback, link-local (cloud metadata), carrier-grade NAT (Alibaba's
|
|
40
|
+
* `100.100.100.200` metadata endpoint), IETF protocol assignments, benchmarking,
|
|
41
|
+
* 6to4 anycast, the documentation TEST-NETs, multicast and reserved space.
|
|
42
|
+
*/
|
|
43
|
+
const PRIVATE_IPV4_BLOCKS = [
|
|
44
|
+
['0.0.0.0', 8],
|
|
45
|
+
['10.0.0.0', 8],
|
|
46
|
+
['100.64.0.0', 10],
|
|
47
|
+
['127.0.0.0', 8],
|
|
48
|
+
['169.254.0.0', 16],
|
|
49
|
+
['172.16.0.0', 12],
|
|
50
|
+
['192.0.0.0', 24],
|
|
51
|
+
['192.0.2.0', 24],
|
|
52
|
+
['192.88.99.0', 24],
|
|
53
|
+
['192.168.0.0', 16],
|
|
54
|
+
['198.18.0.0', 15],
|
|
55
|
+
['198.51.100.0', 24],
|
|
56
|
+
['203.0.113.0', 24],
|
|
57
|
+
['224.0.0.0', 4],
|
|
58
|
+
['240.0.0.0', 4],
|
|
59
|
+
];
|
|
60
|
+
const toUint32 = (octets) => ((octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3]) >>> 0;
|
|
61
|
+
const PRIVATE_IPV4_RANGES = PRIVATE_IPV4_BLOCKS.map(([base, prefix]) => {
|
|
62
|
+
const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0;
|
|
63
|
+
return [(toUint32(parseIPv4Octets(base)) & mask) >>> 0, mask];
|
|
64
|
+
});
|
|
65
|
+
const isPrivateIPv4 = (octets) => {
|
|
66
|
+
const addr = toUint32(octets);
|
|
67
|
+
return PRIVATE_IPV4_RANGES.some(([base, mask]) => (addr & mask) >>> 0 === base);
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Expands an IPv6 literal into its eight 16-bit groups, handling `::` elision
|
|
71
|
+
* and a trailing dotted-quad. `null` when not a well-formed IPv6 literal.
|
|
72
|
+
*/
|
|
73
|
+
function parseIPv6Groups(host) {
|
|
74
|
+
const zoneless = host.split('%')[0];
|
|
75
|
+
const halves = zoneless.split('::');
|
|
76
|
+
if (halves.length > 2)
|
|
77
|
+
return null;
|
|
78
|
+
const parseSide = (side) => {
|
|
79
|
+
if (side === '')
|
|
80
|
+
return [];
|
|
81
|
+
const parts = side.split(':');
|
|
82
|
+
const groups = [];
|
|
83
|
+
for (let i = 0; i < parts.length; i++) {
|
|
84
|
+
const part = parts[i];
|
|
85
|
+
if (i === parts.length - 1 && part.includes('.')) {
|
|
86
|
+
const v4 = parseIPv4Octets(part);
|
|
87
|
+
if (!v4)
|
|
88
|
+
return null;
|
|
89
|
+
groups.push((v4[0] << 8) | v4[1], (v4[2] << 8) | v4[3]);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!/^[0-9a-f]{1,4}$/.test(part))
|
|
93
|
+
return null;
|
|
94
|
+
groups.push(parseInt(part, 16));
|
|
95
|
+
}
|
|
96
|
+
return groups;
|
|
97
|
+
};
|
|
98
|
+
const head = parseSide(halves[0]);
|
|
99
|
+
if (head === null)
|
|
100
|
+
return null;
|
|
101
|
+
if (halves.length === 1) {
|
|
102
|
+
return head.length === 8 ? head : null;
|
|
103
|
+
}
|
|
104
|
+
const tail = parseSide(halves[1]);
|
|
105
|
+
if (tail === null)
|
|
106
|
+
return null;
|
|
107
|
+
if (head.length + tail.length > 7)
|
|
108
|
+
return null;
|
|
109
|
+
return [...head, ...new Array(8 - head.length - tail.length).fill(0), ...tail];
|
|
110
|
+
}
|
|
111
|
+
const embeddedIPv4 = (hi, lo) => [
|
|
112
|
+
(hi >> 8) & 0xff,
|
|
113
|
+
hi & 0xff,
|
|
114
|
+
(lo >> 8) & 0xff,
|
|
115
|
+
lo & 0xff,
|
|
116
|
+
];
|
|
117
|
+
function isPrivateIPv6(groups) {
|
|
118
|
+
const [g0, g1, g2, g3, g4, g5, g6, g7] = groups;
|
|
119
|
+
if (groups.every((g) => g === 0))
|
|
120
|
+
return true; // :: unspecified
|
|
121
|
+
if (groups.slice(0, 7).every((g) => g === 0) && g7 === 1)
|
|
122
|
+
return true; // ::1
|
|
123
|
+
// IPv4-mapped ::ffff:0:0/96 and IPv4-compatible ::/96
|
|
124
|
+
if (g0 === 0 && g1 === 0 && g2 === 0 && g3 === 0 && g4 === 0) {
|
|
125
|
+
if (g5 === 0xffff || g5 === 0)
|
|
126
|
+
return isPrivateIPv4(embeddedIPv4(g6, g7));
|
|
127
|
+
}
|
|
128
|
+
// NAT64 well-known prefix 64:ff9b::/96
|
|
129
|
+
if (g0 === 0x64 &&
|
|
130
|
+
g1 === 0xff9b &&
|
|
131
|
+
g2 === 0 &&
|
|
132
|
+
g3 === 0 &&
|
|
133
|
+
g4 === 0 &&
|
|
134
|
+
g5 === 0)
|
|
135
|
+
return isPrivateIPv4(embeddedIPv4(g6, g7));
|
|
136
|
+
// 6to4 2002::/16 carries the IPv4 address in the next 32 bits
|
|
137
|
+
if (g0 === 0x2002)
|
|
138
|
+
return isPrivateIPv4(embeddedIPv4(g1, g2));
|
|
139
|
+
if (g0 === 0x100 && g1 === 0 && g2 === 0 && g3 === 0)
|
|
140
|
+
return true; // discard-only 100::/64
|
|
141
|
+
if ((g0 & 0xffc0) === 0xfe80)
|
|
142
|
+
return true; // link-local fe80::/10
|
|
143
|
+
if ((g0 & 0xfe00) === 0xfc00)
|
|
144
|
+
return true; // unique-local fc00::/7
|
|
145
|
+
if ((g0 & 0xffc0) === 0xfec0)
|
|
146
|
+
return true; // deprecated site-local fec0::/10
|
|
147
|
+
if ((g0 & 0xff00) === 0xff00)
|
|
148
|
+
return true; // multicast ff00::/8
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
37
151
|
/**
|
|
38
152
|
* Whether a hostname is an obvious internal target. Best-effort literal
|
|
39
153
|
* matching only: it cannot catch a public hostname that *resolves* to a
|
|
@@ -47,36 +161,56 @@ export function isPrivateHost(hostname) {
|
|
|
47
161
|
if (host === '' || host === 'localhost' || host.endsWith('.localhost'))
|
|
48
162
|
return true;
|
|
49
163
|
if (host.includes(':')) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const mappedV4 = host.match(/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/);
|
|
53
|
-
if (mappedV4)
|
|
54
|
-
return isPrivateHost(mappedV4[1]);
|
|
55
|
-
const mappedHex = host.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
|
|
56
|
-
if (mappedHex) {
|
|
57
|
-
const hi = parseInt(mappedHex[1], 16);
|
|
58
|
-
const lo = parseInt(mappedHex[2], 16);
|
|
59
|
-
return isPrivateHost(`${(hi >> 8) & 0xff}.${hi & 0xff}.${(lo >> 8) & 0xff}.${lo & 0xff}`);
|
|
60
|
-
}
|
|
61
|
-
if (/^fe[89ab]/.test(host))
|
|
62
|
-
return true; // link-local fe80::/10
|
|
63
|
-
if (host.startsWith('fc') || host.startsWith('fd'))
|
|
64
|
-
return true; // unique-local fc00::/7
|
|
65
|
-
return false;
|
|
164
|
+
const groups = parseIPv6Groups(host);
|
|
165
|
+
return groups ? isPrivateIPv6(groups) : false;
|
|
66
166
|
}
|
|
67
167
|
const v4 = parseIPv4Octets(host);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
168
|
+
return v4 ? isPrivateIPv4(v4) : false;
|
|
169
|
+
}
|
|
170
|
+
let defaultHostResolver;
|
|
171
|
+
/**
|
|
172
|
+
* Installs the resolver {@link safeFetch} uses when a call passes no
|
|
173
|
+
* `resolveHost` of its own.
|
|
174
|
+
*
|
|
175
|
+
* Core cannot resolve DNS itself — Workers has no DNS API — so without a
|
|
176
|
+
* resolver the guard is literal-only and a public name pointing at
|
|
177
|
+
* `169.254.169.254` passes. Node runtimes install
|
|
178
|
+
* `nodeHostResolver` from `@pikku/core/node-host-resolver` at startup.
|
|
179
|
+
*/
|
|
180
|
+
export function setDefaultHostResolver(resolver) {
|
|
181
|
+
defaultHostResolver = resolver;
|
|
182
|
+
}
|
|
183
|
+
/** Whether a hostname is already an IP literal, which the sync check covers. */
|
|
184
|
+
function isIpLiteral(hostname) {
|
|
185
|
+
const host = hostname.replace(/^\[|\]$/g, '').replace(/\.$/, '');
|
|
186
|
+
if (host.includes(':'))
|
|
187
|
+
return parseIPv6Groups(host.toLowerCase()) !== null;
|
|
188
|
+
return parseIPv4Octets(host) !== null;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Rejects a hostname that resolves to an internal address.
|
|
192
|
+
*
|
|
193
|
+
* Resolution happens once per hop and the connection is not pinned to the
|
|
194
|
+
* address checked, so a rebind between this check and the socket connecting is
|
|
195
|
+
* still possible; catching that needs a runtime-level connect hook.
|
|
196
|
+
*/
|
|
197
|
+
async function assertResolvedHostAllowed(hostname, options) {
|
|
198
|
+
if (options.allowedHosts)
|
|
199
|
+
return;
|
|
200
|
+
const resolver = options.resolveHost === null
|
|
201
|
+
? undefined
|
|
202
|
+
: (options.resolveHost ?? defaultHostResolver);
|
|
203
|
+
if (!resolver || isIpLiteral(hostname))
|
|
204
|
+
return;
|
|
205
|
+
const addresses = await resolver(hostname);
|
|
206
|
+
if (addresses.length === 0) {
|
|
207
|
+
throw new Error(`Refusing to fetch: '${hostname}' resolved to no addresses`);
|
|
208
|
+
}
|
|
209
|
+
for (const address of addresses) {
|
|
210
|
+
if (isPrivateHost(address)) {
|
|
211
|
+
throw new Error(`Refusing to fetch from a private/internal host: '${hostname}' resolves to ${address}`);
|
|
212
|
+
}
|
|
78
213
|
}
|
|
79
|
-
return false;
|
|
80
214
|
}
|
|
81
215
|
export function assertFetchableUrl(url, options = {}) {
|
|
82
216
|
const parsed = new URL(url);
|
|
@@ -126,7 +260,9 @@ function redirectInit(status, init) {
|
|
|
126
260
|
*/
|
|
127
261
|
export async function safeFetch(url, init = {}, options = {}) {
|
|
128
262
|
const maxRedirects = options.maxRedirects ?? 3;
|
|
129
|
-
|
|
263
|
+
const initial = assertFetchableUrl(url, options);
|
|
264
|
+
await assertResolvedHostAllowed(initial.hostname, options);
|
|
265
|
+
let currentUrl = initial.toString();
|
|
130
266
|
let currentInit = init;
|
|
131
267
|
for (let hop = 0;; hop++) {
|
|
132
268
|
const response = await fetch(currentUrl, {
|
|
@@ -140,7 +276,9 @@ export async function safeFetch(url, init = {}, options = {}) {
|
|
|
140
276
|
if (!location || hop >= maxRedirects) {
|
|
141
277
|
return response;
|
|
142
278
|
}
|
|
143
|
-
const
|
|
279
|
+
const next = assertFetchableUrl(new URL(location, currentUrl).toString(), options);
|
|
280
|
+
await assertResolvedHostAllowed(next.hostname, options);
|
|
281
|
+
const nextUrl = next.toString();
|
|
144
282
|
await response.body?.cancel();
|
|
145
283
|
let nextInit = redirectInit(response.status, currentInit);
|
|
146
284
|
if (new URL(nextUrl).origin !== new URL(currentUrl).origin) {
|
|
@@ -70,12 +70,7 @@ function actorInstructions(persona, task) {
|
|
|
70
70
|
: '',
|
|
71
71
|
`Your goal in this conversation: ${task}.`,
|
|
72
72
|
`Send one message at a time. Set "done" to true only once your goal is clearly accomplished, or clearly impossible.`,
|
|
73
|
-
//
|
|
74
|
-
// that cannot take a JSON *schema* degrades to OpenAI's `json_object`
|
|
75
|
-
// mode — which refuses the request outright unless the word "json" appears
|
|
76
|
-
// somewhere in the prompt. Saying it once here covers all three call sites
|
|
77
|
-
// (turn, approvals, verdict), since each builds on these instructions, and
|
|
78
|
-
// it costs nothing on providers that never needed telling.
|
|
73
|
+
// knowledge: decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md
|
|
79
74
|
`Reply with json matching the schema you are given, and nothing else.`,
|
|
80
75
|
]
|
|
81
76
|
.filter(Boolean)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PikkuRawWire } from '../../types/core.types.js';
|
|
2
|
+
import type { SessionService } from '../../services/user-session-service.js';
|
|
3
|
+
import type { CoreUserSession } from '../../types/core.types.js';
|
|
4
|
+
import type { PikkuRPC } from '../rpc/rpc-types.js';
|
|
5
|
+
export type AgentRPCOptions = {
|
|
6
|
+
sessionService?: SessionService<CoreUserSession>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* `wire.rpc.agent`, implemented.
|
|
10
|
+
*
|
|
11
|
+
* Lives here rather than in `rpc-runner` so the agent surface is one file next
|
|
12
|
+
* to the runner and stream code it delegates to, instead of a wing of the RPC
|
|
13
|
+
* primitive.
|
|
14
|
+
*/
|
|
15
|
+
export declare const createAgentRPC: (wire: PikkuRawWire, options: AgentRPCOptions) => PikkuRPC["agent"];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { runAIAgent, resumeAIAgentSync } from './ai-agent-runner.js';
|
|
2
|
+
import { streamAIAgent, resumeAIAgent, interruptAIAgent, } from './ai-agent-stream.js';
|
|
3
|
+
import { wrapChannelWithAGUI } from './ai-agent-agui.js';
|
|
4
|
+
/**
|
|
5
|
+
* `wire.rpc.agent`, implemented.
|
|
6
|
+
*
|
|
7
|
+
* Lives here rather than in `rpc-runner` so the agent surface is one file next
|
|
8
|
+
* to the runner and stream code it delegates to, instead of a wing of the RPC
|
|
9
|
+
* primitive.
|
|
10
|
+
*/
|
|
11
|
+
export const createAgentRPC = (wire, options) => {
|
|
12
|
+
const params = () => ({
|
|
13
|
+
sessionService: options.sessionService,
|
|
14
|
+
getCredential: wire.getCredential?.bind(wire),
|
|
15
|
+
});
|
|
16
|
+
const streamingChannel = () => {
|
|
17
|
+
const channel = wire.channel;
|
|
18
|
+
if (!channel)
|
|
19
|
+
throw new Error('No channel available for streaming');
|
|
20
|
+
return channel;
|
|
21
|
+
};
|
|
22
|
+
/** `run` and `approve` return the same shape; only the call differs. */
|
|
23
|
+
const asRunResult = (result) => ({
|
|
24
|
+
runId: result.runId,
|
|
25
|
+
result: result.object ?? result.text,
|
|
26
|
+
usage: result.usage,
|
|
27
|
+
...(result.status === 'suspended' && {
|
|
28
|
+
status: 'suspended',
|
|
29
|
+
pendingApprovals: result.pendingApprovals,
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
run: async (agentName, input) => asRunResult(await runAIAgent(agentName, input, params())),
|
|
34
|
+
stream: async (agentName, input, streamOptions) => {
|
|
35
|
+
let currentRunId;
|
|
36
|
+
await streamAIAgent(agentName, input, wrapChannelWithAGUI(streamingChannel(), {
|
|
37
|
+
threadId: input.threadId,
|
|
38
|
+
getRunId: () => currentRunId,
|
|
39
|
+
}), params(), undefined, {
|
|
40
|
+
...streamOptions,
|
|
41
|
+
onRunCreated: (runId) => {
|
|
42
|
+
currentRunId = runId;
|
|
43
|
+
streamOptions?.onRunCreated?.(runId);
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
resume: async (runId, input, streamOptions) => {
|
|
48
|
+
await resumeAIAgent({ runId, ...input }, wrapChannelWithAGUI(streamingChannel(), { runId }), params(), streamOptions);
|
|
49
|
+
},
|
|
50
|
+
interrupt: async (runId, reason) => interruptAIAgent({ runId, ...(reason ? { reason } : {}) }, { sessionService: options.sessionService }),
|
|
51
|
+
approve: async (runId, approvals, expectedAgentName) => asRunResult(await resumeAIAgentSync(runId, approvals, { sessionService: options.sessionService }, expectedAgentName)),
|
|
52
|
+
};
|
|
53
|
+
};
|
|
@@ -296,11 +296,7 @@ export function wrapChannelWithAGUI(inner, options) {
|
|
|
296
296
|
});
|
|
297
297
|
break;
|
|
298
298
|
}
|
|
299
|
-
//
|
|
300
|
-
// pikku-specific ones. Dropping it instead — which is what this did —
|
|
301
|
-
// means a voice agent reached over HTTP is inaudible: `voiceOutput`
|
|
302
|
-
// synthesizes every sentence, the provider bills for it, and nothing
|
|
303
|
-
// gets past the mapper.
|
|
299
|
+
// knowledge: decisions/internals/agent-speech-travels-as-a-custom-agui-event.md
|
|
304
300
|
case 'audio-delta': {
|
|
305
301
|
send({
|
|
306
302
|
type: 'CUSTOM',
|
|
@@ -2,7 +2,9 @@ import { randomUUID } from 'crypto';
|
|
|
2
2
|
export function resolveMemoryServices(agent, singletonServices) {
|
|
3
3
|
const memoryConfig = agent.memory;
|
|
4
4
|
const storage = memoryConfig?.storage
|
|
5
|
-
?
|
|
5
|
+
? // The service is named by string in agent config, so the lookup cannot be
|
|
6
|
+
// checked — only the shape it is required to have.
|
|
7
|
+
singletonServices[memoryConfig.storage]
|
|
6
8
|
: singletonServices.aiStorage;
|
|
7
9
|
return { storage };
|
|
8
10
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PikkuError } from '../../errors/error-handler.js';
|
|
2
|
-
import { checkAuthPermissions, runPermissions } from '../../permissions.js';
|
|
2
|
+
import { checkAuthPermissions, runPermissions, } from '../../permissions.js';
|
|
3
3
|
import { AIProviderNotConfiguredError } from '../../errors/errors.js';
|
|
4
4
|
import { ForbiddenError } from '../../errors/errors.js';
|
|
5
5
|
import { verifyScopes } from '../../scopes.js';
|
|
@@ -648,10 +648,7 @@ export async function prepareAgentRun(agentName, input, params, agentSessionMap,
|
|
|
648
648
|
}
|
|
649
649
|
let messages = [];
|
|
650
650
|
if (storage) {
|
|
651
|
-
//
|
|
652
|
-
// thread. In voice the next turn lands within a second or two, so without
|
|
653
|
-
// this the model would load context that is missing the very thing it is
|
|
654
|
-
// about to be asked about.
|
|
651
|
+
// knowledge: decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md
|
|
655
652
|
await awaitPendingInterruptNote(threadId);
|
|
656
653
|
messages = await storage.getMessages(threadId, {
|
|
657
654
|
lastN: memoryConfig?.lastMessages ?? 20,
|
|
@@ -660,10 +657,7 @@ export async function prepareAgentRun(agentName, input, params, agentSessionMap,
|
|
|
660
657
|
const contextMessages = await loadContextMessages(memoryConfig, storage, input, workingMemoryJsonSchema);
|
|
661
658
|
const userContent = input.attachments?.length
|
|
662
659
|
? [
|
|
663
|
-
//
|
|
664
|
-
// real turn — a spoken one carries audio and no text at all — and an
|
|
665
|
-
// empty text part alongside it is a part providers are entitled to
|
|
666
|
-
// reject, for a caller who never wrote one.
|
|
660
|
+
// knowledge: decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md
|
|
667
661
|
...(input.message
|
|
668
662
|
? [{ type: 'text', text: input.message }]
|
|
669
663
|
: []),
|