@warlock.js/ai 4.1.1
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/README.md +72 -0
- package/cjs/index.cjs +8625 -0
- package/cjs/index.cjs.map +1 -0
- package/esm/agent/agent-config.type.d.mts +160 -0
- package/esm/agent/agent-config.type.d.mts.map +1 -0
- package/esm/agent/agent-input-builder.mjs +111 -0
- package/esm/agent/agent-input-builder.mjs.map +1 -0
- package/esm/agent/agent-log-event.mjs +101 -0
- package/esm/agent/agent-log-event.mjs.map +1 -0
- package/esm/agent/agent-stream.d.mts +47 -0
- package/esm/agent/agent-stream.d.mts.map +1 -0
- package/esm/agent/agent-stream.mjs +82 -0
- package/esm/agent/agent-stream.mjs.map +1 -0
- package/esm/agent/agent-to-stream-event.mjs +86 -0
- package/esm/agent/agent-to-stream-event.mjs.map +1 -0
- package/esm/agent/agent.d.mts +38 -0
- package/esm/agent/agent.d.mts.map +1 -0
- package/esm/agent/agent.mjs +974 -0
- package/esm/agent/agent.mjs.map +1 -0
- package/esm/agent/index.d.mts +3 -0
- package/esm/agent/index.mjs +4 -0
- package/esm/agent/json-stream-guard.mjs +400 -0
- package/esm/agent/json-stream-guard.mjs.map +1 -0
- package/esm/ai.d.mts +64 -0
- package/esm/ai.d.mts.map +1 -0
- package/esm/ai.mjs +65 -0
- package/esm/ai.mjs.map +1 -0
- package/esm/config.d.mts +79 -0
- package/esm/config.d.mts.map +1 -0
- package/esm/config.mjs +43 -0
- package/esm/config.mjs.map +1 -0
- package/esm/contracts/agent/agent-options.type.d.mts +132 -0
- package/esm/contracts/agent/agent-options.type.d.mts.map +1 -0
- package/esm/contracts/agent/agent.contract.d.mts +92 -0
- package/esm/contracts/agent/agent.contract.d.mts.map +1 -0
- package/esm/contracts/agent/index.d.mts +2 -0
- package/esm/contracts/attachment.type.d.mts +100 -0
- package/esm/contracts/attachment.type.d.mts.map +1 -0
- package/esm/contracts/content-part.type.d.mts +55 -0
- package/esm/contracts/content-part.type.d.mts.map +1 -0
- package/esm/contracts/conversation-message.type.d.mts +51 -0
- package/esm/contracts/conversation-message.type.d.mts.map +1 -0
- package/esm/contracts/embedder.contract.d.mts +69 -0
- package/esm/contracts/embedder.contract.d.mts.map +1 -0
- package/esm/contracts/end.type.d.mts +53 -0
- package/esm/contracts/end.type.d.mts.map +1 -0
- package/esm/contracts/end.type.mjs +47 -0
- package/esm/contracts/end.type.mjs.map +1 -0
- package/esm/contracts/events/agent-events.type.d.mts +71 -0
- package/esm/contracts/events/agent-events.type.d.mts.map +1 -0
- package/esm/contracts/events/complete-event.type.d.mts +33 -0
- package/esm/contracts/events/complete-event.type.d.mts.map +1 -0
- package/esm/contracts/events/event-identity.type.d.mts +30 -0
- package/esm/contracts/events/event-identity.type.d.mts.map +1 -0
- package/esm/contracts/events/event-map.type.d.mts +80 -0
- package/esm/contracts/events/event-map.type.d.mts.map +1 -0
- package/esm/contracts/events/index.d.mts +7 -0
- package/esm/contracts/events/supervisor-events.type.d.mts +162 -0
- package/esm/contracts/events/supervisor-events.type.d.mts.map +1 -0
- package/esm/contracts/events/usage-event.type.d.mts +39 -0
- package/esm/contracts/events/usage-event.type.d.mts.map +1 -0
- package/esm/contracts/events/workflow-events.type.d.mts +51 -0
- package/esm/contracts/events/workflow-events.type.d.mts.map +1 -0
- package/esm/contracts/executable.contract.d.mts +25 -0
- package/esm/contracts/executable.contract.d.mts.map +1 -0
- package/esm/contracts/finish-reason.type.d.mts +17 -0
- package/esm/contracts/finish-reason.type.d.mts.map +1 -0
- package/esm/contracts/index.d.mts +63 -0
- package/esm/contracts/middleware/index.d.mts +3 -0
- package/esm/contracts/middleware/middleware-context.type.d.mts +99 -0
- package/esm/contracts/middleware/middleware-context.type.d.mts.map +1 -0
- package/esm/contracts/middleware/middleware-state.type.d.mts +34 -0
- package/esm/contracts/middleware/middleware-state.type.d.mts.map +1 -0
- package/esm/contracts/middleware/middleware.contract.d.mts +140 -0
- package/esm/contracts/middleware/middleware.contract.d.mts.map +1 -0
- package/esm/contracts/model-tool-call-request.type.d.mts +58 -0
- package/esm/contracts/model-tool-call-request.type.d.mts.map +1 -0
- package/esm/contracts/model.contract.d.mts +164 -0
- package/esm/contracts/model.contract.d.mts.map +1 -0
- package/esm/contracts/placeholders.type.d.mts +23 -0
- package/esm/contracts/placeholders.type.d.mts.map +1 -0
- package/esm/contracts/result/agent-result.type.d.mts +33 -0
- package/esm/contracts/result/agent-result.type.d.mts.map +1 -0
- package/esm/contracts/result/attempt-entry.type.d.mts +40 -0
- package/esm/contracts/result/attempt-entry.type.d.mts.map +1 -0
- package/esm/contracts/result/base-report.type.d.mts +137 -0
- package/esm/contracts/result/base-report.type.d.mts.map +1 -0
- package/esm/contracts/result/base-report.type.mjs +17 -0
- package/esm/contracts/result/base-report.type.mjs.map +1 -0
- package/esm/contracts/result/base-result.type.d.mts +26 -0
- package/esm/contracts/result/base-result.type.d.mts.map +1 -0
- package/esm/contracts/result/execute-result.type.d.mts +40 -0
- package/esm/contracts/result/execute-result.type.d.mts.map +1 -0
- package/esm/contracts/result/execution-report.type.d.mts +50 -0
- package/esm/contracts/result/execution-report.type.d.mts.map +1 -0
- package/esm/contracts/result/index.d.mts +14 -0
- package/esm/contracts/result/llm-trip.type.d.mts +36 -0
- package/esm/contracts/result/llm-trip.type.d.mts.map +1 -0
- package/esm/contracts/result/model-pricing.type.d.mts +51 -0
- package/esm/contracts/result/model-pricing.type.d.mts.map +1 -0
- package/esm/contracts/result/session-send-result.type.d.mts +29 -0
- package/esm/contracts/result/session-send-result.type.d.mts.map +1 -0
- package/esm/contracts/result/step-result.type.d.mts +43 -0
- package/esm/contracts/result/step-result.type.d.mts.map +1 -0
- package/esm/contracts/result/supervisor-result.type.d.mts +86 -0
- package/esm/contracts/result/supervisor-result.type.d.mts.map +1 -0
- package/esm/contracts/result/tool-call.type.d.mts +44 -0
- package/esm/contracts/result/tool-call.type.d.mts.map +1 -0
- package/esm/contracts/result/usage.type.d.mts +57 -0
- package/esm/contracts/result/usage.type.d.mts.map +1 -0
- package/esm/contracts/result/workflow-result.type.d.mts +45 -0
- package/esm/contracts/result/workflow-result.type.d.mts.map +1 -0
- package/esm/contracts/sdk-adapter.contract.d.mts +77 -0
- package/esm/contracts/sdk-adapter.contract.d.mts.map +1 -0
- package/esm/contracts/stream/stream-event.type.d.mts +68 -0
- package/esm/contracts/stream/stream-event.type.d.mts.map +1 -0
- package/esm/contracts/stream/stream.contract.d.mts +61 -0
- package/esm/contracts/stream/stream.contract.d.mts.map +1 -0
- package/esm/contracts/streaming-tool-guard-config.type.d.mts +78 -0
- package/esm/contracts/streaming-tool-guard-config.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/ack-entry.type.d.mts +97 -0
- package/esm/contracts/supervisor/ack-entry.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/classifier-context.type.d.mts +189 -0
- package/esm/contracts/supervisor/classifier-context.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/dispatch-context.type.d.mts +235 -0
- package/esm/contracts/supervisor/dispatch-context.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/evaluate-context.type.d.mts +124 -0
- package/esm/contracts/supervisor/evaluate-context.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/index.d.mts +16 -0
- package/esm/contracts/supervisor/intent-entry.type.d.mts +254 -0
- package/esm/contracts/supervisor/intent-entry.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/iteration-snapshot.type.d.mts +117 -0
- package/esm/contracts/supervisor/iteration-snapshot.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/next.type.d.mts +25 -0
- package/esm/contracts/supervisor/next.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/route-context.type.d.mts +91 -0
- package/esm/contracts/supervisor/route-context.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/router-entry.type.d.mts +67 -0
- package/esm/contracts/supervisor/router-entry.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/supervisor-config.type.d.mts +356 -0
- package/esm/contracts/supervisor/supervisor-config.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/supervisor-execute-options.type.d.mts +90 -0
- package/esm/contracts/supervisor/supervisor-execute-options.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/supervisor-input.type.d.mts +24 -0
- package/esm/contracts/supervisor/supervisor-input.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/supervisor-snapshot.type.d.mts +49 -0
- package/esm/contracts/supervisor/supervisor-snapshot.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/supervisor-stream-event.type.d.mts +16 -0
- package/esm/contracts/supervisor/supervisor-stream-event.type.d.mts.map +1 -0
- package/esm/contracts/supervisor/supervisor.contract.d.mts +126 -0
- package/esm/contracts/supervisor/supervisor.contract.d.mts.map +1 -0
- package/esm/contracts/system-prompt.contract.d.mts +118 -0
- package/esm/contracts/system-prompt.contract.d.mts.map +1 -0
- package/esm/contracts/tool.contract.d.mts +161 -0
- package/esm/contracts/tool.contract.d.mts.map +1 -0
- package/esm/contracts/workflow/index.d.mts +6 -0
- package/esm/contracts/workflow/next-step-result.type.d.mts +17 -0
- package/esm/contracts/workflow/next-step-result.type.d.mts.map +1 -0
- package/esm/contracts/workflow/retry-config.type.d.mts +19 -0
- package/esm/contracts/workflow/retry-config.type.d.mts.map +1 -0
- package/esm/contracts/workflow/step.contract.d.mts +189 -0
- package/esm/contracts/workflow/step.contract.d.mts.map +1 -0
- package/esm/contracts/workflow/workflow-context.type.d.mts +45 -0
- package/esm/contracts/workflow/workflow-context.type.d.mts.map +1 -0
- package/esm/contracts/workflow/workflow-snapshot.type.d.mts +24 -0
- package/esm/contracts/workflow/workflow-snapshot.type.d.mts.map +1 -0
- package/esm/contracts/workflow/workflow.contract.d.mts +142 -0
- package/esm/contracts/workflow/workflow.contract.d.mts.map +1 -0
- package/esm/errors/agent-cancelled-error.d.mts +49 -0
- package/esm/errors/agent-cancelled-error.d.mts.map +1 -0
- package/esm/errors/agent-cancelled-error.mjs +43 -0
- package/esm/errors/agent-cancelled-error.mjs.map +1 -0
- package/esm/errors/agent-execution-error.d.mts +32 -0
- package/esm/errors/agent-execution-error.d.mts.map +1 -0
- package/esm/errors/agent-execution-error.mjs +35 -0
- package/esm/errors/agent-execution-error.mjs.map +1 -0
- package/esm/errors/agent-max-trips-error.d.mts +45 -0
- package/esm/errors/agent-max-trips-error.d.mts.map +1 -0
- package/esm/errors/agent-max-trips-error.mjs +41 -0
- package/esm/errors/agent-max-trips-error.mjs.map +1 -0
- package/esm/errors/ai-error.d.mts +94 -0
- package/esm/errors/ai-error.d.mts.map +1 -0
- package/esm/errors/ai-error.mjs +68 -0
- package/esm/errors/ai-error.mjs.map +1 -0
- package/esm/errors/budget-exceeded-error.d.mts +41 -0
- package/esm/errors/budget-exceeded-error.d.mts.map +1 -0
- package/esm/errors/budget-exceeded-error.mjs +31 -0
- package/esm/errors/budget-exceeded-error.mjs.map +1 -0
- package/esm/errors/content-filter-error.d.mts +33 -0
- package/esm/errors/content-filter-error.d.mts.map +1 -0
- package/esm/errors/content-filter-error.mjs +28 -0
- package/esm/errors/content-filter-error.mjs.map +1 -0
- package/esm/errors/context-length-exceeded-error.d.mts +38 -0
- package/esm/errors/context-length-exceeded-error.d.mts.map +1 -0
- package/esm/errors/context-length-exceeded-error.mjs +31 -0
- package/esm/errors/context-length-exceeded-error.mjs.map +1 -0
- package/esm/errors/error-category.type.d.mts +30 -0
- package/esm/errors/error-category.type.d.mts.map +1 -0
- package/esm/errors/error-code.type.d.mts +19 -0
- package/esm/errors/error-code.type.d.mts.map +1 -0
- package/esm/errors/guardrail-violation-error.d.mts +55 -0
- package/esm/errors/guardrail-violation-error.d.mts.map +1 -0
- package/esm/errors/guardrail-violation-error.mjs +41 -0
- package/esm/errors/guardrail-violation-error.mjs.map +1 -0
- package/esm/errors/index.d.mts +29 -0
- package/esm/errors/index.mjs +29 -0
- package/esm/errors/invalid-request-error.d.mts +27 -0
- package/esm/errors/invalid-request-error.d.mts.map +1 -0
- package/esm/errors/invalid-request-error.mjs +31 -0
- package/esm/errors/invalid-request-error.mjs.map +1 -0
- package/esm/errors/max-iterations-error.d.mts +31 -0
- package/esm/errors/max-iterations-error.d.mts.map +1 -0
- package/esm/errors/max-iterations-error.mjs +32 -0
- package/esm/errors/max-iterations-error.mjs.map +1 -0
- package/esm/errors/max-steps-exceeded-error.d.mts +20 -0
- package/esm/errors/max-steps-exceeded-error.d.mts.map +1 -0
- package/esm/errors/max-steps-exceeded-error.mjs +21 -0
- package/esm/errors/max-steps-exceeded-error.mjs.map +1 -0
- package/esm/errors/provider-auth-error.d.mts +22 -0
- package/esm/errors/provider-auth-error.d.mts.map +1 -0
- package/esm/errors/provider-auth-error.mjs +26 -0
- package/esm/errors/provider-auth-error.mjs.map +1 -0
- package/esm/errors/provider-error.d.mts +38 -0
- package/esm/errors/provider-error.d.mts.map +1 -0
- package/esm/errors/provider-error.mjs +42 -0
- package/esm/errors/provider-error.mjs.map +1 -0
- package/esm/errors/provider-rate-limit-error.d.mts +34 -0
- package/esm/errors/provider-rate-limit-error.d.mts.map +1 -0
- package/esm/errors/provider-rate-limit-error.mjs +31 -0
- package/esm/errors/provider-rate-limit-error.mjs.map +1 -0
- package/esm/errors/provider-timeout-error.d.mts +22 -0
- package/esm/errors/provider-timeout-error.d.mts.map +1 -0
- package/esm/errors/provider-timeout-error.mjs +26 -0
- package/esm/errors/provider-timeout-error.mjs.map +1 -0
- package/esm/errors/quota-exceeded-error.d.mts +36 -0
- package/esm/errors/quota-exceeded-error.d.mts.map +1 -0
- package/esm/errors/quota-exceeded-error.mjs +40 -0
- package/esm/errors/quota-exceeded-error.mjs.map +1 -0
- package/esm/errors/routing-error.d.mts +23 -0
- package/esm/errors/routing-error.d.mts.map +1 -0
- package/esm/errors/routing-error.mjs +23 -0
- package/esm/errors/routing-error.mjs.map +1 -0
- package/esm/errors/schema-validation-error.d.mts +40 -0
- package/esm/errors/schema-validation-error.d.mts.map +1 -0
- package/esm/errors/schema-validation-error.mjs +37 -0
- package/esm/errors/schema-validation-error.mjs.map +1 -0
- package/esm/errors/step-failed-error.d.mts +20 -0
- package/esm/errors/step-failed-error.d.mts.map +1 -0
- package/esm/errors/step-failed-error.mjs +19 -0
- package/esm/errors/step-failed-error.mjs.map +1 -0
- package/esm/errors/supervisor-cancelled-error.d.mts +29 -0
- package/esm/errors/supervisor-cancelled-error.d.mts.map +1 -0
- package/esm/errors/supervisor-cancelled-error.mjs +29 -0
- package/esm/errors/supervisor-cancelled-error.mjs.map +1 -0
- package/esm/errors/supervisor-drift-error.d.mts +31 -0
- package/esm/errors/supervisor-drift-error.d.mts.map +1 -0
- package/esm/errors/supervisor-drift-error.mjs +30 -0
- package/esm/errors/supervisor-drift-error.mjs.map +1 -0
- package/esm/errors/supervisor-failed-error.d.mts +40 -0
- package/esm/errors/supervisor-failed-error.d.mts.map +1 -0
- package/esm/errors/supervisor-failed-error.mjs +43 -0
- package/esm/errors/supervisor-failed-error.mjs.map +1 -0
- package/esm/errors/supervisor-routing-error.d.mts +44 -0
- package/esm/errors/supervisor-routing-error.d.mts.map +1 -0
- package/esm/errors/supervisor-routing-error.mjs +39 -0
- package/esm/errors/supervisor-routing-error.mjs.map +1 -0
- package/esm/errors/tool-execution-error.d.mts +35 -0
- package/esm/errors/tool-execution-error.d.mts.map +1 -0
- package/esm/errors/tool-execution-error.mjs +32 -0
- package/esm/errors/tool-execution-error.mjs.map +1 -0
- package/esm/errors/workflow-cancelled-error.d.mts +23 -0
- package/esm/errors/workflow-cancelled-error.d.mts.map +1 -0
- package/esm/errors/workflow-cancelled-error.mjs +23 -0
- package/esm/errors/workflow-cancelled-error.mjs.map +1 -0
- package/esm/errors/workflow-drift-error.d.mts +25 -0
- package/esm/errors/workflow-drift-error.d.mts.map +1 -0
- package/esm/errors/workflow-drift-error.mjs +24 -0
- package/esm/errors/workflow-drift-error.mjs.map +1 -0
- package/esm/errors/workflow-error.d.mts +15 -0
- package/esm/errors/workflow-error.d.mts.map +1 -0
- package/esm/errors/workflow-error.mjs +18 -0
- package/esm/errors/workflow-error.mjs.map +1 -0
- package/esm/index.d.mts +137 -0
- package/esm/index.mjs +81 -0
- package/esm/middleware/builtins/budget.d.mts +103 -0
- package/esm/middleware/builtins/budget.d.mts.map +1 -0
- package/esm/middleware/builtins/budget.mjs +100 -0
- package/esm/middleware/builtins/budget.mjs.map +1 -0
- package/esm/middleware/builtins/guardrail.d.mts +92 -0
- package/esm/middleware/builtins/guardrail.d.mts.map +1 -0
- package/esm/middleware/builtins/guardrail.mjs +76 -0
- package/esm/middleware/builtins/guardrail.mjs.map +1 -0
- package/esm/middleware/builtins/semantic-cache.d.mts +106 -0
- package/esm/middleware/builtins/semantic-cache.d.mts.map +1 -0
- package/esm/middleware/builtins/semantic-cache.mjs +149 -0
- package/esm/middleware/builtins/semantic-cache.mjs.map +1 -0
- package/esm/middleware/helpers/compose.d.mts +42 -0
- package/esm/middleware/helpers/compose.d.mts.map +1 -0
- package/esm/middleware/helpers/compose.mjs +52 -0
- package/esm/middleware/helpers/compose.mjs.map +1 -0
- package/esm/middleware/helpers/for-tool.d.mts +55 -0
- package/esm/middleware/helpers/for-tool.d.mts.map +1 -0
- package/esm/middleware/helpers/for-tool.mjs +80 -0
- package/esm/middleware/helpers/for-tool.mjs.map +1 -0
- package/esm/middleware/helpers/index.d.mts +2 -0
- package/esm/middleware/index.d.mts +8 -0
- package/esm/middleware/index.mjs +10 -0
- package/esm/middleware/pipeline.d.mts +70 -0
- package/esm/middleware/pipeline.d.mts.map +1 -0
- package/esm/middleware/pipeline.mjs +100 -0
- package/esm/middleware/pipeline.mjs.map +1 -0
- package/esm/middleware/utils/extract-user-text.d.mts +33 -0
- package/esm/middleware/utils/extract-user-text.d.mts.map +1 -0
- package/esm/middleware/utils/extract-user-text.mjs +40 -0
- package/esm/middleware/utils/extract-user-text.mjs.map +1 -0
- package/esm/middleware/utils/index.d.mts +2 -0
- package/esm/middleware/utils/namespaced-state.d.mts +60 -0
- package/esm/middleware/utils/namespaced-state.d.mts.map +1 -0
- package/esm/middleware/utils/namespaced-state.mjs +60 -0
- package/esm/middleware/utils/namespaced-state.mjs.map +1 -0
- package/esm/mock/index.d.mts +4 -0
- package/esm/mock/index.mjs +5 -0
- package/esm/mock/mock-agent.d.mts +41 -0
- package/esm/mock/mock-agent.d.mts.map +1 -0
- package/esm/mock/mock-agent.mjs +39 -0
- package/esm/mock/mock-agent.mjs.map +1 -0
- package/esm/mock/mock-config.type.d.mts +31 -0
- package/esm/mock/mock-config.type.d.mts.map +1 -0
- package/esm/mock/mock-model.d.mts +94 -0
- package/esm/mock/mock-model.d.mts.map +1 -0
- package/esm/mock/mock-model.mjs +150 -0
- package/esm/mock/mock-model.mjs.map +1 -0
- package/esm/mock/mock-sdk.d.mts +25 -0
- package/esm/mock/mock-sdk.d.mts.map +1 -0
- package/esm/mock/mock-sdk.mjs +37 -0
- package/esm/mock/mock-sdk.mjs.map +1 -0
- package/esm/supervisor/as-tool.d.mts +45 -0
- package/esm/supervisor/as-tool.d.mts.map +1 -0
- package/esm/supervisor/as-tool.mjs +80 -0
- package/esm/supervisor/as-tool.mjs.map +1 -0
- package/esm/supervisor/cancellation.d.mts +12 -0
- package/esm/supervisor/cancellation.d.mts.map +1 -0
- package/esm/supervisor/cancellation.mjs +22 -0
- package/esm/supervisor/cancellation.mjs.map +1 -0
- package/esm/supervisor/decide.mjs +272 -0
- package/esm/supervisor/decide.mjs.map +1 -0
- package/esm/supervisor/emitter.d.mts +27 -0
- package/esm/supervisor/emitter.d.mts.map +1 -0
- package/esm/supervisor/emitter.mjs +47 -0
- package/esm/supervisor/emitter.mjs.map +1 -0
- package/esm/supervisor/entries.d.mts +141 -0
- package/esm/supervisor/entries.d.mts.map +1 -0
- package/esm/supervisor/entries.mjs +206 -0
- package/esm/supervisor/entries.mjs.map +1 -0
- package/esm/supervisor/execution.d.mts +637 -0
- package/esm/supervisor/execution.d.mts.map +1 -0
- package/esm/supervisor/execution.mjs +1868 -0
- package/esm/supervisor/execution.mjs.map +1 -0
- package/esm/supervisor/index.d.mts +11 -0
- package/esm/supervisor/index.mjs +12 -0
- package/esm/supervisor/router-prompt.d.mts +56 -0
- package/esm/supervisor/router-prompt.d.mts.map +1 -0
- package/esm/supervisor/router-prompt.mjs +52 -0
- package/esm/supervisor/router-prompt.mjs.map +1 -0
- package/esm/supervisor/signature.d.mts +28 -0
- package/esm/supervisor/signature.d.mts.map +1 -0
- package/esm/supervisor/signature.mjs +89 -0
- package/esm/supervisor/signature.mjs.map +1 -0
- package/esm/supervisor/snapshot.d.mts +54 -0
- package/esm/supervisor/snapshot.d.mts.map +1 -0
- package/esm/supervisor/snapshot.mjs +77 -0
- package/esm/supervisor/snapshot.mjs.map +1 -0
- package/esm/supervisor/supervisor-stream.d.mts +29 -0
- package/esm/supervisor/supervisor-stream.d.mts.map +1 -0
- package/esm/supervisor/supervisor-stream.mjs +89 -0
- package/esm/supervisor/supervisor-stream.mjs.map +1 -0
- package/esm/supervisor/supervisor.d.mts +27 -0
- package/esm/supervisor/supervisor.d.mts.map +1 -0
- package/esm/supervisor/supervisor.mjs +138 -0
- package/esm/supervisor/supervisor.mjs.map +1 -0
- package/esm/system-prompt/instruction.d.mts +52 -0
- package/esm/system-prompt/instruction.d.mts.map +1 -0
- package/esm/system-prompt/instruction.mjs +57 -0
- package/esm/system-prompt/instruction.mjs.map +1 -0
- package/esm/system-prompt/persona.d.mts +52 -0
- package/esm/system-prompt/persona.d.mts.map +1 -0
- package/esm/system-prompt/persona.mjs +57 -0
- package/esm/system-prompt/persona.mjs.map +1 -0
- package/esm/system-prompt/render-placeholders.d.mts +31 -0
- package/esm/system-prompt/render-placeholders.d.mts.map +1 -0
- package/esm/system-prompt/render-placeholders.mjs +58 -0
- package/esm/system-prompt/render-placeholders.mjs.map +1 -0
- package/esm/system-prompt/system-prompt.d.mts +117 -0
- package/esm/system-prompt/system-prompt.d.mts.map +1 -0
- package/esm/system-prompt/system-prompt.mjs +137 -0
- package/esm/system-prompt/system-prompt.mjs.map +1 -0
- package/esm/tool/index.d.mts +1 -0
- package/esm/tool/index.mjs +3 -0
- package/esm/tool/tool.d.mts +64 -0
- package/esm/tool/tool.d.mts.map +1 -0
- package/esm/tool/tool.mjs +184 -0
- package/esm/tool/tool.mjs.map +1 -0
- package/esm/utils/compute-cost.d.mts +58 -0
- package/esm/utils/compute-cost.d.mts.map +1 -0
- package/esm/utils/compute-cost.mjs +87 -0
- package/esm/utils/compute-cost.mjs.map +1 -0
- package/esm/utils/extract-json-payload.d.mts +33 -0
- package/esm/utils/extract-json-payload.d.mts.map +1 -0
- package/esm/utils/extract-json-payload.mjs +39 -0
- package/esm/utils/extract-json-payload.mjs.map +1 -0
- package/esm/utils/generate-run-id.d.mts +20 -0
- package/esm/utils/generate-run-id.d.mts.map +1 -0
- package/esm/utils/generate-run-id.mjs +23 -0
- package/esm/utils/generate-run-id.mjs.map +1 -0
- package/esm/utils/index.d.mts +9 -0
- package/esm/utils/index.mjs +11 -0
- package/esm/utils/json-schema.d.mts +57 -0
- package/esm/utils/json-schema.d.mts.map +1 -0
- package/esm/utils/json-schema.mjs +89 -0
- package/esm/utils/json-schema.mjs.map +1 -0
- package/esm/utils/prepare-attachment-part.d.mts +36 -0
- package/esm/utils/prepare-attachment-part.d.mts.map +1 -0
- package/esm/utils/prepare-attachment-part.mjs +140 -0
- package/esm/utils/prepare-attachment-part.mjs.map +1 -0
- package/esm/utils/resolve-attachment.d.mts +33 -0
- package/esm/utils/resolve-attachment.d.mts.map +1 -0
- package/esm/utils/resolve-attachment.mjs +73 -0
- package/esm/utils/resolve-attachment.mjs.map +1 -0
- package/esm/utils/safe-json-parse.d.mts +14 -0
- package/esm/utils/safe-json-parse.d.mts.map +1 -0
- package/esm/utils/safe-json-parse.mjs +22 -0
- package/esm/utils/safe-json-parse.mjs.map +1 -0
- package/esm/utils/stamp-report-lineage.d.mts +61 -0
- package/esm/utils/stamp-report-lineage.d.mts.map +1 -0
- package/esm/utils/stamp-report-lineage.mjs +47 -0
- package/esm/utils/stamp-report-lineage.mjs.map +1 -0
- package/esm/utils/token-count.d.mts +15 -0
- package/esm/utils/token-count.d.mts.map +1 -0
- package/esm/utils/token-count.mjs +18 -0
- package/esm/utils/token-count.mjs.map +1 -0
- package/esm/workflow/as-tool.mjs +55 -0
- package/esm/workflow/as-tool.mjs.map +1 -0
- package/esm/workflow/cancellation.mjs +44 -0
- package/esm/workflow/cancellation.mjs.map +1 -0
- package/esm/workflow/emitter.mjs +40 -0
- package/esm/workflow/emitter.mjs.map +1 -0
- package/esm/workflow/engine.mjs +362 -0
- package/esm/workflow/engine.mjs.map +1 -0
- package/esm/workflow/index.d.mts +3 -0
- package/esm/workflow/index.mjs +5 -0
- package/esm/workflow/retry.mjs +32 -0
- package/esm/workflow/retry.mjs.map +1 -0
- package/esm/workflow/router.mjs +56 -0
- package/esm/workflow/router.mjs.map +1 -0
- package/esm/workflow/run-scoped-emitter.mjs +32 -0
- package/esm/workflow/run-scoped-emitter.mjs.map +1 -0
- package/esm/workflow/signature.d.mts +7 -0
- package/esm/workflow/signature.d.mts.map +1 -0
- package/esm/workflow/signature.mjs +34 -0
- package/esm/workflow/signature.mjs.map +1 -0
- package/esm/workflow/snapshot.mjs +71 -0
- package/esm/workflow/snapshot.mjs.map +1 -0
- package/esm/workflow/state.mjs +37 -0
- package/esm/workflow/state.mjs.map +1 -0
- package/esm/workflow/step-runner.mjs +371 -0
- package/esm/workflow/step-runner.mjs.map +1 -0
- package/esm/workflow/step.d.mts +19 -0
- package/esm/workflow/step.d.mts.map +1 -0
- package/esm/workflow/step.mjs +37 -0
- package/esm/workflow/step.mjs.map +1 -0
- package/esm/workflow/workflow.d.mts +12 -0
- package/esm/workflow/workflow.d.mts.map +1 -0
- package/esm/workflow/workflow.mjs +116 -0
- package/esm/workflow/workflow.mjs.map +1 -0
- package/llms-full.txt +2293 -0
- package/llms.txt +20 -0
- package/package.json +37 -0
- package/skills/README.md +53 -0
- package/skills/ai-basics/SKILL.md +95 -0
- package/skills/attach-ai-middleware/SKILL.md +184 -0
- package/skills/define-ai-tool/SKILL.md +186 -0
- package/skills/embed-text/SKILL.md +105 -0
- package/skills/handle-ai-errors/SKILL.md +194 -0
- package/skills/log-ai-calls/SKILL.md +141 -0
- package/skills/persist-ai-data/SKILL.md +207 -0
- package/skills/pick-ai-provider/SKILL.md +164 -0
- package/skills/run-ai-agent/SKILL.md +267 -0
- package/skills/run-ai-workflow/SKILL.md +258 -0
- package/skills/run-supervisor/SKILL.md +309 -0
- package/skills/write-system-prompt/SKILL.md +129 -0
|
@@ -0,0 +1,1868 @@
|
|
|
1
|
+
import { AIError } from "../errors/ai-error.mjs";
|
|
2
|
+
import { SupervisorFailedError } from "../errors/supervisor-failed-error.mjs";
|
|
3
|
+
import { MaxIterationsError } from "../errors/max-iterations-error.mjs";
|
|
4
|
+
import { SchemaValidationError } from "../errors/schema-validation-error.mjs";
|
|
5
|
+
import { SupervisorCancelledError } from "../errors/supervisor-cancelled-error.mjs";
|
|
6
|
+
import "../errors/index.mjs";
|
|
7
|
+
import { stampReportLineage } from "../utils/stamp-report-lineage.mjs";
|
|
8
|
+
import "../utils/index.mjs";
|
|
9
|
+
import { isAgentResult, isWorkflowResult } from "./entries.mjs";
|
|
10
|
+
import { END } from "../contracts/end.type.mjs";
|
|
11
|
+
import { createCancelledError } from "./cancellation.mjs";
|
|
12
|
+
import { decide } from "./decide.mjs";
|
|
13
|
+
import { persistSupervisorSnapshot } from "./snapshot.mjs";
|
|
14
|
+
import { log } from "@warlock.js/logger";
|
|
15
|
+
|
|
16
|
+
//#region ../../@warlock.js/ai/src/supervisor/execution.ts
|
|
17
|
+
const DEFAULT_MAX_ITERATIONS = 10;
|
|
18
|
+
const LOG_MODULE_BASE = "ai.supervisor";
|
|
19
|
+
/**
|
|
20
|
+
* Per-call driver that owns the full lifecycle of one supervisor run.
|
|
21
|
+
*
|
|
22
|
+
* **Role.** Short-lived state container and phase orchestrator —
|
|
23
|
+
* mirrors `agent/Execution` and `workflow/runWorkflow`, one level up.
|
|
24
|
+
*
|
|
25
|
+
* **Responsibility.**
|
|
26
|
+
* - Owns: the iteration loop, per-iteration dispatch (single or
|
|
27
|
+
* fan-out), evaluate scheduling, usage aggregation across router +
|
|
28
|
+
* every branch + evaluate, snapshot collection, event emission
|
|
29
|
+
* through all three tiers, KV-store checkpointing, final result
|
|
30
|
+
* assembly (state validation against the output schema → typed data).
|
|
31
|
+
* - Does NOT own: how child agents produce responses (delegated via
|
|
32
|
+
* `agent.execute` / `workflow.execute`), the routing decision
|
|
33
|
+
* itself (delegated to `decide.ts`), snapshot persistence mechanics
|
|
34
|
+
* (delegated to `snapshot.ts`), the stream queue plumbing
|
|
35
|
+
* (delegated to `supervisor-stream.ts`).
|
|
36
|
+
*
|
|
37
|
+
* `execute()` never throws — every unexpected failure funnels into
|
|
38
|
+
* `this.error` and is returned on `result.error` with an appropriate
|
|
39
|
+
* `SupervisorFailedError` / `MaxIterationsError` / `SupervisorRoutingError`
|
|
40
|
+
* / `SupervisorCancelledError`.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* // Inside supervisor.execute() — never constructed by user code directly:
|
|
44
|
+
* return new SupervisorExecution(params).run();
|
|
45
|
+
*/
|
|
46
|
+
var SupervisorExecution = class {
|
|
47
|
+
constructor(params) {
|
|
48
|
+
this.logger = log;
|
|
49
|
+
this.snapshots = [];
|
|
50
|
+
this.childReports = [];
|
|
51
|
+
this.usage = {
|
|
52
|
+
input: 0,
|
|
53
|
+
output: 0,
|
|
54
|
+
total: 0
|
|
55
|
+
};
|
|
56
|
+
this.startPerf = performance.now();
|
|
57
|
+
this.iteration = 0;
|
|
58
|
+
this.terminatedBy = "error";
|
|
59
|
+
this.status = "failed";
|
|
60
|
+
this.lastDispatchIntents = [];
|
|
61
|
+
this.state = {};
|
|
62
|
+
this.currentArtifacts = {};
|
|
63
|
+
this.capturedIterationArtifacts = Object.freeze({});
|
|
64
|
+
this.classifierHalted = false;
|
|
65
|
+
this.config = params.config;
|
|
66
|
+
this.entries = params.entries;
|
|
67
|
+
this.signature = params.signature;
|
|
68
|
+
this.emitter = params.emitter;
|
|
69
|
+
this.input = params.input;
|
|
70
|
+
this.runId = params.runId;
|
|
71
|
+
this.options = params.options;
|
|
72
|
+
this.streamController = params.streamController;
|
|
73
|
+
this.resumeFrom = params.resumeFrom;
|
|
74
|
+
this.maxIterations = params.config.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
75
|
+
this.logModule = `${LOG_MODULE_BASE}.${params.config.name}`;
|
|
76
|
+
this.context = Object.freeze({ ...params.options?.context ?? {} });
|
|
77
|
+
this.history = Object.freeze([...params.options?.history ?? params.config.history ?? []]);
|
|
78
|
+
if (typeof params.config.goal === "string") this.goal = params.config.goal;
|
|
79
|
+
else if (params.config.goal) this.goal = params.config.goal.resolve();
|
|
80
|
+
else this.goal = void 0;
|
|
81
|
+
if (params.resumeFrom) {
|
|
82
|
+
this.snapshots.push(...params.resumeFrom.snapshots);
|
|
83
|
+
this.iteration = params.resumeFrom.iteration + 1;
|
|
84
|
+
this.startedAtIso = params.resumeFrom.startedAt;
|
|
85
|
+
const lastSnapshot = params.resumeFrom.snapshots[params.resumeFrom.snapshots.length - 1];
|
|
86
|
+
this.state = { ...lastSnapshot?.state ?? params.config.state ?? {} };
|
|
87
|
+
} else {
|
|
88
|
+
this.startedAtIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
89
|
+
this.state = { ...params.config.state ?? {} };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Resolve the history slice forwarded to a child execution (router /
|
|
94
|
+
* dispatched agent / ack). Precedence:
|
|
95
|
+
*
|
|
96
|
+
* 1. Per-entry `history` callback — full override; whatever it
|
|
97
|
+
* returns goes through (after defensive copy).
|
|
98
|
+
* 2. `SupervisorConfig.historyWindow.<role>` — last-N slice of the
|
|
99
|
+
* caller-supplied history.
|
|
100
|
+
* 3. Default — full history for `router`/`agents`, empty for `ack`
|
|
101
|
+
* (receptionists rarely benefit from scroll-back).
|
|
102
|
+
*
|
|
103
|
+
* Always returns a fresh `Message[]` (the agent layer
|
|
104
|
+
* mutates by reference internally, e.g. via `messages.push(...)`).
|
|
105
|
+
*/
|
|
106
|
+
resolveHistoryFor(role, routeContext, entrySlicer) {
|
|
107
|
+
if (entrySlicer) {
|
|
108
|
+
const sliced = entrySlicer(routeContext);
|
|
109
|
+
return sliced ? [...sliced] : [];
|
|
110
|
+
}
|
|
111
|
+
const window = this.config.historyWindow?.[role];
|
|
112
|
+
if (role === "ack") {
|
|
113
|
+
if (window === void 0 || window <= 0) return [];
|
|
114
|
+
return this.history.slice(-window);
|
|
115
|
+
}
|
|
116
|
+
if (window === void 0 || window < 0) return [...this.history];
|
|
117
|
+
if (window === 0) return [];
|
|
118
|
+
return this.history.slice(-window);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Apply only the global `historyWindow.agents` slice — used by the
|
|
122
|
+
* recursive `ctx.intents.X.execute()` re-entry path where no
|
|
123
|
+
* `RouteContext` is available to feed the per-entry slicer.
|
|
124
|
+
*/
|
|
125
|
+
applyAgentsWindow() {
|
|
126
|
+
const window = this.config.historyWindow?.agents;
|
|
127
|
+
if (window === void 0 || window < 0) return [...this.history];
|
|
128
|
+
if (window === 0) return [];
|
|
129
|
+
return this.history.slice(-window);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Entry point. Drives the iteration loop, finalizes the result, and
|
|
133
|
+
* emits terminal events. Returns the uniform
|
|
134
|
+
* `{ data, report, usage, error }` shape and closes the stream if
|
|
135
|
+
* one was attached. Never throws.
|
|
136
|
+
*/
|
|
137
|
+
async run() {
|
|
138
|
+
this.emit("supervisor.starting", {
|
|
139
|
+
supervisorName: this.config.name,
|
|
140
|
+
input: this.input
|
|
141
|
+
});
|
|
142
|
+
this.logger.info(this.logModule, "starting", "supervisor starting", {
|
|
143
|
+
runId: this.runId,
|
|
144
|
+
maxIterations: this.maxIterations
|
|
145
|
+
});
|
|
146
|
+
try {
|
|
147
|
+
await this.runIterationLoop();
|
|
148
|
+
} catch (thrown) {
|
|
149
|
+
this.error = toAIError(thrown);
|
|
150
|
+
this.status = this.error instanceof SupervisorCancelledError ? "cancelled" : "failed";
|
|
151
|
+
this.terminatedBy = this.error instanceof SupervisorCancelledError ? "cancelled" : "error";
|
|
152
|
+
if (this.error instanceof SupervisorCancelledError) this.cancelledAtIso = this.error.cancelledAt;
|
|
153
|
+
if (this.error instanceof MaxIterationsError) {
|
|
154
|
+
this.status = "max-iterations";
|
|
155
|
+
this.terminatedBy = "max-iterations";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const result = await this.finalize();
|
|
159
|
+
if (result.error) if (this.status === "cancelled") this.emit("supervisor.cancelled", {
|
|
160
|
+
cancelledAt: this.cancelledAtIso ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
161
|
+
reason: result.error.reason
|
|
162
|
+
});
|
|
163
|
+
else this.emit("supervisor.error", { error: result.error });
|
|
164
|
+
this.emit("supervisor.completed", { result });
|
|
165
|
+
this.streamController?.end(result);
|
|
166
|
+
this.logger.info(this.logModule, "completed", "supervisor completed", {
|
|
167
|
+
runId: this.runId,
|
|
168
|
+
status: this.status,
|
|
169
|
+
iterations: this.snapshots.length,
|
|
170
|
+
duration: performance.now() - this.startPerf
|
|
171
|
+
});
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Drive the iteration loop until a terminal condition fires:
|
|
176
|
+
* `END` / `satisfied:true` / `maxIterations` / signal abort /
|
|
177
|
+
* routing error. Between-iteration cancellation is guaranteed —
|
|
178
|
+
* the signal is checked before every iteration starts.
|
|
179
|
+
*/
|
|
180
|
+
async runIterationLoop() {
|
|
181
|
+
while (this.iteration < this.maxIterations) {
|
|
182
|
+
this.throwIfCancelled();
|
|
183
|
+
if (!await this.runIteration()) return;
|
|
184
|
+
this.iteration += 1;
|
|
185
|
+
}
|
|
186
|
+
throw new MaxIterationsError(`supervisor "${this.config.name}" exceeded maxIterations=${this.maxIterations}`, { maxIterations: this.maxIterations });
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Run one iteration end-to-end: decide → dispatch → evaluate →
|
|
190
|
+
* snapshot. Returns `true` when the loop should continue to the
|
|
191
|
+
* next iteration, `false` when this iteration terminated the run
|
|
192
|
+
* (success or satisfied-verdict). Failures throw — the loop's
|
|
193
|
+
* outer catch converts them into typed errors on the result.
|
|
194
|
+
*/
|
|
195
|
+
async runIteration() {
|
|
196
|
+
const iterationStartedAt = /* @__PURE__ */ new Date();
|
|
197
|
+
const iterationStart = performance.now();
|
|
198
|
+
const iterationUsage = {
|
|
199
|
+
input: 0,
|
|
200
|
+
output: 0,
|
|
201
|
+
total: 0
|
|
202
|
+
};
|
|
203
|
+
this.capturedIterationArtifacts = Object.freeze({});
|
|
204
|
+
this.emit("supervisor.iteration.starting", { iteration: this.iteration });
|
|
205
|
+
const ackPromise = this.iteration === 0 && !this.resumeFrom && this.config.ack ? this.runAck() : void 0;
|
|
206
|
+
if (this.iteration === 0 && !this.resumeFrom && this.config.classifier) {
|
|
207
|
+
await this.runClassifier();
|
|
208
|
+
if (this.classifierHalted) {
|
|
209
|
+
await this.settleAck(ackPromise, iterationUsage);
|
|
210
|
+
this.terminatedBy = "classifier";
|
|
211
|
+
this.status = "completed";
|
|
212
|
+
await this.recordTerminalDecisionSnapshot({
|
|
213
|
+
kind: "end",
|
|
214
|
+
source: "classifier",
|
|
215
|
+
raw: END,
|
|
216
|
+
durationMs: 0
|
|
217
|
+
}, iterationStartedAt, iterationStart, iterationUsage);
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const decision = await this.decideDispatch();
|
|
222
|
+
this.aggregateUsage(iterationUsage, decision.usage);
|
|
223
|
+
if (decision.kind === "end") {
|
|
224
|
+
await this.settleAck(ackPromise, iterationUsage);
|
|
225
|
+
this.terminatedBy = decision.source === "route" ? "route" : "router";
|
|
226
|
+
this.status = "completed";
|
|
227
|
+
await this.recordTerminalDecisionSnapshot(decision, iterationStartedAt, iterationStart, iterationUsage);
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
const branchSnapshots = await this.dispatchBranches(decision);
|
|
231
|
+
for (const snapshot of branchSnapshots) this.aggregateUsage(iterationUsage, snapshot.usage);
|
|
232
|
+
await this.settleAck(ackPromise, iterationUsage);
|
|
233
|
+
this.mergeBranchesIntoState(decision.intents, branchSnapshots);
|
|
234
|
+
await this.mergeArtifactsIntoState();
|
|
235
|
+
this.lastDispatchIntents = decision.intents;
|
|
236
|
+
const evaluateVerdict = await this.runEvaluate(branchSnapshots);
|
|
237
|
+
if (evaluateVerdict !== void 0 && evaluateVerdict !== null) this.emit("supervisor.evaluate.verdict", {
|
|
238
|
+
iteration: this.iteration,
|
|
239
|
+
verdict: evaluateVerdict
|
|
240
|
+
});
|
|
241
|
+
const iterationEndedAt = /* @__PURE__ */ new Date();
|
|
242
|
+
const duration = performance.now() - iterationStart;
|
|
243
|
+
const snapshot = Object.freeze({
|
|
244
|
+
iteration: this.iteration,
|
|
245
|
+
result: indexByIntent(branchSnapshots),
|
|
246
|
+
decision: {
|
|
247
|
+
source: decision.source,
|
|
248
|
+
next: decision.raw,
|
|
249
|
+
reasoning: decision.reasoning,
|
|
250
|
+
durationMs: decision.durationMs
|
|
251
|
+
},
|
|
252
|
+
evaluateVerdict,
|
|
253
|
+
state: { ...this.state },
|
|
254
|
+
artifacts: this.capturedIterationArtifacts,
|
|
255
|
+
startedAt: iterationStartedAt.toISOString(),
|
|
256
|
+
endedAt: iterationEndedAt.toISOString(),
|
|
257
|
+
duration,
|
|
258
|
+
usage: iterationUsage
|
|
259
|
+
});
|
|
260
|
+
this.snapshots.push(snapshot);
|
|
261
|
+
this.emit("supervisor.iteration.completed", {
|
|
262
|
+
iteration: this.iteration,
|
|
263
|
+
snapshot
|
|
264
|
+
});
|
|
265
|
+
await this.checkpoint("running");
|
|
266
|
+
if (evaluateVerdict?.satisfied) {
|
|
267
|
+
this.terminatedBy = "evaluate";
|
|
268
|
+
this.status = "completed";
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
this.carriedFeedback = evaluateVerdict;
|
|
272
|
+
if (!(evaluateVerdict?.reassignTo !== void 0 && normalizeReassign(evaluateVerdict.reassignTo).length > 0)) {
|
|
273
|
+
const collected = this.collectIntentNext(decision.intents, branchSnapshots);
|
|
274
|
+
if (collected?.kind === "end") {
|
|
275
|
+
this.terminatedBy = "route";
|
|
276
|
+
this.status = "completed";
|
|
277
|
+
this.carriedNextDispatch = void 0;
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
if (collected?.kind === "dispatch") this.carriedNextDispatch = { intents: collected.intents };
|
|
281
|
+
}
|
|
282
|
+
if (this.iteration === 0 && this.config.classifier && !this.config.router && !this.config.route && !this.carriedNextDispatch) {
|
|
283
|
+
this.terminatedBy = "classifier";
|
|
284
|
+
this.status = "completed";
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Resolve the dispatch decision for this iteration — defers to
|
|
291
|
+
* `decide.ts`. When `carriedFeedback.reassignTo` is set the
|
|
292
|
+
* supervisor overrides the router/route decision with an
|
|
293
|
+
* evaluator-forced dispatch (design §2 — "Evaluate can override
|
|
294
|
+
* router").
|
|
295
|
+
*/
|
|
296
|
+
async decideDispatch() {
|
|
297
|
+
if (this.config.router) this.emit("supervisor.router.deciding", { iteration: this.iteration });
|
|
298
|
+
const reassignTo = normalizeReassign(this.carriedFeedback?.reassignTo);
|
|
299
|
+
if (reassignTo.length > 0) {
|
|
300
|
+
this.carriedNextDispatch = void 0;
|
|
301
|
+
for (const intent of reassignTo) if (!this.entries.has(intent)) throw new SupervisorFailedError(`evaluate.reassignTo targeted unknown agent "${intent}"`, { context: { available: [...this.entries.keys()] } });
|
|
302
|
+
const decision = {
|
|
303
|
+
kind: "dispatch",
|
|
304
|
+
intents: reassignTo,
|
|
305
|
+
source: "route",
|
|
306
|
+
raw: reassignTo.length === 1 ? reassignTo[0] : reassignTo,
|
|
307
|
+
durationMs: 0
|
|
308
|
+
};
|
|
309
|
+
this.emit("supervisor.router.decided", {
|
|
310
|
+
iteration: this.iteration,
|
|
311
|
+
next: decision.raw,
|
|
312
|
+
reasoning: this.carriedFeedback?.feedback,
|
|
313
|
+
durationMs: 0
|
|
314
|
+
});
|
|
315
|
+
return decision;
|
|
316
|
+
}
|
|
317
|
+
if (this.carriedClassifierDispatch) {
|
|
318
|
+
const carried = this.carriedClassifierDispatch;
|
|
319
|
+
this.carriedClassifierDispatch = void 0;
|
|
320
|
+
const decision = {
|
|
321
|
+
kind: "dispatch",
|
|
322
|
+
intents: [carried.intent],
|
|
323
|
+
source: "classifier",
|
|
324
|
+
raw: carried.intent,
|
|
325
|
+
durationMs: 0
|
|
326
|
+
};
|
|
327
|
+
this.emit("supervisor.router.decided", {
|
|
328
|
+
iteration: this.iteration,
|
|
329
|
+
next: decision.raw,
|
|
330
|
+
reasoning: this.classifierSnapshot?.reasoning,
|
|
331
|
+
durationMs: 0
|
|
332
|
+
});
|
|
333
|
+
return decision;
|
|
334
|
+
}
|
|
335
|
+
if (this.carriedNextDispatch) {
|
|
336
|
+
const carried = this.carriedNextDispatch;
|
|
337
|
+
this.carriedNextDispatch = void 0;
|
|
338
|
+
const decision = {
|
|
339
|
+
kind: "dispatch",
|
|
340
|
+
intents: carried.intents,
|
|
341
|
+
source: "route",
|
|
342
|
+
raw: carried.intents.length === 1 ? carried.intents[0] : carried.intents,
|
|
343
|
+
durationMs: 0
|
|
344
|
+
};
|
|
345
|
+
this.emit("supervisor.router.decided", {
|
|
346
|
+
iteration: this.iteration,
|
|
347
|
+
next: decision.raw,
|
|
348
|
+
reasoning: void 0,
|
|
349
|
+
durationMs: 0
|
|
350
|
+
});
|
|
351
|
+
return decision;
|
|
352
|
+
}
|
|
353
|
+
const decision = await decide({
|
|
354
|
+
config: this.config,
|
|
355
|
+
entries: this.entries,
|
|
356
|
+
iteration: this.iteration,
|
|
357
|
+
maxIterations: this.maxIterations,
|
|
358
|
+
iterations: this.snapshots,
|
|
359
|
+
input: this.input,
|
|
360
|
+
state: this.state,
|
|
361
|
+
context: this.context,
|
|
362
|
+
history: this.history,
|
|
363
|
+
goal: this.goal,
|
|
364
|
+
evaluateFeedback: this.carriedFeedback,
|
|
365
|
+
classifier: this.classifierSnapshot,
|
|
366
|
+
signal: this.options?.signal,
|
|
367
|
+
useInitialAgent: this.iteration === 0 && !this.resumeFrom
|
|
368
|
+
});
|
|
369
|
+
if (decision.routerReport) this.childReports.push(decision.routerReport);
|
|
370
|
+
this.emit("supervisor.router.decided", {
|
|
371
|
+
iteration: this.iteration,
|
|
372
|
+
next: decision.raw,
|
|
373
|
+
reasoning: decision.reasoning,
|
|
374
|
+
durationMs: decision.durationMs
|
|
375
|
+
});
|
|
376
|
+
return decision;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Dispatch every intent named by the decision in parallel. Per-
|
|
380
|
+
* branch errors don't abort siblings — they're recorded on the
|
|
381
|
+
* branch snapshot and let evaluate (or default termination logic)
|
|
382
|
+
* decide the response.
|
|
383
|
+
*/
|
|
384
|
+
async dispatchBranches(decision) {
|
|
385
|
+
return await Promise.all(decision.intents.map((intent) => this.dispatchOne(intent)));
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Execute a single branch — resolve the input, invoke the
|
|
389
|
+
* agent / workflow / callback, apply the per-intent `output`
|
|
390
|
+
* transformer, and produce an immutable `AgentBranchSnapshot`.
|
|
391
|
+
*/
|
|
392
|
+
async dispatchOne(intent) {
|
|
393
|
+
const entry = this.entries.get(intent);
|
|
394
|
+
if (entry.type === "callback") return this.dispatchCallback(entry);
|
|
395
|
+
const routeContext = {
|
|
396
|
+
iteration: this.iteration,
|
|
397
|
+
input: this.input,
|
|
398
|
+
state: this.state,
|
|
399
|
+
iterations: this.snapshots,
|
|
400
|
+
feedback: typeof this.carriedFeedback?.feedback === "string" ? this.carriedFeedback.feedback : void 0,
|
|
401
|
+
evaluateFeedback: this.carriedFeedback,
|
|
402
|
+
context: this.context,
|
|
403
|
+
history: this.history,
|
|
404
|
+
goal: this.goal,
|
|
405
|
+
classifier: this.classifierSnapshot
|
|
406
|
+
};
|
|
407
|
+
const resolvedInput = this.resolveBranchInput(entry, routeContext);
|
|
408
|
+
const dispatchCtxForPlaceholders = this.seedDispatchContext(intent, resolvedInput, new Set([intent]), []);
|
|
409
|
+
const placeholders = entry.placeholders ? entry.placeholders(dispatchCtxForPlaceholders) : void 0;
|
|
410
|
+
this.emit("supervisor.agent.starting", {
|
|
411
|
+
iteration: this.iteration,
|
|
412
|
+
intent,
|
|
413
|
+
input: resolvedInput
|
|
414
|
+
});
|
|
415
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
416
|
+
const startPerf = performance.now();
|
|
417
|
+
let rawResult;
|
|
418
|
+
let branchError;
|
|
419
|
+
let branchUsage = {
|
|
420
|
+
input: 0,
|
|
421
|
+
output: 0,
|
|
422
|
+
total: 0
|
|
423
|
+
};
|
|
424
|
+
try {
|
|
425
|
+
rawResult = await this.invokeUnit(entry, resolvedInput, placeholders, routeContext);
|
|
426
|
+
if (rawResult.error) branchError = rawResult.error;
|
|
427
|
+
branchUsage = rawResult.usage;
|
|
428
|
+
if (rawResult.report) this.childReports.push(rawResult.report);
|
|
429
|
+
} catch (thrown) {
|
|
430
|
+
branchError = toAIError(thrown);
|
|
431
|
+
}
|
|
432
|
+
const sliceOutcome = await this.applyOutputSchema(entry, rawResult);
|
|
433
|
+
const transformedOutput = sliceOutcome.value;
|
|
434
|
+
if (sliceOutcome.error && !branchError) branchError = sliceOutcome.error;
|
|
435
|
+
const endedAt = /* @__PURE__ */ new Date();
|
|
436
|
+
const duration = performance.now() - startPerf;
|
|
437
|
+
const snapshot = Object.freeze({
|
|
438
|
+
intent,
|
|
439
|
+
input: resolvedInput,
|
|
440
|
+
output: transformedOutput,
|
|
441
|
+
usage: branchUsage,
|
|
442
|
+
startedAt: startedAt.toISOString(),
|
|
443
|
+
endedAt: endedAt.toISOString(),
|
|
444
|
+
duration,
|
|
445
|
+
error: branchError
|
|
446
|
+
});
|
|
447
|
+
if (branchError) this.emit("supervisor.agent.failed", {
|
|
448
|
+
iteration: this.iteration,
|
|
449
|
+
intent,
|
|
450
|
+
error: branchError
|
|
451
|
+
});
|
|
452
|
+
else this.emit("supervisor.agent.completed", {
|
|
453
|
+
iteration: this.iteration,
|
|
454
|
+
intent,
|
|
455
|
+
output: transformedOutput,
|
|
456
|
+
usage: branchUsage,
|
|
457
|
+
duration
|
|
458
|
+
});
|
|
459
|
+
return snapshot;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Dispatch a callback intent as a top-level branch — produces an
|
|
463
|
+
* `AgentBranchSnapshot` and pushes the synthesized callback report
|
|
464
|
+
* onto the supervisor's recursive children. Delegates the actual
|
|
465
|
+
* callback invocation to {@link runCallback} so nested
|
|
466
|
+
* `ctx.intents.X.execute()` calls can reuse the same machinery.
|
|
467
|
+
*
|
|
468
|
+
* Each branch dispatch starts with a fresh per-branch call stack —
|
|
469
|
+
* sibling fan-out branches don't share cycle-detection state, so
|
|
470
|
+
* branch A and branch B both invoking the same intent isn't a
|
|
471
|
+
* cycle. The branch's own intent name is seeded onto the stack so
|
|
472
|
+
* a callback that re-enters itself via `ctx.intents.X.execute()` trips
|
|
473
|
+
* cycle detection on the first recursion.
|
|
474
|
+
*/
|
|
475
|
+
async dispatchCallback(entry) {
|
|
476
|
+
const intent = entry.intent;
|
|
477
|
+
const callStack = new Set([intent]);
|
|
478
|
+
const callbackInput = entry.input ? entry.input(this.seedDispatchContext(intent, this.input, callStack, [])) : this.input;
|
|
479
|
+
const inputForSnapshot = typeof callbackInput === "string" ? callbackInput : safeStringify(callbackInput);
|
|
480
|
+
this.emit("supervisor.agent.starting", {
|
|
481
|
+
iteration: this.iteration,
|
|
482
|
+
intent,
|
|
483
|
+
input: inputForSnapshot
|
|
484
|
+
});
|
|
485
|
+
const outcome = await this.runCallback(entry, callbackInput, callStack, this.childReports);
|
|
486
|
+
const snapshot = Object.freeze({
|
|
487
|
+
intent,
|
|
488
|
+
input: inputForSnapshot,
|
|
489
|
+
output: outcome.output,
|
|
490
|
+
usage: outcome.report.usage,
|
|
491
|
+
startedAt: outcome.report.startedAt,
|
|
492
|
+
endedAt: outcome.report.endedAt,
|
|
493
|
+
duration: outcome.report.duration,
|
|
494
|
+
error: outcome.error
|
|
495
|
+
});
|
|
496
|
+
if (outcome.error) this.emit("supervisor.agent.failed", {
|
|
497
|
+
iteration: this.iteration,
|
|
498
|
+
intent,
|
|
499
|
+
error: outcome.error
|
|
500
|
+
});
|
|
501
|
+
else this.emit("supervisor.agent.completed", {
|
|
502
|
+
iteration: this.iteration,
|
|
503
|
+
intent,
|
|
504
|
+
output: outcome.output,
|
|
505
|
+
usage: outcome.report.usage,
|
|
506
|
+
duration: outcome.report.duration
|
|
507
|
+
});
|
|
508
|
+
return snapshot;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Run a callback intent and produce its leaf report + final
|
|
512
|
+
* output. Used both for top-level branch dispatch (via
|
|
513
|
+
* {@link dispatchCallback}) and for nested `dispatch.byName`
|
|
514
|
+
* recursion. The synthesized report is appended to `reportSink`,
|
|
515
|
+
* which is either `this.childReports` (top-level) or the calling
|
|
516
|
+
* callback's own `children[]` (nested) — that's what gives the
|
|
517
|
+
* unified report tree its compositional shape.
|
|
518
|
+
*
|
|
519
|
+
* Usage on the report rolls up children's usage; the callback
|
|
520
|
+
* itself contributes zero (it's dev code, no token spend).
|
|
521
|
+
*/
|
|
522
|
+
async runCallback(entry, input, callStack, reportSink) {
|
|
523
|
+
const childReports = [];
|
|
524
|
+
const dispatchCtx = this.seedDispatchContext(entry.intent, input, callStack, childReports);
|
|
525
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
526
|
+
const startPerf = performance.now();
|
|
527
|
+
let rawOutput;
|
|
528
|
+
let error;
|
|
529
|
+
try {
|
|
530
|
+
rawOutput = await entry.callback(dispatchCtx);
|
|
531
|
+
} catch (thrown) {
|
|
532
|
+
error = thrown instanceof AIError ? thrown : new SupervisorFailedError(`callback intent "${entry.intent}" threw: ${thrown instanceof Error ? thrown.message : String(thrown)}`, { cause: thrown });
|
|
533
|
+
}
|
|
534
|
+
let transformedOutput = rawOutput;
|
|
535
|
+
if (!error && entry.output) {
|
|
536
|
+
const validation = await entry.output["~standard"].validate(rawOutput);
|
|
537
|
+
if (validation.issues) {
|
|
538
|
+
error = new SchemaValidationError(`intent "${entry.intent}" output failed validation: ${validation.issues.map((issue) => issue.message).join("; ")}`, { issues: validation.issues });
|
|
539
|
+
transformedOutput = void 0;
|
|
540
|
+
} else transformedOutput = validation.value;
|
|
541
|
+
}
|
|
542
|
+
const endedAt = /* @__PURE__ */ new Date();
|
|
543
|
+
const duration = performance.now() - startPerf;
|
|
544
|
+
const rolledUsage = aggregateChildUsage(childReports);
|
|
545
|
+
const report = {
|
|
546
|
+
runId: `${this.runId}.${entry.intent}`,
|
|
547
|
+
rootRunId: this.runId,
|
|
548
|
+
name: entry.intent,
|
|
549
|
+
type: "callback",
|
|
550
|
+
status: error ? "failed" : "completed",
|
|
551
|
+
startedAt: startedAt.toISOString(),
|
|
552
|
+
endedAt: endedAt.toISOString(),
|
|
553
|
+
duration,
|
|
554
|
+
usage: rolledUsage,
|
|
555
|
+
children: childReports
|
|
556
|
+
};
|
|
557
|
+
reportSink.push(report);
|
|
558
|
+
return {
|
|
559
|
+
output: transformedOutput,
|
|
560
|
+
error,
|
|
561
|
+
report
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Build a {@link DispatchContext} with a typed `intents` map of
|
|
566
|
+
* `IntentRunner` closures, each closing over the supplied call
|
|
567
|
+
* stack and report sink. Cycle detection uses the call stack —
|
|
568
|
+
* re-entering an intent already on it throws
|
|
569
|
+
* `SupervisorFailedError` with code `SUPERVISOR_DISPATCH_CYCLE`
|
|
570
|
+
* and the offending chain in the message.
|
|
571
|
+
*
|
|
572
|
+
* Replaces the Phase-3.3 `ctx.dispatch.byName` plumbing with
|
|
573
|
+
* property-access on a typed map (Q5/Q6) — autocomplete, no typo
|
|
574
|
+
* crashes, `.execute()` matches every other primitive's verb.
|
|
575
|
+
*/
|
|
576
|
+
seedDispatchContext(intent, input, callStack, reportSink) {
|
|
577
|
+
const intentsMap = {};
|
|
578
|
+
for (const target of this.entries.keys()) intentsMap[target] = {
|
|
579
|
+
execute: (override) => this.runIntent(target, override === void 0 ? input : override, callStack, reportSink),
|
|
580
|
+
stream: (override) => this.streamIntent(target, override === void 0 ? input : override, callStack, reportSink, intent)
|
|
581
|
+
};
|
|
582
|
+
return {
|
|
583
|
+
iteration: this.iteration,
|
|
584
|
+
intent,
|
|
585
|
+
input,
|
|
586
|
+
state: this.state,
|
|
587
|
+
result: {},
|
|
588
|
+
iterations: this.snapshots,
|
|
589
|
+
signal: this.options?.signal ?? new AbortController().signal,
|
|
590
|
+
intents: intentsMap,
|
|
591
|
+
context: this.context,
|
|
592
|
+
history: this.history,
|
|
593
|
+
goal: this.goal,
|
|
594
|
+
run: (executable, runInput, runOptions) => this.runInline(executable, runInput, runOptions, callStack, reportSink),
|
|
595
|
+
stream: (executable, runInput, runOptions) => this.streamInline(executable, runInput, runOptions, callStack, reportSink, intent),
|
|
596
|
+
classifier: this.classifierSnapshot
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Backing implementation for `ctx.intents.X.execute(input?)`.
|
|
601
|
+
* Looks up the named intent in the supervisor's registry, asserts
|
|
602
|
+
* the call wouldn't close a cycle, and runs the dispatchable
|
|
603
|
+
* through the same machinery a top-level branch would — except
|
|
604
|
+
* the resulting report nests under the calling callback's
|
|
605
|
+
* `children[]` rather than the supervisor's top-level child list,
|
|
606
|
+
* and only the final output is returned (no snapshot).
|
|
607
|
+
*/
|
|
608
|
+
async runIntent(target, callerInput, callStack, reportSink) {
|
|
609
|
+
if (callStack.has(target)) throw new SupervisorFailedError(`ctx.intents.${target}.execute: cycle detected (${[...callStack, target].join(" → ")})`, { context: { intent: target } }, "SUPERVISOR_DISPATCH_CYCLE");
|
|
610
|
+
const entry = this.entries.get(target);
|
|
611
|
+
if (!entry) throw new SupervisorFailedError(`ctx.intents.${target}.execute: unknown intent "${target}" — must be a key in the supervisor's \`intents\` map`, { context: { intent: target } });
|
|
612
|
+
callStack.add(target);
|
|
613
|
+
try {
|
|
614
|
+
if (entry.type === "callback") {
|
|
615
|
+
const { output, error } = await this.runCallback(entry, callerInput, callStack, reportSink);
|
|
616
|
+
if (error) throw error;
|
|
617
|
+
return output;
|
|
618
|
+
}
|
|
619
|
+
const inputString = typeof callerInput === "string" ? callerInput : safeStringify(callerInput);
|
|
620
|
+
if (entry.type === "agent") {
|
|
621
|
+
const reentryHistory = this.applyAgentsWindow();
|
|
622
|
+
const result = await entry.unit.execute(inputString, {
|
|
623
|
+
signal: this.options?.signal,
|
|
624
|
+
...reentryHistory.length > 0 ? { history: reentryHistory } : {}
|
|
625
|
+
});
|
|
626
|
+
if (result.report) reportSink.push(result.report);
|
|
627
|
+
if (result.error) throw result.error;
|
|
628
|
+
return result.data ?? result.text ?? void 0;
|
|
629
|
+
}
|
|
630
|
+
const result = await entry.unit.execute(inputString, { signal: this.options?.signal });
|
|
631
|
+
if (result.report) reportSink.push(result.report);
|
|
632
|
+
if (result.error) throw result.error;
|
|
633
|
+
return result.data;
|
|
634
|
+
} finally {
|
|
635
|
+
callStack.delete(target);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Backing implementation for `ctx.intents.X.stream(input?)` (Phase 6
|
|
640
|
+
* / decisions §36). Streaming sibling of {@link runIntent} — same
|
|
641
|
+
* cycle protection, same auto-merge of supervisor-level concerns,
|
|
642
|
+
* but routes through the unit's `.stream()` method when available
|
|
643
|
+
* and bubbles deltas as `supervisor.agent.streaming` under the
|
|
644
|
+
* **calling callback's** intent name (not the dispatched intent's).
|
|
645
|
+
*/
|
|
646
|
+
streamIntent(target, callerInput, callStack, reportSink, callerIntent) {
|
|
647
|
+
if (callStack.has(target)) throw new SupervisorFailedError(`ctx.intents.${target}.stream: cycle detected (${[...callStack, target].join(" → ")})`, { context: { intent: target } }, "SUPERVISOR_DISPATCH_CYCLE");
|
|
648
|
+
const entry = this.entries.get(target);
|
|
649
|
+
if (!entry) throw new SupervisorFailedError(`ctx.intents.${target}.stream: unknown intent "${target}" — must be a key in the supervisor's \`intents\` map`, { context: { intent: target } });
|
|
650
|
+
if (entry.type === "callback") throw new SupervisorFailedError(`ctx.intents.${target}.stream: callback intents are not streamable — use \`.execute(input?)\` instead`, { context: { intent: target } });
|
|
651
|
+
callStack.add(target);
|
|
652
|
+
const inputString = typeof callerInput === "string" ? callerInput : safeStringify(callerInput);
|
|
653
|
+
return this.streamSupervisedExecutable(entry.unit, inputString, void 0, callerIntent, reportSink, () => callStack.delete(target));
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Backing implementation for `ctx.run(executable, input, options?)`
|
|
657
|
+
* (Phase 6 / decisions §36). Runs an inline / un-registered
|
|
658
|
+
* executable under supervision: auto-merges `signal`, `toolCtx`,
|
|
659
|
+
* `history` defaults; nests the resulting report under the
|
|
660
|
+
* calling callback's `children[]`. Per-call options REPLACE auto-
|
|
661
|
+
* defaults — standard Warlock convention.
|
|
662
|
+
*
|
|
663
|
+
* Cycle protection by executable `name` matches the registered-
|
|
664
|
+
* intent path so a callback that recurses on the same agent trips
|
|
665
|
+
* the same error, regardless of whether the agent was looked up
|
|
666
|
+
* via `ctx.intents.X.execute()` or passed inline.
|
|
667
|
+
*/
|
|
668
|
+
async runInline(executable, input, options, callStack, reportSink) {
|
|
669
|
+
const name = executable.name;
|
|
670
|
+
if (callStack.has(name)) throw new SupervisorFailedError(`ctx.run("${name}"): cycle detected (${[...callStack, name].join(" → ")})`, { context: { intent: name } }, "SUPERVISOR_DISPATCH_CYCLE");
|
|
671
|
+
callStack.add(name);
|
|
672
|
+
try {
|
|
673
|
+
const merged = this.mergeInlineOptions(options);
|
|
674
|
+
const inputForExecutable = this.coerceInlineInput(executable, input);
|
|
675
|
+
const result = await executable.execute(inputForExecutable, merged);
|
|
676
|
+
if (result.report) reportSink.push(result.report);
|
|
677
|
+
return result;
|
|
678
|
+
} finally {
|
|
679
|
+
callStack.delete(name);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Backing implementation for `ctx.stream(executable, input, options?)`
|
|
684
|
+
* (Phase 6 / decisions §36). Streaming sibling of {@link runInline}.
|
|
685
|
+
* Routes through the executable's native `.stream()` method,
|
|
686
|
+
* subscribes to delta events, and bubbles them as
|
|
687
|
+
* `supervisor.agent.streaming` under the calling callback's intent
|
|
688
|
+
* name. The returned `StreamContract` is the executable's own —
|
|
689
|
+
* iteration and `.result` work identically.
|
|
690
|
+
*
|
|
691
|
+
* Cycle protection on entry mirrors {@link runInline}; release runs
|
|
692
|
+
* after `.result` settles so a same-callback recursion is caught
|
|
693
|
+
* regardless of which path closed the cycle.
|
|
694
|
+
*/
|
|
695
|
+
streamInline(executable, input, options, callStack, reportSink, callerIntent) {
|
|
696
|
+
const name = executable.name;
|
|
697
|
+
if (callStack.has(name)) throw new SupervisorFailedError(`ctx.stream("${name}"): cycle detected (${[...callStack, name].join(" → ")})`, { context: { intent: name } }, "SUPERVISOR_DISPATCH_CYCLE");
|
|
698
|
+
callStack.add(name);
|
|
699
|
+
return this.streamSupervisedExecutable(executable, this.coerceInlineInput(executable, input), options, callerIntent, reportSink, () => callStack.delete(name));
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Shared wiring for both `ctx.intents.X.stream()` and
|
|
703
|
+
* `ctx.stream(...)`. Subscribes to the executable's stream, re-
|
|
704
|
+
* emits deltas as `supervisor.agent.streaming` under the calling
|
|
705
|
+
* callback's intent name, and pushes the inner report onto the
|
|
706
|
+
* reportSink once `.result` settles. The returned StreamContract
|
|
707
|
+
* is the executable's own — the framework attaches handlers
|
|
708
|
+
* transparently via `.on(...)`.
|
|
709
|
+
*/
|
|
710
|
+
streamSupervisedExecutable(executable, input, options, callerIntent, reportSink, release) {
|
|
711
|
+
const merged = this.mergeInlineOptions(options);
|
|
712
|
+
const stream = executable.stream(input, merged);
|
|
713
|
+
stream.on({
|
|
714
|
+
"agent.trip.streaming": ({ delta }) => {
|
|
715
|
+
this.emit("supervisor.agent.streaming", {
|
|
716
|
+
iteration: this.iteration,
|
|
717
|
+
intent: callerIntent,
|
|
718
|
+
delta
|
|
719
|
+
});
|
|
720
|
+
},
|
|
721
|
+
"supervisor.agent.streaming": ({ delta }) => {
|
|
722
|
+
this.emit("supervisor.agent.streaming", {
|
|
723
|
+
iteration: this.iteration,
|
|
724
|
+
intent: callerIntent,
|
|
725
|
+
delta
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
stream.result.then((result) => {
|
|
730
|
+
if (result?.report) reportSink.push(result.report);
|
|
731
|
+
release();
|
|
732
|
+
}, () => release());
|
|
733
|
+
return stream;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Build the options object passed into an inline `.execute()` /
|
|
737
|
+
* `.stream()` call. Auto-merges supervisor-level defaults
|
|
738
|
+
* (`signal`, `toolCtx`, `history` window) under the caller's
|
|
739
|
+
* options. Per-call values REPLACE the auto-defaults — when the
|
|
740
|
+
* dev passes `signal: undefined` they explicitly opt out.
|
|
741
|
+
*/
|
|
742
|
+
mergeInlineOptions(options) {
|
|
743
|
+
const supplied = options ?? {};
|
|
744
|
+
const merged = { ...supplied };
|
|
745
|
+
if (!("signal" in supplied)) merged.signal = this.options?.signal;
|
|
746
|
+
if (!("toolCtx" in supplied)) merged.toolCtx = {
|
|
747
|
+
artifacts: this.currentArtifacts,
|
|
748
|
+
signal: this.options?.signal
|
|
749
|
+
};
|
|
750
|
+
if (!("history" in supplied)) {
|
|
751
|
+
const window = this.applyAgentsWindow();
|
|
752
|
+
if (window.length > 0) merged.history = window;
|
|
753
|
+
}
|
|
754
|
+
return merged;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Coerce an arbitrary inline input into the shape the underlying
|
|
758
|
+
* executable expects. Agents take `string`; workflows + supervisors
|
|
759
|
+
* take whatever they declared. We safe-stringify objects only when
|
|
760
|
+
* passing to an agent — workflow / supervisor calls hand the value
|
|
761
|
+
* through unchanged so structured inputs work.
|
|
762
|
+
*/
|
|
763
|
+
coerceInlineInput(executable, input) {
|
|
764
|
+
if (!("signature" in executable) && typeof executable.execute === "function" && !this.isSupervisor(executable) && typeof input !== "string") return safeStringify(input);
|
|
765
|
+
return input;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Heuristic detection of `SupervisorContract` — the contract carries
|
|
769
|
+
* a `signature` getter same as workflows, but supervisors expose
|
|
770
|
+
* `resume()` while workflows expose `resume(runId, options)` too.
|
|
771
|
+
* Cleanest distinguisher in the public surface: supervisors carry
|
|
772
|
+
* the `asTool` method name `as` … unfortunately so do workflows.
|
|
773
|
+
* Use the `streamableType` brand if we add one in v2; for now lean
|
|
774
|
+
* on a duck-typed check that's good enough for the ctx.run path
|
|
775
|
+
* (incorrect routing for workflows would still produce a runnable
|
|
776
|
+
* call — workflow.execute accepts the same args either way).
|
|
777
|
+
*/
|
|
778
|
+
isSupervisor(executable) {
|
|
779
|
+
return typeof executable.resume === "function" && typeof executable.signature === "string" && typeof executable.stream === "function";
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Invoke the underlying dispatchable unit. Agents and workflows
|
|
783
|
+
* both satisfy `ExecutableContract<string, …>` so the call shape
|
|
784
|
+
* is uniform; the `type` discriminator picks which options get
|
|
785
|
+
* threaded through (e.g. per-call stream event bubbling for
|
|
786
|
+
* agents, which we wire inline so child agent tokens surface as
|
|
787
|
+
* `supervisor.agent.streaming`).
|
|
788
|
+
*/
|
|
789
|
+
async invokeUnit(entry, input, placeholders, routeContext) {
|
|
790
|
+
const isStreaming = this.streamController !== void 0;
|
|
791
|
+
if (entry.type === "agent") {
|
|
792
|
+
const agent = entry.unit;
|
|
793
|
+
const handlers = { "agent.trip.streaming": ({ delta }) => {
|
|
794
|
+
this.emit("supervisor.agent.streaming", {
|
|
795
|
+
iteration: this.iteration,
|
|
796
|
+
intent: entry.intent,
|
|
797
|
+
delta
|
|
798
|
+
});
|
|
799
|
+
} };
|
|
800
|
+
const isStreamMode = entry.mode === "stream";
|
|
801
|
+
const resolvedHistory = this.resolveHistoryFor("agents", routeContext, entry.history);
|
|
802
|
+
const agentOptions = {
|
|
803
|
+
signal: this.options?.signal,
|
|
804
|
+
on: handlers,
|
|
805
|
+
...placeholders ? { placeholders } : {},
|
|
806
|
+
...entry.output && !isStreamMode ? { output: entry.output } : {},
|
|
807
|
+
...resolvedHistory.length > 0 ? { history: resolvedHistory } : {},
|
|
808
|
+
toolCtx: {
|
|
809
|
+
artifacts: this.currentArtifacts,
|
|
810
|
+
signal: this.options?.signal
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
if (isStreamMode || isStreaming) return agent.stream(input, agentOptions).result;
|
|
814
|
+
return agent.execute(input, agentOptions);
|
|
815
|
+
}
|
|
816
|
+
return entry.unit.execute(input, {
|
|
817
|
+
signal: this.options?.signal,
|
|
818
|
+
on: { "workflow.step.streaming": ({ delta }) => {
|
|
819
|
+
this.emit("supervisor.agent.streaming", {
|
|
820
|
+
iteration: this.iteration,
|
|
821
|
+
intent: entry.intent,
|
|
822
|
+
delta
|
|
823
|
+
});
|
|
824
|
+
} }
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Build the input string passed to a branch's child execution.
|
|
829
|
+
* Default: pass the supervisor's original `ctx.input` through
|
|
830
|
+
* unchanged. The per-intent `entry.input` override is the escape
|
|
831
|
+
* hatch for the rare case where the agent's user message itself
|
|
832
|
+
* must vary per intent.
|
|
833
|
+
*
|
|
834
|
+
* Q17 lock: dropped `composeAgentInput` + `defaultComposeAgentInput`.
|
|
835
|
+
* Their three jobs (carry original / prior outputs / feedback) all
|
|
836
|
+
* have cleaner homes in the new model — original is the input
|
|
837
|
+
* itself, prior outputs are state (Stage 4b), feedback is a
|
|
838
|
+
* router-only signal (Q18).
|
|
839
|
+
*/
|
|
840
|
+
resolveBranchInput(entry, ctx) {
|
|
841
|
+
const override = entry.input?.(ctx);
|
|
842
|
+
if (typeof override === "string") return override;
|
|
843
|
+
return typeof ctx.input === "string" ? ctx.input : safeStringify(ctx.input);
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Strip-merge the agent/workflow's raw output against the per-intent
|
|
847
|
+
* `output` schema (Q11/Q13). Returns the validated slice that:
|
|
848
|
+
*
|
|
849
|
+
* 1. Lands on `IterationSnapshot.result[intent].output` (so
|
|
850
|
+
* consumers see the same shape that hit state).
|
|
851
|
+
* 2. Shallow-merges into `this.state` (handled by the caller).
|
|
852
|
+
*
|
|
853
|
+
* When `entry.output` is omitted the agent's full `data` (or `text`
|
|
854
|
+
* fallback for unstructured agents) flows through unvalidated — but
|
|
855
|
+
* is NOT auto-merged into state. State contribution is opt-in via
|
|
856
|
+
* declaring the slice schema.
|
|
857
|
+
*
|
|
858
|
+
* Validation failure surfaces as a per-branch error on the
|
|
859
|
+
* snapshot; sibling branches still run.
|
|
860
|
+
*/
|
|
861
|
+
async applyOutputSchema(entry, raw) {
|
|
862
|
+
if (!raw) return { value: void 0 };
|
|
863
|
+
const sourceValue = isAgentResult(raw) ? raw.data ?? raw.text ?? void 0 : isWorkflowResult(raw) ? raw.data : void 0;
|
|
864
|
+
if (entry.type === "agent" && entry.mode === "stream") {
|
|
865
|
+
const text = typeof sourceValue === "string" ? sourceValue : "";
|
|
866
|
+
return { value: { [entry.streamTo]: text } };
|
|
867
|
+
}
|
|
868
|
+
if (!entry.output) return { value: sourceValue };
|
|
869
|
+
const validation = await entry.output["~standard"].validate(sourceValue);
|
|
870
|
+
if (validation.issues) return {
|
|
871
|
+
value: void 0,
|
|
872
|
+
error: new SchemaValidationError(`intent "${entry.intent}" output failed validation: ${validation.issues.map((issue) => issue.message).join("; ")}`, { issues: validation.issues })
|
|
873
|
+
};
|
|
874
|
+
return { value: validation.value };
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Fire the receptionist (`ack`) — runs in parallel with phase A on
|
|
878
|
+
* iteration 0 only. Accepts three shapes:
|
|
879
|
+
*
|
|
880
|
+
* - `AckEntry` — `{ agent, placeholders?, input?, output? }`. LLM
|
|
881
|
+
* form. Streams tokens via `supervisor.ack.streaming`; report
|
|
882
|
+
* node pushes onto `childReports[]`.
|
|
883
|
+
* - `AckRunEntry` — `{ run, output? }`. Pure-code callback. Settles
|
|
884
|
+
* without an LLM call. No streaming events; just `.completed`.
|
|
885
|
+
* - `AckCallback` — bare `(ctx) => slice` shorthand for the
|
|
886
|
+
* pure-code form when no schema is declared.
|
|
887
|
+
*
|
|
888
|
+
* Failures are recorded but never abort the run — the receptionist
|
|
889
|
+
* tripping doesn't stop the specialist from doing the actual job.
|
|
890
|
+
* The returned outcome is what `mergeAckIntoState` consumes.
|
|
891
|
+
*/
|
|
892
|
+
async runAck() {
|
|
893
|
+
const ack = this.config.ack;
|
|
894
|
+
if (!ack) return void 0;
|
|
895
|
+
const routeContext = {
|
|
896
|
+
iteration: this.iteration,
|
|
897
|
+
input: this.input,
|
|
898
|
+
state: this.state,
|
|
899
|
+
iterations: this.snapshots,
|
|
900
|
+
feedback: typeof this.carriedFeedback?.feedback === "string" ? this.carriedFeedback.feedback : void 0,
|
|
901
|
+
evaluateFeedback: this.carriedFeedback,
|
|
902
|
+
context: this.context,
|
|
903
|
+
history: this.history,
|
|
904
|
+
goal: this.goal,
|
|
905
|
+
classifier: this.classifierSnapshot
|
|
906
|
+
};
|
|
907
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
908
|
+
const startPerf = performance.now();
|
|
909
|
+
if (typeof ack === "function") return this.runAckCallback(ack, void 0, routeContext, startedAt, startPerf);
|
|
910
|
+
if ("run" in ack && typeof ack.run === "function") {
|
|
911
|
+
const runEntry = ack;
|
|
912
|
+
return this.runAckCallback(runEntry.run, runEntry.output, routeContext, startedAt, startPerf);
|
|
913
|
+
}
|
|
914
|
+
return this.runAckAgent(ack, routeContext, startedAt, startPerf);
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Pure-code receptionist path — invokes the callback, strip-validates
|
|
918
|
+
* the return value (when an `output` schema is declared), records the
|
|
919
|
+
* snapshot, emits `supervisor.ack.completed`, returns the outcome.
|
|
920
|
+
* No streaming events fire (callbacks settle synchronously from the
|
|
921
|
+
* supervisor's POV).
|
|
922
|
+
*/
|
|
923
|
+
async runAckCallback(run, output, routeContext, startedAt, startPerf) {
|
|
924
|
+
const usage = {
|
|
925
|
+
input: 0,
|
|
926
|
+
output: 0,
|
|
927
|
+
total: 0
|
|
928
|
+
};
|
|
929
|
+
let validatedOutput;
|
|
930
|
+
let ackError;
|
|
931
|
+
try {
|
|
932
|
+
const raw = await run(routeContext);
|
|
933
|
+
if (output) {
|
|
934
|
+
const validation = await output["~standard"].validate(raw);
|
|
935
|
+
if (validation.issues) ackError = new SchemaValidationError(`ack output failed validation: ${validation.issues.map((issue) => issue.message).join("; ")}`, { issues: validation.issues });
|
|
936
|
+
else validatedOutput = validation.value;
|
|
937
|
+
} else validatedOutput = raw;
|
|
938
|
+
} catch (thrown) {
|
|
939
|
+
ackError = toAIError(thrown);
|
|
940
|
+
}
|
|
941
|
+
const endedAt = /* @__PURE__ */ new Date();
|
|
942
|
+
const duration = performance.now() - startPerf;
|
|
943
|
+
this.ackSnapshot = Object.freeze({
|
|
944
|
+
input: typeof this.input === "string" ? this.input : safeStringify(this.input),
|
|
945
|
+
output: validatedOutput,
|
|
946
|
+
usage,
|
|
947
|
+
startedAt: startedAt.toISOString(),
|
|
948
|
+
endedAt: endedAt.toISOString(),
|
|
949
|
+
duration,
|
|
950
|
+
error: ackError
|
|
951
|
+
});
|
|
952
|
+
this.emit("supervisor.ack.completed", {
|
|
953
|
+
output: validatedOutput,
|
|
954
|
+
usage,
|
|
955
|
+
duration,
|
|
956
|
+
error: ackError
|
|
957
|
+
});
|
|
958
|
+
return {
|
|
959
|
+
output: validatedOutput,
|
|
960
|
+
usage,
|
|
961
|
+
duration,
|
|
962
|
+
error: ackError
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Agent-driven receptionist path — invokes the agent, streams tokens
|
|
967
|
+
* via `supervisor.ack.streaming`, captures the report node, strip-
|
|
968
|
+
* validates against `output` (when declared), records the snapshot,
|
|
969
|
+
* emits `supervisor.ack.completed`.
|
|
970
|
+
*/
|
|
971
|
+
async runAckAgent(ack, routeContext, startedAt, startPerf) {
|
|
972
|
+
const placeholders = ack.placeholders?.(routeContext);
|
|
973
|
+
const inputForAck = ack.input?.(routeContext) ?? (typeof this.input === "string" ? this.input : safeStringify(this.input));
|
|
974
|
+
const isStreaming = this.streamController !== void 0;
|
|
975
|
+
const handlers = { "agent.trip.streaming": ({ delta }) => {
|
|
976
|
+
this.emit("supervisor.ack.streaming", { delta });
|
|
977
|
+
} };
|
|
978
|
+
const resolvedHistory = this.resolveHistoryFor("ack", routeContext, ack.history);
|
|
979
|
+
const agentOptions = {
|
|
980
|
+
signal: this.options?.signal,
|
|
981
|
+
on: handlers,
|
|
982
|
+
...placeholders ? { placeholders } : {},
|
|
983
|
+
...ack.output ? { output: ack.output } : {},
|
|
984
|
+
...resolvedHistory.length > 0 ? { history: resolvedHistory } : {}
|
|
985
|
+
};
|
|
986
|
+
let rawResult;
|
|
987
|
+
let ackError;
|
|
988
|
+
let usage = {
|
|
989
|
+
input: 0,
|
|
990
|
+
output: 0,
|
|
991
|
+
total: 0
|
|
992
|
+
};
|
|
993
|
+
try {
|
|
994
|
+
if (isStreaming) rawResult = await ack.agent.stream(inputForAck, agentOptions).result;
|
|
995
|
+
else rawResult = await ack.agent.execute(inputForAck, agentOptions);
|
|
996
|
+
if (rawResult.error) ackError = rawResult.error;
|
|
997
|
+
usage = rawResult.usage ?? usage;
|
|
998
|
+
if (rawResult.report) this.childReports.push(rawResult.report);
|
|
999
|
+
} catch (thrown) {
|
|
1000
|
+
ackError = toAIError(thrown);
|
|
1001
|
+
}
|
|
1002
|
+
const endedAt = /* @__PURE__ */ new Date();
|
|
1003
|
+
const duration = performance.now() - startPerf;
|
|
1004
|
+
let validatedOutput;
|
|
1005
|
+
if (rawResult && !ackError && ack.output) {
|
|
1006
|
+
const sourceValue = rawResult.data ?? rawResult.text ?? void 0;
|
|
1007
|
+
const validation = await ack.output["~standard"].validate(sourceValue);
|
|
1008
|
+
if (validation.issues) ackError = new SchemaValidationError(`ack output failed validation: ${validation.issues.map((issue) => issue.message).join("; ")}`, { issues: validation.issues });
|
|
1009
|
+
else validatedOutput = validation.value;
|
|
1010
|
+
} else if (rawResult && !ackError) validatedOutput = rawResult.data ?? rawResult.text ?? void 0;
|
|
1011
|
+
this.ackSnapshot = Object.freeze({
|
|
1012
|
+
input: inputForAck,
|
|
1013
|
+
output: validatedOutput,
|
|
1014
|
+
usage,
|
|
1015
|
+
startedAt: startedAt.toISOString(),
|
|
1016
|
+
endedAt: endedAt.toISOString(),
|
|
1017
|
+
duration,
|
|
1018
|
+
error: ackError
|
|
1019
|
+
});
|
|
1020
|
+
this.emit("supervisor.ack.completed", {
|
|
1021
|
+
output: validatedOutput,
|
|
1022
|
+
usage,
|
|
1023
|
+
duration,
|
|
1024
|
+
error: ackError
|
|
1025
|
+
});
|
|
1026
|
+
return {
|
|
1027
|
+
output: validatedOutput,
|
|
1028
|
+
usage,
|
|
1029
|
+
duration,
|
|
1030
|
+
error: ackError
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Probe the ack promise non-blockingly. Yields one macrotask cycle
|
|
1035
|
+
* (`setImmediate`) so an already-resolved ack wins via microtask
|
|
1036
|
+
* priority; if the probe returns first, the slice is abandoned —
|
|
1037
|
+
* warning logged, error captured on `report.ack`, run completes
|
|
1038
|
+
* regardless. Specialists own the actual answer; the receptionist
|
|
1039
|
+
* was just a reassuring preview.
|
|
1040
|
+
*/
|
|
1041
|
+
async settleAck(ackPromise, iterationUsage) {
|
|
1042
|
+
if (!ackPromise) return;
|
|
1043
|
+
const NOT_READY = Symbol("ack-not-ready");
|
|
1044
|
+
const probe = await Promise.race([ackPromise, new Promise((resolve) => setTimeout(() => resolve(NOT_READY), 0))]);
|
|
1045
|
+
if (probe === NOT_READY) {
|
|
1046
|
+
this.logger.warn(this.logModule, "ack.abandoned", "ack receptionist did not settle before iteration completed; slice dropped");
|
|
1047
|
+
const abandonedAt = /* @__PURE__ */ new Date();
|
|
1048
|
+
this.ackSnapshot = Object.freeze({
|
|
1049
|
+
input: typeof this.input === "string" ? this.input : safeStringify(this.input),
|
|
1050
|
+
output: void 0,
|
|
1051
|
+
usage: {
|
|
1052
|
+
input: 0,
|
|
1053
|
+
output: 0,
|
|
1054
|
+
total: 0
|
|
1055
|
+
},
|
|
1056
|
+
startedAt: abandonedAt.toISOString(),
|
|
1057
|
+
endedAt: abandonedAt.toISOString(),
|
|
1058
|
+
duration: 0,
|
|
1059
|
+
error: new SupervisorFailedError("ack receptionist did not settle before iteration completed", { context: { ackAbandoned: true } })
|
|
1060
|
+
});
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
const ackOutcome = probe;
|
|
1064
|
+
if (ackOutcome) {
|
|
1065
|
+
this.aggregateUsage(iterationUsage, ackOutcome.usage);
|
|
1066
|
+
this.mergeAckIntoState(ackOutcome);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Merge the receptionist's strip-validated slice into state. Called
|
|
1071
|
+
* from `settleAck` BEFORE branch merges so specialists override the
|
|
1072
|
+
* receptionist on key collision — the receptionist hedges, the
|
|
1073
|
+
* specialist commits.
|
|
1074
|
+
*/
|
|
1075
|
+
mergeAckIntoState(ackOutcome) {
|
|
1076
|
+
if (ackOutcome.error || !ackOutcome.output) return;
|
|
1077
|
+
if (typeof ackOutcome.output !== "object" || ackOutcome.output === null) return;
|
|
1078
|
+
const slice = ackOutcome.output;
|
|
1079
|
+
for (const [key, value] of Object.entries(slice)) this.state[key] = value;
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Run the iter-0 classifier prelude (Phase 7 / decisions §37).
|
|
1083
|
+
* Resolves the configured classifier (agent / callback / entry
|
|
1084
|
+
* form), invokes it, runs the optional `refine` post-process hook,
|
|
1085
|
+
* and either:
|
|
1086
|
+
*
|
|
1087
|
+
* - sets `carriedClassifierDispatch` so the upcoming
|
|
1088
|
+
* `decideDispatch` short-circuits to the chosen intent, OR
|
|
1089
|
+
* - sets `classifierHalted = true` so `runIteration` terminates
|
|
1090
|
+
* before any dispatch (refine returned `END`).
|
|
1091
|
+
*
|
|
1092
|
+
* Captures the full forensic record on `classifierSnapshot` —
|
|
1093
|
+
* surfaced on `SupervisorReport.classifier` and threaded into
|
|
1094
|
+
* `ctx.classifier` on every downstream context.
|
|
1095
|
+
*
|
|
1096
|
+
* Errors in the classifier OR the refine hook abort the run with
|
|
1097
|
+
* a `SupervisorFailedError` so issues surface loudly instead of
|
|
1098
|
+
* silently falling through to router/route.
|
|
1099
|
+
*/
|
|
1100
|
+
async runClassifier() {
|
|
1101
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
1102
|
+
const startPerf = performance.now();
|
|
1103
|
+
const startedAtIso = startedAt.toISOString();
|
|
1104
|
+
this.emit("supervisor.classifier.starting", { iteration: 0 });
|
|
1105
|
+
const ctx = this.buildClassifierContext();
|
|
1106
|
+
const config = this.config.classifier;
|
|
1107
|
+
let raw;
|
|
1108
|
+
let usage = {
|
|
1109
|
+
input: 0,
|
|
1110
|
+
output: 0,
|
|
1111
|
+
total: 0
|
|
1112
|
+
};
|
|
1113
|
+
let executionError;
|
|
1114
|
+
try {
|
|
1115
|
+
const outcome = await this.invokeClassifier(config, ctx);
|
|
1116
|
+
raw = outcome.output;
|
|
1117
|
+
usage = outcome.usage;
|
|
1118
|
+
} catch (thrown) {
|
|
1119
|
+
executionError = toAIError(thrown);
|
|
1120
|
+
}
|
|
1121
|
+
if (executionError || !raw) {
|
|
1122
|
+
const error = executionError ?? new SupervisorFailedError(`ai.supervisor("${this.config.name}"): classifier produced no output`, { context: { iteration: 0 } });
|
|
1123
|
+
this.classifierSnapshot = {
|
|
1124
|
+
intent: void 0,
|
|
1125
|
+
refined: false,
|
|
1126
|
+
halted: true,
|
|
1127
|
+
raw: raw ?? { intent: "" },
|
|
1128
|
+
startedAt: startedAtIso,
|
|
1129
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1130
|
+
duration: performance.now() - startPerf,
|
|
1131
|
+
usage,
|
|
1132
|
+
error
|
|
1133
|
+
};
|
|
1134
|
+
this.aggregateUsage(this.usage, usage);
|
|
1135
|
+
this.emit("supervisor.classifier.failed", { error });
|
|
1136
|
+
throw error;
|
|
1137
|
+
}
|
|
1138
|
+
if (!this.entries.has(raw.intent)) {
|
|
1139
|
+
const error = new SupervisorFailedError(`ai.supervisor("${this.config.name}"): classifier picked unknown intent "${raw.intent}" — must be a key in \`intents\``, { context: {
|
|
1140
|
+
iteration: 0,
|
|
1141
|
+
available: [...this.entries.keys()]
|
|
1142
|
+
} }, "SUPERVISOR_INVALID_ROUTE");
|
|
1143
|
+
this.classifierSnapshot = {
|
|
1144
|
+
intent: void 0,
|
|
1145
|
+
refined: false,
|
|
1146
|
+
halted: true,
|
|
1147
|
+
raw,
|
|
1148
|
+
startedAt: startedAtIso,
|
|
1149
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1150
|
+
duration: performance.now() - startPerf,
|
|
1151
|
+
usage,
|
|
1152
|
+
error
|
|
1153
|
+
};
|
|
1154
|
+
this.aggregateUsage(this.usage, usage);
|
|
1155
|
+
this.emit("supervisor.classifier.failed", { error });
|
|
1156
|
+
throw error;
|
|
1157
|
+
}
|
|
1158
|
+
const refineHook = this.resolveRefineHook(config);
|
|
1159
|
+
let final = raw;
|
|
1160
|
+
let refined = false;
|
|
1161
|
+
let halted = false;
|
|
1162
|
+
if (refineHook) {
|
|
1163
|
+
let refineResult;
|
|
1164
|
+
try {
|
|
1165
|
+
refineResult = await refineHook(this.buildClassifierRefineContext(ctx, raw));
|
|
1166
|
+
} catch (thrown) {
|
|
1167
|
+
const error = toAIError(thrown);
|
|
1168
|
+
this.classifierSnapshot = {
|
|
1169
|
+
intent: void 0,
|
|
1170
|
+
refined: false,
|
|
1171
|
+
halted: true,
|
|
1172
|
+
raw,
|
|
1173
|
+
startedAt: startedAtIso,
|
|
1174
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1175
|
+
duration: performance.now() - startPerf,
|
|
1176
|
+
usage,
|
|
1177
|
+
error
|
|
1178
|
+
};
|
|
1179
|
+
this.aggregateUsage(this.usage, usage);
|
|
1180
|
+
this.emit("supervisor.classifier.failed", { error });
|
|
1181
|
+
throw error;
|
|
1182
|
+
}
|
|
1183
|
+
const interpretation = this.interpretRefineResult(refineResult, raw);
|
|
1184
|
+
if (interpretation.error) {
|
|
1185
|
+
this.classifierSnapshot = {
|
|
1186
|
+
intent: void 0,
|
|
1187
|
+
refined: true,
|
|
1188
|
+
halted: true,
|
|
1189
|
+
raw,
|
|
1190
|
+
startedAt: startedAtIso,
|
|
1191
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1192
|
+
duration: performance.now() - startPerf,
|
|
1193
|
+
usage,
|
|
1194
|
+
error: interpretation.error
|
|
1195
|
+
};
|
|
1196
|
+
this.aggregateUsage(this.usage, usage);
|
|
1197
|
+
this.emit("supervisor.classifier.failed", { error: interpretation.error });
|
|
1198
|
+
throw interpretation.error;
|
|
1199
|
+
}
|
|
1200
|
+
refined = interpretation.refined;
|
|
1201
|
+
halted = interpretation.halted;
|
|
1202
|
+
final = interpretation.final ?? raw;
|
|
1203
|
+
if (interpretation.sliceToMerge) for (const [key, value] of Object.entries(interpretation.sliceToMerge)) this.state[key] = value;
|
|
1204
|
+
}
|
|
1205
|
+
for (const [key, value] of Object.entries(final)) this.state[key] = value;
|
|
1206
|
+
this.classifierSnapshot = {
|
|
1207
|
+
intent: halted ? void 0 : final.intent,
|
|
1208
|
+
reasoning: final.reasoning,
|
|
1209
|
+
confidence: final.confidence,
|
|
1210
|
+
refined,
|
|
1211
|
+
halted,
|
|
1212
|
+
raw,
|
|
1213
|
+
startedAt: startedAtIso,
|
|
1214
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1215
|
+
duration: performance.now() - startPerf,
|
|
1216
|
+
usage
|
|
1217
|
+
};
|
|
1218
|
+
this.aggregateUsage(this.usage, usage);
|
|
1219
|
+
this.emit("supervisor.classifier.completed", {
|
|
1220
|
+
output: {
|
|
1221
|
+
intent: this.classifierSnapshot.intent,
|
|
1222
|
+
reasoning: this.classifierSnapshot.reasoning,
|
|
1223
|
+
confidence: this.classifierSnapshot.confidence
|
|
1224
|
+
},
|
|
1225
|
+
intent: this.classifierSnapshot.intent,
|
|
1226
|
+
refined,
|
|
1227
|
+
halted,
|
|
1228
|
+
duration: this.classifierSnapshot.duration,
|
|
1229
|
+
usage
|
|
1230
|
+
});
|
|
1231
|
+
if (halted) {
|
|
1232
|
+
this.classifierHalted = true;
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
if (!this.entries.has(final.intent)) {
|
|
1236
|
+
const error = new SupervisorFailedError(`ai.supervisor("${this.config.name}"): classifier.refine returned unknown intent "${final.intent}" — must be a key in \`intents\``, { context: {
|
|
1237
|
+
iteration: 0,
|
|
1238
|
+
available: [...this.entries.keys()]
|
|
1239
|
+
} }, "SUPERVISOR_INVALID_ROUTE");
|
|
1240
|
+
this.classifierSnapshot = {
|
|
1241
|
+
...this.classifierSnapshot,
|
|
1242
|
+
halted: true,
|
|
1243
|
+
error
|
|
1244
|
+
};
|
|
1245
|
+
this.classifierHalted = true;
|
|
1246
|
+
this.emit("supervisor.classifier.failed", { error });
|
|
1247
|
+
throw error;
|
|
1248
|
+
}
|
|
1249
|
+
this.carriedClassifierDispatch = { intent: final.intent };
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Resolve the configured classifier into a callable that returns
|
|
1253
|
+
* `{ output, usage }`. Handles the four accepted shapes — bare
|
|
1254
|
+
* agent / bare callback / agent-entry / run-entry. Pure shape
|
|
1255
|
+
* normalization; no side effects.
|
|
1256
|
+
*/
|
|
1257
|
+
async invokeClassifier(config, ctx) {
|
|
1258
|
+
if (typeof config === "function") return {
|
|
1259
|
+
output: await config(ctx),
|
|
1260
|
+
usage: {
|
|
1261
|
+
input: 0,
|
|
1262
|
+
output: 0,
|
|
1263
|
+
total: 0
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
1266
|
+
if (typeof config.run === "function") {
|
|
1267
|
+
const runFn = config.run;
|
|
1268
|
+
return {
|
|
1269
|
+
output: await runFn(ctx),
|
|
1270
|
+
usage: {
|
|
1271
|
+
input: 0,
|
|
1272
|
+
output: 0,
|
|
1273
|
+
total: 0
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
if (typeof config.agent?.execute === "function") {
|
|
1278
|
+
const entry = config;
|
|
1279
|
+
return this.invokeClassifierAgent(entry.agent, ctx, entry.placeholders, entry.input, entry.history);
|
|
1280
|
+
}
|
|
1281
|
+
if (typeof config.execute === "function") return this.invokeClassifierAgent(config, ctx);
|
|
1282
|
+
throw new SupervisorFailedError(`ai.supervisor("${this.config.name}"): \`classifier\` is not an agent, callback, or entry object`, { context: { authoring: true } });
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Invoke a classifier agent with the supervisor's standard wiring
|
|
1286
|
+
* — placeholders, input override, history slicing, signal,
|
|
1287
|
+
* streaming bubble. Output schema validation belongs to the agent
|
|
1288
|
+
* itself; we just pull the typed `data` (or fall back to parsing
|
|
1289
|
+
* `text`) and assert the locked `intent` field.
|
|
1290
|
+
*/
|
|
1291
|
+
async invokeClassifierAgent(agent, ctx, placeholders, inputResolver, historySlicer) {
|
|
1292
|
+
const inputForAgent = inputResolver?.(ctx) ?? (typeof ctx.input === "string" ? ctx.input : safeStringify(ctx.input));
|
|
1293
|
+
const history = historySlicer ? [...historySlicer(ctx)] : this.applyAgentsWindow();
|
|
1294
|
+
const isStreaming = this.streamController !== void 0;
|
|
1295
|
+
const agentOptions = {
|
|
1296
|
+
signal: this.options?.signal,
|
|
1297
|
+
on: { "agent.trip.streaming": ({ delta }) => {
|
|
1298
|
+
this.emit("supervisor.classifier.streaming", { delta });
|
|
1299
|
+
} },
|
|
1300
|
+
...placeholders ? { placeholders: placeholders(ctx) } : {},
|
|
1301
|
+
...history.length > 0 ? { history } : {}
|
|
1302
|
+
};
|
|
1303
|
+
let result;
|
|
1304
|
+
if (isStreaming) result = await agent.stream(inputForAgent, agentOptions).result;
|
|
1305
|
+
else result = await agent.execute(inputForAgent, agentOptions);
|
|
1306
|
+
if (result.error) throw result.error;
|
|
1307
|
+
if (result.report) this.childReports.push(result.report);
|
|
1308
|
+
const data = result.data ?? result.text ?? void 0;
|
|
1309
|
+
return {
|
|
1310
|
+
output: this.coerceClassifierOutput(data),
|
|
1311
|
+
usage: result.usage
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Coerce an agent's output into the locked classifier shape.
|
|
1316
|
+
* Accepts a typed object with `intent` (the canonical case) or a
|
|
1317
|
+
* plain string (interpreted as the intent name with no reasoning).
|
|
1318
|
+
* Throws `SupervisorFailedError` if neither shape matches.
|
|
1319
|
+
*/
|
|
1320
|
+
coerceClassifierOutput(data) {
|
|
1321
|
+
if (typeof data === "string") return { intent: data };
|
|
1322
|
+
if (data && typeof data === "object" && typeof data.intent === "string") {
|
|
1323
|
+
const record = data;
|
|
1324
|
+
return {
|
|
1325
|
+
intent: record.intent,
|
|
1326
|
+
reasoning: typeof record.reasoning === "string" ? record.reasoning : void 0,
|
|
1327
|
+
confidence: typeof record.confidence === "number" ? record.confidence : void 0
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
throw new SupervisorFailedError(`ai.supervisor("${this.config.name}"): classifier output missing required \`intent\` field — got ${JSON.stringify(data)?.slice(0, 200)}`, { context: { iteration: 0 } });
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* Build the read-only context passed to a classifier callback / agent
|
|
1334
|
+
* resolvers. No dispatch helpers — registered intents haven't fired
|
|
1335
|
+
* yet; pre-running them from the classifier would be confusing.
|
|
1336
|
+
*/
|
|
1337
|
+
buildClassifierContext() {
|
|
1338
|
+
return {
|
|
1339
|
+
iteration: 0,
|
|
1340
|
+
input: this.input,
|
|
1341
|
+
state: this.state,
|
|
1342
|
+
context: this.context,
|
|
1343
|
+
history: this.history,
|
|
1344
|
+
signal: this.options?.signal ?? new AbortController().signal,
|
|
1345
|
+
goal: this.goal
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
/**
|
|
1349
|
+
* Build the refine context — extends ClassifierContext with the
|
|
1350
|
+
* classifier's just-resolved output plus `run` / `stream` so the
|
|
1351
|
+
* refine hook can spin up secondary classifiers / validators
|
|
1352
|
+
* inline (Phase 6 features).
|
|
1353
|
+
*/
|
|
1354
|
+
buildClassifierRefineContext(base, raw) {
|
|
1355
|
+
const callStack = /* @__PURE__ */ new Set();
|
|
1356
|
+
const reportSink = this.childReports;
|
|
1357
|
+
return {
|
|
1358
|
+
...base,
|
|
1359
|
+
result: { data: raw },
|
|
1360
|
+
run: (executable, runInput, runOptions) => this.runInline(executable, runInput, runOptions, callStack, reportSink),
|
|
1361
|
+
stream: (executable, runInput, runOptions) => this.streamInline(executable, runInput, runOptions, callStack, reportSink, "classifier")
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Pull the optional `refine` hook off whichever classifier-config
|
|
1366
|
+
* shape was supplied. Bare-callback and bare-agent forms have no
|
|
1367
|
+
* refine; only entry forms do.
|
|
1368
|
+
*/
|
|
1369
|
+
resolveRefineHook(config) {
|
|
1370
|
+
if (typeof config === "function") return;
|
|
1371
|
+
const refine = config.refine;
|
|
1372
|
+
return typeof refine === "function" ? refine : void 0;
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Interpret a refine return value into actionable bits — final
|
|
1376
|
+
* classifier output to dispatch, slice-to-merge, halted/refined
|
|
1377
|
+
* flags, or an error. See {@link ClassifierRefineResult} for the
|
|
1378
|
+
* accepted shapes.
|
|
1379
|
+
*/
|
|
1380
|
+
interpretRefineResult(refineResult, raw) {
|
|
1381
|
+
if (refineResult === void 0) return {
|
|
1382
|
+
final: raw,
|
|
1383
|
+
refined: false,
|
|
1384
|
+
halted: false
|
|
1385
|
+
};
|
|
1386
|
+
if (refineResult === "__warlock:end__") return {
|
|
1387
|
+
refined: true,
|
|
1388
|
+
halted: true
|
|
1389
|
+
};
|
|
1390
|
+
if (typeof refineResult !== "object" || refineResult === null) return {
|
|
1391
|
+
refined: false,
|
|
1392
|
+
halted: true,
|
|
1393
|
+
error: new SupervisorFailedError(`ai.supervisor("${this.config.name}"): classifier.refine returned an unsupported value — expected undefined, END, or an object`, { context: { iteration: 0 } })
|
|
1394
|
+
};
|
|
1395
|
+
const record = refineResult;
|
|
1396
|
+
const intentField = record.intent;
|
|
1397
|
+
const halted = intentField === END;
|
|
1398
|
+
const intentOverride = typeof intentField === "string" ? intentField : void 0;
|
|
1399
|
+
const slice = {};
|
|
1400
|
+
for (const [key, value] of Object.entries(record)) {
|
|
1401
|
+
if (key === "intent") continue;
|
|
1402
|
+
slice[key] = value;
|
|
1403
|
+
}
|
|
1404
|
+
const final = {
|
|
1405
|
+
...raw,
|
|
1406
|
+
...intentOverride ? { intent: intentOverride } : {}
|
|
1407
|
+
};
|
|
1408
|
+
return {
|
|
1409
|
+
final: halted ? void 0 : final,
|
|
1410
|
+
sliceToMerge: Object.keys(slice).length > 0 ? slice : void 0,
|
|
1411
|
+
refined: true,
|
|
1412
|
+
halted
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
/**
|
|
1416
|
+
* Run the `evaluate` callback (when configured) after the
|
|
1417
|
+
* iteration's branches settle and outputs have merged into state.
|
|
1418
|
+
* Errors in the callback surface as `SupervisorFailedError` so a
|
|
1419
|
+
* buggy evaluate doesn't silently swallow the whole run.
|
|
1420
|
+
*
|
|
1421
|
+
* Phase 3.4 (Stage 4b) — `EvaluateContext.state` carries the
|
|
1422
|
+
* post-merge accumulator so verdicts can be state-aware. Q9
|
|
1423
|
+
* lifted the router-only restriction; evaluate now runs in both
|
|
1424
|
+
* router and route modes.
|
|
1425
|
+
*/
|
|
1426
|
+
async runEvaluate(branches) {
|
|
1427
|
+
if (!this.config.evaluate) return;
|
|
1428
|
+
const evaluateContext = {
|
|
1429
|
+
iteration: this.iteration,
|
|
1430
|
+
input: this.input,
|
|
1431
|
+
state: this.state,
|
|
1432
|
+
result: indexBranchesForEvaluate(branches),
|
|
1433
|
+
iterations: this.snapshots,
|
|
1434
|
+
context: this.context,
|
|
1435
|
+
history: this.history,
|
|
1436
|
+
goal: this.goal,
|
|
1437
|
+
classifier: this.classifierSnapshot
|
|
1438
|
+
};
|
|
1439
|
+
try {
|
|
1440
|
+
return await this.config.evaluate(evaluateContext);
|
|
1441
|
+
} catch (thrown) {
|
|
1442
|
+
throw new SupervisorFailedError(`evaluate callback threw: ${thrown instanceof Error ? thrown.message : String(thrown)}`, { cause: thrown });
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Merge each branch's output into supervisor `state` in
|
|
1447
|
+
* `decision.intents` order — Q15 conflict rule: last intent in
|
|
1448
|
+
* the array wins on key collisions. Errored branches don't
|
|
1449
|
+
* contribute. Non-object outputs (primitives, null) are skipped
|
|
1450
|
+
* with a warning log; they can't shallow-merge into an object.
|
|
1451
|
+
*
|
|
1452
|
+
* For agent/workflow intents: merging is opt-in via declaring an
|
|
1453
|
+
* `output` schema (the strip-merge gate). Without a schema, the
|
|
1454
|
+
* raw output stays on the branch snapshot but doesn't pollute
|
|
1455
|
+
* state. For callback intents: their return is already strip-merged
|
|
1456
|
+
* (or pass-through) inside `runCallback` — we just merge what's on
|
|
1457
|
+
* the branch snapshot.
|
|
1458
|
+
*/
|
|
1459
|
+
mergeBranchesIntoState(intentsOrder, branches) {
|
|
1460
|
+
const indexed = /* @__PURE__ */ new Map();
|
|
1461
|
+
for (const branch of branches) indexed.set(branch.intent, branch);
|
|
1462
|
+
const mergedKeys = /* @__PURE__ */ new Map();
|
|
1463
|
+
for (const intent of intentsOrder) {
|
|
1464
|
+
const branch = indexed.get(intent);
|
|
1465
|
+
if (!branch || branch.error) continue;
|
|
1466
|
+
const entry = this.entries.get(intent);
|
|
1467
|
+
const isStreamModeAgent = entry?.type === "agent" && entry.mode === "stream";
|
|
1468
|
+
if (!(entry?.type === "callback" || entry && entry.output !== void 0 || isStreamModeAgent)) continue;
|
|
1469
|
+
const slice = branch.output;
|
|
1470
|
+
if (!slice || typeof slice !== "object" || Array.isArray(slice)) {
|
|
1471
|
+
if (slice !== void 0) this.logger.warn(this.logModule, "state.merge.skip", `intent "${intent}" output is not a mergeable object — skipping state merge`, {
|
|
1472
|
+
intent,
|
|
1473
|
+
type: typeof slice
|
|
1474
|
+
});
|
|
1475
|
+
continue;
|
|
1476
|
+
}
|
|
1477
|
+
for (const [key, value] of Object.entries(slice)) {
|
|
1478
|
+
const previousOwner = mergedKeys.get(key);
|
|
1479
|
+
if (previousOwner !== void 0 && previousOwner !== intent) this.logger.warn(this.logModule, "state.merge.conflict", `state key "${key}" written by both "${previousOwner}" and "${intent}" — last-in-decision-array wins (Q15)`, {
|
|
1480
|
+
key,
|
|
1481
|
+
previousOwner,
|
|
1482
|
+
currentIntent: intent
|
|
1483
|
+
});
|
|
1484
|
+
this.state[key] = value;
|
|
1485
|
+
mergedKeys.set(key, intent);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Merge the iteration's accumulated `currentArtifacts` bag into
|
|
1491
|
+
* supervisor state (Phase 5 / decisions §35). Runs once per
|
|
1492
|
+
* iteration after branch slices land and before evaluate.
|
|
1493
|
+
*
|
|
1494
|
+
* Order of operations:
|
|
1495
|
+
*
|
|
1496
|
+
* 1. **Empty-bag fast path** — if no tool wrote anything, skip
|
|
1497
|
+
* validation and merge entirely; reset the bag for the next
|
|
1498
|
+
* iteration is also a no-op (already empty).
|
|
1499
|
+
* 2. **Schema validation** — when `config.artifactsSchema` is set,
|
|
1500
|
+
* validate the bag against it. Failure aborts the iteration via
|
|
1501
|
+
* a thrown `SchemaValidationError`; the iteration loop's outer
|
|
1502
|
+
* catch surfaces it on `result.error`. Validation is opt-in
|
|
1503
|
+
* (no schema → no validation cost).
|
|
1504
|
+
* 3. **Merge** — `config.finalizeArtifacts` when supplied, else
|
|
1505
|
+
* auto-spread `state = { ...state, ...artifacts }`. Replace
|
|
1506
|
+
* semantics under auto-spread; `finalizeArtifacts` carries
|
|
1507
|
+
* full responsibility for concat / dedupe / cross-iteration
|
|
1508
|
+
* accumulation when configured.
|
|
1509
|
+
* 4. **Reset** — `currentArtifacts = {}`. The next iteration's
|
|
1510
|
+
* tool calls start with a fresh empty bag; long runs never
|
|
1511
|
+
* accumulate raw artifacts here.
|
|
1512
|
+
*/
|
|
1513
|
+
async mergeArtifactsIntoState() {
|
|
1514
|
+
const artifacts = this.currentArtifacts;
|
|
1515
|
+
const keys = Object.keys(artifacts);
|
|
1516
|
+
this.capturedIterationArtifacts = Object.freeze({ ...artifacts });
|
|
1517
|
+
if (keys.length === 0) return;
|
|
1518
|
+
const schema = this.config.artifactsSchema;
|
|
1519
|
+
if (schema) {
|
|
1520
|
+
const validation = await schema["~standard"].validate(artifacts);
|
|
1521
|
+
if (validation.issues) throw new SchemaValidationError(`supervisor "${this.config.name}": iteration ${this.iteration} artifacts failed validation: ${validation.issues.map((issue) => issue.message).join("; ")}`, {
|
|
1522
|
+
issues: validation.issues,
|
|
1523
|
+
context: { iteration: this.iteration }
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
const finalize = this.config.finalizeArtifacts;
|
|
1527
|
+
if (finalize) {
|
|
1528
|
+
const merged = finalize(this.state, artifacts);
|
|
1529
|
+
for (const key of Object.keys(this.state)) if (!(key in merged)) delete this.state[key];
|
|
1530
|
+
for (const [key, value] of Object.entries(merged)) this.state[key] = value;
|
|
1531
|
+
} else for (const [key, value] of Object.entries(artifacts)) this.state[key] = value;
|
|
1532
|
+
this.currentArtifacts = {};
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Collect each branch's `intent.next(ctx)` directive after state
|
|
1536
|
+
* merge (Stage 4d / Q24). Iterates `decision.intents` order so
|
|
1537
|
+
* union resolution is deterministic.
|
|
1538
|
+
*
|
|
1539
|
+
* Rules:
|
|
1540
|
+
* - Errored branch → silent (treated as if no `next` defined).
|
|
1541
|
+
* - Branch with no `next` → silent; abstains (does NOT drag the
|
|
1542
|
+
* iteration to the router).
|
|
1543
|
+
* - Branch returns `END` → supreme; terminates immediately and
|
|
1544
|
+
* discards other branches' opinions.
|
|
1545
|
+
* - Branch returns `string` or `string[]` → contributes to the
|
|
1546
|
+
* union of unique intent names. Validated against the
|
|
1547
|
+
* supervisor's registry; unknown keys throw `SupervisorFailedError`.
|
|
1548
|
+
* - All branches silent → returns `undefined`; caller falls back
|
|
1549
|
+
* to router/route.
|
|
1550
|
+
*/
|
|
1551
|
+
collectIntentNext(intentsOrder, branches) {
|
|
1552
|
+
const indexed = /* @__PURE__ */ new Map();
|
|
1553
|
+
for (const branch of branches) indexed.set(branch.intent, branch);
|
|
1554
|
+
const collected = [];
|
|
1555
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1556
|
+
for (const intent of intentsOrder) {
|
|
1557
|
+
const branch = indexed.get(intent);
|
|
1558
|
+
if (!branch || branch.error) continue;
|
|
1559
|
+
const entry = this.entries.get(intent);
|
|
1560
|
+
if (!entry?.next) continue;
|
|
1561
|
+
const dispatchCtx = this.seedDispatchContext(intent, branch.input, new Set([intent]), []);
|
|
1562
|
+
let raw;
|
|
1563
|
+
try {
|
|
1564
|
+
raw = entry.next(dispatchCtx);
|
|
1565
|
+
} catch (thrown) {
|
|
1566
|
+
throw new SupervisorFailedError(`intent "${intent}" \`next\` resolver threw: ${thrown instanceof Error ? thrown.message : String(thrown)}`, {
|
|
1567
|
+
cause: thrown,
|
|
1568
|
+
context: { intent }
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
if (raw === void 0) continue;
|
|
1572
|
+
if (raw === "__warlock:end__") return { kind: "end" };
|
|
1573
|
+
const proposed = Array.isArray(raw) ? raw : [raw];
|
|
1574
|
+
for (const target of proposed) {
|
|
1575
|
+
if (typeof target !== "string") throw new SupervisorFailedError(`intent "${intent}" \`next\` returned a non-string value`, { context: { intent } });
|
|
1576
|
+
if (!this.entries.has(target)) throw new SupervisorFailedError(`intent "${intent}" \`next\` returned unknown intent "${target}"`, { context: {
|
|
1577
|
+
intent,
|
|
1578
|
+
target,
|
|
1579
|
+
available: [...this.entries.keys()]
|
|
1580
|
+
} });
|
|
1581
|
+
if (!seen.has(target)) {
|
|
1582
|
+
seen.add(target);
|
|
1583
|
+
collected.push(target);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
if (collected.length === 0) return;
|
|
1588
|
+
return {
|
|
1589
|
+
kind: "dispatch",
|
|
1590
|
+
intents: collected
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
/**
|
|
1594
|
+
* Finalize the supervisor result: validate accumulated state
|
|
1595
|
+
* against the output schema and build the public `SupervisorResult`.
|
|
1596
|
+
* Assemble-only — event emission and stream close happen in
|
|
1597
|
+
* `run()` around this call.
|
|
1598
|
+
*/
|
|
1599
|
+
async finalize() {
|
|
1600
|
+
if (this.status === "completed" && !this.error) try {
|
|
1601
|
+
this.data = await this.buildTypedData();
|
|
1602
|
+
} catch (thrown) {
|
|
1603
|
+
this.error = toAIError(thrown);
|
|
1604
|
+
this.status = "failed";
|
|
1605
|
+
this.terminatedBy = "error";
|
|
1606
|
+
}
|
|
1607
|
+
const endedAt = /* @__PURE__ */ new Date();
|
|
1608
|
+
const finalStatus = this.status === "max-iterations" ? "failed" : this.status;
|
|
1609
|
+
await this.checkpoint(finalStatus);
|
|
1610
|
+
const report = {
|
|
1611
|
+
runId: this.runId,
|
|
1612
|
+
rootRunId: this.runId,
|
|
1613
|
+
name: this.config.name,
|
|
1614
|
+
version: this.config.version,
|
|
1615
|
+
type: "supervisor",
|
|
1616
|
+
supervisorName: this.config.name,
|
|
1617
|
+
signature: this.signature,
|
|
1618
|
+
status: this.status,
|
|
1619
|
+
terminatedBy: this.terminatedBy,
|
|
1620
|
+
iterations: this.snapshots.length,
|
|
1621
|
+
startedAt: this.startedAtIso,
|
|
1622
|
+
endedAt: endedAt.toISOString(),
|
|
1623
|
+
duration: performance.now() - this.startPerf,
|
|
1624
|
+
cancelledAt: this.cancelledAtIso,
|
|
1625
|
+
usage: this.usage,
|
|
1626
|
+
children: this.childReports,
|
|
1627
|
+
snapshots: this.snapshots,
|
|
1628
|
+
ack: this.ackSnapshot,
|
|
1629
|
+
classifier: this.classifierSnapshot
|
|
1630
|
+
};
|
|
1631
|
+
stampReportLineage(report, {
|
|
1632
|
+
rootRunId: this.runId,
|
|
1633
|
+
sessionId: this.options?.sessionId
|
|
1634
|
+
});
|
|
1635
|
+
return {
|
|
1636
|
+
type: "supervisor",
|
|
1637
|
+
data: this.data,
|
|
1638
|
+
report,
|
|
1639
|
+
usage: this.usage,
|
|
1640
|
+
error: this.error
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
/**
|
|
1644
|
+
* Build the typed `data` at finalize. Stage 4c — single mode:
|
|
1645
|
+
*
|
|
1646
|
+
* - When `config.output` is declared, validate the accumulated
|
|
1647
|
+
* `state` against it and return the validated value (Q8).
|
|
1648
|
+
* `result.data` always matches the schema, or `result.error`
|
|
1649
|
+
* carries the validation issues.
|
|
1650
|
+
* - When `config.output` is omitted, return the raw state object.
|
|
1651
|
+
*
|
|
1652
|
+
* Validation failure surfaces as `SchemaValidationError` on
|
|
1653
|
+
* `result.error`; the run is still considered semantically
|
|
1654
|
+
* "completed" (intents ran, evaluate said done) but the typed
|
|
1655
|
+
* data slot is empty.
|
|
1656
|
+
*/
|
|
1657
|
+
async buildTypedData() {
|
|
1658
|
+
if (this.config.output) return validateOutput(this.config.output, this.state);
|
|
1659
|
+
return this.state;
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Record a snapshot for an iteration whose first decision was
|
|
1663
|
+
* `END` — no dispatch, no evaluate, just the decision record. Keeps
|
|
1664
|
+
* the snapshot log uniform so a late-route-to-END still appears in
|
|
1665
|
+
* the forensic history rather than vanishing.
|
|
1666
|
+
*/
|
|
1667
|
+
async recordTerminalDecisionSnapshot(decision, iterationStartedAt, iterationStart, iterationUsage) {
|
|
1668
|
+
const snapshot = Object.freeze({
|
|
1669
|
+
iteration: this.iteration,
|
|
1670
|
+
result: {},
|
|
1671
|
+
decision: {
|
|
1672
|
+
source: decision.source,
|
|
1673
|
+
next: decision.raw,
|
|
1674
|
+
reasoning: decision.reasoning,
|
|
1675
|
+
durationMs: decision.durationMs
|
|
1676
|
+
},
|
|
1677
|
+
state: { ...this.state },
|
|
1678
|
+
artifacts: this.capturedIterationArtifacts,
|
|
1679
|
+
startedAt: iterationStartedAt.toISOString(),
|
|
1680
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1681
|
+
duration: performance.now() - iterationStart,
|
|
1682
|
+
usage: iterationUsage
|
|
1683
|
+
});
|
|
1684
|
+
this.snapshots.push(snapshot);
|
|
1685
|
+
this.emit("supervisor.iteration.completed", {
|
|
1686
|
+
iteration: this.iteration,
|
|
1687
|
+
snapshot
|
|
1688
|
+
});
|
|
1689
|
+
await this.checkpoint("running");
|
|
1690
|
+
}
|
|
1691
|
+
/**
|
|
1692
|
+
* Write the current run state to the configured KV store (if any).
|
|
1693
|
+
* Persistence failures surface as `supervisor.error` events and
|
|
1694
|
+
* logged warnings but never abort the run — checkpoint best-effort
|
|
1695
|
+
* by design, matching `workflow` semantics.
|
|
1696
|
+
*/
|
|
1697
|
+
async checkpoint(status) {
|
|
1698
|
+
if (!(await persistSupervisorSnapshot({
|
|
1699
|
+
config: this.config,
|
|
1700
|
+
signature: this.signature,
|
|
1701
|
+
runId: this.runId,
|
|
1702
|
+
input: this.input,
|
|
1703
|
+
startedAt: this.startedAtIso,
|
|
1704
|
+
iteration: this.snapshots.length - 1,
|
|
1705
|
+
snapshots: this.snapshots,
|
|
1706
|
+
status
|
|
1707
|
+
})).ok) this.logger.warn(this.logModule, "persist.failed", "snapshot persist failed", { runId: this.runId });
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Between-iteration cancellation check. Called at the top of
|
|
1711
|
+
* every iteration; signal abort here means the loop exits before
|
|
1712
|
+
* any routing happens.
|
|
1713
|
+
*/
|
|
1714
|
+
throwIfCancelled() {
|
|
1715
|
+
if (this.options?.signal?.aborted) throw createCancelledError(this.options.signal);
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Aggregate one usage record (typically a branch or a router call)
|
|
1719
|
+
* into both the run-wide total and the iteration-local total.
|
|
1720
|
+
*/
|
|
1721
|
+
aggregateUsage(iterationUsage, partial) {
|
|
1722
|
+
if (!partial) return;
|
|
1723
|
+
this.usage.input += partial.input;
|
|
1724
|
+
this.usage.output += partial.output;
|
|
1725
|
+
this.usage.total += partial.total;
|
|
1726
|
+
iterationUsage.input += partial.input;
|
|
1727
|
+
iterationUsage.output += partial.output;
|
|
1728
|
+
iterationUsage.total += partial.total;
|
|
1729
|
+
}
|
|
1730
|
+
/**
|
|
1731
|
+
* Fan an event out through the three-tier emitter AND mirror it
|
|
1732
|
+
* into the stream controller when streaming. Event names map 1:1
|
|
1733
|
+
* to stream event types so consumers iterating the stream see the
|
|
1734
|
+
* exact same surface as `.on()` / `options.on` handlers.
|
|
1735
|
+
*/
|
|
1736
|
+
emit(event, payload) {
|
|
1737
|
+
const identity = {
|
|
1738
|
+
runId: this.runId,
|
|
1739
|
+
rootRunId: this.runId
|
|
1740
|
+
};
|
|
1741
|
+
const fullPayload = {
|
|
1742
|
+
...payload,
|
|
1743
|
+
...identity
|
|
1744
|
+
};
|
|
1745
|
+
this.emitter.emit(event, fullPayload, this.options?.on);
|
|
1746
|
+
this.logEvent(event, fullPayload);
|
|
1747
|
+
if (this.streamController) this.streamController.push({
|
|
1748
|
+
type: event,
|
|
1749
|
+
...fullPayload
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
logEvent(event, payload) {
|
|
1753
|
+
const action = event.replace(/^supervisor\./, "");
|
|
1754
|
+
switch (event) {
|
|
1755
|
+
case "supervisor.starting":
|
|
1756
|
+
this.logger.info(this.logModule, action, "supervisor starting", { runId: this.runId });
|
|
1757
|
+
return;
|
|
1758
|
+
case "supervisor.iteration.starting":
|
|
1759
|
+
this.logger.debug(this.logModule, action, "iteration starting", { iteration: payload.iteration });
|
|
1760
|
+
return;
|
|
1761
|
+
case "supervisor.router.decided":
|
|
1762
|
+
this.logger.debug(this.logModule, action, "router decided", {
|
|
1763
|
+
iteration: payload.iteration,
|
|
1764
|
+
next: payload.next
|
|
1765
|
+
});
|
|
1766
|
+
return;
|
|
1767
|
+
case "supervisor.agent.completed": {
|
|
1768
|
+
const typed = payload;
|
|
1769
|
+
this.logger.success(this.logModule, action, `branch "${typed.intent}" done`, {
|
|
1770
|
+
duration: typed.duration,
|
|
1771
|
+
usage: typed.usage
|
|
1772
|
+
});
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
case "supervisor.agent.failed": {
|
|
1776
|
+
const typed = payload;
|
|
1777
|
+
this.logger.warn(this.logModule, action, `branch "${typed.intent}" failed`, {
|
|
1778
|
+
code: typed.error.code,
|
|
1779
|
+
message: typed.error.message
|
|
1780
|
+
});
|
|
1781
|
+
return;
|
|
1782
|
+
}
|
|
1783
|
+
case "supervisor.error": {
|
|
1784
|
+
const { error } = payload;
|
|
1785
|
+
this.logger.error(this.logModule, action, error.message, { code: error.code });
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
case "supervisor.cancelled": {
|
|
1789
|
+
const typed = payload;
|
|
1790
|
+
this.logger.warn(this.logModule, action, "supervisor cancelled", {
|
|
1791
|
+
cancelledAt: typed.cancelledAt,
|
|
1792
|
+
reason: typed.reason
|
|
1793
|
+
});
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
case "supervisor.iteration.completed":
|
|
1797
|
+
this.logger.debug(this.logModule, action, "iteration completed", { iteration: payload.iteration });
|
|
1798
|
+
return;
|
|
1799
|
+
default: return;
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
};
|
|
1803
|
+
function indexByIntent(branches) {
|
|
1804
|
+
const indexed = {};
|
|
1805
|
+
for (const branch of branches) indexed[branch.intent] = branch;
|
|
1806
|
+
return indexed;
|
|
1807
|
+
}
|
|
1808
|
+
function indexBranchesForEvaluate(branches) {
|
|
1809
|
+
const indexed = {};
|
|
1810
|
+
for (const branch of branches) indexed[branch.intent] = {
|
|
1811
|
+
output: branch.output,
|
|
1812
|
+
input: branch.input,
|
|
1813
|
+
usage: branch.usage,
|
|
1814
|
+
durationMs: branch.duration,
|
|
1815
|
+
error: branch.error
|
|
1816
|
+
};
|
|
1817
|
+
return indexed;
|
|
1818
|
+
}
|
|
1819
|
+
function normalizeReassign(reassignTo) {
|
|
1820
|
+
if (!reassignTo) return [];
|
|
1821
|
+
if (Array.isArray(reassignTo)) return reassignTo;
|
|
1822
|
+
return [reassignTo];
|
|
1823
|
+
}
|
|
1824
|
+
function toAIError(thrown) {
|
|
1825
|
+
if (thrown instanceof AIError) return thrown;
|
|
1826
|
+
return new SupervisorFailedError(thrown instanceof Error ? thrown.message : String(thrown), { cause: thrown });
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Sum a list of child `BaseReport.usage` values. Callbacks
|
|
1830
|
+
* themselves contribute zero own-cost (they're dev code, not LLM
|
|
1831
|
+
* calls); their report's `usage` equals the sum of whatever
|
|
1832
|
+
* agents / workflows / nested callbacks they dispatched via
|
|
1833
|
+
* `ctx.intents.X.execute()`. Mirrors `compositeAsTool` semantics.
|
|
1834
|
+
*/
|
|
1835
|
+
function aggregateChildUsage(children) {
|
|
1836
|
+
return children.reduce((acc, child) => ({
|
|
1837
|
+
input: acc.input + child.usage.input,
|
|
1838
|
+
output: acc.output + child.usage.output,
|
|
1839
|
+
total: acc.total + child.usage.total
|
|
1840
|
+
}), {
|
|
1841
|
+
input: 0,
|
|
1842
|
+
output: 0,
|
|
1843
|
+
total: 0
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* Best-effort stringification for the snapshot's `input` field when
|
|
1848
|
+
* a callback intent's resolved input is a non-string value. Falls
|
|
1849
|
+
* back to a typed placeholder if `JSON.stringify` throws (circular
|
|
1850
|
+
* refs, BigInt, etc.) so a snapshot write never fails on its own.
|
|
1851
|
+
*/
|
|
1852
|
+
function safeStringify(value) {
|
|
1853
|
+
if (value === void 0) return "undefined";
|
|
1854
|
+
try {
|
|
1855
|
+
return JSON.stringify(value);
|
|
1856
|
+
} catch {
|
|
1857
|
+
return `[unserializable: ${typeof value}]`;
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
async function validateOutput(schema, value) {
|
|
1861
|
+
const validation = await schema["~standard"].validate(value);
|
|
1862
|
+
if (validation.issues) throw new SchemaValidationError(validation.issues.map((issue) => issue.message).join("; "), { issues: validation.issues });
|
|
1863
|
+
return validation.value;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
//#endregion
|
|
1867
|
+
export { SupervisorExecution };
|
|
1868
|
+
//# sourceMappingURL=execution.mjs.map
|