@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
|
@@ -0,0 +1,728 @@
|
|
|
1
|
+
import { PikkuError } from '../../errors/error-handler.js';
|
|
2
|
+
import { checkAuthPermissions, runPermissions, } from '../../permissions.js';
|
|
3
|
+
import { AIProviderNotConfiguredError } from '../../errors/errors.js';
|
|
4
|
+
import { ForbiddenError } from '../../errors/errors.js';
|
|
5
|
+
import { verifyScopes } from '../../scopes.js';
|
|
6
|
+
import { pikkuState, getSingletonServices } from '../../pikku-state.js';
|
|
7
|
+
import { createMiddlewareSessionWireProps } from '../../services/user-session-service.js';
|
|
8
|
+
import { randomUUID } from './agent-utils.js';
|
|
9
|
+
import { awaitPendingInterruptNote } from './agent-interrupt.js';
|
|
10
|
+
import { streamAgent } from './agent-stream.js';
|
|
11
|
+
import { runAgent } from './agent-runner.js';
|
|
12
|
+
import { resolveNamespace, ContextAwareRPCService, } from '../../wirings/rpc/rpc-runner.js';
|
|
13
|
+
import { getOrCreatePackageSingletonServices } from '../../wirings/rpc/addon-runner.js';
|
|
14
|
+
import { resolveMemoryServices, loadContextMessages, trimMessages, } from './agent-memory.js';
|
|
15
|
+
import { resolveModelConfig } from './agent-model-config.js';
|
|
16
|
+
export function resolveSessionPrincipal(params, sessionScope) {
|
|
17
|
+
const session = params.sessionService?.get();
|
|
18
|
+
if ((sessionScope ?? 'user') === 'org') {
|
|
19
|
+
if (!session?.orgId) {
|
|
20
|
+
throw new ForbiddenError('This agent is org-scoped but the session has no organization');
|
|
21
|
+
}
|
|
22
|
+
return session.orgId;
|
|
23
|
+
}
|
|
24
|
+
return session?.userId;
|
|
25
|
+
}
|
|
26
|
+
export function resolveOwnerResourceId(params, sessionScope, requestedResourceId) {
|
|
27
|
+
const principal = resolveSessionPrincipal(params, sessionScope);
|
|
28
|
+
if (!principal) {
|
|
29
|
+
params.anonymousOwnerResourceId ??= `anon-${randomUUID()}`;
|
|
30
|
+
return params.anonymousOwnerResourceId;
|
|
31
|
+
}
|
|
32
|
+
if (requestedResourceId === principal ||
|
|
33
|
+
requestedResourceId.startsWith(`${principal}:`)) {
|
|
34
|
+
return requestedResourceId;
|
|
35
|
+
}
|
|
36
|
+
return `${principal}:${requestedResourceId}`;
|
|
37
|
+
}
|
|
38
|
+
export function assertResourcePrincipalOwner(params, sessionScope, storedResourceId, kind) {
|
|
39
|
+
const principal = resolveSessionPrincipal(params, sessionScope);
|
|
40
|
+
if (!principal || !isOwnedByPrincipal(storedResourceId, principal)) {
|
|
41
|
+
throw new ForbiddenError(`Not authorized to access this ${kind}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function agentSessionScope(agentName) {
|
|
45
|
+
return resolveAgent(agentName).agent.sessionScope ?? 'user';
|
|
46
|
+
}
|
|
47
|
+
export function assertResourceOwner(ownerResourceId, storedResourceId, kind) {
|
|
48
|
+
if (storedResourceId !== ownerResourceId) {
|
|
49
|
+
throw new ForbiddenError(`Not authorized to access this ${kind}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function sessionPrincipals(session) {
|
|
53
|
+
return [session?.userId, session?.orgId].filter((principal) => Boolean(principal));
|
|
54
|
+
}
|
|
55
|
+
export function isOwnedByPrincipal(storedResourceId, principal) {
|
|
56
|
+
return (storedResourceId === principal ||
|
|
57
|
+
storedResourceId.startsWith(`${principal}:`));
|
|
58
|
+
}
|
|
59
|
+
export function threadOwnerConstraint(session) {
|
|
60
|
+
return sessionPrincipals(session);
|
|
61
|
+
}
|
|
62
|
+
export function canAccessThread(storedResourceId, session) {
|
|
63
|
+
const principals = sessionPrincipals(session);
|
|
64
|
+
if (principals.length === 0)
|
|
65
|
+
return false;
|
|
66
|
+
return principals.some((principal) => isOwnedByPrincipal(storedResourceId, principal));
|
|
67
|
+
}
|
|
68
|
+
export const APPROVAL_REQUIRED = Symbol('pikku.ai.approvalRequired');
|
|
69
|
+
/**
|
|
70
|
+
* In-process brand proving a credential request was produced by pikku itself and
|
|
71
|
+
* not by tool output an attacker can influence. Never serialized — a value that
|
|
72
|
+
* has crossed a JSON boundary has lost the brand and is treated as untrusted.
|
|
73
|
+
*/
|
|
74
|
+
export const CREDENTIAL_REQUIRED = Symbol('pikku.ai.credentialRequired');
|
|
75
|
+
export class ToolApprovalRequired extends PikkuError {
|
|
76
|
+
toolCallId;
|
|
77
|
+
toolName;
|
|
78
|
+
args;
|
|
79
|
+
reason;
|
|
80
|
+
displayToolName;
|
|
81
|
+
displayArgs;
|
|
82
|
+
agentRunId;
|
|
83
|
+
constructor(toolCallId, toolName, args, reason, displayToolName, displayArgs, agentRunId) {
|
|
84
|
+
super(`Tool '${displayToolName ?? toolName}' requires approval`);
|
|
85
|
+
this.toolCallId = toolCallId;
|
|
86
|
+
this.toolName = toolName;
|
|
87
|
+
this.args = args;
|
|
88
|
+
this.reason = reason;
|
|
89
|
+
this.displayToolName = displayToolName;
|
|
90
|
+
this.displayArgs = displayArgs;
|
|
91
|
+
this.agentRunId = agentRunId;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export class ToolCredentialRequired extends PikkuError {
|
|
95
|
+
toolCallId;
|
|
96
|
+
toolName;
|
|
97
|
+
args;
|
|
98
|
+
credentialName;
|
|
99
|
+
credentialType;
|
|
100
|
+
connectUrl;
|
|
101
|
+
constructor(toolCallId, toolName, args, credentialName, credentialType, connectUrl) {
|
|
102
|
+
super(`Tool '${toolName}' requires credential '${credentialName}'`);
|
|
103
|
+
this.toolCallId = toolCallId;
|
|
104
|
+
this.toolName = toolName;
|
|
105
|
+
this.args = args;
|
|
106
|
+
this.credentialName = credentialName;
|
|
107
|
+
this.credentialType = credentialType;
|
|
108
|
+
this.connectUrl = connectUrl;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export function getAddonCredentialRequirements(toolNames) {
|
|
112
|
+
const requirements = new Map();
|
|
113
|
+
for (const toolName of toolNames) {
|
|
114
|
+
if (!toolName.includes(':'))
|
|
115
|
+
continue;
|
|
116
|
+
const resolved = resolveNamespace(toolName);
|
|
117
|
+
if (!resolved)
|
|
118
|
+
continue;
|
|
119
|
+
const credsMeta = pikkuState(resolved.package, 'package', 'credentialsMeta');
|
|
120
|
+
if (!credsMeta)
|
|
121
|
+
continue;
|
|
122
|
+
for (const [name, meta] of Object.entries(credsMeta)) {
|
|
123
|
+
if (meta.type === 'wire' && meta.oauth2 && !requirements.has(name)) {
|
|
124
|
+
requirements.set(name, {
|
|
125
|
+
credentialName: name,
|
|
126
|
+
displayName: meta.displayName ?? name,
|
|
127
|
+
addonNamespace: toolName.split(':')[0],
|
|
128
|
+
type: 'wire',
|
|
129
|
+
oauth2: true,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return [...requirements.values()];
|
|
135
|
+
}
|
|
136
|
+
export const resolveAgent = (agentName) => {
|
|
137
|
+
if (!agentName) {
|
|
138
|
+
console.error('[resolveAgent] agentName is undefined/null! Stack:', new Error().stack);
|
|
139
|
+
throw new Error('resolveAgent called with undefined agentName');
|
|
140
|
+
}
|
|
141
|
+
const mainAgent = pikkuState(null, 'agent', 'agents').get(agentName);
|
|
142
|
+
if (mainAgent) {
|
|
143
|
+
return { agent: mainAgent, packageName: null, resolvedName: agentName };
|
|
144
|
+
}
|
|
145
|
+
const colonIndex = agentName.indexOf(':');
|
|
146
|
+
if (colonIndex !== -1) {
|
|
147
|
+
const namespace = agentName.substring(0, colonIndex);
|
|
148
|
+
const localName = agentName.substring(colonIndex + 1);
|
|
149
|
+
const addons = pikkuState(null, 'addons', 'packages');
|
|
150
|
+
const pkgConfig = addons.get(namespace);
|
|
151
|
+
if (pkgConfig) {
|
|
152
|
+
const extAgent = pikkuState(pkgConfig.package, 'agent', 'agents').get(localName);
|
|
153
|
+
if (extAgent) {
|
|
154
|
+
return {
|
|
155
|
+
agent: extAgent,
|
|
156
|
+
packageName: pkgConfig.package,
|
|
157
|
+
resolvedName: localName,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw new Error(`AI agent not found: ${agentName}`);
|
|
163
|
+
};
|
|
164
|
+
export async function assertAgentAuthorized(agent, params, packageName) {
|
|
165
|
+
const session = params.sessionService
|
|
166
|
+
? await params.sessionService.get()
|
|
167
|
+
: undefined;
|
|
168
|
+
if (agent.auth === true && !session) {
|
|
169
|
+
throw new ForbiddenError('Authentication required');
|
|
170
|
+
}
|
|
171
|
+
verifyScopes(agent.scopes, session);
|
|
172
|
+
const singletonServices = getSingletonServices();
|
|
173
|
+
const wire = params.sessionService
|
|
174
|
+
? createMiddlewareSessionWireProps(params.sessionService)
|
|
175
|
+
: { session: undefined };
|
|
176
|
+
await runPermissions({
|
|
177
|
+
funcPermissions: agent.permissions,
|
|
178
|
+
services: singletonServices,
|
|
179
|
+
wire: wire,
|
|
180
|
+
data: {},
|
|
181
|
+
packageName,
|
|
182
|
+
label: 'agent',
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
export async function buildInstructions(agentName, packageName) {
|
|
186
|
+
const meta = pikkuState(packageName, 'agent', 'agentsMeta')[agentName];
|
|
187
|
+
const parts = [];
|
|
188
|
+
if (meta?.role)
|
|
189
|
+
parts.push(meta.role);
|
|
190
|
+
if (meta?.personality)
|
|
191
|
+
parts.push(meta.personality);
|
|
192
|
+
if (meta?.goal)
|
|
193
|
+
parts.push(meta.goal);
|
|
194
|
+
let instructions = parts.join('\n\n');
|
|
195
|
+
if (meta?.tools?.length || meta?.workflows?.length) {
|
|
196
|
+
instructions +=
|
|
197
|
+
'\n\nTool usage rules:\n' +
|
|
198
|
+
'- Act immediately with the information given. Do NOT ask clarifying questions unless a required field is truly missing.\n' +
|
|
199
|
+
'- Only use fields defined in your tool schemas. Never mention or ask for fields that do not exist.\n' +
|
|
200
|
+
'- Never fill optional fields with placeholder or zero values. Omit them entirely unless the user provides a real value.\n' +
|
|
201
|
+
'- Never stuff unrelated information into the wrong field.\n' +
|
|
202
|
+
'- Keep responses concise.';
|
|
203
|
+
}
|
|
204
|
+
if (meta?.agents?.length) {
|
|
205
|
+
instructions +=
|
|
206
|
+
'\n\nWhen calling a sub-agent, provide a short session name that describes the task. ' +
|
|
207
|
+
'Use the same session name to continue a previous conversation with that agent. ' +
|
|
208
|
+
'Use a new session name for a new independent task. ' +
|
|
209
|
+
'When a request involves multiple actions for the same domain, combine them into a single sub-agent call rather than making separate calls.';
|
|
210
|
+
}
|
|
211
|
+
return instructions;
|
|
212
|
+
}
|
|
213
|
+
export function createScopedChannel(parent, agentName, session) {
|
|
214
|
+
const capturedApprovals = [];
|
|
215
|
+
return {
|
|
216
|
+
channelId: `${parent.channelId}:${agentName}:${session}`,
|
|
217
|
+
openingData: parent.openingData,
|
|
218
|
+
get state() {
|
|
219
|
+
return parent.state;
|
|
220
|
+
},
|
|
221
|
+
get approvals() {
|
|
222
|
+
return capturedApprovals;
|
|
223
|
+
},
|
|
224
|
+
close: () => { },
|
|
225
|
+
sendBinary: (data) => parent.sendBinary(data),
|
|
226
|
+
send: (event) => {
|
|
227
|
+
if (event.type === 'done')
|
|
228
|
+
return;
|
|
229
|
+
if (event.type === 'approval-request') {
|
|
230
|
+
capturedApprovals.push({
|
|
231
|
+
toolCallId: event.toolCallId,
|
|
232
|
+
toolName: event.toolName,
|
|
233
|
+
args: event.args,
|
|
234
|
+
...(event.reason !== undefined ? { reason: event.reason } : {}),
|
|
235
|
+
runId: event.runId,
|
|
236
|
+
});
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (event.type === 'step-start' ||
|
|
240
|
+
event.type === 'text-delta' ||
|
|
241
|
+
event.type === 'reasoning-delta' ||
|
|
242
|
+
event.type === 'tool-call' ||
|
|
243
|
+
event.type === 'tool-result' ||
|
|
244
|
+
event.type === 'usage' ||
|
|
245
|
+
event.type === 'error') {
|
|
246
|
+
parent.send({ ...event, agent: agentName, session });
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
parent.send(event);
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
setState: (s) => parent.setState(s),
|
|
253
|
+
getState: () => parent.getState(),
|
|
254
|
+
clearState: () => parent.clearState(),
|
|
255
|
+
remote: (funcName, data) => parent.remote(funcName, data),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
export function buildSubAgentRunInput(message, threadId, resourceId, parentContext) {
|
|
259
|
+
return { message, threadId, resourceId, context: parentContext };
|
|
260
|
+
}
|
|
261
|
+
export async function buildToolDefs(params, agentSessionMap, resourceId, agentName, packageName, streamContext, agentMiddlewares, agentMode, parentContext) {
|
|
262
|
+
const singletonServices = getSingletonServices();
|
|
263
|
+
const tools = [];
|
|
264
|
+
const missingRpcs = [];
|
|
265
|
+
const approvalPolicy = streamContext?.options?.requiresToolApproval ?? 'explicit';
|
|
266
|
+
const meta = pikkuState(packageName, 'agent', 'agentsMeta')[agentName];
|
|
267
|
+
if (!meta)
|
|
268
|
+
return { tools, missingRpcs };
|
|
269
|
+
/**
|
|
270
|
+
* A tool's description is what the model is told it does, and the main thing
|
|
271
|
+
* it chooses between tools on. It does not survive into the meta registered at
|
|
272
|
+
* boot: `description` is classed as a verbose field, so the bundled copy has
|
|
273
|
+
* it stripped and `fnMeta.description` is always undefined — every tool was
|
|
274
|
+
* being offered under its own bare name regardless of what its author wrote.
|
|
275
|
+
*
|
|
276
|
+
* `getFunctionsMeta` reads the verbose file and falls back to the minimal one,
|
|
277
|
+
* recovering the authored description wherever the generated `.pikku`
|
|
278
|
+
* directory is readable. Where it is not — no `metaService`, or a deployment
|
|
279
|
+
* shipping only the stripped copy — tools fall back to their name, which is
|
|
280
|
+
* what they did before this lookup existed.
|
|
281
|
+
*/
|
|
282
|
+
const describedFunctions = await singletonServices.metaService
|
|
283
|
+
?.getFunctionsMeta()
|
|
284
|
+
.catch(() => undefined);
|
|
285
|
+
const session = params.sessionService
|
|
286
|
+
? await params.sessionService.get()
|
|
287
|
+
: null;
|
|
288
|
+
const metaTools = meta.tools;
|
|
289
|
+
const metaAgents = meta.agents;
|
|
290
|
+
if (metaTools?.length) {
|
|
291
|
+
for (const toolName of metaTools) {
|
|
292
|
+
let fnMeta;
|
|
293
|
+
let resolvedPkg = null;
|
|
294
|
+
let schemas;
|
|
295
|
+
const resolved = toolName.includes(':')
|
|
296
|
+
? resolveNamespace(toolName)
|
|
297
|
+
: null;
|
|
298
|
+
let pikkuFuncId;
|
|
299
|
+
if (resolved) {
|
|
300
|
+
resolvedPkg = resolved.package;
|
|
301
|
+
pikkuFuncId = resolved.function;
|
|
302
|
+
fnMeta = pikkuState(resolvedPkg, 'function', 'meta')[pikkuFuncId];
|
|
303
|
+
schemas = pikkuState(resolvedPkg, 'misc', 'schemas');
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
const rpcMeta = pikkuState(null, 'rpc', 'meta');
|
|
307
|
+
pikkuFuncId = rpcMeta[toolName];
|
|
308
|
+
if (!pikkuFuncId) {
|
|
309
|
+
missingRpcs.push(toolName);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
fnMeta = pikkuState(null, 'function', 'meta')[pikkuFuncId];
|
|
313
|
+
schemas = pikkuState(null, 'misc', 'schemas');
|
|
314
|
+
}
|
|
315
|
+
if (!fnMeta) {
|
|
316
|
+
missingRpcs.push(toolName);
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (fnMeta.permissions?.length) {
|
|
320
|
+
if (!session)
|
|
321
|
+
continue;
|
|
322
|
+
const funcConfig = pikkuFuncId
|
|
323
|
+
? pikkuState(resolvedPkg, 'function', 'functions').get(pikkuFuncId)
|
|
324
|
+
: undefined;
|
|
325
|
+
const allowed = await checkAuthPermissions(funcConfig?.permissions, session, singletonServices, resolvedPkg);
|
|
326
|
+
if (!allowed)
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
const inputSchemaName = fnMeta?.inputSchemaName;
|
|
330
|
+
let inputSchema = inputSchemaName
|
|
331
|
+
? schemas.get(inputSchemaName)
|
|
332
|
+
: undefined;
|
|
333
|
+
if (!inputSchema ||
|
|
334
|
+
(typeof inputSchema === 'object' &&
|
|
335
|
+
inputSchema.type === 'object' &&
|
|
336
|
+
!inputSchema.properties)) {
|
|
337
|
+
inputSchema = { type: 'object', properties: {} };
|
|
338
|
+
}
|
|
339
|
+
const needsApproval = approvalPolicy === 'all' ||
|
|
340
|
+
(approvalPolicy === 'explicit' && fnMeta?.approvalRequired);
|
|
341
|
+
let approvalDescriptionFn;
|
|
342
|
+
if (needsApproval && pikkuFuncId) {
|
|
343
|
+
const funcConfig = pikkuState(resolvedPkg, 'function', 'functions').get(pikkuFuncId);
|
|
344
|
+
if (funcConfig?.approvalDescription) {
|
|
345
|
+
const descFn = funcConfig.approvalDescription;
|
|
346
|
+
const capturedPkg = resolvedPkg;
|
|
347
|
+
const capturedAddonConfig = resolved?.addonConfig;
|
|
348
|
+
const capturedNamespace = toolName.includes(':')
|
|
349
|
+
? toolName.slice(0, toolName.indexOf(':'))
|
|
350
|
+
: null;
|
|
351
|
+
approvalDescriptionFn = async (input) => {
|
|
352
|
+
let services = singletonServices;
|
|
353
|
+
if (capturedPkg) {
|
|
354
|
+
const addonInstance = capturedNamespace
|
|
355
|
+
? {
|
|
356
|
+
namespace: capturedNamespace,
|
|
357
|
+
secretOverrides: capturedAddonConfig?.secretOverrides,
|
|
358
|
+
variableOverrides: capturedAddonConfig?.variableOverrides,
|
|
359
|
+
credentialOverrides: capturedAddonConfig?.credentialOverrides,
|
|
360
|
+
secretGrants: capturedAddonConfig?.secretGrants,
|
|
361
|
+
credentialGrants: capturedAddonConfig?.credentialGrants,
|
|
362
|
+
globalSecrets: capturedAddonConfig?.globalSecrets,
|
|
363
|
+
globalCredentials: capturedAddonConfig?.globalCredentials,
|
|
364
|
+
}
|
|
365
|
+
: undefined;
|
|
366
|
+
services = await getOrCreatePackageSingletonServices(capturedPkg, singletonServices, addonInstance);
|
|
367
|
+
}
|
|
368
|
+
return descFn(services, input);
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
tools.push({
|
|
373
|
+
name: toolName.replaceAll('@', '_').replaceAll(':', '__'),
|
|
374
|
+
description: describedFunctions?.[pikkuFuncId]?.description ||
|
|
375
|
+
fnMeta?.description ||
|
|
376
|
+
toolName,
|
|
377
|
+
inputSchema,
|
|
378
|
+
needsApproval: needsApproval || undefined,
|
|
379
|
+
approvalDescriptionFn,
|
|
380
|
+
readonly: fnMeta?.readonly || undefined,
|
|
381
|
+
execute: async (toolInput) => {
|
|
382
|
+
const wire = params.sessionService
|
|
383
|
+
? { ...createMiddlewareSessionWireProps(params.sessionService) }
|
|
384
|
+
: {};
|
|
385
|
+
const rpcService = new ContextAwareRPCService(singletonServices, wire, { sessionService: params.sessionService });
|
|
386
|
+
return rpcService.rpc(toolName, toolInput);
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (metaAgents?.length) {
|
|
392
|
+
const allAgentsMeta = pikkuState(null, 'agent', 'agentsMeta');
|
|
393
|
+
for (const subAgentName of metaAgents) {
|
|
394
|
+
const subMeta = allAgentsMeta[subAgentName];
|
|
395
|
+
if (!subMeta) {
|
|
396
|
+
singletonServices.logger.warn(`Sub-agent '${subAgentName}' not found in agent registry`);
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
if (subMeta.permissions?.length) {
|
|
400
|
+
if (!session)
|
|
401
|
+
continue;
|
|
402
|
+
const subAgent = pikkuState(null, 'agent', 'agents').get(subAgentName);
|
|
403
|
+
const allowed = await checkAuthPermissions(subAgent?.permissions, session, singletonServices);
|
|
404
|
+
if (!allowed)
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
tools.push({
|
|
408
|
+
name: subAgentName,
|
|
409
|
+
description: subMeta.description,
|
|
410
|
+
forwardsApproval: true,
|
|
411
|
+
inputSchema: {
|
|
412
|
+
type: 'object',
|
|
413
|
+
properties: {
|
|
414
|
+
message: { type: 'string' },
|
|
415
|
+
session: {
|
|
416
|
+
type: 'string',
|
|
417
|
+
description: 'Short session label for thread continuity',
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
required: ['message', 'session'],
|
|
421
|
+
},
|
|
422
|
+
execute: async (toolInput) => {
|
|
423
|
+
const { message, session } = toolInput;
|
|
424
|
+
const sessionKey = `${subAgentName}::${session}`;
|
|
425
|
+
let threadId = agentSessionMap.get(sessionKey);
|
|
426
|
+
if (!threadId) {
|
|
427
|
+
threadId = randomUUID();
|
|
428
|
+
agentSessionMap.set(sessionKey, threadId);
|
|
429
|
+
}
|
|
430
|
+
if (streamContext) {
|
|
431
|
+
const isDelegate = agentMode !== 'supervise';
|
|
432
|
+
if (isDelegate && streamContext.delegateState) {
|
|
433
|
+
streamContext.delegateState.delegated = true;
|
|
434
|
+
}
|
|
435
|
+
const { channel } = streamContext;
|
|
436
|
+
channel.send({
|
|
437
|
+
type: 'agent-call',
|
|
438
|
+
agentName: subAgentName,
|
|
439
|
+
session,
|
|
440
|
+
input: message,
|
|
441
|
+
});
|
|
442
|
+
const subChannel = createScopedChannel(channel, subAgentName, session);
|
|
443
|
+
const effectiveChannel = isDelegate
|
|
444
|
+
? subChannel
|
|
445
|
+
: {
|
|
446
|
+
...subChannel,
|
|
447
|
+
send: (event) => {
|
|
448
|
+
if (event.type === 'text-delta' ||
|
|
449
|
+
event.type === 'reasoning-delta')
|
|
450
|
+
return;
|
|
451
|
+
subChannel.send(event);
|
|
452
|
+
},
|
|
453
|
+
};
|
|
454
|
+
const resultText = await streamAgent(subAgentName, buildSubAgentRunInput(message, threadId, resourceId, parentContext), effectiveChannel, params, agentSessionMap, streamContext.options);
|
|
455
|
+
if (subChannel.approvals.length > 0) {
|
|
456
|
+
return {
|
|
457
|
+
[APPROVAL_REQUIRED]: true,
|
|
458
|
+
__approvalRequired: true,
|
|
459
|
+
toolName: subAgentName,
|
|
460
|
+
args: toolInput,
|
|
461
|
+
agentRunId: subChannel.approvals[0].runId,
|
|
462
|
+
subApprovals: subChannel.approvals,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
channel.send({
|
|
466
|
+
type: 'agent-result',
|
|
467
|
+
agentName: subAgentName,
|
|
468
|
+
session,
|
|
469
|
+
result: resultText,
|
|
470
|
+
});
|
|
471
|
+
return resultText;
|
|
472
|
+
}
|
|
473
|
+
const result = await runAgent(subAgentName, buildSubAgentRunInput(message, threadId, resourceId, parentContext), params, agentSessionMap);
|
|
474
|
+
if (result.status === 'suspended' &&
|
|
475
|
+
result.pendingApprovals?.length) {
|
|
476
|
+
return {
|
|
477
|
+
[APPROVAL_REQUIRED]: true,
|
|
478
|
+
__approvalRequired: true,
|
|
479
|
+
toolName: subAgentName,
|
|
480
|
+
args: toolInput,
|
|
481
|
+
agentRunId: result.runId,
|
|
482
|
+
subApprovals: result.pendingApprovals.map((a) => ({
|
|
483
|
+
toolCallId: a.toolCallId,
|
|
484
|
+
toolName: a.toolName,
|
|
485
|
+
args: a.args,
|
|
486
|
+
reason: a.reason,
|
|
487
|
+
runId: a.runId,
|
|
488
|
+
})),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
return result.object ?? result.text;
|
|
492
|
+
},
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
const metaWorkflows = meta.workflows;
|
|
497
|
+
if (metaWorkflows?.length) {
|
|
498
|
+
const workflowMetaMap = pikkuState(null, 'workflows', 'meta');
|
|
499
|
+
const functionMeta = pikkuState(null, 'function', 'meta');
|
|
500
|
+
const schemas = pikkuState(null, 'misc', 'schemas');
|
|
501
|
+
for (const workflowName of metaWorkflows) {
|
|
502
|
+
const wfMeta = workflowMetaMap[workflowName];
|
|
503
|
+
if (!wfMeta) {
|
|
504
|
+
missingRpcs.push(workflowName);
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
const inputSchemaName = wfMeta.pikkuFuncId
|
|
508
|
+
? functionMeta[wfMeta.pikkuFuncId]?.inputSchemaName
|
|
509
|
+
: undefined;
|
|
510
|
+
let inputSchema = inputSchemaName
|
|
511
|
+
? schemas.get(inputSchemaName)
|
|
512
|
+
: undefined;
|
|
513
|
+
if (!inputSchema ||
|
|
514
|
+
(typeof inputSchema === 'object' &&
|
|
515
|
+
inputSchema.type === 'object' &&
|
|
516
|
+
!inputSchema.properties)) {
|
|
517
|
+
inputSchema = { type: 'object', properties: {} };
|
|
518
|
+
}
|
|
519
|
+
tools.push({
|
|
520
|
+
name: workflowName,
|
|
521
|
+
description: wfMeta.description || workflowName,
|
|
522
|
+
inputSchema,
|
|
523
|
+
execute: async (toolInput) => {
|
|
524
|
+
const workflowService = singletonServices.workflowService;
|
|
525
|
+
if (!workflowService) {
|
|
526
|
+
throw new Error(`workflowService is not configured — cannot run workflow tool '${workflowName}'`);
|
|
527
|
+
}
|
|
528
|
+
const wire = params.sessionService
|
|
529
|
+
? { ...createMiddlewareSessionWireProps(params.sessionService) }
|
|
530
|
+
: {};
|
|
531
|
+
const rpcService = new ContextAwareRPCService(singletonServices, wire, { sessionService: params.sessionService });
|
|
532
|
+
return workflowService.runToCompletion(workflowName, toolInput, rpcService, { wire: { type: 'internal' } });
|
|
533
|
+
},
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
for (const tool of tools) {
|
|
538
|
+
const originalExecute = tool.execute;
|
|
539
|
+
tool.execute = async (toolInput) => {
|
|
540
|
+
try {
|
|
541
|
+
return await originalExecute(toolInput);
|
|
542
|
+
}
|
|
543
|
+
catch (err) {
|
|
544
|
+
if (err?.payload?.error === 'missing_credential') {
|
|
545
|
+
return {
|
|
546
|
+
[CREDENTIAL_REQUIRED]: true,
|
|
547
|
+
__credentialRequired: true,
|
|
548
|
+
...err.payload,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
singletonServices.logger.error(`AI agent tool '${tool.name}' threw during execute()`, err);
|
|
552
|
+
throw err;
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
const hasToolHooks = agentMiddlewares?.some((mw) => mw.beforeToolCall || mw.afterToolCall);
|
|
557
|
+
if (hasToolHooks) {
|
|
558
|
+
for (const tool of tools) {
|
|
559
|
+
const originalExecute = tool.execute;
|
|
560
|
+
tool.execute = async (toolInput) => {
|
|
561
|
+
const toolCallId = randomUUID();
|
|
562
|
+
let args = (toolInput ?? {});
|
|
563
|
+
for (const mw of agentMiddlewares) {
|
|
564
|
+
if (mw.beforeToolCall) {
|
|
565
|
+
const beforeResult = await mw.beforeToolCall(singletonServices, {
|
|
566
|
+
toolName: tool.name,
|
|
567
|
+
toolCallId,
|
|
568
|
+
args,
|
|
569
|
+
});
|
|
570
|
+
if (beforeResult && 'args' in beforeResult) {
|
|
571
|
+
args = beforeResult.args;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
const startTime = Date.now();
|
|
576
|
+
let result;
|
|
577
|
+
let execError;
|
|
578
|
+
try {
|
|
579
|
+
result = await originalExecute(args);
|
|
580
|
+
}
|
|
581
|
+
catch (err) {
|
|
582
|
+
execError = err;
|
|
583
|
+
if (err?.payload?.error === 'missing_credential')
|
|
584
|
+
throw err;
|
|
585
|
+
result = err instanceof Error ? err.message : String(err);
|
|
586
|
+
}
|
|
587
|
+
const durationMs = Date.now() - startTime;
|
|
588
|
+
for (let i = agentMiddlewares.length - 1; i >= 0; i--) {
|
|
589
|
+
const mw = agentMiddlewares[i];
|
|
590
|
+
if (mw.afterToolCall) {
|
|
591
|
+
const afterResult = await mw.afterToolCall(singletonServices, {
|
|
592
|
+
toolName: tool.name,
|
|
593
|
+
toolCallId,
|
|
594
|
+
args,
|
|
595
|
+
result,
|
|
596
|
+
durationMs,
|
|
597
|
+
});
|
|
598
|
+
if (afterResult && 'result' in afterResult) {
|
|
599
|
+
result = afterResult.result;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (execError)
|
|
604
|
+
throw execError;
|
|
605
|
+
return result;
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return { tools, missingRpcs };
|
|
610
|
+
}
|
|
611
|
+
export async function prepareAgentRun(agentName, input, params, agentSessionMap, streamContext) {
|
|
612
|
+
const singletonServices = getSingletonServices();
|
|
613
|
+
const { agent, packageName, resolvedName } = resolveAgent(agentName);
|
|
614
|
+
await assertAgentAuthorized(agent, params, packageName);
|
|
615
|
+
let agentRunner = singletonServices.agentRunner;
|
|
616
|
+
if (!agentRunner) {
|
|
617
|
+
throw new AIProviderNotConfiguredError();
|
|
618
|
+
}
|
|
619
|
+
if (params.getCredential && agentRunner.withApiKey) {
|
|
620
|
+
const aiCredential = await params.getCredential('AI_API_KEY');
|
|
621
|
+
if (aiCredential?.apiKey?.trim()) {
|
|
622
|
+
agentRunner = agentRunner.withApiKey(aiCredential.apiKey);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const { storage } = resolveMemoryServices(agent, singletonServices);
|
|
626
|
+
const memoryConfig = agent.memory;
|
|
627
|
+
const threadId = input.threadId;
|
|
628
|
+
const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta');
|
|
629
|
+
const meta = agentsMeta[resolvedName];
|
|
630
|
+
const outputSchemaName = meta?.outputSchema;
|
|
631
|
+
const outputSchema = outputSchemaName
|
|
632
|
+
? pikkuState(packageName, 'misc', 'schemas').get(outputSchemaName)
|
|
633
|
+
: undefined;
|
|
634
|
+
const workingMemorySchemaName = meta?.workingMemorySchema ?? null;
|
|
635
|
+
const workingMemoryJsonSchema = workingMemorySchemaName
|
|
636
|
+
? pikkuState(packageName, 'misc', 'schemas').get(workingMemorySchemaName)
|
|
637
|
+
: undefined;
|
|
638
|
+
if (storage) {
|
|
639
|
+
let thread = null;
|
|
640
|
+
try {
|
|
641
|
+
thread = await storage.getThread(threadId);
|
|
642
|
+
}
|
|
643
|
+
catch {
|
|
644
|
+
thread = null;
|
|
645
|
+
}
|
|
646
|
+
if (thread) {
|
|
647
|
+
assertResourceOwner(input.resourceId, thread.resourceId, 'thread');
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
await storage.createThread(input.resourceId, { threadId });
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
let messages = [];
|
|
654
|
+
if (storage) {
|
|
655
|
+
// knowledge: decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md
|
|
656
|
+
await awaitPendingInterruptNote(threadId);
|
|
657
|
+
messages = await storage.getMessages(threadId, {
|
|
658
|
+
lastN: memoryConfig?.lastMessages ?? 20,
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
const contextMessages = await loadContextMessages(memoryConfig, storage, input, workingMemoryJsonSchema);
|
|
662
|
+
const userContent = input.attachments?.length
|
|
663
|
+
? [
|
|
664
|
+
// knowledge: decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md
|
|
665
|
+
...(input.message
|
|
666
|
+
? [{ type: 'text', text: input.message }]
|
|
667
|
+
: []),
|
|
668
|
+
...input.attachments.map((a) => ({
|
|
669
|
+
type: a.type,
|
|
670
|
+
data: a.data,
|
|
671
|
+
url: a.url,
|
|
672
|
+
mediaType: a.mediaType,
|
|
673
|
+
...(a.filename ? { filename: a.filename } : {}),
|
|
674
|
+
})),
|
|
675
|
+
]
|
|
676
|
+
: input.message;
|
|
677
|
+
const userMessage = {
|
|
678
|
+
id: randomUUID(),
|
|
679
|
+
role: 'user',
|
|
680
|
+
content: userContent,
|
|
681
|
+
createdAt: new Date(),
|
|
682
|
+
};
|
|
683
|
+
const allMessages = [...contextMessages, ...messages, userMessage];
|
|
684
|
+
const trimmedMessages = trimMessages(allMessages);
|
|
685
|
+
const agentMiddlewares = agent.agentMiddleware ?? [];
|
|
686
|
+
const { tools, missingRpcs } = await buildToolDefs(params, agentSessionMap, input.resourceId, resolvedName, packageName, streamContext, agentMiddlewares, agent.agentMode, input.context);
|
|
687
|
+
let instructions = await buildInstructions(resolvedName, packageName);
|
|
688
|
+
if (input.context) {
|
|
689
|
+
instructions = `${instructions}\n\nCurrent context (use these identifiers directly in tool calls — do not ask the user for them):\n${input.context}`;
|
|
690
|
+
}
|
|
691
|
+
const resolved = resolveModelConfig(resolvedName, agent);
|
|
692
|
+
if (input.model) {
|
|
693
|
+
resolved.model = resolveModelConfig(resolvedName, {
|
|
694
|
+
...agent,
|
|
695
|
+
model: input.model,
|
|
696
|
+
}).model;
|
|
697
|
+
}
|
|
698
|
+
if (input.temperature !== undefined) {
|
|
699
|
+
resolved.temperature = input.temperature;
|
|
700
|
+
}
|
|
701
|
+
const maxSteps = resolved.maxSteps ?? 10;
|
|
702
|
+
const runnerParams = {
|
|
703
|
+
model: resolved.model,
|
|
704
|
+
temperature: resolved.temperature,
|
|
705
|
+
instructions,
|
|
706
|
+
messages: trimmedMessages,
|
|
707
|
+
tools,
|
|
708
|
+
maxSteps: 1,
|
|
709
|
+
toolChoice: agent.toolChoice ?? 'auto',
|
|
710
|
+
providerOptions: agent.providerOptions,
|
|
711
|
+
outputSchema,
|
|
712
|
+
};
|
|
713
|
+
return {
|
|
714
|
+
agent,
|
|
715
|
+
packageName,
|
|
716
|
+
resolvedName,
|
|
717
|
+
agentRunner,
|
|
718
|
+
storage,
|
|
719
|
+
memoryConfig,
|
|
720
|
+
threadId,
|
|
721
|
+
userMessage,
|
|
722
|
+
runnerParams,
|
|
723
|
+
maxSteps,
|
|
724
|
+
missingRpcs,
|
|
725
|
+
workingMemoryJsonSchema,
|
|
726
|
+
workingMemorySchemaName,
|
|
727
|
+
};
|
|
728
|
+
}
|