@pikku/core 0.12.83 → 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 +68 -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 +4 -4
- package/dist/services/index.js +1 -1
- package/dist/services/meta-service.d.ts +3 -3
- 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 +10 -10
- package/dist/types/core.types.js +1 -1
- package/dist/types/state.types.d.ts +5 -5
- 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/voice-output.d.ts +1 -2
- package/dist/wirings/channel/channel-rpc.types.d.ts +2 -2
- package/dist/wirings/rpc/rpc-runner.d.ts +1 -1
- package/dist/wirings/rpc/rpc-runner.js +2 -2
- package/dist/wirings/rpc/rpc-types.d.ts +1 -1
- 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/run-timeline.d.ts +1 -2
- 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 -3
- 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 +173 -68
- 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 +9 -9
- package/src/services/meta-service.ts +3 -3
- 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 +12 -12
- package/src/types/state.types.ts +6 -9
- 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} +52 -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/rpc-runner.ts +2 -2
- package/src/wirings/rpc/rpc-types.ts +1 -1
- 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/run-timeline.ts +1 -1
- 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,580 @@
|
|
|
1
|
+
import { saveMessages, resolveMemoryServices, loadContextMessages, trimMessages, getWorkingMemoryMiddleware, } from './agent-memory.js';
|
|
2
|
+
import { prepareAgentRun, resolveAgent, buildInstructions, buildToolDefs, resolveOwnerResourceId, agentSessionScope, assertResourcePrincipalOwner, assertAgentAuthorized, } from './agent-prepare.js';
|
|
3
|
+
import { checkForApprovals, appendStepMessages } from './agent-stream.js';
|
|
4
|
+
import { AgentInterruptedError, isAbortError, persistOrphanedToolResults, registerInterruptibleRun, trackInterruptNote, trackToolExecution, } from './agent-interrupt.js';
|
|
5
|
+
import { pikkuState, getSingletonServices } from '../../pikku-state.js';
|
|
6
|
+
import { applyInputMiddleware, describeApprovals, notifyAfterStep, toAccumulatedStep, } from './agent-turn.js';
|
|
7
|
+
import { applyOutputMiddleware, finalizeAgentRun, lastUserMessageText, } from './agent-finalize.js';
|
|
8
|
+
import { resolveModelConfig } from './agent-model-config.js';
|
|
9
|
+
import { AIProviderNotConfiguredError } from '../../errors/errors.js';
|
|
10
|
+
import { randomUUID } from './agent-utils.js';
|
|
11
|
+
function stripNulls(obj) {
|
|
12
|
+
if (obj === null)
|
|
13
|
+
return undefined;
|
|
14
|
+
if (Array.isArray(obj))
|
|
15
|
+
return obj.map(stripNulls);
|
|
16
|
+
if (typeof obj !== 'object')
|
|
17
|
+
return obj;
|
|
18
|
+
const result = {};
|
|
19
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
20
|
+
if (value !== null) {
|
|
21
|
+
result[key] = stripNulls(value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
function extractStructuredAssistantOutput(object) {
|
|
27
|
+
if (!object || typeof object !== 'object') {
|
|
28
|
+
return { text: null, uiSpec: null };
|
|
29
|
+
}
|
|
30
|
+
const record = object;
|
|
31
|
+
return {
|
|
32
|
+
text: typeof record.text === 'string' ? record.text : null,
|
|
33
|
+
uiSpec: record.ui ?? null,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export async function runAgent(agentName, input, params, agentSessionMap) {
|
|
37
|
+
const sessionMap = agentSessionMap ?? new Map();
|
|
38
|
+
input = {
|
|
39
|
+
...input,
|
|
40
|
+
resourceId: resolveOwnerResourceId(params, agentSessionScope(agentName), input.resourceId),
|
|
41
|
+
};
|
|
42
|
+
const { agent, agentRunner, storage, memoryConfig, threadId, userMessage, runnerParams, maxSteps, missingRpcs, workingMemorySchemaName, } = await prepareAgentRun(agentName, input, params, sessionMap);
|
|
43
|
+
runnerParams.agentId = agentName;
|
|
44
|
+
const singletonServices = getSingletonServices();
|
|
45
|
+
const { agentRunState } = singletonServices;
|
|
46
|
+
if (!agentRunState) {
|
|
47
|
+
throw new Error('AgentRunStateService not available in singletonServices');
|
|
48
|
+
}
|
|
49
|
+
if (missingRpcs.length > 0) {
|
|
50
|
+
const runId = await agentRunState.createRun({
|
|
51
|
+
agentName,
|
|
52
|
+
threadId,
|
|
53
|
+
resourceId: input.resourceId,
|
|
54
|
+
status: 'suspended',
|
|
55
|
+
suspendReason: 'rpc-missing',
|
|
56
|
+
missingRpcs,
|
|
57
|
+
usage: { inputTokens: 0, outputTokens: 0, model: agent.model },
|
|
58
|
+
createdAt: new Date(),
|
|
59
|
+
updatedAt: new Date(),
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
runId,
|
|
63
|
+
text: '',
|
|
64
|
+
threadId,
|
|
65
|
+
steps: [],
|
|
66
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const agentMiddlewares = [
|
|
70
|
+
...getWorkingMemoryMiddleware(memoryConfig, storage, {
|
|
71
|
+
threadId,
|
|
72
|
+
workingMemorySchemaName,
|
|
73
|
+
logger: singletonServices.logger,
|
|
74
|
+
schemaService: singletonServices.schema,
|
|
75
|
+
}),
|
|
76
|
+
...(agent.agentMiddleware ?? []),
|
|
77
|
+
];
|
|
78
|
+
// One bag per run, shared by every middleware — see PikkuAgentMiddlewareHooks.
|
|
79
|
+
const sharedNotes = {};
|
|
80
|
+
const { messages: modifiedMessages, instructions: modifiedInstructions } = await applyInputMiddleware(agentMiddlewares, singletonServices, {
|
|
81
|
+
messages: runnerParams.messages,
|
|
82
|
+
instructions: runnerParams.instructions,
|
|
83
|
+
}, sharedNotes);
|
|
84
|
+
runnerParams.messages = modifiedMessages;
|
|
85
|
+
runnerParams.instructions = modifiedInstructions;
|
|
86
|
+
// knowledge: decisions/internals/thread-history-records-the-transcript-not-the-audio.md
|
|
87
|
+
const lastModified = modifiedMessages[modifiedMessages.length - 1];
|
|
88
|
+
const persistedUserMessage = lastModified?.id === userMessage.id ? lastModified : userMessage;
|
|
89
|
+
const runId = await agentRunState.createRun({
|
|
90
|
+
agentName,
|
|
91
|
+
threadId,
|
|
92
|
+
resourceId: input.resourceId,
|
|
93
|
+
status: 'running',
|
|
94
|
+
usage: { inputTokens: 0, outputTokens: 0, model: agent.model },
|
|
95
|
+
createdAt: new Date(),
|
|
96
|
+
updatedAt: new Date(),
|
|
97
|
+
});
|
|
98
|
+
// knowledge: decisions/internals/a-non-streaming-agent-run-registers-with-airunstate-too.md
|
|
99
|
+
const interruptHandle = registerInterruptibleRun(runId);
|
|
100
|
+
runnerParams.abortSignal = interruptHandle.signal;
|
|
101
|
+
runnerParams.tools = trackToolExecution(runnerParams.tools, interruptHandle);
|
|
102
|
+
try {
|
|
103
|
+
const accumulatedSteps = [];
|
|
104
|
+
const totalUsage = { inputTokens: 0, outputTokens: 0 };
|
|
105
|
+
let lastStepResult = null;
|
|
106
|
+
for (let step = 0; step < maxSteps; step++) {
|
|
107
|
+
if (agent.prepareStep) {
|
|
108
|
+
let stopped = false;
|
|
109
|
+
await agent.prepareStep({
|
|
110
|
+
stepNumber: step,
|
|
111
|
+
messages: runnerParams.messages,
|
|
112
|
+
tools: runnerParams.tools,
|
|
113
|
+
toolChoice: runnerParams.toolChoice,
|
|
114
|
+
model: runnerParams.model,
|
|
115
|
+
stop: () => {
|
|
116
|
+
stopped = true;
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
if (stopped)
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
const stepResult = await agentRunner.run(runnerParams);
|
|
123
|
+
lastStepResult = stepResult;
|
|
124
|
+
totalUsage.inputTokens += stepResult.usage.inputTokens;
|
|
125
|
+
totalUsage.outputTokens += stepResult.usage.outputTokens;
|
|
126
|
+
await notifyAfterStep(agentMiddlewares, singletonServices, step, stepResult);
|
|
127
|
+
accumulatedSteps.push(toAccumulatedStep(stepResult));
|
|
128
|
+
if (stepResult.toolCalls.length === 0)
|
|
129
|
+
break;
|
|
130
|
+
const approvalsNeeded = checkForApprovals(stepResult, runnerParams.tools);
|
|
131
|
+
if (approvalsNeeded.length > 0) {
|
|
132
|
+
await describeApprovals(approvalsNeeded, runnerParams.tools);
|
|
133
|
+
const pendingApprovals = approvalsNeeded.map((a) => a.agentRunId
|
|
134
|
+
? {
|
|
135
|
+
type: 'agent-call',
|
|
136
|
+
toolCallId: a.toolCallId,
|
|
137
|
+
agentName: a.toolName,
|
|
138
|
+
agentRunId: a.agentRunId,
|
|
139
|
+
displayToolName: a.displayToolName ?? a.toolName,
|
|
140
|
+
displayArgs: a.displayArgs ?? a.args,
|
|
141
|
+
}
|
|
142
|
+
: {
|
|
143
|
+
type: 'tool-call',
|
|
144
|
+
toolCallId: a.toolCallId,
|
|
145
|
+
toolName: a.toolName,
|
|
146
|
+
args: a.args,
|
|
147
|
+
});
|
|
148
|
+
const completedStepsForSave = accumulatedSteps.slice(0, -1);
|
|
149
|
+
await saveMessages(storage, threadId, input.resourceId, memoryConfig, persistedUserMessage, { text: '', steps: completedStepsForSave });
|
|
150
|
+
if (storage) {
|
|
151
|
+
await storage.saveMessages(threadId, [
|
|
152
|
+
{
|
|
153
|
+
id: randomUUID(),
|
|
154
|
+
role: 'assistant',
|
|
155
|
+
toolCalls: stepResult.toolCalls.map((tc) => ({
|
|
156
|
+
id: tc.toolCallId,
|
|
157
|
+
name: tc.toolName,
|
|
158
|
+
args: tc.args,
|
|
159
|
+
})),
|
|
160
|
+
createdAt: new Date(),
|
|
161
|
+
},
|
|
162
|
+
]);
|
|
163
|
+
}
|
|
164
|
+
await agentRunState.updateRun(runId, {
|
|
165
|
+
status: 'suspended',
|
|
166
|
+
suspendReason: 'approval',
|
|
167
|
+
pendingApprovals,
|
|
168
|
+
usage: { ...totalUsage, model: agent.model },
|
|
169
|
+
});
|
|
170
|
+
const suspendedFinalText = lastStepResult?.text ?? '';
|
|
171
|
+
return {
|
|
172
|
+
runId,
|
|
173
|
+
text: suspendedFinalText,
|
|
174
|
+
threadId,
|
|
175
|
+
steps: accumulatedSteps,
|
|
176
|
+
usage: totalUsage,
|
|
177
|
+
status: 'suspended',
|
|
178
|
+
pendingApprovals: approvalsNeeded.map((a) => ({
|
|
179
|
+
toolCallId: a.toolCallId,
|
|
180
|
+
toolName: a.displayToolName ?? a.toolName,
|
|
181
|
+
args: a.displayArgs ?? a.args,
|
|
182
|
+
reason: a.reason,
|
|
183
|
+
runId,
|
|
184
|
+
})),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
appendStepMessages(runnerParams, stepResult);
|
|
188
|
+
}
|
|
189
|
+
const finalObject = lastStepResult?.object;
|
|
190
|
+
const structuredOutput = extractStructuredAssistantOutput(finalObject);
|
|
191
|
+
const finalText = structuredOutput.text ?? lastStepResult?.text ?? '';
|
|
192
|
+
const result = {
|
|
193
|
+
text: finalText,
|
|
194
|
+
steps: accumulatedSteps,
|
|
195
|
+
};
|
|
196
|
+
const { text: outputText, steps: outputSteps } = await applyOutputMiddleware(agentMiddlewares, singletonServices, {
|
|
197
|
+
text: finalText,
|
|
198
|
+
messages: runnerParams.messages,
|
|
199
|
+
steps: result.steps,
|
|
200
|
+
usage: totalUsage,
|
|
201
|
+
});
|
|
202
|
+
await saveMessages(storage, threadId, input.resourceId, memoryConfig, persistedUserMessage, {
|
|
203
|
+
text: outputText,
|
|
204
|
+
steps: outputSteps,
|
|
205
|
+
uiSpec: structuredOutput.uiSpec,
|
|
206
|
+
});
|
|
207
|
+
await finalizeAgentRun(agentRunState, {
|
|
208
|
+
runId,
|
|
209
|
+
agentName,
|
|
210
|
+
threadId,
|
|
211
|
+
resourceId: input.resourceId,
|
|
212
|
+
input: lastUserMessageText(runnerParams.messages),
|
|
213
|
+
text: outputText,
|
|
214
|
+
steps: outputSteps,
|
|
215
|
+
usage: { ...totalUsage, model: agent.model },
|
|
216
|
+
});
|
|
217
|
+
return {
|
|
218
|
+
runId,
|
|
219
|
+
text: outputText,
|
|
220
|
+
object: finalObject,
|
|
221
|
+
threadId,
|
|
222
|
+
steps: outputSteps,
|
|
223
|
+
usage: totalUsage,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
// knowledge: decisions/internals/an-agent-interrupt-is-not-a-failure.md
|
|
228
|
+
const interruption = interruptHandle.interruption;
|
|
229
|
+
if (interruption || isAbortError(error)) {
|
|
230
|
+
await agentRunState.updateRun(runId, { status: 'interrupted' });
|
|
231
|
+
if (storage) {
|
|
232
|
+
trackInterruptNote(threadId, persistOrphanedToolResults(interruptHandle, storage, threadId));
|
|
233
|
+
}
|
|
234
|
+
throw new AgentInterruptedError(runId, interruption ?? { reason: 'user' });
|
|
235
|
+
}
|
|
236
|
+
for (const mw of agentMiddlewares) {
|
|
237
|
+
if (mw.onError) {
|
|
238
|
+
try {
|
|
239
|
+
await mw.onError(singletonServices, {
|
|
240
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
241
|
+
stepNumber: -1,
|
|
242
|
+
messages: runnerParams.messages,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
catch { }
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
await agentRunState.updateRun(runId, {
|
|
249
|
+
status: 'failed',
|
|
250
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
251
|
+
});
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
finally {
|
|
255
|
+
interruptHandle.release();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
export async function resumeAgentSync(runId, approvals, params, expectedAgentName) {
|
|
259
|
+
const singletonServices = getSingletonServices();
|
|
260
|
+
const { agentRunState } = singletonServices;
|
|
261
|
+
if (!agentRunState) {
|
|
262
|
+
throw new Error('AgentRunStateService not available in singletonServices');
|
|
263
|
+
}
|
|
264
|
+
const run = await agentRunState.getRun(runId);
|
|
265
|
+
if (!run)
|
|
266
|
+
throw new Error(`No run found for runId ${runId}`);
|
|
267
|
+
assertResourcePrincipalOwner(params, agentSessionScope(run.agentName), run.resourceId, 'run');
|
|
268
|
+
if (expectedAgentName && run.agentName !== expectedAgentName) {
|
|
269
|
+
throw new Error(`Run ${runId} belongs to agent '${run.agentName}', not '${expectedAgentName}'`);
|
|
270
|
+
}
|
|
271
|
+
if (run.status !== 'suspended') {
|
|
272
|
+
throw new Error(`Run ${runId} is not suspended (status: ${run.status})`);
|
|
273
|
+
}
|
|
274
|
+
const { agent, packageName, resolvedName } = resolveAgent(run.agentName);
|
|
275
|
+
await assertAgentAuthorized(agent, params, packageName);
|
|
276
|
+
const { storage } = resolveMemoryServices(agent, singletonServices);
|
|
277
|
+
const memoryConfig = agent.memory;
|
|
278
|
+
const agentRunner = singletonServices.agentRunner;
|
|
279
|
+
if (!agentRunner) {
|
|
280
|
+
throw new AIProviderNotConfiguredError();
|
|
281
|
+
}
|
|
282
|
+
const approvedIds = new Set(approvals.filter((a) => a.approved).map((a) => a.toolCallId));
|
|
283
|
+
const rejectedIds = new Set(approvals.filter((a) => !a.approved).map((a) => a.toolCallId));
|
|
284
|
+
const savedPendingApprovals = [...(run.pendingApprovals ?? [])];
|
|
285
|
+
// The read above is not a claim — concurrent resumes all see the same pending
|
|
286
|
+
// list. `resolveApproval` is the claim, and only the caller it returns true
|
|
287
|
+
// for may run the tool.
|
|
288
|
+
const claimedIds = new Set();
|
|
289
|
+
for (const { toolCallId, approved } of approvals) {
|
|
290
|
+
const claimed = await agentRunState.resolveApproval(toolCallId, approved ? 'approved' : 'denied');
|
|
291
|
+
if (claimed) {
|
|
292
|
+
claimedIds.add(toolCallId);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (approvals.length > 0 && claimedIds.size === 0) {
|
|
296
|
+
throw new Error(`Approvals for run ${runId} were already resolved by another caller`);
|
|
297
|
+
}
|
|
298
|
+
const { tools } = await buildToolDefs(params, new Map(), run.resourceId, resolvedName, packageName, undefined, agent.agentMiddleware ?? []);
|
|
299
|
+
const toolCallMessages = [];
|
|
300
|
+
for (const pending of savedPendingApprovals) {
|
|
301
|
+
if (pending.type !== 'tool-call')
|
|
302
|
+
continue;
|
|
303
|
+
const toolCallId = pending.toolCallId;
|
|
304
|
+
if (!claimedIds.has(toolCallId))
|
|
305
|
+
continue;
|
|
306
|
+
let resultStr;
|
|
307
|
+
let toolError;
|
|
308
|
+
if (rejectedIds.has(toolCallId)) {
|
|
309
|
+
resultStr =
|
|
310
|
+
'The user explicitly declined this action. Inform them that it was declined and do not retry.';
|
|
311
|
+
}
|
|
312
|
+
else if (approvedIds.has(toolCallId)) {
|
|
313
|
+
const matchingTool = tools.find((t) => t.name === pending.toolName);
|
|
314
|
+
if (!matchingTool) {
|
|
315
|
+
throw new Error(`Tool "${pending.toolName}" not found in agent definition`);
|
|
316
|
+
}
|
|
317
|
+
const rawArgs = typeof pending.args === 'string'
|
|
318
|
+
? JSON.parse(pending.args)
|
|
319
|
+
: pending.args;
|
|
320
|
+
const toolArgs = stripNulls(rawArgs) ?? {};
|
|
321
|
+
try {
|
|
322
|
+
const toolResult = await matchingTool.execute(toolArgs);
|
|
323
|
+
resultStr =
|
|
324
|
+
typeof toolResult === 'string'
|
|
325
|
+
? toolResult
|
|
326
|
+
: JSON.stringify(toolResult);
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
if (err?.payload?.error === 'missing_credential') {
|
|
330
|
+
resultStr = JSON.stringify(err.payload);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
toolError = err instanceof Error ? err.message : String(err);
|
|
334
|
+
resultStr = `Error: ${toolError}`;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
toolCallMessages.push({
|
|
342
|
+
toolCallId,
|
|
343
|
+
toolName: pending.toolName,
|
|
344
|
+
args: typeof pending.args === 'string'
|
|
345
|
+
? JSON.parse(pending.args)
|
|
346
|
+
: pending.args,
|
|
347
|
+
result: resultStr,
|
|
348
|
+
...(toolError ? { error: toolError } : {}),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
if (storage && toolCallMessages.length > 0) {
|
|
352
|
+
await storage.saveMessages(run.threadId, [
|
|
353
|
+
{
|
|
354
|
+
id: randomUUID(),
|
|
355
|
+
role: 'tool',
|
|
356
|
+
toolResults: toolCallMessages.map((tc) => ({
|
|
357
|
+
id: tc.toolCallId,
|
|
358
|
+
name: tc.toolName,
|
|
359
|
+
result: tc.result,
|
|
360
|
+
})),
|
|
361
|
+
createdAt: new Date(),
|
|
362
|
+
},
|
|
363
|
+
]);
|
|
364
|
+
}
|
|
365
|
+
await agentRunState.updateRun(runId, { status: 'running' });
|
|
366
|
+
return continueAfterToolResultSync(run, agent, packageName, resolvedName, storage, memoryConfig, agentRunner, params, agentRunState,
|
|
367
|
+
// The approved tools were executed here, before the model was re-entered,
|
|
368
|
+
// so they belong to the run's step record — otherwise a tool that failed
|
|
369
|
+
// after approval leaves no trace on the run at all.
|
|
370
|
+
toolCallMessages.length > 0
|
|
371
|
+
? {
|
|
372
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
373
|
+
toolCalls: toolCallMessages.map((tc) => ({
|
|
374
|
+
name: tc.toolName,
|
|
375
|
+
args: (tc.args ?? {}),
|
|
376
|
+
result: tc.result,
|
|
377
|
+
...(tc.error ? { error: tc.error } : {}),
|
|
378
|
+
})),
|
|
379
|
+
}
|
|
380
|
+
: undefined);
|
|
381
|
+
}
|
|
382
|
+
async function continueAfterToolResultSync(run, agent, packageName, resolvedName, storage, memoryConfig, agentRunner, params, agentRunState, resumedToolStep) {
|
|
383
|
+
const singletonServices = getSingletonServices();
|
|
384
|
+
const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta');
|
|
385
|
+
const meta = agentsMeta[resolvedName];
|
|
386
|
+
const workingMemorySchemaName = meta?.workingMemorySchema ?? null;
|
|
387
|
+
const messages = storage
|
|
388
|
+
? await storage.getMessages(run.threadId, {
|
|
389
|
+
lastN: memoryConfig?.lastMessages ?? 20,
|
|
390
|
+
})
|
|
391
|
+
: [];
|
|
392
|
+
const workingMemoryJsonSchema = workingMemorySchemaName
|
|
393
|
+
? pikkuState(packageName, 'misc', 'schemas').get(workingMemorySchemaName)
|
|
394
|
+
: undefined;
|
|
395
|
+
const contextMessages = await loadContextMessages(memoryConfig, storage, { message: '', threadId: run.threadId, resourceId: run.resourceId }, workingMemoryJsonSchema);
|
|
396
|
+
const allMessages = [...contextMessages, ...messages];
|
|
397
|
+
const trimmedMessages = trimMessages(allMessages);
|
|
398
|
+
const instructions = await buildInstructions(resolvedName, packageName);
|
|
399
|
+
const agentMiddlewares = [
|
|
400
|
+
...getWorkingMemoryMiddleware(memoryConfig, storage, {
|
|
401
|
+
threadId: run.threadId,
|
|
402
|
+
workingMemorySchemaName,
|
|
403
|
+
logger: singletonServices.logger,
|
|
404
|
+
schemaService: singletonServices.schema,
|
|
405
|
+
}),
|
|
406
|
+
...(agent.agentMiddleware ?? []),
|
|
407
|
+
];
|
|
408
|
+
// One bag per run, shared by every middleware — see PikkuAgentMiddlewareHooks.
|
|
409
|
+
const sharedNotes = {};
|
|
410
|
+
const { messages: modifiedMessages, instructions: modifiedInstructions } = await applyInputMiddleware(agentMiddlewares, singletonServices, { messages: trimmedMessages, instructions: instructions }, sharedNotes);
|
|
411
|
+
const { tools: resumeTools } = await buildToolDefs(params, new Map(), run.resourceId, resolvedName, packageName, undefined, agentMiddlewares);
|
|
412
|
+
const resolved = resolveModelConfig(resolvedName, agent);
|
|
413
|
+
const maxSteps = resolved.maxSteps ?? 10;
|
|
414
|
+
const runnerParams = {
|
|
415
|
+
model: resolved.model,
|
|
416
|
+
temperature: resolved.temperature,
|
|
417
|
+
instructions: modifiedInstructions,
|
|
418
|
+
messages: modifiedMessages,
|
|
419
|
+
tools: resumeTools,
|
|
420
|
+
maxSteps: 1,
|
|
421
|
+
toolChoice: (agent.toolChoice ?? 'auto'),
|
|
422
|
+
providerOptions: agent.providerOptions,
|
|
423
|
+
outputSchema: meta?.outputSchema
|
|
424
|
+
? pikkuState(packageName, 'misc', 'schemas').get(meta.outputSchema)
|
|
425
|
+
: undefined,
|
|
426
|
+
agentId: run.agentName,
|
|
427
|
+
};
|
|
428
|
+
// A resumed run keeps its original runId, so the handle registered for the
|
|
429
|
+
// first leg is long gone — an approval that kicks off more generation has to
|
|
430
|
+
// be interruptible on its own terms.
|
|
431
|
+
const interruptHandle = registerInterruptibleRun(run.runId);
|
|
432
|
+
runnerParams.abortSignal = interruptHandle.signal;
|
|
433
|
+
runnerParams.tools = trackToolExecution(runnerParams.tools, interruptHandle);
|
|
434
|
+
try {
|
|
435
|
+
// Kept out of `accumulatedSteps` deliberately: that array drives
|
|
436
|
+
// `saveMessages`, and the approved tool's messages were already written to
|
|
437
|
+
// the thread before the model was re-entered. It belongs to the run's step
|
|
438
|
+
// record, not to persistence.
|
|
439
|
+
const withResumedStep = (steps) => resumedToolStep ? [resumedToolStep, ...steps] : steps;
|
|
440
|
+
const accumulatedSteps = [];
|
|
441
|
+
const totalUsage = { inputTokens: 0, outputTokens: 0 };
|
|
442
|
+
let lastStepResult = null;
|
|
443
|
+
for (let step = 0; step < maxSteps; step++) {
|
|
444
|
+
const stepResult = await agentRunner.run(runnerParams);
|
|
445
|
+
lastStepResult = stepResult;
|
|
446
|
+
totalUsage.inputTokens += stepResult.usage.inputTokens;
|
|
447
|
+
totalUsage.outputTokens += stepResult.usage.outputTokens;
|
|
448
|
+
await notifyAfterStep(agentMiddlewares, singletonServices, step, stepResult);
|
|
449
|
+
accumulatedSteps.push(toAccumulatedStep(stepResult));
|
|
450
|
+
if (stepResult.toolCalls.length === 0)
|
|
451
|
+
break;
|
|
452
|
+
const approvalsNeeded = checkForApprovals(stepResult, runnerParams.tools);
|
|
453
|
+
if (approvalsNeeded.length > 0) {
|
|
454
|
+
await describeApprovals(approvalsNeeded, runnerParams.tools);
|
|
455
|
+
const pendingApprovals = approvalsNeeded.map((a) => a.agentRunId
|
|
456
|
+
? {
|
|
457
|
+
type: 'agent-call',
|
|
458
|
+
toolCallId: a.toolCallId,
|
|
459
|
+
agentName: a.toolName,
|
|
460
|
+
agentRunId: a.agentRunId,
|
|
461
|
+
displayToolName: a.displayToolName ?? a.toolName,
|
|
462
|
+
displayArgs: a.displayArgs ?? a.args,
|
|
463
|
+
}
|
|
464
|
+
: {
|
|
465
|
+
type: 'tool-call',
|
|
466
|
+
toolCallId: a.toolCallId,
|
|
467
|
+
toolName: a.toolName,
|
|
468
|
+
args: a.args,
|
|
469
|
+
});
|
|
470
|
+
const completedSteps = accumulatedSteps.slice(0, -1);
|
|
471
|
+
if (completedSteps.length > 0) {
|
|
472
|
+
await saveMessages(storage, run.threadId, run.resourceId, memoryConfig, null, { text: '', steps: completedSteps });
|
|
473
|
+
}
|
|
474
|
+
if (storage) {
|
|
475
|
+
await storage.saveMessages(run.threadId, [
|
|
476
|
+
{
|
|
477
|
+
id: randomUUID(),
|
|
478
|
+
role: 'assistant',
|
|
479
|
+
toolCalls: stepResult.toolCalls.map((tc) => ({
|
|
480
|
+
id: tc.toolCallId,
|
|
481
|
+
name: tc.toolName,
|
|
482
|
+
args: tc.args,
|
|
483
|
+
})),
|
|
484
|
+
createdAt: new Date(),
|
|
485
|
+
},
|
|
486
|
+
]);
|
|
487
|
+
}
|
|
488
|
+
await agentRunState.updateRun(run.runId, {
|
|
489
|
+
status: 'suspended',
|
|
490
|
+
suspendReason: 'approval',
|
|
491
|
+
pendingApprovals,
|
|
492
|
+
usage: { ...totalUsage, model: agent.model },
|
|
493
|
+
});
|
|
494
|
+
const suspendedText = lastStepResult?.text ?? '';
|
|
495
|
+
return {
|
|
496
|
+
runId: run.runId,
|
|
497
|
+
text: suspendedText,
|
|
498
|
+
threadId: run.threadId,
|
|
499
|
+
steps: withResumedStep(accumulatedSteps),
|
|
500
|
+
usage: totalUsage,
|
|
501
|
+
status: 'suspended',
|
|
502
|
+
pendingApprovals: approvalsNeeded.map((a) => ({
|
|
503
|
+
toolCallId: a.toolCallId,
|
|
504
|
+
toolName: a.displayToolName ?? a.toolName,
|
|
505
|
+
args: a.displayArgs ?? a.args,
|
|
506
|
+
reason: a.reason,
|
|
507
|
+
runId: run.runId,
|
|
508
|
+
})),
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
appendStepMessages(runnerParams, stepResult);
|
|
512
|
+
}
|
|
513
|
+
const finalText = lastStepResult?.text ?? '';
|
|
514
|
+
const finalObject = lastStepResult?.object;
|
|
515
|
+
const result = {
|
|
516
|
+
text: finalText,
|
|
517
|
+
steps: accumulatedSteps,
|
|
518
|
+
};
|
|
519
|
+
const { text: outputText, steps: outputSteps } = await applyOutputMiddleware(agentMiddlewares, singletonServices, {
|
|
520
|
+
text: finalText,
|
|
521
|
+
messages: runnerParams.messages,
|
|
522
|
+
steps: withResumedStep(result.steps),
|
|
523
|
+
usage: totalUsage,
|
|
524
|
+
});
|
|
525
|
+
await saveMessages(storage, run.threadId, run.resourceId, memoryConfig, null, {
|
|
526
|
+
text: outputText,
|
|
527
|
+
// The approved tool's messages were written before the model was
|
|
528
|
+
// re-entered, so only the steps this leg generated are persisted here.
|
|
529
|
+
steps: accumulatedSteps,
|
|
530
|
+
});
|
|
531
|
+
await finalizeAgentRun(agentRunState, {
|
|
532
|
+
runId: run.runId,
|
|
533
|
+
agentName: resolvedName,
|
|
534
|
+
threadId: run.threadId,
|
|
535
|
+
resourceId: run.resourceId,
|
|
536
|
+
input: lastUserMessageText(runnerParams.messages),
|
|
537
|
+
text: outputText,
|
|
538
|
+
steps: outputSteps,
|
|
539
|
+
usage: { ...totalUsage, model: agent.model },
|
|
540
|
+
});
|
|
541
|
+
return {
|
|
542
|
+
runId: run.runId,
|
|
543
|
+
text: outputText,
|
|
544
|
+
object: finalObject,
|
|
545
|
+
threadId: run.threadId,
|
|
546
|
+
steps: outputSteps,
|
|
547
|
+
usage: totalUsage,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
catch (error) {
|
|
551
|
+
const interruption = interruptHandle.interruption;
|
|
552
|
+
if (interruption || isAbortError(error)) {
|
|
553
|
+
await agentRunState.updateRun(run.runId, { status: 'interrupted' });
|
|
554
|
+
if (storage) {
|
|
555
|
+
trackInterruptNote(run.threadId, persistOrphanedToolResults(interruptHandle, storage, run.threadId));
|
|
556
|
+
}
|
|
557
|
+
throw new AgentInterruptedError(run.runId, interruption ?? { reason: 'user' });
|
|
558
|
+
}
|
|
559
|
+
for (const mw of agentMiddlewares) {
|
|
560
|
+
if (mw.onError) {
|
|
561
|
+
try {
|
|
562
|
+
await mw.onError(singletonServices, {
|
|
563
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
564
|
+
stepNumber: -1,
|
|
565
|
+
messages: runnerParams.messages,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
catch { }
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
await agentRunState.updateRun(run.runId, {
|
|
572
|
+
status: 'failed',
|
|
573
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
574
|
+
});
|
|
575
|
+
throw error;
|
|
576
|
+
}
|
|
577
|
+
finally {
|
|
578
|
+
interruptHandle.release();
|
|
579
|
+
}
|
|
580
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { AgentStreamChannel } from './agent.types.js';
|
|
2
|
+
import type { AgentRunnerParams, AgentStepResult } from '../../services/agent-runner-service.js';
|
|
3
|
+
import { ToolApprovalRequired, ToolCredentialRequired, type RunAgentParams, type StreamAgentOptions } from './agent-prepare.js';
|
|
4
|
+
import { type AgentInterruption, type AgentInterruptResult } from './agent-interrupt.js';
|
|
5
|
+
export declare function checkForApprovals(stepResult: AgentStepResult, tools: AgentRunnerParams['tools']): ToolApprovalRequired[];
|
|
6
|
+
export declare function checkForCredentialRequests(stepResult: AgentStepResult): ToolCredentialRequired[];
|
|
7
|
+
export declare function appendStepMessages(runnerParams: AgentRunnerParams, stepResult: AgentStepResult): void;
|
|
8
|
+
export declare function streamAgent(agentName: string, input: {
|
|
9
|
+
message: string;
|
|
10
|
+
threadId: string;
|
|
11
|
+
resourceId: string;
|
|
12
|
+
model?: string;
|
|
13
|
+
temperature?: number;
|
|
14
|
+
}, channel: AgentStreamChannel, params: RunAgentParams, agentSessionMap?: Map<string, string>, options?: StreamAgentOptions): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Stop an in-flight run on behalf of a caller, after checking the run is theirs.
|
|
17
|
+
*
|
|
18
|
+
* Unlike {@link resumeAgent} this needs no channel: it does not continue the
|
|
19
|
+
* stream, it ends one. The `interrupted` event and the truncated message are
|
|
20
|
+
* emitted by the original {@link streamAgent} call on its own channel, so this
|
|
21
|
+
* can be reached over a plain RPC while the stream is held open elsewhere —
|
|
22
|
+
* which is exactly the shape a voice barge-in needs.
|
|
23
|
+
*
|
|
24
|
+
* Ownership is the whole gate, deliberately without {@link assertAgentAuthorized}.
|
|
25
|
+
* Resuming re-enters the agent and approves a tool call, so a revoked grant must
|
|
26
|
+
* block it; stopping is the opposite — if a caller's access to an agent was
|
|
27
|
+
* revoked mid-run, being able to stop their own run is harmless and arguably
|
|
28
|
+
* what you want.
|
|
29
|
+
*
|
|
30
|
+
* Returns `false` when there was nothing to stop. Racing a run that finishes on
|
|
31
|
+
* its own is the normal case in voice, so it is not an error.
|
|
32
|
+
*/
|
|
33
|
+
export declare function interruptAgent(input: {
|
|
34
|
+
runId: string;
|
|
35
|
+
reason?: AgentInterruption['reason'];
|
|
36
|
+
}, params: RunAgentParams): Promise<AgentInterruptResult>;
|
|
37
|
+
export declare function resumeAgent(input: {
|
|
38
|
+
runId: string;
|
|
39
|
+
toolCallId: string;
|
|
40
|
+
approved: boolean;
|
|
41
|
+
}, channel: AgentStreamChannel, params: RunAgentParams, options?: StreamAgentOptions): Promise<void>;
|