@pikku/core 0.12.82 → 0.12.84
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 +101 -0
- package/README.md +1 -1
- package/dist/ecosystem/agent-scorer.d.ts +12 -0
- package/dist/ecosystem/agent-scorer.js +5 -0
- package/dist/ecosystem/agent.d.ts +18 -0
- package/dist/ecosystem/agent.js +6 -0
- package/dist/ecosystem/ai-agent.d.ts +18 -0
- package/dist/ecosystem/ai-agent.js +6 -0
- package/dist/ecosystem/ai-scorer.d.ts +12 -0
- package/dist/ecosystem/ai-scorer.js +5 -0
- package/dist/ecosystem/channel/local.d.ts +1 -0
- package/dist/ecosystem/channel/local.js +1 -0
- package/dist/ecosystem/channel.d.ts +12 -0
- package/dist/ecosystem/channel.js +4 -0
- package/dist/ecosystem/cli/channel.d.ts +16 -0
- package/dist/ecosystem/cli/channel.js +2 -0
- package/dist/ecosystem/cli.d.ts +9 -0
- package/dist/ecosystem/cli.js +2 -0
- package/dist/ecosystem/credential.d.ts +9 -0
- package/dist/ecosystem/credential.js +1 -0
- package/dist/ecosystem/dev.d.ts +8 -0
- package/dist/ecosystem/dev.js +1 -0
- package/dist/ecosystem/function.d.ts +13 -0
- package/dist/ecosystem/function.js +2 -0
- package/dist/ecosystem/gateway.d.ts +1 -0
- package/dist/ecosystem/gateway.js +1 -0
- package/dist/ecosystem/hmac.d.ts +1 -0
- package/dist/ecosystem/hmac.js +1 -0
- package/dist/ecosystem/http.d.ts +11 -0
- package/dist/ecosystem/http.js +3 -0
- package/dist/ecosystem/mcp.d.ts +3 -0
- package/dist/ecosystem/mcp.js +2 -0
- package/dist/ecosystem/middleware.d.ts +17 -0
- package/dist/ecosystem/middleware.js +6 -0
- package/dist/ecosystem/node-host-resolver.d.ts +1 -0
- package/dist/ecosystem/node-host-resolver.js +1 -0
- package/dist/ecosystem/node.d.ts +1 -0
- package/dist/ecosystem/node.js +1 -0
- package/dist/ecosystem/oauth2.d.ts +1 -0
- package/dist/ecosystem/oauth2.js +1 -0
- package/dist/ecosystem/persona.d.ts +15 -0
- package/dist/ecosystem/persona.js +4 -0
- package/dist/ecosystem/queue.d.ts +11 -0
- package/dist/ecosystem/queue.js +2 -0
- package/dist/ecosystem/remote.d.ts +8 -0
- package/dist/ecosystem/remote.js +1 -0
- package/dist/ecosystem/role.d.ts +3 -0
- package/dist/ecosystem/role.js +2 -0
- package/dist/ecosystem/rpc.d.ts +4 -0
- package/dist/ecosystem/rpc.js +2 -0
- package/dist/ecosystem/safe-fetch.d.ts +7 -0
- package/dist/ecosystem/safe-fetch.js +1 -0
- package/dist/ecosystem/scenario.d.ts +15 -0
- package/dist/ecosystem/scenario.js +4 -0
- package/dist/ecosystem/scheduler.d.ts +2 -0
- package/dist/ecosystem/scheduler.js +1 -0
- package/dist/ecosystem/schema.d.ts +1 -0
- package/dist/ecosystem/schema.js +1 -0
- package/dist/ecosystem/scope.d.ts +3 -0
- package/dist/ecosystem/scope.js +2 -0
- package/dist/ecosystem/secret.d.ts +9 -0
- package/dist/ecosystem/secret.js +1 -0
- package/dist/ecosystem/services/istanbul-coverage.d.ts +1 -0
- package/dist/ecosystem/services/istanbul-coverage.js +1 -0
- package/dist/ecosystem/services/local-content-request-handler.d.ts +9 -0
- package/dist/ecosystem/services/local-content-request-handler.js +1 -0
- package/dist/ecosystem/services/local-content.d.ts +1 -0
- package/dist/ecosystem/services/local-content.js +1 -0
- package/dist/ecosystem/services/v8-coverage.d.ts +1 -0
- package/dist/ecosystem/services/v8-coverage.js +1 -0
- package/dist/ecosystem/services.d.ts +28 -0
- package/dist/ecosystem/services.js +6 -0
- package/dist/ecosystem/testing.d.ts +7 -0
- package/dist/ecosystem/testing.js +1 -0
- package/dist/ecosystem/trigger.d.ts +1 -0
- package/dist/ecosystem/trigger.js +1 -0
- package/dist/ecosystem/types.d.ts +9 -0
- package/dist/ecosystem/types.js +5 -0
- package/dist/ecosystem/variable.d.ts +8 -0
- package/dist/ecosystem/variable.js +1 -0
- package/dist/ecosystem/virtual-user.d.ts +21 -0
- package/dist/ecosystem/virtual-user.js +5 -0
- package/dist/ecosystem/workflow.d.ts +18 -0
- package/dist/ecosystem/workflow.js +4 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.js +3 -3
- package/dist/middleware/auth-apikey.d.ts +1 -1
- package/dist/middleware/auth-bearer.d.ts +1 -1
- package/dist/middleware/auth-cookie.d.ts +1 -1
- package/dist/middleware/cors.d.ts +1 -1
- package/dist/middleware/remote-auth.d.ts +1 -1
- package/dist/middleware/telemetry.d.ts +2 -2
- package/dist/services/agent-run-state-service.d.ts +29 -0
- package/dist/services/agent-run-state-service.js +1 -0
- package/dist/services/agent-runner-service.d.ts +220 -0
- package/dist/services/agent-runner-service.js +1 -0
- package/dist/services/agent-storage-service.d.ts +18 -0
- package/dist/services/agent-storage-service.js +1 -0
- package/dist/services/http-personas.d.ts +1 -1
- package/dist/services/http-personas.js +3 -3
- package/dist/services/in-memory-agent-run-state-service.d.ts +19 -0
- package/dist/services/in-memory-agent-run-state-service.js +54 -0
- package/dist/services/index.d.ts +9 -10
- package/dist/services/index.js +3 -3
- package/dist/services/meta-service.d.ts +5 -4
- package/dist/testing/service-tests/agent-run-service-tests.d.ts +1 -1
- package/dist/testing/service-tests/agent-run-service-tests.js +3 -3
- package/dist/testing/service-tests/agent-storage-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/agent-storage-service-tests.js +302 -0
- package/dist/testing/service-tests.d.ts +4 -4
- package/dist/testing/service-tests.js +4 -4
- package/dist/types/core.types.d.ts +12 -11
- package/dist/types/core.types.js +1 -1
- package/dist/types/state.types.d.ts +11 -6
- package/dist/wirings/actor-flow/run-conversation.d.ts +2 -2
- package/dist/wirings/agent/agent-agui.d.ts +79 -0
- package/dist/wirings/agent/agent-agui.js +327 -0
- package/dist/wirings/agent/agent-finalize.d.ts +58 -0
- package/dist/wirings/agent/agent-finalize.js +138 -0
- package/dist/wirings/agent/agent-helpers.d.ts +35 -0
- package/dist/wirings/agent/agent-helpers.js +47 -0
- package/dist/wirings/agent/agent-interrupt.d.ts +153 -0
- package/dist/wirings/agent/agent-interrupt.js +257 -0
- package/dist/wirings/agent/agent-memory.d.ts +42 -0
- package/dist/wirings/agent/agent-memory.js +333 -0
- package/dist/wirings/agent/agent-model-config.d.ts +16 -0
- package/dist/wirings/agent/agent-model-config.js +51 -0
- package/dist/wirings/agent/agent-prepare.d.ts +117 -0
- package/dist/wirings/agent/agent-prepare.js +728 -0
- package/dist/wirings/agent/agent-registry.d.ts +16 -0
- package/dist/wirings/agent/agent-registry.js +51 -0
- package/dist/wirings/agent/agent-rpc.d.ts +15 -0
- package/dist/wirings/agent/agent-rpc.js +53 -0
- package/dist/wirings/agent/agent-runner.d.ts +7 -0
- package/dist/wirings/agent/agent-runner.js +580 -0
- package/dist/wirings/agent/agent-stream.d.ts +41 -0
- package/dist/wirings/agent/agent-stream.js +1029 -0
- package/dist/wirings/agent/agent-turn.d.ts +57 -0
- package/dist/wirings/agent/agent-turn.js +82 -0
- package/dist/wirings/agent/agent-utils.d.ts +1 -0
- package/dist/wirings/agent/agent-utils.js +1 -0
- package/dist/wirings/agent/agent.types.d.ts +625 -0
- package/dist/wirings/agent/agent.types.js +1 -0
- package/dist/wirings/agent/index.d.ts +12 -0
- package/dist/wirings/agent/index.js +10 -0
- package/dist/wirings/agent/voice-input.d.ts +69 -0
- package/dist/wirings/agent/voice-input.js +150 -0
- package/dist/wirings/agent/voice-output.d.ts +85 -0
- package/dist/wirings/agent/voice-output.js +196 -0
- package/dist/wirings/agent-scorer/agent-scorer-grade.d.ts +26 -0
- package/dist/wirings/agent-scorer/agent-scorer-grade.js +33 -0
- package/dist/wirings/agent-scorer/agent-scorer-judge.d.ts +17 -0
- package/dist/wirings/agent-scorer/agent-scorer-judge.js +92 -0
- package/dist/wirings/agent-scorer/agent-scorer-live.d.ts +15 -0
- package/dist/wirings/agent-scorer/agent-scorer-live.js +38 -0
- package/dist/wirings/agent-scorer/agent-scorer-registry.d.ts +18 -0
- package/dist/wirings/agent-scorer/agent-scorer-registry.js +46 -0
- package/dist/wirings/agent-scorer/agent-scorer-sampling.d.ts +8 -0
- package/dist/wirings/agent-scorer/agent-scorer-sampling.js +31 -0
- package/dist/wirings/agent-scorer/agent-scorer-snapshots.d.ts +10 -0
- package/dist/wirings/agent-scorer/agent-scorer-snapshots.js +40 -0
- package/dist/wirings/agent-scorer/agent-scorer-worker.d.ts +15 -0
- package/dist/wirings/agent-scorer/agent-scorer-worker.js +60 -0
- package/dist/wirings/agent-scorer/agent-scorer.d.ts +39 -0
- package/dist/wirings/agent-scorer/agent-scorer.js +40 -0
- package/dist/wirings/agent-scorer/agent-scorer.types.d.ts +90 -0
- package/dist/wirings/agent-scorer/agent-scorer.types.js +4 -0
- package/dist/wirings/agent-scorer/index.d.ts +6 -0
- package/dist/wirings/agent-scorer/index.js +5 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.js +2 -0
- package/dist/wirings/ai-agent/voice-output.d.ts +1 -2
- package/dist/wirings/channel/channel-rpc.types.d.ts +2 -2
- package/dist/wirings/rpc/addon-runner.d.ts +4 -0
- package/dist/wirings/rpc/addon-runner.js +14 -2
- package/dist/wirings/rpc/rpc-runner.d.ts +1 -1
- package/dist/wirings/rpc/rpc-runner.js +4 -2
- package/dist/wirings/rpc/rpc-types.d.ts +5 -1
- package/dist/wirings/rpc/wire-addon.d.ts +12 -0
- package/dist/wirings/rpc/wire-addon.js +4 -0
- package/dist/wirings/virtual-user/prepare-virtual-user-run.js +1 -1
- package/dist/wirings/virtual-user/virtual-user-agents.d.ts +1 -1
- package/dist/wirings/workflow/feature.d.ts +2 -1
- package/dist/wirings/workflow/index.d.ts +3 -14
- package/dist/wirings/workflow/index.js +0 -8
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +10 -0
- package/dist/wirings/workflow/pikku-scenario-service.js +9 -0
- package/dist/wirings/workflow/run-timeline.d.ts +1 -2
- package/dist/wirings/workflow/scenario.types.d.ts +37 -0
- package/dist/wirings/workflow/scenario.types.js +1 -0
- package/dist/wirings/workflow/workflow.types.d.ts +2 -37
- package/knowledge/decisions/internals/{a-non-streaming-agent-run-registers-with-airunstate-too.md → a-non-streaming-agent-run-registers-with-agentrunstate-too.md} +6 -6
- package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +1 -1
- package/knowledge/decisions/internals/actor-flow-drives-the-target-through-a-transport-seam.md +1 -1
- package/knowledge/decisions/internals/{ai-agent-agui-bridge-obeys-the-client-ordering-contract.md → agent-agui-bridge-obeys-the-client-ordering-contract.md} +5 -5
- package/knowledge/decisions/internals/{ai-agent-audio-chunks-carry-the-format-the-provider-returned.md → agent-audio-chunks-carry-the-format-the-provider-returned.md} +2 -2
- package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +1 -1
- package/knowledge/decisions/internals/{ai-agent-credential-suspensions-hide-the-tool-result.md → agent-credential-suspensions-hide-the-tool-result.md} +2 -2
- package/knowledge/decisions/internals/{ai-agent-delegate-and-supervise-hide-different-text.md → agent-delegate-and-supervise-hide-different-text.md} +4 -4
- package/knowledge/decisions/internals/{ai-agent-llm-tool-arguments-have-nulls-stripped.md → agent-llm-tool-arguments-have-nulls-stripped.md} +3 -3
- package/knowledge/decisions/internals/{ai-agent-model-config-stays-a-single-resolution-seam.md → agent-model-config-stays-a-single-resolution-seam.md} +3 -3
- package/knowledge/decisions/internals/{ai-agent-onerror-hooks-cannot-change-the-failure.md → agent-onerror-hooks-cannot-change-the-failure.md} +6 -6
- package/knowledge/decisions/internals/{ai-agent-runner-methods-must-keep-their-receiver.md → agent-runner-methods-must-keep-their-receiver.md} +4 -4
- package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +1 -1
- package/knowledge/decisions/internals/{ai-agent-stream-persistence-is-best-effort.md → agent-stream-persistence-is-best-effort.md} +4 -4
- package/knowledge/decisions/internals/{ai-agent-sub-agents-inherit-the-parent-context-block.md → agent-sub-agents-inherit-the-parent-context-block.md} +3 -3
- package/knowledge/decisions/internals/{ai-agent-tool-execute-failures-are-logged-unconditionally.md → agent-tool-execute-failures-are-logged-unconditionally.md} +3 -3
- package/knowledge/decisions/internals/{ai-agent-voice-input-transcribes-audio-parts-in-place.md → agent-voice-input-transcribes-audio-parts-in-place.md} +2 -2
- package/knowledge/decisions/internals/{ai-agent-working-memory-is-persisted-only-when-valid.md → agent-working-memory-is-persisted-only-when-valid.md} +2 -2
- package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +2 -2
- package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +3 -3
- package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +2 -2
- package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +1 -1
- package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +1 -1
- package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +1 -1
- package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +1 -1
- package/knowledge/decisions/internals/the-embedding-model-is-pinned-per-service-and-doc-query-embedding-is-split.md +1 -1
- package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +5 -5
- package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +2 -2
- package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +2 -2
- package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +1 -1
- package/knowledge/decisions/security/{ai-agent-approval-forwarding-requires-a-symbol-brand.md → agent-approval-forwarding-requires-a-symbol-brand.md} +3 -3
- package/knowledge/decisions/security/{ai-agent-credential-requests-are-symbol-branded.md → agent-credential-requests-are-symbol-branded.md} +4 -4
- package/knowledge/decisions/security/{ai-agent-gate-requires-a-session-only-when-auth-is-true.md → agent-gate-requires-a-session-only-when-auth-is-true.md} +3 -3
- package/knowledge/decisions/security/{ai-agent-ownership-failures-never-echo-the-resource.md → agent-ownership-failures-never-echo-the-resource.md} +2 -2
- package/knowledge/decisions/security/{ai-agent-resume-re-runs-the-authorization-gate.md → agent-resume-re-runs-the-authorization-gate.md} +4 -4
- package/knowledge/decisions/security/{ai-agent-sessionless-deployments-have-no-thread-ownership.md → agent-sessionless-deployments-have-no-thread-ownership.md} +5 -5
- package/knowledge/decisions/security/{ai-agent-thread-ownership-composes-the-session-principal.md → agent-thread-ownership-composes-the-session-principal.md} +2 -2
- package/knowledge/decisions/security/{ai-agent-tool-filtering-reads-the-live-function-config.md → agent-tool-filtering-reads-the-live-function-config.md} +2 -2
- package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +3 -3
- package/knowledge/decisions/security/an-empty-owners-constraint-matches-nothing.md +3 -3
- package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +20 -13
- package/knowledge/decisions/security/index.md +1 -2
- package/package.json +4 -4
- package/scripts/generate-api-report.mts +32 -8
- package/src/agent-naming.test.ts +68 -0
- package/src/ecosystem/agent-scorer.ts +24 -0
- package/src/ecosystem/agent.ts +42 -0
- package/src/ecosystem/channel/local.ts +1 -0
- package/src/ecosystem/channel.ts +22 -0
- package/src/ecosystem/cli/channel.ts +25 -0
- package/src/ecosystem/cli.ts +21 -0
- package/src/ecosystem/credential.ts +10 -0
- package/src/ecosystem/dev.ts +12 -0
- package/src/ecosystem/function.ts +28 -0
- package/src/ecosystem/gateway.ts +4 -0
- package/src/ecosystem/hmac.ts +1 -0
- package/src/ecosystem/http.ts +22 -0
- package/src/ecosystem/mcp.ts +17 -0
- package/src/ecosystem/middleware.ts +27 -0
- package/src/ecosystem/node-host-resolver.ts +1 -0
- package/src/ecosystem/node.ts +5 -0
- package/src/ecosystem/oauth2.ts +4 -0
- package/src/ecosystem/persona.ts +30 -0
- package/src/ecosystem/queue.ts +23 -0
- package/src/ecosystem/remote.ts +9 -0
- package/src/ecosystem/role.ts +13 -0
- package/src/ecosystem/rpc.ts +7 -0
- package/src/ecosystem/safe-fetch.ts +8 -0
- package/src/ecosystem/scenario.ts +32 -0
- package/src/ecosystem/scheduler.ts +5 -0
- package/src/ecosystem/schema.ts +1 -0
- package/src/ecosystem/scope.ts +14 -0
- package/src/ecosystem/secret.ts +15 -0
- package/src/ecosystem/services/istanbul-coverage.ts +1 -0
- package/src/ecosystem/services/local-content-request-handler.ts +16 -0
- package/src/ecosystem/services/local-content.ts +1 -0
- package/src/ecosystem/services/v8-coverage.ts +1 -0
- package/src/ecosystem/services.ts +82 -0
- package/src/ecosystem/testing.ts +8 -0
- package/src/ecosystem/trigger.ts +7 -0
- package/src/ecosystem/types.ts +34 -0
- package/src/ecosystem/variable.ts +14 -0
- package/src/ecosystem/virtual-user.ts +48 -0
- package/src/ecosystem/workflow.ts +68 -0
- package/src/index.ts +8 -19
- package/src/pikku-state.ts +3 -3
- package/src/public-surface.json +183 -91
- package/src/public-surface.json.README +20 -2
- package/src/public-surface.test.ts +31 -3
- package/src/services/{ai-run-state-service.ts → agent-run-state-service.ts} +5 -5
- package/src/services/{ai-agent-runner-service.ts → agent-runner-service.ts} +18 -18
- package/src/services/{ai-storage-service.ts → agent-storage-service.ts} +7 -7
- package/src/services/http-personas-converse.test.ts +3 -3
- package/src/services/http-personas.ts +4 -4
- package/src/services/{in-memory-ai-run-state-service.ts → in-memory-agent-run-state-service.ts} +7 -7
- package/src/services/index.ts +13 -24
- package/src/services/meta-service.ts +5 -7
- package/src/testing/service-tests/agent-run-service-tests.ts +4 -4
- package/src/testing/service-tests/{ai-storage-service-tests.ts → agent-storage-service-tests.ts} +8 -8
- package/src/testing/service-tests.ts +10 -8
- package/src/types/core.types.ts +13 -13
- package/src/types/state.types.ts +11 -10
- package/src/wirings/actor-flow/run-conversation.test.ts +2 -2
- package/src/wirings/actor-flow/run-conversation.ts +7 -9
- package/src/wirings/{ai-agent/ai-agent-agui.test.ts → agent/agent-agui.test.ts} +126 -105
- package/src/wirings/{ai-agent/ai-agent-agui.ts → agent/agent-agui.ts} +7 -7
- package/src/wirings/{ai-agent/ai-agent-authorization.test.ts → agent/agent-authorization.test.ts} +6 -6
- package/src/wirings/{ai-agent/ai-agent-finalize.test.ts → agent/agent-finalize.test.ts} +17 -16
- package/src/wirings/{ai-agent/ai-agent-finalize.ts → agent/agent-finalize.ts} +22 -22
- package/src/wirings/{ai-agent/ai-agent-helpers.test.ts → agent/agent-helpers.test.ts} +1 -1
- package/src/wirings/{ai-agent/ai-agent-helpers.ts → agent/agent-helpers.ts} +1 -1
- package/src/wirings/{ai-agent/ai-agent-interrupt.test.ts → agent/agent-interrupt.test.ts} +67 -77
- package/src/wirings/{ai-agent/ai-agent-interrupt.ts → agent/agent-interrupt.ts} +5 -5
- package/src/wirings/{ai-agent/ai-agent-memory.test.ts → agent/agent-memory.test.ts} +12 -12
- package/src/wirings/{ai-agent/ai-agent-memory.ts → agent/agent-memory.ts} +36 -36
- package/src/wirings/{ai-agent/ai-agent-model-config.test.ts → agent/agent-model-config.test.ts} +2 -5
- package/src/wirings/{ai-agent/ai-agent-model-config.ts → agent/agent-model-config.ts} +1 -1
- package/src/wirings/{ai-agent/ai-agent-prepare.test.ts → agent/agent-prepare.test.ts} +18 -18
- package/src/wirings/{ai-agent/ai-agent-prepare.ts → agent/agent-prepare.ts} +54 -51
- package/src/wirings/{ai-agent/ai-agent-registry.test.ts → agent/agent-registry.test.ts} +49 -43
- package/src/wirings/{ai-agent/ai-agent-registry.ts → agent/agent-registry.ts} +14 -14
- package/src/wirings/{ai-agent/ai-agent-resume-authorization.test.ts → agent/agent-resume-authorization.test.ts} +18 -18
- package/src/wirings/{ai-agent → agent}/agent-rpc.ts +16 -20
- package/src/wirings/{ai-agent/ai-agent-runner.test.ts → agent/agent-runner.test.ts} +105 -106
- package/src/wirings/{ai-agent/ai-agent-runner.ts → agent/agent-runner.ts} +90 -80
- package/src/wirings/{ai-agent/ai-agent-stream-output-hooks.test.ts → agent/agent-stream-output-hooks.test.ts} +25 -25
- package/src/wirings/{ai-agent/ai-agent-stream.test.ts → agent/agent-stream.test.ts} +147 -143
- package/src/wirings/{ai-agent/ai-agent-stream.ts → agent/agent-stream.ts} +147 -146
- package/src/wirings/{ai-agent/ai-agent-thread-ownership.test.ts → agent/agent-thread-ownership.test.ts} +24 -28
- package/src/wirings/{ai-agent/ai-agent-turn.test.ts → agent/agent-turn.test.ts} +3 -5
- package/src/wirings/{ai-agent/ai-agent-turn.ts → agent/agent-turn.ts} +7 -7
- package/src/wirings/{ai-agent/ai-agent.types.ts → agent/agent.types.ts} +51 -48
- package/src/wirings/agent/index.ts +56 -0
- package/src/wirings/{ai-agent → agent}/voice-input.test.ts +15 -15
- package/src/wirings/{ai-agent → agent}/voice-input.ts +11 -11
- package/src/wirings/{ai-agent → agent}/voice-output.test.ts +23 -23
- package/src/wirings/{ai-agent → agent}/voice-output.ts +11 -11
- package/src/wirings/{ai-scorer/ai-scorer-grade.test.ts → agent-scorer/agent-scorer-grade.test.ts} +8 -8
- package/src/wirings/{ai-scorer/ai-scorer-grade.ts → agent-scorer/agent-scorer-grade.ts} +9 -9
- package/src/wirings/{ai-scorer/ai-scorer-judge.test.ts → agent-scorer/agent-scorer-judge.test.ts} +11 -14
- package/src/wirings/{ai-scorer/ai-scorer-judge.ts → agent-scorer/agent-scorer-judge.ts} +11 -11
- package/src/wirings/{ai-scorer/ai-scorer-live.test.ts → agent-scorer/agent-scorer-live.test.ts} +11 -11
- package/src/wirings/{ai-scorer/ai-scorer-live.ts → agent-scorer/agent-scorer-live.ts} +3 -3
- package/src/wirings/{ai-scorer/ai-scorer-registry.ts → agent-scorer/agent-scorer-registry.ts} +11 -8
- package/src/wirings/{ai-scorer/ai-scorer-sampling.test.ts → agent-scorer/agent-scorer-sampling.test.ts} +1 -1
- package/src/wirings/{ai-scorer/ai-scorer-snapshots.test.ts → agent-scorer/agent-scorer-snapshots.test.ts} +2 -2
- package/src/wirings/{ai-scorer/ai-scorer-snapshots.ts → agent-scorer/agent-scorer-snapshots.ts} +1 -1
- package/src/wirings/{ai-scorer/ai-scorer-worker.test.ts → agent-scorer/agent-scorer-worker.test.ts} +15 -15
- package/src/wirings/{ai-scorer/ai-scorer-worker.ts → agent-scorer/agent-scorer-worker.ts} +9 -7
- package/src/wirings/{ai-scorer/ai-scorer.ts → agent-scorer/agent-scorer.ts} +7 -7
- package/src/wirings/{ai-scorer/ai-scorer.types.ts → agent-scorer/agent-scorer.types.ts} +6 -6
- package/src/wirings/agent-scorer/index.ts +22 -0
- package/src/wirings/channel/channel-rpc.types.ts +2 -2
- package/src/wirings/rpc/addon-runner.ts +31 -3
- package/src/wirings/rpc/addon-secrets.test.ts +130 -0
- package/src/wirings/rpc/rpc-runner.ts +4 -2
- package/src/wirings/rpc/rpc-types.ts +5 -1
- package/src/wirings/rpc/wire-addon.ts +16 -0
- package/src/wirings/virtual-user/prepare-virtual-user-run.ts +1 -1
- package/src/wirings/virtual-user/run-virtual-user.test.ts +2 -2
- package/src/wirings/virtual-user/run-virtual-user.ts +3 -3
- package/src/wirings/virtual-user/virtual-user-agents.ts +1 -1
- package/src/wirings/workflow/feature.ts +2 -5
- package/src/wirings/workflow/index.ts +1 -49
- package/src/wirings/workflow/pikku-scenario-service.ts +21 -1
- package/src/wirings/workflow/run-timeline.ts +1 -1
- package/src/wirings/workflow/scenario.types.ts +63 -0
- package/src/wirings/workflow/workflow.types.ts +1 -54
- package/src/wirings-stay-decoupled.test.ts +3 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/src/wirings/ai-agent/index.ts +0 -65
- package/src/wirings/ai-scorer/index.ts +0 -24
- /package/src/wirings/{ai-agent/ai-agent-utils.ts → agent/agent-utils.ts} +0 -0
- /package/src/wirings/{ai-scorer/ai-scorer-sampling.ts → agent-scorer/agent-scorer-sampling.ts} +0 -0
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: An interrupt for a run owned by another instance says so, rather than returning false
|
|
4
4
|
description: A bare false is indistinguishable from "already finished", which is the one deployment shape the in-process registry cannot cover
|
|
5
|
-
tags: core,
|
|
5
|
+
tags: core, agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# An interrupt for a run owned by another instance says so
|
|
9
9
|
|
|
10
|
-
`
|
|
10
|
+
`interruptAgent` resolves a run through `agentRunState`, then tries to abort it
|
|
11
11
|
via the in-process registry. A run still marked `running` that this process has
|
|
12
12
|
no abort handle for is executing on another instance.
|
|
13
13
|
|
|
@@ -19,7 +19,7 @@ the condition instead.
|
|
|
19
19
|
|
|
20
20
|
**What this rules out:** collapsing the two outcomes into one boolean because
|
|
21
21
|
the caller "only cares whether it stopped". The caller cares a great deal about
|
|
22
|
-
the difference between
|
|
22
|
+
the difference between _stopped_ and _cannot be stopped from here_.
|
|
23
23
|
|
|
24
24
|
The fix for the underlying gap is `signalRunInterrupt`, which fans the interrupt
|
|
25
25
|
out over `eventHub` so every instance tries locally.
|
package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: A wrapped agent-stream send must return the inner send's promise
|
|
4
4
|
description: Middleware runs asynchronously, so dropping the returned promise turns every awaited channel.send upstream into a no-op
|
|
5
|
-
tags: core,
|
|
5
|
+
tags: core, agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# A wrapped agent-stream send must return the inner send's promise
|
|
9
9
|
|
|
10
|
-
`
|
|
10
|
+
`streamAgent` wraps the caller's channel so every event passes through the
|
|
11
11
|
stream middleware. That wrapper's `send` returns whatever the inner `send`
|
|
12
12
|
returns, and it must: the middleware chain is asynchronous, so a wrapper that
|
|
13
13
|
calls the inner `send` and returns `undefined` resolves immediately while the
|
package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: A message with nothing to say carries no text part at all
|
|
4
4
|
description: An attachment on its own is a real turn, and providers are entitled to reject an empty text part sitting beside it
|
|
5
|
-
tags: core,
|
|
5
|
+
tags: core, agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# A message with nothing to say carries no text part
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: A transcript is recorded only when something was actually heard
|
|
4
4
|
description: Recording an empty string sends a transcript event saying the user said nothing, which renders as an empty bubble rather than a pending one
|
|
5
|
-
tags: core,
|
|
5
|
+
tags: core, agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# A transcript is recorded only when something was actually heard
|
package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: Speech synthesis picks a voice per sentence but announces a limitation once
|
|
4
4
|
description: A bilingual reply should speak the half it can, and repeating the notice for every sentence would bury the reply itself
|
|
5
|
-
tags: core,
|
|
5
|
+
tags: core, agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Speech synthesis picks a voice per sentence, and warns once per reply
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: The agent `done` event goes through the middleware and is awaited
|
|
4
4
|
description: `done` is the only end-of-reply signal a stream hook gets, and buffering hooks flush on it — sending it raw discards work already paid for
|
|
5
|
-
tags: core,
|
|
5
|
+
tags: core, agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# The agent `done` event goes through the middleware and is awaited
|
|
@@ -24,6 +24,6 @@ same call.
|
|
|
24
24
|
|
|
25
25
|
**What this rules out:** adding a per-call `model` parameter, collapsing
|
|
26
26
|
`embedDocuments` and `embedQuery` into one `embed`, and pointing a vector store
|
|
27
|
-
at `
|
|
27
|
+
at `AgentRunnerService.embed` / `embedMany` instead — those take a per-call
|
|
28
28
|
model on purpose and drag in the whole agent-runner tool loop, and they give back
|
|
29
29
|
neither guarantee.
|
|
@@ -11,16 +11,16 @@ tags: core, rpc, performance
|
|
|
11
11
|
an object literal, and that literal declared `get agent()` so the agent facade
|
|
12
12
|
stayed lazy — most requests never touch it, and reading it builds five closures.
|
|
13
13
|
|
|
14
|
-
The laziness is right. Declaring the accessor
|
|
14
|
+
The laziness is right. Declaring the accessor _on the literal_ was not: a
|
|
15
15
|
literal containing an accessor needs a real property descriptor per instance,
|
|
16
16
|
which takes it off V8's fast object-literal construction path and slows the
|
|
17
17
|
whole object, not just the accessor.
|
|
18
18
|
|
|
19
19
|
Measured with `benchmarks/bench-profile-granular.ts`, three runs each:
|
|
20
20
|
|
|
21
|
-
| shape
|
|
22
|
-
|
|
|
23
|
-
| object literal with `get agent()`
|
|
21
|
+
| shape | per call |
|
|
22
|
+
| ----------------------------------- | ------------------------ |
|
|
23
|
+
| object literal with `get agent()` | 1.106 / 1.433 / 1.147 µs |
|
|
24
24
|
| class with `agent` on the prototype | 0.523 / 0.461 / 0.525 µs |
|
|
25
25
|
|
|
26
26
|
Roughly 2.4×, well outside the run-to-run variance, on a path every request
|
|
@@ -28,7 +28,7 @@ takes. On the same machine a full `fetchData` measures 12–19µs, so this was o
|
|
|
28
28
|
the order of a tenth of a request spent constructing one object.
|
|
29
29
|
|
|
30
30
|
The obvious alternative is worse. Making `agent` an eager property removes the
|
|
31
|
-
accessor but builds those five closures unconditionally, and measured
|
|
31
|
+
accessor but builds those five closures unconditionally, and measured _slower_
|
|
32
32
|
than the original at 1.967µs. A prototype accessor is the only shape that keeps
|
|
33
33
|
the laziness and the fast construction path.
|
|
34
34
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: A voice turn's transcript is sent before the run starts, on the raw channel
|
|
4
4
|
description: The client does not know what it said, and an answer starts streaming within a few hundred milliseconds — a question arriving after its answer reads as the wrong question
|
|
5
|
-
tags: core,
|
|
5
|
+
tags: core, agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# A voice turn's transcript is sent ahead of the run
|
|
@@ -10,7 +10,7 @@ tags: core, ai-agent
|
|
|
10
10
|
A voice client sends audio, so it does not know what it said. Until the
|
|
11
11
|
transcript reaches it, its own message renders as a blank bubble.
|
|
12
12
|
|
|
13
|
-
The event is sent
|
|
13
|
+
The event is sent _before_ the run rather than alongside it because the answer
|
|
14
14
|
begins streaming within a few hundred milliseconds. Sent concurrently, the
|
|
15
15
|
transcript routinely lands after the first tokens of its own answer — and a
|
|
16
16
|
question that appears beneath its answer reads as a question about something
|
package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: Thread history records what the model was asked, which for a spoken turn is the transcript
|
|
4
4
|
description: The wire carried a base64 audio blob; persisting it writes megabytes of unreadable data and loses the only readable record of the turn
|
|
5
|
-
tags: core,
|
|
5
|
+
tags: core, agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Thread history records the transcript, not the audio that arrived
|
|
@@ -13,7 +13,7 @@ carried a base64 audio blob, and `voiceInput` replaced it with a transcript
|
|
|
13
13
|
before the model ever saw it.
|
|
14
14
|
|
|
15
15
|
Persisting the blob would write megabytes of unreadable data into the history
|
|
16
|
-
|
|
16
|
+
_and_ discard the only readable record of what was said — the worst of both.
|
|
17
17
|
|
|
18
18
|
Both the streaming and non-streaming paths do this, and both check identity
|
|
19
19
|
rather than assuming: a middleware is free to rewrite the message list into
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: Only an explicit `false` silences a spoken reply
|
|
4
4
|
description: The key being absent means no voice input is wired and nothing reported either way, so those callers keep the behaviour they had before the option existed
|
|
5
|
-
tags: core,
|
|
5
|
+
tags: core, agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Only an explicit `false` silences a spoken reply
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: Only a Symbol-branded framework result can request tool approval
|
|
4
4
|
description: Approval markers are trusted from the APPROVAL_REQUIRED Symbol on a forwardsApproval tool, never from a JSON key an LLM could emit
|
|
5
|
-
tags:
|
|
5
|
+
tags: agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Only a Symbol-branded framework result can request tool approval
|
|
9
9
|
|
|
10
10
|
`checkForApprovals` in
|
|
11
|
-
`packages/core/src/wirings/
|
|
11
|
+
`packages/core/src/wirings/agent/agent-stream.ts` honours a forwarded
|
|
12
12
|
approval only when both hold: the tool declares `forwardsApproval` (set solely by
|
|
13
13
|
framework code on the sub-agent delegating tools built in
|
|
14
|
-
`
|
|
14
|
+
`agent-prepare.ts`), and its result carries the `APPROVAL_REQUIRED` unique
|
|
15
15
|
Symbol. The companion `__approvalRequired` string key exists for transport, but
|
|
16
16
|
is never what the decision reads.
|
|
17
17
|
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: Credential requests are trusted only when Symbol-branded
|
|
4
4
|
description: The string key is a wire field; the Symbol is the capability, and only core can mint it
|
|
5
|
-
tags:
|
|
5
|
+
tags: agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Credential requests are trusted only when Symbol-branded
|
|
9
9
|
|
|
10
10
|
A tool result asking the run to suspend and prompt for a credential is honoured
|
|
11
11
|
only when it carries the `CREDENTIAL_REQUIRED` Symbol minted in
|
|
12
|
-
`
|
|
12
|
+
`agent-prepare.ts`. `checkForCredentialRequests` in `agent-stream.ts`
|
|
13
13
|
tests for that Symbol, never for the `__credentialRequired` string key that
|
|
14
14
|
travels beside it on the wire.
|
|
15
15
|
|
|
@@ -24,7 +24,7 @@ primitive inside the product's own UI. This mirrors the `APPROVAL_REQUIRED`
|
|
|
24
24
|
brand, which exists for the identical reason one function over.
|
|
25
25
|
|
|
26
26
|
The brand survives because the object is passed by reference the whole way —
|
|
27
|
-
core's `buildToolDefs` wrapper returns it, the Vercel adapter's `
|
|
27
|
+
core's `buildToolDefs` wrapper returns it, the Vercel adapter's `agentTool` hands
|
|
28
28
|
it back verbatim, and the AI SDK puts the raw value on the stream part rather
|
|
29
29
|
than the JSON form it builds separately for the model.
|
|
30
30
|
|
|
@@ -33,5 +33,5 @@ result` for convenience, or introducing any path where a credential request is
|
|
|
33
33
|
reconstructed from parsed JSON rather than passed by reference — either one
|
|
34
34
|
silently converts the gate into a formality. Note the inverse holds for the
|
|
35
35
|
`credentialFilteredChannel` suppression later in the same file: that one
|
|
36
|
-
deliberately matches the
|
|
36
|
+
deliberately matches the _string_ key, because it hides tool results from the
|
|
37
37
|
client and a broader match leaks less, not more.
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: An agent requires a session only when auth is true, but always enforces scopes and permissions
|
|
4
4
|
description: Agents follow pikkuSessionlessFunc semantics so crons and queue workers can run them; scopes are an AND gate checked before any permission I/O
|
|
5
|
-
tags:
|
|
5
|
+
tags: agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# An agent requires a session only when auth is true, but always enforces scopes and permissions
|
|
9
9
|
|
|
10
10
|
`assertAgentAuthorized` in
|
|
11
|
-
`packages/core/src/wirings/
|
|
12
|
-
session presence (only when `
|
|
11
|
+
`packages/core/src/wirings/agent/agent-prepare.ts` enforces, in order:
|
|
12
|
+
session presence (only when `CoreAgent.auth === true`), then `scopes`, then
|
|
13
13
|
`permissions`. The ordering mirrors the function runner — scopes AND together so
|
|
14
14
|
they can only narrow access, and a missing scope short-circuits before any
|
|
15
15
|
permission function does I/O. Declared `scopes` are narrowed to the generated
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: An agent ownership failure never echoes the resource it refused
|
|
4
4
|
description: assertResourceOwner throws a bare ForbiddenError so the error cannot be used as an existence oracle, at the cost of thinner debugging output
|
|
5
|
-
tags:
|
|
5
|
+
tags: agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# An agent ownership failure never echoes the resource it refused
|
|
9
9
|
|
|
10
10
|
`assertResourceOwner` in
|
|
11
|
-
`packages/core/src/wirings/
|
|
11
|
+
`packages/core/src/wirings/agent/agent-prepare.ts` compares a stored
|
|
12
12
|
thread/run `resourceId` against the caller's composed owner key and throws
|
|
13
13
|
`ForbiddenError('Not authorized to access this thread' | '… run')` on a
|
|
14
14
|
mismatch. The message deliberately carries no id.
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: Resuming a suspended agent run re-runs the agent's authorization gate
|
|
4
4
|
description: Ownership of the run is not enough — a grant revoked while the run was suspended must block the approval
|
|
5
|
-
tags:
|
|
5
|
+
tags: agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Resuming a suspended agent run re-runs the agent's authorization gate
|
|
9
9
|
|
|
10
|
-
Both `
|
|
11
|
-
(`
|
|
12
|
-
ownership and before touching `
|
|
10
|
+
Both `resumeAgent` (`agent-stream.ts`) and `resumeAgentSync`
|
|
11
|
+
(`agent-runner.ts`) call `assertAgentAuthorized` after asserting run
|
|
12
|
+
ownership and before touching `agentRunState.resolveApproval`.
|
|
13
13
|
|
|
14
14
|
Ownership proves the run belongs to the caller, not that the caller may still
|
|
15
15
|
act on it: a suspension can outlive the permission that created it, and the whole
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: Agent thread ownership fails closed when there is no principal
|
|
4
4
|
description: A sessionless caller gets an ephemeral owner and reaches no stored thread, rather than reaching all of them
|
|
5
|
-
tags:
|
|
5
|
+
tags: agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Agent thread ownership fails closed when there is no principal
|
|
9
9
|
|
|
10
10
|
`canAccessThread` returns `false` and `threadOwnerConstraint` returns `[]` when
|
|
11
11
|
the session carries neither `userId` nor `orgId`
|
|
12
|
-
(`packages/core/src/wirings/
|
|
12
|
+
(`packages/core/src/wirings/agent/agent-prepare.ts`).
|
|
13
13
|
`resolveOwnerResourceId` never accepts the client-supplied `resourceId` as an
|
|
14
14
|
ownership key; without a principal it mints an ephemeral `anon-<uuid>` owner
|
|
15
15
|
memoized on the request's params object.
|
|
@@ -17,8 +17,8 @@ memoized on the request's params object.
|
|
|
17
17
|
Agent wirings default to `auth: false`, so "no session" is the common case
|
|
18
18
|
rather than an exotic one. Treating it as "no ownership model to enforce" made
|
|
19
19
|
every ownership check vacuous: a caller named any `resourceId` and reached that
|
|
20
|
-
thread, and because `undefined` on `AgentRunService.listThreads` means
|
|
21
|
-
|
|
20
|
+
thread, and because `undefined` on `AgentRunService.listThreads` means _no
|
|
21
|
+
filter_ rather than _no rows_, a sessionless `getAgentThreads` returned every
|
|
22
22
|
thread in the deployment. The ephemeral owner is what keeps one-shot
|
|
23
23
|
conversations working while denying continuity — it is unguessable, so nothing
|
|
24
24
|
stored can be reached, and it is stable within a request, so a sub-agent
|
|
@@ -32,7 +32,7 @@ field, never in its body.
|
|
|
32
32
|
|
|
33
33
|
**What this rules out:** returning `undefined` from `threadOwnerConstraint` for
|
|
34
34
|
any caller; deriving `owners` from request input instead of the session; reading
|
|
35
|
-
`auth: false` as permission to skip ownership on a
|
|
35
|
+
`auth: false` as permission to skip ownership on a _stored_ thread; and
|
|
36
36
|
persisting the anonymous owner anywhere, which would turn an ephemeral identity
|
|
37
37
|
into a forgeable one. Cross-request thread continuity without a session is not
|
|
38
38
|
available by design — wire a session to get it back. See
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: An agent thread key is always prefixed with the trusted principal
|
|
4
4
|
description: Ownership keys are composed as principal:resourceId, so a client id can sub-divide its own boundary but never widen it
|
|
5
|
-
tags:
|
|
5
|
+
tags: agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# An agent thread key is always prefixed with the trusted principal
|
|
9
9
|
|
|
10
|
-
`resolveOwnerResourceId` in `packages/core/src/wirings/
|
|
10
|
+
`resolveOwnerResourceId` in `packages/core/src/wirings/agent/agent-prepare.ts`
|
|
11
11
|
composes the ownership key for a thread or run as `principal:resourceId`. The
|
|
12
12
|
principal is the trusted one — `session.userId` for the default `'user'`
|
|
13
13
|
`SessionScope`, `session.orgId` for `'org'` — and the client-supplied
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: Agent tool permission filtering reads the live function config, not the metadata
|
|
4
4
|
description: The pikkuAuth brand survives only on live permission objects, so a metadata-driven check would silently admit every gated tool
|
|
5
|
-
tags:
|
|
5
|
+
tags: agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Agent tool permission filtering reads the live function config, not the metadata
|
|
9
9
|
|
|
10
|
-
`buildToolDefs` in `packages/core/src/wirings/
|
|
10
|
+
`buildToolDefs` in `packages/core/src/wirings/agent/agent-prepare.ts`
|
|
11
11
|
decides which tools and sub-agents to expose to the model by calling
|
|
12
12
|
`checkAuthPermissions` against the permission objects held in the live function
|
|
13
13
|
(or agent) config in `pikkuState`, using the metadata only to learn that a
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: An agent approval is claimed before the tool runs
|
|
4
4
|
description: resolveApproval is a compare-and-swap returning whether this caller won, because the read that precedes it is not a claim and ten concurrent approvals would otherwise mean ten refunds
|
|
5
|
-
tags:
|
|
5
|
+
tags: agent
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# An agent approval is claimed before the tool runs
|
|
@@ -15,7 +15,7 @@ same tool call all observed `suspended`, all snapshotted the same list, and all
|
|
|
15
15
|
reached `execute`. `resolveApproval` returned `void`, so a loser could not even
|
|
16
16
|
tell.
|
|
17
17
|
|
|
18
|
-
`resolveApproval` is now the claim, and returns whether
|
|
18
|
+
`resolveApproval` is now the claim, and returns whether _this_ caller made it.
|
|
19
19
|
The stores implement it as a compare-and-swap: Kysely updates the run row only
|
|
20
20
|
while `status = 'suspended'` and `pendingApprovals` still equals the list it
|
|
21
21
|
read; the tool-call stores move the row off `approvalStatus = 'pending'` and
|
|
@@ -23,7 +23,7 @@ count the rows they changed. Both resume paths run the tool only for the ids the
|
|
|
23
23
|
claimed, and a caller that claimed nothing gets an error rather than a silent
|
|
24
24
|
re-run.
|
|
25
25
|
|
|
26
|
-
The claim is per tool call, not per run, so concurrent approvals of
|
|
26
|
+
The claim is per tool call, not per run, so concurrent approvals of _different_
|
|
27
27
|
tool calls on one run all proceed — which is the case that made a run-level
|
|
28
28
|
`claimSuspendedRun` the worse fit.
|
|
29
29
|
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: An empty owners constraint matches nothing
|
|
4
4
|
description: owners is an authorization boundary, so every storage backend must treat [] as no rows rather than no filter
|
|
5
|
-
tags:
|
|
5
|
+
tags: agent, storage
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# An empty owners constraint matches nothing
|
|
9
9
|
|
|
10
10
|
Every `AgentRunService.listThreads` implementation returns `[]` immediately when
|
|
11
|
-
`owners` is present and empty — `kysely-
|
|
11
|
+
`owners` is present and empty — `kysely-agent-run-service.ts`,
|
|
12
12
|
`redis-agent-run-service.ts`, `mongodb-agent-run-service.ts`. The conformance
|
|
13
13
|
suite in `packages/core/src/testing/service-tests.ts` pins it, so a new backend
|
|
14
14
|
inherits the requirement instead of rediscovering it.
|
|
@@ -27,4 +27,4 @@ return; the two decisions only hold together.
|
|
|
27
27
|
`if (owners)` path; treating `owners: []` as equivalent to `owners: undefined`
|
|
28
28
|
at any layer; and adding a backend without the conformance suite.
|
|
29
29
|
|
|
30
|
-
See [[
|
|
30
|
+
See [[agent-sessionless-deployments-have-no-thread-ownership]].
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: decision
|
|
3
3
|
title: The console addon's privileged functions gate themselves
|
|
4
4
|
description: Thread listing is owner-scoped unless the caller holds admin, and addon installation requires an admin session, rather than trusting the host to register a global permission
|
|
5
|
-
tags: addon,
|
|
5
|
+
tags: addon, agent, rpc
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# The console addon's privileged functions gate themselves
|
|
@@ -27,14 +27,18 @@ runner's, which for a `pikkuSessionlessFunc` fires only when the function itself
|
|
|
27
27
|
sets `auth: true`.
|
|
28
28
|
|
|
29
29
|
The addon's original authorization story was a single package-scoped
|
|
30
|
-
`addGlobalPermission([isAdmin], '@pikku/addon-console')` that the
|
|
30
|
+
`addGlobalPermission([isAdmin], '@pikku/addon-console')` that the _host_
|
|
31
31
|
registered, which `runPermissions` resolves in the callee's package namespace and
|
|
32
32
|
applies to every function at once. Nothing generated that call — the scaffold
|
|
33
33
|
emitted only a comment recommending it, and `resolveGlobalPermissions` returns an
|
|
34
34
|
empty list when the host registered none, which `runPermissions` treats as
|
|
35
35
|
allow. An app that followed the scaffold and stopped there had a fully open
|
|
36
|
-
console surface.
|
|
37
|
-
instead, which the function runner
|
|
36
|
+
console surface. Every console function now declares its own
|
|
37
|
+
`pikku:console:<area>:<action>` scope instead, which the function runner
|
|
38
|
+
enforces per call. A blanket `wireAddon({ …, scopes: ['admin'] })` stood in for
|
|
39
|
+
that briefly; it is gone, because a single grant covering secrets, source
|
|
40
|
+
editing and the audit trail alike is not something an operator can reason
|
|
41
|
+
about.
|
|
38
42
|
|
|
39
43
|
The two functions whose failure mode is worst still do not depend on any
|
|
40
44
|
package-wide gate:
|
|
@@ -51,16 +55,19 @@ storage backend already treats as no rows.
|
|
|
51
55
|
attacker-chosen package name and write a wiring file into the project. They were
|
|
52
56
|
`pikkuSessionlessFunc` with `auth: false`, which — behind a `no-auth` RPC route
|
|
53
57
|
and with no host global permission — meant an unauthenticated POST reached
|
|
54
|
-
`execFileSync`. They now declare `auth: true` and
|
|
55
|
-
by `verifyScopes` in the
|
|
56
|
-
|
|
57
|
-
not the
|
|
58
|
-
`wireAddon({ name: 'console', package: '@pikku/addon-console' })`
|
|
58
|
+
`execFileSync`. They now declare `auth: true` and
|
|
59
|
+
`scopes: ['pikku:console:addons:install']`, enforced by `verifyScopes` in the
|
|
60
|
+
function runner before the body runs. Both gates are
|
|
61
|
+
checked whether or not the host registered a global permission — an app that
|
|
62
|
+
hand-wires `wireAddon({ name: 'console', package: '@pikku/addon-console' })`
|
|
59
63
|
still cannot reach `execFileSync` unauthenticated.
|
|
60
64
|
|
|
61
|
-
|
|
62
|
-
`wireScope` tree
|
|
63
|
-
functions come from.
|
|
65
|
+
The scopes are spelled as literals here because they are scope ids in the
|
|
66
|
+
addon's own `wireScope` tree — the same tree the `pikku:console:scopes:*` gates
|
|
67
|
+
on the scope-admin functions come from. The generated secret and variable
|
|
68
|
+
brokers live in the app's own scaffold rather than the addon, so the addon
|
|
69
|
+
boundary never covered them; they carry `pikku:console:secrets:*` and
|
|
70
|
+
`pikku:console:variables:*` themselves.
|
|
64
71
|
|
|
65
72
|
**What this rules out:** treating the host's `addGlobalPermission` as the only
|
|
66
73
|
gate in front of an operation that installs code or reads another principal's
|
|
@@ -72,5 +79,5 @@ is-this-localhost test.
|
|
|
72
79
|
|
|
73
80
|
See [[addon-scopes-are-resolved-where-the-function-runs]],
|
|
74
81
|
[[an-empty-owners-constraint-matches-nothing]],
|
|
75
|
-
[[
|
|
82
|
+
[[agent-sessionless-deployments-have-no-thread-ownership]] and
|
|
76
83
|
[[global-permissions-and-function-permissions-are-independent-gates]].
|
|
@@ -15,7 +15,7 @@ A rule about who may do what, and which way it fails when it is unsure.
|
|
|
15
15
|
- [A permission gets a wire it cannot reply on](a-permission-gets-a-wire-it-cannot-reply-on.md) — The permission wire is typed with Out = never so a permission cannot send on the channel; that narrowing is not a subtype, so the call site asserts
|
|
16
16
|
- [A step runs the function the workflow dispatched it with](a-step-runs-the-function-the-workflow-dispatched-it-with.md) — StepState records the step's function name so the worker can reject a queue message naming a different one, because the step executes under the run owner's identity
|
|
17
17
|
- [A virtual user is never offered a scenario, platform or addon step](a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md) — Being able to invoke "the webhook arrives" lets the user manufacture the outcome it exists to discover, which invalidates every finding downstream
|
|
18
|
-
- [A workflow run is read
|
|
18
|
+
- [A workflow run is read and approved by its owner](a-workflow-run-is-read-and-approved-by-its-owner.md) — A run started through a session records that user and only that user may read it or answer its approval gates; a run with no recorded owner has no ownership to enforce
|
|
19
19
|
- [An actor's missing approval decision defaults to denied](actor-flow-missing-approval-decisions-default-to-denied.md) — Every pending tool call gets an explicit decision; an id the persona LLM omitted is denied, so a dropped field can never read as consent
|
|
20
20
|
- [Actor sign-in is proven by Set-Cookie, not a non-empty jar](actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md) — HttpScenarioActor tracks its own signedIn flag and requires the sign-in response itself to set a cookie, because a populated jar proves nothing
|
|
21
21
|
- [Actor sign-in only works for actor-flagged users](actor-sign-in-only-works-for-actor-flagged-users.md) — The scenario actor secret mints sessions for user rows flagged actor and nothing else, so holding it never impersonates a real user
|
|
@@ -31,7 +31,6 @@ A rule about who may do what, and which way it fails when it is unsure.
|
|
|
31
31
|
- [An agent thread key is always prefixed with the trusted principal](ai-agent-thread-ownership-composes-the-session-principal.md) — Ownership keys are composed as principal:resourceId, so a client id can sub-divide its own boundary but never widen it
|
|
32
32
|
- [Agent tool permission filtering reads the live function config, not the metadata](ai-agent-tool-filtering-reads-the-live-function-config.md) — The pikkuAuth brand survives only on live permission objects, so a metadata-driven check would silently admit every gated tool
|
|
33
33
|
- [An agent approval is claimed before the tool runs](an-agent-approval-is-claimed-before-the-tool-runs.md) — resolveApproval is a compare-and-swap returning whether this caller won, because the read that precedes it is not a claim and ten concurrent approvals would otherwise mean ten refunds
|
|
34
|
-
- [An approval answer outlives the run it answered](an-approval-answer-outlives-the-run-it-answered.md) — Run state holds a decision only while the gate is open, so the settled answer carries decidedBy/decidedAt into the step result and every attempt is written to the audit sink, which has no foreign key to the run
|
|
35
34
|
- [An empty owners constraint matches nothing](an-empty-owners-constraint-matches-nothing.md) — owners is an authorization boundary, so every storage backend must treat [] as no rows rather than no filter
|
|
36
35
|
- [An exposed function with no gate is reported at codegen, not at boot](an-exposed-ungated-function-is-a-codegen-warning.md) — The check runs in the inspector where function meta and every wireAddon declaration are both in hand, because neither source alone can tell a gated function from an ungated one
|
|
37
36
|
- [An upload is counted as it arrives, not buffered and then measured](an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md) — Reading the whole body before checking its size hands an unauthenticated caller a way to spend the server's memory
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/core",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.84",
|
|
4
4
|
"description": "The Pikku runtime — functions, wirings, services, middleware and types",
|
|
5
5
|
"author": "yasser.fadl@gmail.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"./safe-fetch": "./dist/utils/safe-fetch.js",
|
|
50
50
|
"./node-host-resolver": "./dist/utils/node-host-resolver.js",
|
|
51
51
|
"./mcp": "./dist/wirings/mcp/index.js",
|
|
52
|
-
"./
|
|
53
|
-
"./
|
|
52
|
+
"./agent": "./dist/wirings/agent/index.js",
|
|
53
|
+
"./agent-scorer": "./dist/wirings/agent-scorer/index.js",
|
|
54
54
|
"./gateway": "./dist/wirings/gateway/index.js",
|
|
55
55
|
"./cli": "./dist/wirings/cli/index.js",
|
|
56
56
|
"./cli/command-parser": "./dist/wirings/cli/command-parser.js",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"./crypto-utils": "./dist/crypto-utils.js",
|
|
75
75
|
"./hmac": "./dist/utils/hmac.js",
|
|
76
76
|
"./ecosystem": "./dist/ecosystem.js",
|
|
77
|
-
"./
|
|
77
|
+
"./ecosystem/*": "./dist/ecosystem/*.js",
|
|
78
78
|
"./schema": "./dist/schema.js",
|
|
79
79
|
"./testing": "./dist/testing/index.js",
|
|
80
80
|
"./dev": "./dist/dev/hot-reload.js"
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*
|
|
23
23
|
* Run: yarn api-report
|
|
24
24
|
*/
|
|
25
|
-
import { readFileSync, writeFileSync } from 'node:fs'
|
|
25
|
+
import { readdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
26
26
|
import { resolve, dirname } from 'node:path'
|
|
27
27
|
import { fileURLToPath } from 'node:url'
|
|
28
28
|
import ts from 'typescript'
|
|
@@ -32,15 +32,36 @@ const pkg = JSON.parse(
|
|
|
32
32
|
readFileSync(resolve(packageRoot, 'package.json'), 'utf-8')
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
/**
|
|
36
|
+
* A wildcard subpath stands for as many entry points as there are files behind
|
|
37
|
+
* it, and the report is per entry point. Left as a pattern it resolves to no
|
|
38
|
+
* source file at all, so every sub-barrel it publishes goes unreported.
|
|
39
|
+
*/
|
|
40
|
+
const expand = ([subpath, dist]: [string, string]): [string, string][] => {
|
|
41
|
+
if (!subpath.includes('*')) return [[subpath, dist]]
|
|
42
|
+
const [prefix] = dist.split('*') as [string]
|
|
43
|
+
return readdirSync(
|
|
44
|
+
resolve(packageRoot, prefix.replace('./dist/', './src/')),
|
|
45
|
+
{
|
|
46
|
+
recursive: true,
|
|
47
|
+
encoding: 'utf-8',
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
.filter((file) => file.endsWith('.ts') && !file.endsWith('.d.ts'))
|
|
51
|
+
.map((file) => file.slice(0, -'.ts'.length))
|
|
52
|
+
.sort()
|
|
53
|
+
.map((area) => [subpath.replace('*', area), dist.replace('*', area)])
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const entryPoints = Object.entries(pkg.exports as Record<string, string>)
|
|
57
|
+
.flatMap(expand)
|
|
58
|
+
.map(([subpath, dist]) => ({
|
|
37
59
|
subpath,
|
|
38
60
|
file: resolve(
|
|
39
61
|
packageRoot,
|
|
40
62
|
dist.replace('./dist/', './src/').replace(/\.js$/, '.ts')
|
|
41
63
|
),
|
|
42
|
-
})
|
|
43
|
-
)
|
|
64
|
+
}))
|
|
44
65
|
|
|
45
66
|
const program = ts.createProgram(
|
|
46
67
|
entryPoints.map((e) => e.file),
|
|
@@ -218,7 +239,10 @@ const signature = (exported: ts.Symbol): string => {
|
|
|
218
239
|
*
|
|
219
240
|
* knowledge: decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md
|
|
220
241
|
*/
|
|
221
|
-
const
|
|
242
|
+
const isEcosystem = (subpath: string) =>
|
|
243
|
+
subpath === './ecosystem' ||
|
|
244
|
+
subpath === './internal' ||
|
|
245
|
+
subpath.startsWith('./ecosystem/')
|
|
222
246
|
|
|
223
247
|
type Exported = { name: string; members: number; signature: string }
|
|
224
248
|
|
|
@@ -264,7 +288,7 @@ for (const { subpath, file } of entryPoints) {
|
|
|
264
288
|
const tierOf = new Map<string, string>()
|
|
265
289
|
const membersOf = new Map<string, number>()
|
|
266
290
|
for (const [subpath, exports] of modules) {
|
|
267
|
-
const tier =
|
|
291
|
+
const tier = isEcosystem(subpath) ? 'ecosystem' : 'stable'
|
|
268
292
|
for (const { name, members } of exports) {
|
|
269
293
|
if (tier === 'stable' || !tierOf.has(name)) tierOf.set(name, tier)
|
|
270
294
|
membersOf.set(name, Math.max(membersOf.get(name) ?? 0, members))
|
|
@@ -275,7 +299,7 @@ const tally = (tier: string) => {
|
|
|
275
299
|
const names = [...tierOf].filter(([, t]) => t === tier).map(([n]) => n)
|
|
276
300
|
return {
|
|
277
301
|
entryPoints: [...modules.keys()].filter(
|
|
278
|
-
(s) => (
|
|
302
|
+
(s) => (isEcosystem(s) ? 'ecosystem' : 'stable') === tier
|
|
279
303
|
).length,
|
|
280
304
|
names: names.length,
|
|
281
305
|
members: names.reduce((total, n) => total + (membersOf.get(n) ?? 0), 0),
|