effect-agent 0.1.0-beta.85 → 0.1.0-beta.86
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/dist/Agent-CN0EWRsR.d.mts +226 -0
- package/dist/Agent-RklVROrm.mjs +83 -0
- package/dist/Agent-RklVROrm.mjs.map +1 -0
- package/dist/AgentError-CggcAQ7V.d.mts +101 -0
- package/dist/AgentPolicy-CetmQrf3.d.mts +96 -0
- package/dist/AgentRuntime-BLyETCPo.mjs +4562 -0
- package/dist/AgentRuntime-BLyETCPo.mjs.map +1 -0
- package/dist/AgentRuntime-uxR_a4VM.d.mts +346 -0
- package/dist/AgentUpdates-DMJ-_xow.d.mts +53 -0
- package/dist/ContextCompactor-Dxu5zhOL.d.mts +161 -0
- package/dist/ContextWindow-DRQ5Mbm2.d.mts +58 -0
- package/dist/DurableAgentRuntime-sOrBVu4B.mjs +6366 -0
- package/dist/DurableAgentRuntime-sOrBVu4B.mjs.map +1 -0
- package/dist/DurableFailpoint-Oinuuedm.d.mts +41 -0
- package/dist/DurableStep-Cry_iol4.d.mts +103 -0
- package/dist/Ephemeral.d.mts +22 -0
- package/dist/Ephemeral.mjs +22 -0
- package/dist/Ephemeral.mjs.map +1 -0
- package/dist/EventSource-OqS_P9ug.d.mts +51 -0
- package/dist/Identifiers-CN2s30fF.d.mts +40 -0
- package/dist/MemoryNamespace-BbRW5p71.d.mts +36 -0
- package/dist/MemoryReference-B8Q3UJoJ.d.mts +79 -0
- package/dist/Messaging-CkcIdt4I.d.mts +490 -0
- package/dist/MessagingHost-CRk0kXzT.d.mts +246 -0
- package/dist/MessagingHost-CWi_PP3g.mjs +137 -0
- package/dist/MessagingHost-CWi_PP3g.mjs.map +1 -0
- package/dist/OperationAuthorizer-TzcQtpX5.d.mts +61 -0
- package/dist/Output-D6SkedVx.d.mts +25 -0
- package/dist/PageCapture-Df8Xwskw.d.mts +583 -0
- package/dist/PageScreenshot-BpgZKAxl.d.mts +48 -0
- package/dist/PreparedInputAdmission-C4HbjHgl.d.mts +38 -0
- package/dist/Receipt-BAHyigAF.d.mts +33 -0
- package/dist/Records-BAGkekt8.d.mts +3570 -0
- package/dist/RunEvent-DIrzkjLr.d.mts +460 -0
- package/dist/RunEventSink-D5aVhISG.d.mts +108 -0
- package/dist/RunJournal-Cgg1kNvc.mjs +825 -0
- package/dist/RunJournal-Cgg1kNvc.mjs.map +1 -0
- package/dist/RunOptions-g1aigcKI.d.mts +915 -0
- package/dist/RunPolicyUsage-ClZxyR46.d.mts +21 -0
- package/dist/Sandbox-CEsDWBLq.d.mts +173 -0
- package/dist/Schedule-H3SEYX9g.d.mts +842 -0
- package/dist/Scheduling-C9aIP8zT.mjs +691 -0
- package/dist/Scheduling-C9aIP8zT.mjs.map +1 -0
- package/dist/Subagent-Cy_hU1tv.mjs +1469 -0
- package/dist/Subagent-Cy_hU1tv.mjs.map +1 -0
- package/dist/Subagent-DyfzHNdv.d.mts +826 -0
- package/dist/SubagentContract-DdT7Cqe6.d.mts +116 -0
- package/dist/SubagentHost-CJUBQlcc.d.mts +140 -0
- package/dist/Subscription-BEP8oQO5.d.mts +921 -0
- package/dist/SubscriptionInput-vAAOBrK7.d.mts +41 -0
- package/dist/Thread-DOBQ0QUY.d.mts +96 -0
- package/dist/ThreadHistory-tS1KwxHR.d.mts +60 -0
- package/dist/ToolBroker-DWs4tZra.d.mts +133 -0
- package/dist/ToolExposure-9uF_Yo0T.mjs +10 -0
- package/dist/ToolExposure-9uF_Yo0T.mjs.map +1 -0
- package/dist/ToolExposure-C93U1sPu.mjs +30 -0
- package/dist/ToolExposure-C93U1sPu.mjs.map +1 -0
- package/dist/ToolExposure-UCN12eCd.d.mts +39 -0
- package/dist/ToolExposure-r2eOWk4F.d.mts +46 -0
- package/dist/ToolExposure.d.mts +6 -5
- package/dist/ToolExposure.mjs +17 -9
- package/dist/ToolExposure.mjs.map +1 -1
- package/dist/ToolReconciler-DQVaJfpE.d.mts +76 -0
- package/dist/ToolResult-DmmgoChv.d.mts +61 -0
- package/dist/Usage-Dx0aknfk.d.mts +121 -0
- package/dist/WakeScheduler-EjUF-GhC.d.mts +56 -0
- package/dist/Worker-CaPYykpe.d.mts +228 -0
- package/dist/agent-registration-CWDMOAZA.mjs +172 -0
- package/dist/agent-registration-CWDMOAZA.mjs.map +1 -0
- package/dist/capabilities/Approval.d.mts +114 -0
- package/dist/capabilities/Approval.mjs +226 -0
- package/dist/capabilities/Approval.mjs.map +1 -0
- package/dist/capabilities/Budget.d.mts +112 -0
- package/dist/capabilities/Budget.mjs +346 -0
- package/dist/capabilities/Budget.mjs.map +1 -0
- package/dist/capabilities/CodeMode.d.mts +157 -0
- package/dist/capabilities/CodeMode.mjs +522 -0
- package/dist/capabilities/CodeMode.mjs.map +1 -0
- package/dist/capabilities/Commands.d.mts +55 -0
- package/dist/capabilities/Commands.mjs +57 -0
- package/dist/capabilities/Commands.mjs.map +1 -0
- package/dist/capabilities/ContextTools.d.mts +321 -0
- package/dist/capabilities/ContextTools.mjs +126 -0
- package/dist/capabilities/ContextTools.mjs.map +1 -0
- package/dist/capabilities/Mcp.d.mts +108 -0
- package/dist/capabilities/Mcp.mjs +260 -0
- package/dist/capabilities/Mcp.mjs.map +1 -0
- package/dist/capabilities/McpClient.d.mts +115 -0
- package/dist/capabilities/McpClient.mjs +506 -0
- package/dist/capabilities/McpClient.mjs.map +1 -0
- package/dist/capabilities/MemoryNotes.d.mts +424 -0
- package/dist/capabilities/MemoryNotes.mjs +128 -0
- package/dist/capabilities/MemoryNotes.mjs.map +1 -0
- package/dist/capabilities/Messaging.d.mts +3 -0
- package/dist/capabilities/Messaging.mjs +151 -0
- package/dist/capabilities/Messaging.mjs.map +1 -0
- package/dist/capabilities/ModelContext.d.mts +89 -0
- package/dist/capabilities/ModelContext.mjs +168 -0
- package/dist/capabilities/ModelContext.mjs.map +1 -0
- package/dist/capabilities/Redaction.d.mts +37 -0
- package/dist/capabilities/Redaction.mjs +104 -0
- package/dist/capabilities/Redaction.mjs.map +1 -0
- package/dist/capabilities/Remembering.d.mts +434 -0
- package/dist/capabilities/Remembering.mjs +290 -0
- package/dist/capabilities/Remembering.mjs.map +1 -0
- package/dist/capabilities/RunHooks.d.mts +82 -0
- package/dist/capabilities/RunHooks.mjs +144 -0
- package/dist/capabilities/RunHooks.mjs.map +1 -0
- package/dist/capabilities/SemanticMemory.d.mts +431 -0
- package/dist/capabilities/SemanticMemory.mjs +315 -0
- package/dist/capabilities/SemanticMemory.mjs.map +1 -0
- package/dist/capabilities/Subagent.d.mts +4 -0
- package/dist/capabilities/Subagent.mjs +5 -0
- package/dist/capabilities/SubagentReservations.d.mts +186 -0
- package/dist/capabilities/SubagentReservations.mjs +479 -0
- package/dist/capabilities/SubagentReservations.mjs.map +1 -0
- package/dist/capabilities/ToolDiscovery.d.mts +109 -0
- package/dist/capabilities/ToolDiscovery.mjs +211 -0
- package/dist/capabilities/ToolDiscovery.mjs.map +1 -0
- package/dist/capabilities/WebCapture.d.mts +163 -0
- package/dist/capabilities/WebCapture.mjs +335 -0
- package/dist/capabilities/WebCapture.mjs.map +1 -0
- package/dist/capabilities/WebSearch.d.mts +71 -0
- package/dist/capabilities/WebSearch.mjs +112 -0
- package/dist/capabilities/WebSearch.mjs.map +1 -0
- package/dist/compaction-C2OTceKw.mjs +505 -0
- package/dist/compaction-C2OTceKw.mjs.map +1 -0
- package/dist/core/Agent.d.mts +2 -0
- package/dist/core/Agent.mjs +2 -0
- package/dist/core/AgentError.d.mts +2 -0
- package/dist/core/AgentError.mjs +117 -0
- package/dist/core/AgentError.mjs.map +1 -0
- package/dist/core/AgentPolicy.d.mts +2 -0
- package/dist/core/AgentPolicy.mjs +94 -0
- package/dist/core/AgentPolicy.mjs.map +1 -0
- package/dist/core/AgentUpdates.d.mts +2 -0
- package/dist/core/AgentUpdates.mjs +63 -0
- package/dist/core/AgentUpdates.mjs.map +1 -0
- package/dist/core/IdGenerator.d.mts +26 -0
- package/dist/core/IdGenerator.mjs +27 -0
- package/dist/core/IdGenerator.mjs.map +1 -0
- package/dist/core/Identifiers.d.mts +2 -0
- package/dist/core/Identifiers.mjs +43 -0
- package/dist/core/Identifiers.mjs.map +1 -0
- package/dist/core/Memory.d.mts +57 -0
- package/dist/core/Memory.mjs +203 -0
- package/dist/core/Memory.mjs.map +1 -0
- package/dist/core/MemoryNamespace.d.mts +2 -0
- package/dist/core/MemoryNamespace.mjs +116 -0
- package/dist/core/MemoryNamespace.mjs.map +1 -0
- package/dist/core/MemoryReference.d.mts +2 -0
- package/dist/core/MemoryReference.mjs +109 -0
- package/dist/core/MemoryReference.mjs.map +1 -0
- package/dist/core/MemoryRevalidation.d.mts +65 -0
- package/dist/core/MemoryRevalidation.mjs +126 -0
- package/dist/core/MemoryRevalidation.mjs.map +1 -0
- package/dist/core/MemoryStore.d.mts +186 -0
- package/dist/core/MemoryStore.mjs +243 -0
- package/dist/core/MemoryStore.mjs.map +1 -0
- package/dist/core/Receipt.d.mts +2 -0
- package/dist/core/Receipt.mjs +36 -0
- package/dist/core/Receipt.mjs.map +1 -0
- package/dist/core/RememberingStore.d.mts +1562 -0
- package/dist/core/RememberingStore.mjs +190 -0
- package/dist/core/RememberingStore.mjs.map +1 -0
- package/dist/core/RunEvent.d.mts +2 -0
- package/dist/core/RunEvent.mjs +337 -0
- package/dist/core/RunEvent.mjs.map +1 -0
- package/dist/core/RunPolicyUsage.d.mts +2 -0
- package/dist/core/RunPolicyUsage.mjs +21 -0
- package/dist/core/RunPolicyUsage.mjs.map +1 -0
- package/dist/core/SemanticMemoryIndex.d.mts +826 -0
- package/dist/core/SemanticMemoryIndex.mjs +185 -0
- package/dist/core/SemanticMemoryIndex.mjs.map +1 -0
- package/dist/core/SemanticMemoryRevalidation.d.mts +45 -0
- package/dist/core/SemanticMemoryRevalidation.mjs +191 -0
- package/dist/core/SemanticMemoryRevalidation.mjs.map +1 -0
- package/dist/core/SubagentContract.d.mts +2 -0
- package/dist/core/SubagentContract.mjs +148 -0
- package/dist/core/SubagentContract.mjs.map +1 -0
- package/dist/core/Thread.d.mts +2 -0
- package/dist/core/Thread.mjs +250 -0
- package/dist/core/Thread.mjs.map +1 -0
- package/dist/core/ToolResult.d.mts +2 -0
- package/dist/core/ToolResult.mjs +140 -0
- package/dist/core/ToolResult.mjs.map +1 -0
- package/dist/core/Usage.d.mts +2 -0
- package/dist/core/Usage.mjs +319 -0
- package/dist/core/Usage.mjs.map +1 -0
- package/dist/core/Worker.d.mts +3 -0
- package/dist/core/Worker.mjs +147 -0
- package/dist/core/Worker.mjs.map +1 -0
- package/dist/durable/ActivityStore.d.mts +116 -0
- package/dist/durable/ActivityStore.mjs +131 -0
- package/dist/durable/ActivityStore.mjs.map +1 -0
- package/dist/durable/Admin.d.mts +2 -0
- package/dist/durable/Admin.mjs +328 -0
- package/dist/durable/Admin.mjs.map +1 -0
- package/dist/durable/AgentRegistration.d.mts +2 -0
- package/dist/durable/AgentRegistration.mjs +14 -0
- package/dist/durable/AgentRegistration.mjs.map +1 -0
- package/dist/durable/Certification.d.mts +111 -0
- package/dist/durable/Certification.mjs +159 -0
- package/dist/durable/Certification.mjs.map +1 -0
- package/dist/durable/CommittedActivity.d.mts +62 -0
- package/dist/durable/CommittedActivity.mjs +159 -0
- package/dist/durable/CommittedActivity.mjs.map +1 -0
- package/dist/durable/Digest.d.mts +2 -0
- package/dist/durable/Digest.mjs +50 -0
- package/dist/durable/Digest.mjs.map +1 -0
- package/dist/durable/DurableAgentRuntime.d.mts +3 -0
- package/dist/durable/DurableAgentRuntime.mjs +3 -0
- package/dist/durable/DurableFailpoint.d.mts +2 -0
- package/dist/durable/DurableFailpoint.mjs +94 -0
- package/dist/durable/DurableFailpoint.mjs.map +1 -0
- package/dist/durable/DurableFailpointTestControl.d.mts +15 -0
- package/dist/durable/DurableFailpointTestControl.mjs +19 -0
- package/dist/durable/DurableFailpointTestControl.mjs.map +1 -0
- package/dist/durable/EventSource.d.mts +2 -0
- package/dist/durable/EventSource.mjs +65 -0
- package/dist/durable/EventSource.mjs.map +1 -0
- package/dist/durable/GitHubWorkflowSource.d.mts +144 -0
- package/dist/durable/GitHubWorkflowSource.mjs +209 -0
- package/dist/durable/GitHubWorkflowSource.mjs.map +1 -0
- package/dist/durable/MessageDelivery.d.mts +492 -0
- package/dist/durable/MessageDelivery.mjs +495 -0
- package/dist/durable/MessageDelivery.mjs.map +1 -0
- package/dist/durable/MessageDeliveryStoreConformance.d.mts +358 -0
- package/dist/durable/MessageDeliveryStoreConformance.mjs +243 -0
- package/dist/durable/MessageDeliveryStoreConformance.mjs.map +1 -0
- package/dist/durable/OperationAuthorizer.d.mts +2 -0
- package/dist/durable/OperationAuthorizer.mjs +70 -0
- package/dist/durable/OperationAuthorizer.mjs.map +1 -0
- package/dist/durable/PersistentHistory.d.mts +16 -0
- package/dist/durable/PersistentHistory.mjs +125 -0
- package/dist/durable/PersistentHistory.mjs.map +1 -0
- package/dist/durable/PreparedInputAdmission.d.mts +2 -0
- package/dist/durable/PreparedInputAdmission.mjs +34 -0
- package/dist/durable/PreparedInputAdmission.mjs.map +1 -0
- package/dist/durable/Records.d.mts +2 -0
- package/dist/durable/Records.mjs +795 -0
- package/dist/durable/Records.mjs.map +1 -0
- package/dist/durable/Recovery.d.mts +2 -0
- package/dist/durable/Recovery.mjs +739 -0
- package/dist/durable/Recovery.mjs.map +1 -0
- package/dist/durable/RunJournal.d.mts +2 -0
- package/dist/durable/RunJournal.mjs +2 -0
- package/dist/durable/Schedule.d.mts +2 -0
- package/dist/durable/Schedule.mjs +382 -0
- package/dist/durable/Schedule.mjs.map +1 -0
- package/dist/durable/ScheduleStoreConformance.d.mts +18 -0
- package/dist/durable/ScheduleStoreConformance.mjs +420 -0
- package/dist/durable/ScheduleStoreConformance.mjs.map +1 -0
- package/dist/durable/ScheduleTransition.d.mts +24 -0
- package/dist/durable/ScheduleTransition.mjs +180 -0
- package/dist/durable/ScheduleTransition.mjs.map +1 -0
- package/dist/durable/Scheduling.d.mts +54 -0
- package/dist/durable/Scheduling.mjs +2 -0
- package/dist/durable/SqlMemoryStore.d.mts +30 -0
- package/dist/durable/SqlMemoryStore.mjs +435 -0
- package/dist/durable/SqlMemoryStore.mjs.map +1 -0
- package/dist/durable/SqlMessageDeliveryStore.d.mts +40 -0
- package/dist/durable/SqlMessageDeliveryStore.mjs +167 -0
- package/dist/durable/SqlMessageDeliveryStore.mjs.map +1 -0
- package/dist/durable/SqlStorageV2Upgrade.d.mts +22 -0
- package/dist/durable/SqlStorageV2Upgrade.mjs +459 -0
- package/dist/durable/SqlStorageV2Upgrade.mjs.map +1 -0
- package/dist/durable/SqlSubscriptionStore.d.mts +63 -0
- package/dist/durable/SqlSubscriptionStore.mjs +751 -0
- package/dist/durable/SqlSubscriptionStore.mjs.map +1 -0
- package/dist/durable/SubmissionLedger.d.mts +3 -0
- package/dist/durable/SubmissionLedger.mjs +914 -0
- package/dist/durable/SubmissionLedger.mjs.map +1 -0
- package/dist/durable/SubmissionLedgerConformance.d.mts +30 -0
- package/dist/durable/SubmissionLedgerConformance.mjs +1986 -0
- package/dist/durable/SubmissionLedgerConformance.mjs.map +1 -0
- package/dist/durable/SubmissionStatus.d.mts +2 -0
- package/dist/durable/SubmissionStatus.mjs +18 -0
- package/dist/durable/SubmissionStatus.mjs.map +1 -0
- package/dist/durable/Subscription.d.mts +2 -0
- package/dist/durable/Subscription.mjs +360 -0
- package/dist/durable/Subscription.mjs.map +1 -0
- package/dist/durable/SubscriptionInput.d.mts +2 -0
- package/dist/durable/SubscriptionInput.mjs +52 -0
- package/dist/durable/SubscriptionInput.mjs.map +1 -0
- package/dist/durable/SubscriptionStoreConformance.d.mts +17 -0
- package/dist/durable/SubscriptionStoreConformance.mjs +625 -0
- package/dist/durable/SubscriptionStoreConformance.mjs.map +1 -0
- package/dist/durable/SubscriptionTools.d.mts +273 -0
- package/dist/durable/SubscriptionTools.mjs +190 -0
- package/dist/durable/SubscriptionTools.mjs.map +1 -0
- package/dist/durable/SubscriptionTransition.d.mts +21 -0
- package/dist/durable/SubscriptionTransition.mjs +148 -0
- package/dist/durable/SubscriptionTransition.mjs.map +1 -0
- package/dist/durable/Subscriptions.d.mts +73 -0
- package/dist/durable/Subscriptions.mjs +627 -0
- package/dist/durable/Subscriptions.mjs.map +1 -0
- package/dist/durable/ThreadContextHistory.d.mts +32 -0
- package/dist/durable/ThreadContextHistory.mjs +152 -0
- package/dist/durable/ThreadContextHistory.mjs.map +1 -0
- package/dist/durable/ThreadContextHistoryProjection.d.mts +60 -0
- package/dist/durable/ThreadContextHistoryProjection.mjs +149 -0
- package/dist/durable/ThreadContextHistoryProjection.mjs.map +1 -0
- package/dist/durable/ThreadInvariants.d.mts +74 -0
- package/dist/durable/ThreadInvariants.mjs +200 -0
- package/dist/durable/ThreadInvariants.mjs.map +1 -0
- package/dist/durable/ThreadProjection.d.mts +78 -0
- package/dist/durable/ThreadProjection.mjs +158 -0
- package/dist/durable/ThreadProjection.mjs.map +1 -0
- package/dist/durable/ThreadProjectionMaintenance.d.mts +49 -0
- package/dist/durable/ThreadProjectionMaintenance.mjs +33 -0
- package/dist/durable/ThreadProjectionMaintenance.mjs.map +1 -0
- package/dist/durable/ThreadStore.d.mts +2 -0
- package/dist/durable/ThreadStore.mjs +147 -0
- package/dist/durable/ThreadStore.mjs.map +1 -0
- package/dist/durable/ThreadStoreConformance.d.mts +29 -0
- package/dist/durable/ThreadStoreConformance.mjs +248 -0
- package/dist/durable/ThreadStoreConformance.mjs.map +1 -0
- package/dist/durable/ToolReconciler.d.mts +2 -0
- package/dist/durable/ToolReconciler.mjs +81 -0
- package/dist/durable/ToolReconciler.mjs.map +1 -0
- package/dist/durable/WakeScheduler.d.mts +2 -0
- package/dist/durable/WakeScheduler.mjs +85 -0
- package/dist/durable/WakeScheduler.mjs.map +1 -0
- package/dist/durable/WorkerHost.d.mts +129 -0
- package/dist/durable/WorkerHost.mjs +77 -0
- package/dist/durable/WorkerHost.mjs.map +1 -0
- package/dist/engine/AgentRuntime.d.mts +2 -0
- package/dist/engine/AgentRuntime.mjs +2 -0
- package/dist/engine/Compaction.d.mts +7 -0
- package/dist/engine/Compaction.mjs +16 -0
- package/dist/engine/Compaction.mjs.map +1 -0
- package/dist/engine/ContextCompactor.d.mts +2 -0
- package/dist/engine/ContextCompactor.mjs +119 -0
- package/dist/engine/ContextCompactor.mjs.map +1 -0
- package/dist/engine/ContextHistory.d.mts +65 -0
- package/dist/engine/ContextHistory.mjs +76 -0
- package/dist/engine/ContextHistory.mjs.map +1 -0
- package/dist/engine/ContextWindow.d.mts +2 -0
- package/dist/engine/ContextWindow.mjs +59 -0
- package/dist/engine/ContextWindow.mjs.map +1 -0
- package/dist/engine/DurableStep.d.mts +2 -0
- package/dist/engine/DurableStep.mjs +62 -0
- package/dist/engine/DurableStep.mjs.map +1 -0
- package/dist/engine/MessagingHost.d.mts +2 -0
- package/dist/engine/MessagingHost.mjs +2 -0
- package/dist/engine/Output.d.mts +2 -0
- package/dist/engine/Output.mjs +8 -0
- package/dist/engine/Output.mjs.map +1 -0
- package/dist/engine/RunEventSink.d.mts +2 -0
- package/dist/engine/RunEventSink.mjs +22 -0
- package/dist/engine/RunEventSink.mjs.map +1 -0
- package/dist/engine/RunOptions.d.mts +2 -0
- package/dist/engine/RunOptions.mjs +122 -0
- package/dist/engine/RunOptions.mjs.map +1 -0
- package/dist/engine/SubagentHost.d.mts +2 -0
- package/dist/engine/SubagentHost.mjs +72 -0
- package/dist/engine/SubagentHost.mjs.map +1 -0
- package/dist/engine/ThreadHistory.d.mts +2 -0
- package/dist/engine/ThreadHistory.mjs +67 -0
- package/dist/engine/ThreadHistory.mjs.map +1 -0
- package/dist/engine/ToolBroker.d.mts +2 -0
- package/dist/engine/ToolBroker.mjs +42 -0
- package/dist/engine/ToolBroker.mjs.map +1 -0
- package/dist/foreign-diagnostic-DBQ1XHKh.mjs +37 -0
- package/dist/foreign-diagnostic-DBQ1XHKh.mjs.map +1 -0
- package/dist/index.d.mts +88 -54
- package/dist/index.mjs +97 -54
- package/dist/output-contract-BC7tjlGw.mjs +84 -0
- package/dist/output-contract-BC7tjlGw.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/sandbox/CodeExecutor.d.mts +194 -0
- package/dist/sandbox/CodeExecutor.mjs +278 -0
- package/dist/sandbox/CodeExecutor.mjs.map +1 -0
- package/dist/sandbox/InteractiveBrowser.d.mts +142 -0
- package/dist/sandbox/InteractiveBrowser.mjs +165 -0
- package/dist/sandbox/InteractiveBrowser.mjs.map +1 -0
- package/dist/sandbox/PageCapture.d.mts +2 -0
- package/dist/sandbox/PageCapture.mjs +450 -0
- package/dist/sandbox/PageCapture.mjs.map +1 -0
- package/dist/sandbox/PageCrawl.d.mts +88 -0
- package/dist/sandbox/PageCrawl.mjs +124 -0
- package/dist/sandbox/PageCrawl.mjs.map +1 -0
- package/dist/sandbox/PageScreenshot.d.mts +2 -0
- package/dist/sandbox/PageScreenshot.mjs +53 -0
- package/dist/sandbox/PageScreenshot.mjs.map +1 -0
- package/dist/sandbox/ProtectedBrowser.d.mts +252 -0
- package/dist/sandbox/ProtectedBrowser.mjs +270 -0
- package/dist/sandbox/ProtectedBrowser.mjs.map +1 -0
- package/dist/sandbox/Sandbox.d.mts +2 -0
- package/dist/sandbox/Sandbox.mjs +208 -0
- package/dist/sandbox/Sandbox.mjs.map +1 -0
- package/dist/utf8-BYfhEkcj.mjs +11 -0
- package/dist/utf8-BYfhEkcj.mjs.map +1 -0
- package/dist/utf8-DchGZRBd.mjs +23 -0
- package/dist/utf8-DchGZRBd.mjs.map +1 -0
- package/package.json +1 -1
- package/src/Ephemeral.ts +17 -0
- package/src/ToolExposure.ts +2 -2
- package/src/capabilities/Approval.ts +352 -0
- package/src/capabilities/Budget.ts +536 -0
- package/src/capabilities/CodeMode.ts +1090 -0
- package/src/capabilities/Commands.ts +86 -0
- package/src/capabilities/ContextTools.ts +184 -0
- package/src/capabilities/Mcp.ts +427 -0
- package/src/capabilities/McpClient.ts +967 -0
- package/src/capabilities/MemoryNotes.ts +174 -0
- package/src/capabilities/Messaging.ts +261 -0
- package/src/capabilities/ModelContext.ts +354 -0
- package/src/capabilities/Redaction.ts +157 -0
- package/src/capabilities/Remembering.ts +550 -0
- package/src/capabilities/RunHooks.ts +280 -0
- package/src/capabilities/SemanticMemory.ts +447 -0
- package/src/capabilities/Subagent.ts +2277 -0
- package/src/capabilities/SubagentReservations.ts +850 -0
- package/src/capabilities/ToolDiscovery.ts +339 -0
- package/src/capabilities/WebCapture.ts +722 -0
- package/src/capabilities/WebSearch.ts +184 -0
- package/src/capabilities/internal/subagent-background.ts +1106 -0
- package/src/capabilities/internal/subagent-contract.ts +142 -0
- package/src/capabilities/internal/subagent-policy.ts +235 -0
- package/src/capabilities/internal/subagent-reporting.ts +261 -0
- package/src/capabilities/internal/utf8.ts +10 -0
- package/src/core/Agent.ts +575 -0
- package/src/core/AgentError.ts +152 -0
- package/src/core/AgentPolicy.ts +153 -0
- package/src/core/AgentUpdates.ts +100 -0
- package/src/core/IdGenerator.ts +39 -0
- package/src/core/Identifiers.ts +48 -0
- package/src/core/Memory.ts +290 -0
- package/src/core/MemoryNamespace.ts +196 -0
- package/src/core/MemoryReference.ts +121 -0
- package/src/core/MemoryRevalidation.ts +185 -0
- package/src/core/MemoryStore.ts +368 -0
- package/src/core/Messaging.ts +90 -0
- package/src/core/Receipt.ts +42 -0
- package/src/core/RememberingStore.ts +300 -0
- package/src/core/RunEvent.ts +366 -0
- package/src/core/RunPolicyUsage.ts +17 -0
- package/src/core/SemanticMemoryIndex.ts +250 -0
- package/src/core/SemanticMemoryRevalidation.ts +254 -0
- package/src/core/SubagentContract.ts +196 -0
- package/src/core/Thread.ts +371 -0
- package/src/core/ToolExposure.ts +74 -0
- package/src/core/ToolResult.ts +170 -0
- package/src/core/Usage.ts +529 -0
- package/src/core/Worker.ts +173 -0
- package/src/core/internal/agent-updates.ts +32 -0
- package/src/core/internal/utf8.ts +23 -0
- package/src/durable/ActivityStore.ts +160 -0
- package/src/durable/Admin.ts +496 -0
- package/src/durable/AgentRegistration.ts +13 -0
- package/src/durable/Certification.ts +210 -0
- package/src/durable/CommittedActivity.ts +296 -0
- package/src/durable/Digest.ts +93 -0
- package/src/durable/DurableAgentRuntime.ts +10328 -0
- package/src/durable/DurableFailpoint.ts +103 -0
- package/src/durable/DurableFailpointTestControl.ts +39 -0
- package/src/durable/EventSource.ts +172 -0
- package/src/durable/GitHubWorkflowSource.ts +453 -0
- package/src/durable/MessageDelivery.ts +708 -0
- package/src/durable/MessageDeliveryStoreConformance.ts +363 -0
- package/src/durable/OperationAuthorizer.ts +85 -0
- package/src/durable/PersistentHistory.ts +260 -0
- package/src/durable/PreparedInputAdmission.ts +44 -0
- package/src/durable/Records.ts +1010 -0
- package/src/durable/Recovery.ts +1018 -0
- package/src/durable/RunJournal.ts +1479 -0
- package/src/durable/Schedule.ts +504 -0
- package/src/durable/ScheduleStoreConformance.ts +638 -0
- package/src/durable/ScheduleTransition.ts +265 -0
- package/src/durable/Scheduling.ts +1077 -0
- package/src/durable/SqlMemoryStore.ts +774 -0
- package/src/durable/SqlMessageDeliveryStore.ts +302 -0
- package/src/durable/SqlStorageV2Upgrade.ts +500 -0
- package/src/durable/SqlSubscriptionStore.ts +1440 -0
- package/src/durable/SubmissionLedger.ts +1201 -0
- package/src/durable/SubmissionLedgerConformance.ts +4579 -0
- package/src/durable/SubmissionStatus.ts +14 -0
- package/src/durable/Subscription.ts +544 -0
- package/src/durable/SubscriptionInput.ts +144 -0
- package/src/durable/SubscriptionStoreConformance.ts +945 -0
- package/src/durable/SubscriptionTools.ts +327 -0
- package/src/durable/SubscriptionTransition.ts +302 -0
- package/src/durable/Subscriptions.ts +1210 -0
- package/src/durable/ThreadContextHistory.ts +227 -0
- package/src/durable/ThreadContextHistoryProjection.ts +216 -0
- package/src/durable/ThreadInvariants.ts +442 -0
- package/src/durable/ThreadProjection.ts +269 -0
- package/src/durable/ThreadProjectionMaintenance.ts +67 -0
- package/src/durable/ThreadStore.ts +242 -0
- package/src/durable/ThreadStoreConformance.ts +710 -0
- package/src/durable/ToolReconciler.ts +105 -0
- package/src/durable/WakeScheduler.ts +134 -0
- package/src/durable/WorkerHost.ts +171 -0
- package/src/durable/internal/agent-registration.ts +525 -0
- package/src/durable/internal/agent-updates.ts +302 -0
- package/src/durable/internal/foreign-diagnostic.ts +47 -0
- package/src/durable/internal/journal-checkpoint.ts +104 -0
- package/src/durable/internal/journal-metadata.ts +107 -0
- package/src/durable/internal/messaging-host.ts +608 -0
- package/src/durable/internal/schedule-time.ts +303 -0
- package/src/durable/internal/storage-v2-layout.ts +163 -0
- package/src/durable/internal/storage-v2.ts +73 -0
- package/src/durable/internal/worker-host.ts +2467 -0
- package/src/durable/internal/worker-runtime.ts +9 -0
- package/src/engine/AgentRuntime.ts +37 -0
- package/src/engine/Compaction.ts +11 -0
- package/src/engine/ContextCompactor.ts +277 -0
- package/src/engine/ContextHistory.ts +73 -0
- package/src/engine/ContextWindow.ts +72 -0
- package/src/engine/DurableStep.ts +132 -0
- package/src/engine/MessagingHost.ts +110 -0
- package/src/engine/Output.ts +2 -0
- package/src/engine/RunEventSink.ts +132 -0
- package/src/engine/RunOptions.ts +1077 -0
- package/src/engine/SubagentHost.ts +181 -0
- package/src/engine/ThreadHistory.ts +120 -0
- package/src/engine/ToolBroker.ts +157 -0
- package/src/engine/ToolExposure.ts +48 -0
- package/src/engine/internal/agent-runtime.ts +10117 -0
- package/src/engine/internal/bounded-value.ts +315 -0
- package/src/engine/internal/compaction.ts +678 -0
- package/src/engine/internal/error-diagnostic.ts +39 -0
- package/src/engine/internal/output-contract.ts +173 -0
- package/src/engine/internal/primitive-delta.ts +80 -0
- package/src/engine/internal/provider-result-staging.ts +211 -0
- package/src/engine/internal/tool-derivative.ts +106 -0
- package/src/engine/internal/tool-exposure.ts +224 -0
- package/src/engine/internal/tool-telemetry.ts +484 -0
- package/src/index.ts +96 -53
- package/src/sandbox/CodeExecutor.ts +352 -0
- package/src/sandbox/InteractiveBrowser.ts +257 -0
- package/src/sandbox/PageCapture.ts +692 -0
- package/src/sandbox/PageCrawl.ts +148 -0
- package/src/sandbox/PageScreenshot.ts +81 -0
- package/src/sandbox/ProtectedBrowser.ts +413 -0
- package/src/sandbox/Sandbox.ts +241 -0
- package/dist/Agent.d.mts +0 -5
- package/dist/Agent.mjs +0 -10
- package/dist/Agent.mjs.map +0 -1
- package/dist/AgentError.d.mts +0 -5
- package/dist/AgentError.mjs +0 -10
- package/dist/AgentError.mjs.map +0 -1
- package/dist/AgentPolicy.d.mts +0 -5
- package/dist/AgentPolicy.mjs +0 -10
- package/dist/AgentPolicy.mjs.map +0 -1
- package/dist/AgentRuntime.d.mts +0 -5
- package/dist/AgentRuntime.mjs +0 -10
- package/dist/AgentRuntime.mjs.map +0 -1
- package/dist/AgentUpdates.d.mts +0 -5
- package/dist/AgentUpdates.mjs +0 -10
- package/dist/AgentUpdates.mjs.map +0 -1
- package/dist/Approval.d.mts +0 -5
- package/dist/Approval.mjs +0 -10
- package/dist/Approval.mjs.map +0 -1
- package/dist/Budget.d.mts +0 -5
- package/dist/Budget.mjs +0 -10
- package/dist/Budget.mjs.map +0 -1
- package/dist/CodeMode.d.mts +0 -5
- package/dist/CodeMode.mjs +0 -10
- package/dist/CodeMode.mjs.map +0 -1
- package/dist/Commands.d.mts +0 -5
- package/dist/Commands.mjs +0 -10
- package/dist/Commands.mjs.map +0 -1
- package/dist/Compaction.d.mts +0 -5
- package/dist/Compaction.mjs +0 -10
- package/dist/Compaction.mjs.map +0 -1
- package/dist/ContextCompactor.d.mts +0 -5
- package/dist/ContextCompactor.mjs +0 -10
- package/dist/ContextCompactor.mjs.map +0 -1
- package/dist/ContextHistory.d.mts +0 -5
- package/dist/ContextHistory.mjs +0 -10
- package/dist/ContextHistory.mjs.map +0 -1
- package/dist/ContextTools.d.mts +0 -5
- package/dist/ContextTools.mjs +0 -10
- package/dist/ContextTools.mjs.map +0 -1
- package/dist/ContextWindow.d.mts +0 -5
- package/dist/ContextWindow.mjs +0 -10
- package/dist/ContextWindow.mjs.map +0 -1
- package/dist/DurableStep.d.mts +0 -5
- package/dist/DurableStep.mjs +0 -10
- package/dist/DurableStep.mjs.map +0 -1
- package/dist/EphemeralThreads.d.mts +0 -5
- package/dist/EphemeralThreads.mjs +0 -10
- package/dist/EphemeralThreads.mjs.map +0 -1
- package/dist/IdGenerator.d.mts +0 -5
- package/dist/IdGenerator.mjs +0 -10
- package/dist/IdGenerator.mjs.map +0 -1
- package/dist/Identifiers.d.mts +0 -5
- package/dist/Identifiers.mjs +0 -10
- package/dist/Identifiers.mjs.map +0 -1
- package/dist/Mcp.d.mts +0 -5
- package/dist/Mcp.mjs +0 -10
- package/dist/Mcp.mjs.map +0 -1
- package/dist/McpClient.d.mts +0 -5
- package/dist/McpClient.mjs +0 -10
- package/dist/McpClient.mjs.map +0 -1
- package/dist/Memory.d.mts +0 -5
- package/dist/Memory.mjs +0 -10
- package/dist/Memory.mjs.map +0 -1
- package/dist/MemoryNamespace.d.mts +0 -5
- package/dist/MemoryNamespace.mjs +0 -10
- package/dist/MemoryNamespace.mjs.map +0 -1
- package/dist/MemoryNotes.d.mts +0 -5
- package/dist/MemoryNotes.mjs +0 -10
- package/dist/MemoryNotes.mjs.map +0 -1
- package/dist/MemoryReference.d.mts +0 -5
- package/dist/MemoryReference.mjs +0 -10
- package/dist/MemoryReference.mjs.map +0 -1
- package/dist/MemoryRevalidation.d.mts +0 -5
- package/dist/MemoryRevalidation.mjs +0 -10
- package/dist/MemoryRevalidation.mjs.map +0 -1
- package/dist/MemoryStore.d.mts +0 -5
- package/dist/MemoryStore.mjs +0 -10
- package/dist/MemoryStore.mjs.map +0 -1
- package/dist/Messaging.d.mts +0 -5
- package/dist/Messaging.mjs +0 -10
- package/dist/Messaging.mjs.map +0 -1
- package/dist/MessagingHost.d.mts +0 -5
- package/dist/MessagingHost.mjs +0 -10
- package/dist/MessagingHost.mjs.map +0 -1
- package/dist/ModelContext.d.mts +0 -5
- package/dist/ModelContext.mjs +0 -10
- package/dist/ModelContext.mjs.map +0 -1
- package/dist/Receipt.d.mts +0 -5
- package/dist/Receipt.mjs +0 -10
- package/dist/Receipt.mjs.map +0 -1
- package/dist/Redaction.d.mts +0 -5
- package/dist/Redaction.mjs +0 -10
- package/dist/Redaction.mjs.map +0 -1
- package/dist/Remembering.d.mts +0 -5
- package/dist/Remembering.mjs +0 -10
- package/dist/Remembering.mjs.map +0 -1
- package/dist/RememberingStore.d.mts +0 -5
- package/dist/RememberingStore.mjs +0 -10
- package/dist/RememberingStore.mjs.map +0 -1
- package/dist/RunEvent.d.mts +0 -5
- package/dist/RunEvent.mjs +0 -10
- package/dist/RunEvent.mjs.map +0 -1
- package/dist/RunEventSink.d.mts +0 -5
- package/dist/RunEventSink.mjs +0 -10
- package/dist/RunEventSink.mjs.map +0 -1
- package/dist/RunHooks.d.mts +0 -5
- package/dist/RunHooks.mjs +0 -10
- package/dist/RunHooks.mjs.map +0 -1
- package/dist/RunOptions.d.mts +0 -5
- package/dist/RunOptions.mjs +0 -10
- package/dist/RunOptions.mjs.map +0 -1
- package/dist/RunPolicyUsage.d.mts +0 -5
- package/dist/RunPolicyUsage.mjs +0 -10
- package/dist/RunPolicyUsage.mjs.map +0 -1
- package/dist/SemanticMemory.d.mts +0 -5
- package/dist/SemanticMemory.mjs +0 -10
- package/dist/SemanticMemory.mjs.map +0 -1
- package/dist/SemanticMemoryIndex.d.mts +0 -5
- package/dist/SemanticMemoryIndex.mjs +0 -10
- package/dist/SemanticMemoryIndex.mjs.map +0 -1
- package/dist/SemanticMemoryRevalidation.d.mts +0 -5
- package/dist/SemanticMemoryRevalidation.mjs +0 -10
- package/dist/SemanticMemoryRevalidation.mjs.map +0 -1
- package/dist/Subagent.d.mts +0 -5
- package/dist/Subagent.mjs +0 -10
- package/dist/Subagent.mjs.map +0 -1
- package/dist/SubagentContract.d.mts +0 -5
- package/dist/SubagentContract.mjs +0 -10
- package/dist/SubagentContract.mjs.map +0 -1
- package/dist/SubagentHost.d.mts +0 -5
- package/dist/SubagentHost.mjs +0 -10
- package/dist/SubagentHost.mjs.map +0 -1
- package/dist/SubagentReservations.d.mts +0 -5
- package/dist/SubagentReservations.mjs +0 -10
- package/dist/SubagentReservations.mjs.map +0 -1
- package/dist/ThreadHistory.d.mts +0 -5
- package/dist/ThreadHistory.mjs +0 -10
- package/dist/ThreadHistory.mjs.map +0 -1
- package/dist/ToolBroker.d.mts +0 -5
- package/dist/ToolBroker.mjs +0 -10
- package/dist/ToolBroker.mjs.map +0 -1
- package/dist/ToolDiscovery.d.mts +0 -5
- package/dist/ToolDiscovery.mjs +0 -10
- package/dist/ToolDiscovery.mjs.map +0 -1
- package/dist/ToolResult.d.mts +0 -5
- package/dist/ToolResult.mjs +0 -10
- package/dist/ToolResult.mjs.map +0 -1
- package/dist/Usage.d.mts +0 -5
- package/dist/Usage.mjs +0 -10
- package/dist/Usage.mjs.map +0 -1
- package/dist/WebCapture.d.mts +0 -5
- package/dist/WebCapture.mjs +0 -10
- package/dist/WebCapture.mjs.map +0 -1
- package/dist/WebSearch.d.mts +0 -5
- package/dist/WebSearch.mjs +0 -10
- package/dist/WebSearch.mjs.map +0 -1
- package/dist/Worker.d.mts +0 -5
- package/dist/Worker.mjs +0 -10
- package/dist/Worker.mjs.map +0 -1
- package/dist/rolldown-runtime-BBjsoOtd.mjs +0 -27
- package/src/Agent.ts +0 -1
- package/src/AgentError.ts +0 -1
- package/src/AgentPolicy.ts +0 -1
- package/src/AgentRuntime.ts +0 -1
- package/src/AgentUpdates.ts +0 -1
- package/src/Approval.ts +0 -1
- package/src/Budget.ts +0 -1
- package/src/CodeMode.ts +0 -1
- package/src/Commands.ts +0 -1
- package/src/Compaction.ts +0 -1
- package/src/ContextCompactor.ts +0 -1
- package/src/ContextHistory.ts +0 -1
- package/src/ContextTools.ts +0 -1
- package/src/ContextWindow.ts +0 -1
- package/src/DurableStep.ts +0 -1
- package/src/EphemeralThreads.ts +0 -1
- package/src/IdGenerator.ts +0 -1
- package/src/Identifiers.ts +0 -1
- package/src/Mcp.ts +0 -1
- package/src/McpClient.ts +0 -1
- package/src/Memory.ts +0 -1
- package/src/MemoryNamespace.ts +0 -1
- package/src/MemoryNotes.ts +0 -1
- package/src/MemoryReference.ts +0 -1
- package/src/MemoryRevalidation.ts +0 -1
- package/src/MemoryStore.ts +0 -1
- package/src/Messaging.ts +0 -1
- package/src/MessagingHost.ts +0 -1
- package/src/ModelContext.ts +0 -1
- package/src/Receipt.ts +0 -1
- package/src/Redaction.ts +0 -1
- package/src/Remembering.ts +0 -1
- package/src/RememberingStore.ts +0 -1
- package/src/RunEvent.ts +0 -1
- package/src/RunEventSink.ts +0 -1
- package/src/RunHooks.ts +0 -1
- package/src/RunOptions.ts +0 -1
- package/src/RunPolicyUsage.ts +0 -1
- package/src/SemanticMemory.ts +0 -1
- package/src/SemanticMemoryIndex.ts +0 -1
- package/src/SemanticMemoryRevalidation.ts +0 -1
- package/src/Subagent.ts +0 -1
- package/src/SubagentContract.ts +0 -1
- package/src/SubagentHost.ts +0 -1
- package/src/SubagentReservations.ts +0 -1
- package/src/ThreadHistory.ts +0 -1
- package/src/ToolBroker.ts +0 -1
- package/src/ToolDiscovery.ts +0 -1
- package/src/ToolResult.ts +0 -1
- package/src/Usage.ts +0 -1
- package/src/WebCapture.ts +0 -1
- package/src/WebSearch.ts +0 -1
- package/src/Worker.ts +0 -1
|
@@ -0,0 +1,4562 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { n as utf8ByteLength } from "./utf8-DchGZRBd.mjs";
|
|
3
|
+
import { applyToolResultBounds, unserializableToolResult } from "./core/ToolResult.mjs";
|
|
4
|
+
import { ReceiptId, RunId, SubmissionId, ThreadId, ToolCallId } from "./core/Identifiers.mjs";
|
|
5
|
+
import { IdempotencyKey } from "./core/Receipt.mjs";
|
|
6
|
+
import { Emitter, Update, UpdateError } from "./core/AgentUpdates.mjs";
|
|
7
|
+
import { AgentApprovalDenied, AgentApprovalPending, AgentInputError, AgentOutputError, AgentPolicyError, AgentRunDispositionError, AgentToolAuthorizationDenied, ContextBudgetError, ContextOverflowError, ModelProtocolError } from "./core/AgentError.mjs";
|
|
8
|
+
import { IdGenerator as IdGenerator$1 } from "./core/IdGenerator.mjs";
|
|
9
|
+
import { DelegationDepth, SubagentParentLink, getToolExecutionKind, isSubagentToolAllowed } from "./core/SubagentContract.mjs";
|
|
10
|
+
import { a as PinnedTool, c as ToolNames, i as Limits, l as ToolNamespace, n as DiscoveryTool, o as Selection, r as IncludesCatalogDocumentation, s as Snapshot, t as AdditionalToolCatalog } from "./ToolExposure-C93U1sPu.mjs";
|
|
11
|
+
import { InputTokenUsage, ModelCallUsage, ModelResponseIdentity, OutputTokenUsage, RunTotals, RunUsageReport, emptyRunTotals, sumRunTotals, unknownRunTotals } from "./core/Usage.mjs";
|
|
12
|
+
import { AgentUpdateEmitted, ApprovalRequested, BudgetWarning, CompactionPerformed, ModelStarted, ReasoningDelta, RunCompleted, RunFailed, RunStarted, RunSuspended, SubagentCompleted, SubagentFailed, SubagentInterrupted, SubagentJoined, SubagentProgress, SubagentRequested, SubagentStarted, TextDelta, ToolCallDeclared, ToolCallFailed, ToolCallStarted, ToolCallSucceeded, ToolProgress, TurnCompleted, TurnStarted } from "./core/RunEvent.mjs";
|
|
13
|
+
import { FrameworkMessage } from "./core/Worker.mjs";
|
|
14
|
+
import { t as MessagingHost } from "./MessagingHost-CWi_PP3g.mjs";
|
|
15
|
+
import { SubagentHost } from "./engine/SubagentHost.mjs";
|
|
16
|
+
import { ThreadHistory, ThreadHistoryError } from "./engine/ThreadHistory.mjs";
|
|
17
|
+
import { n as RunToolVisibility, t as CurrentToolCatalog } from "./ToolExposure-9uF_Yo0T.mjs";
|
|
18
|
+
import { n as isTextOutput, r as outputSchemaContract, t as insertOutputContract } from "./output-contract-BC7tjlGw.mjs";
|
|
19
|
+
import { _ as initialCompactionState, b as boundedCanonicalJsonSnapshot, d as collectCoveredMessages, f as contextWindowId, g as evaluateMessageTokenEstimates, h as estimatePromptTokens, s as buildCompactedView, v as isContextOverflowMessage, x as boundedJsonSnapshot } from "./compaction-C2OTceKw.mjs";
|
|
20
|
+
import { ContextRolloverRequest, ContextRolloverSelection, ContextRolloverTool, ContextWindow, ContextWindowStatus, ModelCallContext } from "./engine/ContextWindow.mjs";
|
|
21
|
+
import { DurableStep, DurableStepError, getToolExecutionClass } from "./engine/DurableStep.mjs";
|
|
22
|
+
import { CompactionDecision, CompactionError, ContextCompactor } from "./engine/ContextCompactor.mjs";
|
|
23
|
+
import { RunEventSink, RunEventSinkClosedError } from "./engine/RunEventSink.mjs";
|
|
24
|
+
import { AgentUpdateAcceptance, CurrentToolFailureObserver, ModelUsageAccounting, RunContextPreparation, RunResumeUsageSchema, RunToolAuthorization, RunToolScheduling, RunTurnResumeSettledCallSchema } from "./engine/RunOptions.mjs";
|
|
25
|
+
import { ToolBroker, ToolBrokerConfigurationError, ToolBrokerUnavailableError } from "./engine/ToolBroker.mjs";
|
|
26
|
+
import { AiError, LanguageModel, Model, Prompt, Response, Tool, Toolkit } from "effect/unstable/ai";
|
|
27
|
+
import { Cause, Channel, Clock, Context, DateTime, Duration, Effect, Equal, ErrorReporter, Exit, Fiber, Layer, Metric, Option, PubSub, Queue, Redacted, Result, Schema, Scope, Semaphore, Stream, Tracer } from "effect";
|
|
28
|
+
//#region src/engine/internal/bounded-value.ts
|
|
29
|
+
const DEFAULT_MAX_DEPTH = 128;
|
|
30
|
+
const OBJECT_OVERHEAD_BYTES = 32;
|
|
31
|
+
const PROPERTY_OVERHEAD_BYTES = 8;
|
|
32
|
+
const dateTimeUtcPrototype = Object.getPrototypeOf(DateTime.makeUnsafe(0));
|
|
33
|
+
const redactedPrototype = Object.getPrototypeOf(Redacted.make(void 0));
|
|
34
|
+
const intrinsicViewPrototypes = /* @__PURE__ */ new Set([
|
|
35
|
+
DataView.prototype,
|
|
36
|
+
Int8Array.prototype,
|
|
37
|
+
Uint8Array.prototype,
|
|
38
|
+
Uint8ClampedArray.prototype,
|
|
39
|
+
Int16Array.prototype,
|
|
40
|
+
Uint16Array.prototype,
|
|
41
|
+
Int32Array.prototype,
|
|
42
|
+
Uint32Array.prototype,
|
|
43
|
+
Float32Array.prototype,
|
|
44
|
+
Float64Array.prototype,
|
|
45
|
+
BigInt64Array.prototype,
|
|
46
|
+
BigUint64Array.prototype
|
|
47
|
+
]);
|
|
48
|
+
const arrayBufferByteLengthGetter = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength")?.get;
|
|
49
|
+
const typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);
|
|
50
|
+
const typedArrayBufferGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer")?.get;
|
|
51
|
+
const typedArrayLengthGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "length")?.get;
|
|
52
|
+
const dataViewBufferGetter = Object.getOwnPropertyDescriptor(DataView.prototype, "buffer")?.get;
|
|
53
|
+
const urlConstructor = Reflect.get(globalThis, "URL");
|
|
54
|
+
const urlPrototypeDescriptor = typeof urlConstructor === "function" ? Object.getOwnPropertyDescriptor(urlConstructor, "prototype") : void 0;
|
|
55
|
+
const urlHrefGetter = urlPrototypeDescriptor !== void 0 && "value" in urlPrototypeDescriptor && urlPrototypeDescriptor.value !== null && typeof urlPrototypeDescriptor.value === "object" ? Object.getOwnPropertyDescriptor(urlPrototypeDescriptor.value, "href")?.get : void 0;
|
|
56
|
+
const urlPrototype = urlPrototypeDescriptor !== void 0 && "value" in urlPrototypeDescriptor && urlPrototypeDescriptor.value !== null && typeof urlPrototypeDescriptor.value === "object" ? urlPrototypeDescriptor.value : void 0;
|
|
57
|
+
const intrinsicArrayBufferByteLength = (value) => {
|
|
58
|
+
if (arrayBufferByteLengthGetter === void 0) return void 0;
|
|
59
|
+
try {
|
|
60
|
+
const byteLength = Reflect.apply(arrayBufferByteLengthGetter, value, []);
|
|
61
|
+
return Number.isSafeInteger(byteLength) && byteLength >= 0 ? byteLength : void 0;
|
|
62
|
+
} catch {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const intrinsicViewBackingByteLength = (value) => {
|
|
67
|
+
const getters = [typedArrayBufferGetter, dataViewBufferGetter];
|
|
68
|
+
for (const getter of getters) {
|
|
69
|
+
if (getter === void 0) continue;
|
|
70
|
+
try {
|
|
71
|
+
const buffer = Reflect.apply(getter, value, []);
|
|
72
|
+
if (buffer !== null && typeof buffer === "object") return intrinsicArrayBufferByteLength(buffer);
|
|
73
|
+
} catch {}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const intrinsicTypedArrayLength = (value) => {
|
|
77
|
+
if (typedArrayLengthGetter === void 0) return void 0;
|
|
78
|
+
try {
|
|
79
|
+
const length = Reflect.apply(typedArrayLengthGetter, value, []);
|
|
80
|
+
return Number.isSafeInteger(length) && length >= 0 ? length : void 0;
|
|
81
|
+
} catch {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const intrinsicUrlByteLength = (value) => {
|
|
86
|
+
if (urlHrefGetter === void 0) return void 0;
|
|
87
|
+
try {
|
|
88
|
+
const href = Reflect.apply(urlHrefGetter, value, []);
|
|
89
|
+
return typeof href === "string" ? utf8ByteLength(href) + 2 : void 0;
|
|
90
|
+
} catch {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const inspectPrototype = (prototype, isArray, knownSafePrototypes) => {
|
|
95
|
+
if (prototype === null) return true;
|
|
96
|
+
if (isArray) return prototype === Array.prototype;
|
|
97
|
+
return prototype === Object.prototype || prototype === dateTimeUtcPrototype || knownSafePrototypes.has(prototype);
|
|
98
|
+
};
|
|
99
|
+
const isCanonicalArrayIndex = (key) => {
|
|
100
|
+
const index = Number(key);
|
|
101
|
+
return Number.isInteger(index) && index >= 0 && index < 4294967295 && String(index) === key;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Conservatively measure one retained, engine-owned JavaScript value without invoking getters,
|
|
105
|
+
* coercion, or `toJSON`. `undefined` means the value exceeded the allowance or its shape could not
|
|
106
|
+
* be measured without executing an accessor.
|
|
107
|
+
*
|
|
108
|
+
* This is a memory-retention guard, not an untrusted wire boundary. Callers must first canonicalize
|
|
109
|
+
* provider values into owned data because JavaScript offers no portable, trap-free Proxy test.
|
|
110
|
+
* Object and property overheads deliberately make the estimate larger than the visible primitive
|
|
111
|
+
* payload for ordinary response values.
|
|
112
|
+
*/
|
|
113
|
+
const boundedValueFootprint = (root, maxBytes, knownSafePrototypes = /* @__PURE__ */ new Set(), maxDepth = DEFAULT_MAX_DEPTH) => {
|
|
114
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0 || !Number.isSafeInteger(maxDepth) || maxDepth < 0) return;
|
|
115
|
+
let total = 0;
|
|
116
|
+
const ancestors = /* @__PURE__ */ new WeakSet();
|
|
117
|
+
const add = (bytes) => {
|
|
118
|
+
if (!Number.isSafeInteger(bytes) || bytes < 0 || bytes > maxBytes - total) return false;
|
|
119
|
+
total += bytes;
|
|
120
|
+
return true;
|
|
121
|
+
};
|
|
122
|
+
const canMaterializeIndexedKeys = (count) => {
|
|
123
|
+
if (!Number.isSafeInteger(count) || count < 0) return false;
|
|
124
|
+
if (count === 0) return true;
|
|
125
|
+
const largestKeyBytes = utf8ByteLength(String(count - 1));
|
|
126
|
+
const temporaryBytesPerKey = PROPERTY_OVERHEAD_BYTES + largestKeyBytes;
|
|
127
|
+
return count <= Math.floor((maxBytes - total) / temporaryBytesPerKey);
|
|
128
|
+
};
|
|
129
|
+
const visit = (value, depth) => {
|
|
130
|
+
if (depth > maxDepth) return false;
|
|
131
|
+
if (value === null) return add(4);
|
|
132
|
+
switch (typeof value) {
|
|
133
|
+
case "string": return add(utf8ByteLength(value) + 2);
|
|
134
|
+
case "boolean": return add(4);
|
|
135
|
+
case "number": return add(16);
|
|
136
|
+
case "bigint":
|
|
137
|
+
case "symbol": return false;
|
|
138
|
+
case "undefined": return add(16);
|
|
139
|
+
case "function": return false;
|
|
140
|
+
case "object": {
|
|
141
|
+
if (ancestors.has(value) || !add(OBJECT_OVERHEAD_BYTES)) return false;
|
|
142
|
+
const prototype = Object.getPrototypeOf(value);
|
|
143
|
+
let skipIndexedProperties = false;
|
|
144
|
+
let indexedPropertyCount = 0;
|
|
145
|
+
let supportedSpecialObject = false;
|
|
146
|
+
if (ArrayBuffer.isView(value)) {
|
|
147
|
+
if (!intrinsicViewPrototypes.has(prototype)) return false;
|
|
148
|
+
const byteLength = intrinsicViewBackingByteLength(value);
|
|
149
|
+
if (byteLength === void 0 || !add(byteLength)) return false;
|
|
150
|
+
if (prototype !== DataView.prototype) {
|
|
151
|
+
const length = intrinsicTypedArrayLength(value);
|
|
152
|
+
if (length === void 0) return false;
|
|
153
|
+
indexedPropertyCount = length;
|
|
154
|
+
}
|
|
155
|
+
skipIndexedProperties = true;
|
|
156
|
+
supportedSpecialObject = true;
|
|
157
|
+
}
|
|
158
|
+
if (!supportedSpecialObject) {
|
|
159
|
+
const bufferByteLength = intrinsicArrayBufferByteLength(value);
|
|
160
|
+
if (bufferByteLength !== void 0) {
|
|
161
|
+
if (prototype !== ArrayBuffer.prototype) return false;
|
|
162
|
+
if (!add(bufferByteLength)) return false;
|
|
163
|
+
supportedSpecialObject = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (!supportedSpecialObject) {
|
|
167
|
+
const urlByteLength = intrinsicUrlByteLength(value);
|
|
168
|
+
if (urlByteLength !== void 0) {
|
|
169
|
+
if (prototype !== urlPrototype) return false;
|
|
170
|
+
if (!add(urlByteLength)) return false;
|
|
171
|
+
supportedSpecialObject = true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
let redactedValue;
|
|
175
|
+
let isRedacted = false;
|
|
176
|
+
if (prototype === redactedPrototype) {
|
|
177
|
+
const labelDescriptor = Object.getOwnPropertyDescriptor(value, "label");
|
|
178
|
+
if (labelDescriptor !== void 0 && !("value" in labelDescriptor)) return false;
|
|
179
|
+
try {
|
|
180
|
+
redactedValue = Redacted.value(value);
|
|
181
|
+
isRedacted = true;
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const isArray = Array.isArray(value);
|
|
187
|
+
if (!supportedSpecialObject && !isRedacted && !inspectPrototype(prototype, isArray, knownSafePrototypes)) return false;
|
|
188
|
+
if (isArray) {
|
|
189
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
190
|
+
if (lengthDescriptor === void 0 || !("value" in lengthDescriptor) || !Number.isSafeInteger(lengthDescriptor.value) || lengthDescriptor.value < 0 || !add(lengthDescriptor.value)) return false;
|
|
191
|
+
indexedPropertyCount = lengthDescriptor.value;
|
|
192
|
+
}
|
|
193
|
+
if (!canMaterializeIndexedKeys(indexedPropertyCount)) return false;
|
|
194
|
+
ancestors.add(value);
|
|
195
|
+
try {
|
|
196
|
+
if (isRedacted && !visit(redactedValue, depth + 1)) return false;
|
|
197
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
198
|
+
if (key === "length" && isArray) continue;
|
|
199
|
+
if (typeof key === "symbol") return false;
|
|
200
|
+
if (skipIndexedProperties && isCanonicalArrayIndex(key)) continue;
|
|
201
|
+
if (!add(PROPERTY_OVERHEAD_BYTES)) return false;
|
|
202
|
+
if (!add(utf8ByteLength(key))) return false;
|
|
203
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
204
|
+
if (descriptor === void 0) return false;
|
|
205
|
+
if ("value" in descriptor) {
|
|
206
|
+
if (!visit(descriptor.value, depth + 1)) return false;
|
|
207
|
+
} else return false;
|
|
208
|
+
}
|
|
209
|
+
return true;
|
|
210
|
+
} finally {
|
|
211
|
+
ancestors.delete(value);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
};
|
|
217
|
+
try {
|
|
218
|
+
return visit(root, 0) ? total : void 0;
|
|
219
|
+
} catch {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/engine/internal/primitive-delta.ts
|
|
225
|
+
const keys = [
|
|
226
|
+
"~effect/ai/Content/Part",
|
|
227
|
+
"type",
|
|
228
|
+
"id",
|
|
229
|
+
"delta",
|
|
230
|
+
"metadata"
|
|
231
|
+
];
|
|
232
|
+
const decode = Schema.decodeUnknownOption(Schema.toType(Schema.Union([Response.TextDeltaPart, Response.ReasoningDeltaPart])));
|
|
233
|
+
const overhead = (type) => ({
|
|
234
|
+
source: boundedValueFootprint(Response.makePart(type, {
|
|
235
|
+
id: "",
|
|
236
|
+
delta: ""
|
|
237
|
+
}), Number.MAX_SAFE_INTEGER),
|
|
238
|
+
encoded: boundedValueFootprint({
|
|
239
|
+
type,
|
|
240
|
+
id: "",
|
|
241
|
+
delta: "",
|
|
242
|
+
metadata: {}
|
|
243
|
+
}, Number.MAX_SAFE_INTEGER)
|
|
244
|
+
});
|
|
245
|
+
const overheads = {
|
|
246
|
+
"text-delta": overhead("text-delta"),
|
|
247
|
+
"reasoning-delta": overhead("reasoning-delta")
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* Copy the common primitive delta into owned data before native Schema validation.
|
|
251
|
+
* Descriptor checks select this optimization; they do not replace the native codec.
|
|
252
|
+
* Extra fields, accessors, and nonempty metadata use the general ownership path.
|
|
253
|
+
* No provider object survives, including an empty metadata object's hidden storage.
|
|
254
|
+
* Unlike a generic footprint shortcut, this cannot retain an exotic backing buffer.
|
|
255
|
+
*/
|
|
256
|
+
const ownPrimitiveDelta = (part, maxBytes) => {
|
|
257
|
+
try {
|
|
258
|
+
if (part === null || typeof part !== "object") return void 0;
|
|
259
|
+
if (Array.isArray(part) || ArrayBuffer.isView(part)) return void 0;
|
|
260
|
+
const prototype = Object.getPrototypeOf(part);
|
|
261
|
+
if (prototype !== Object.prototype && prototype !== null) return void 0;
|
|
262
|
+
if (Reflect.ownKeys(part).length !== keys.length) return void 0;
|
|
263
|
+
const snapshot = {};
|
|
264
|
+
for (const key of keys) {
|
|
265
|
+
const descriptor = Object.getOwnPropertyDescriptor(part, key);
|
|
266
|
+
if (descriptor === void 0 || !("value" in descriptor)) return void 0;
|
|
267
|
+
snapshot[key] = descriptor.value;
|
|
268
|
+
}
|
|
269
|
+
const { type, id, delta, metadata } = snapshot;
|
|
270
|
+
if (type !== "text-delta" && type !== "reasoning-delta") return void 0;
|
|
271
|
+
if (typeof id !== "string" || typeof delta !== "string") return void 0;
|
|
272
|
+
if (metadata === null || typeof metadata !== "object") return void 0;
|
|
273
|
+
if (Array.isArray(metadata) || ArrayBuffer.isView(metadata)) return void 0;
|
|
274
|
+
const metadataPrototype = Object.getPrototypeOf(metadata);
|
|
275
|
+
if (metadataPrototype !== Object.prototype && metadataPrototype !== null) return void 0;
|
|
276
|
+
if (Reflect.ownKeys(metadata).length !== 0) return void 0;
|
|
277
|
+
const fixed = overheads[type];
|
|
278
|
+
if (fixed.source === void 0 || fixed.encoded === void 0) return void 0;
|
|
279
|
+
const bytes = utf8ByteLength(id) + utf8ByteLength(delta);
|
|
280
|
+
if (bytes + fixed.source > maxBytes || bytes + fixed.encoded > maxBytes) return void 0;
|
|
281
|
+
snapshot.metadata = {};
|
|
282
|
+
const validated = decode(snapshot);
|
|
283
|
+
if (Option.isNone(validated)) return void 0;
|
|
284
|
+
return {
|
|
285
|
+
ownedPart: validated.value,
|
|
286
|
+
retainedBytes: bytes + fixed.encoded
|
|
287
|
+
};
|
|
288
|
+
} catch {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/engine/internal/tool-derivative.ts
|
|
294
|
+
/** @internal Only the explicitly installed trusted observer sees this live value. */
|
|
295
|
+
const deliverToolFailure = (observer, observation) => isolateToolDerivative(Effect.suspend(() => observer.observe(observation)));
|
|
296
|
+
/**
|
|
297
|
+
* Report failures of derivative work without consuming external interruption. Reporter defects
|
|
298
|
+
* are isolated too. Neither observer delivery nor telemetry owns the authoritative Tool outcome.
|
|
299
|
+
*/
|
|
300
|
+
const reportDerivativeCause = (cause) => {
|
|
301
|
+
const reportableReasons = [];
|
|
302
|
+
const interruptionReasons = [];
|
|
303
|
+
for (const reason of cause.reasons) if (Cause.isInterruptReason(reason)) interruptionReasons.push(reason);
|
|
304
|
+
else reportableReasons.push(reason);
|
|
305
|
+
const reportExit = reportableReasons.length === 0 ? Effect.succeed(Exit.succeed(void 0)) : Effect.exit(ErrorReporter.report(Cause.fromReasons(reportableReasons)));
|
|
306
|
+
return Effect.flatMap(reportExit, (exit) => {
|
|
307
|
+
if (Exit.isFailure(exit)) {
|
|
308
|
+
for (const reason of exit.cause.reasons) if (Cause.isInterruptReason(reason)) interruptionReasons.push(reason);
|
|
309
|
+
}
|
|
310
|
+
return interruptionReasons.length === 0 ? Effect.void : Effect.failCause(Cause.fromReasons(interruptionReasons));
|
|
311
|
+
});
|
|
312
|
+
};
|
|
313
|
+
/** @internal Isolate reporting defects independently of Logger/Tracer configuration. */
|
|
314
|
+
const isolateToolDerivative = (effect) => effect.pipe(Effect.catchCause(reportDerivativeCause));
|
|
315
|
+
/**
|
|
316
|
+
* @internal Emit one authoritative value, then run derivative work from either the next pull or
|
|
317
|
+
* structured finalization on early close. Telemetry and application observation share this one
|
|
318
|
+
* owner. A started action is never retried after interruption.
|
|
319
|
+
*/
|
|
320
|
+
const emitThenAfter = (event, after) => Stream.unwrap(Effect.sync(() => {
|
|
321
|
+
let firstPull = true;
|
|
322
|
+
let afterPhase = "unarmed";
|
|
323
|
+
const runAfter = Effect.suspend(() => {
|
|
324
|
+
if (afterPhase !== "pending") return Effect.void;
|
|
325
|
+
afterPhase = "running";
|
|
326
|
+
return after.pipe(Effect.onExit(() => Effect.sync(() => {
|
|
327
|
+
afterPhase = "completed";
|
|
328
|
+
})));
|
|
329
|
+
});
|
|
330
|
+
const pull = Effect.suspend(() => {
|
|
331
|
+
if (firstPull) return Effect.map(event, (value) => {
|
|
332
|
+
firstPull = false;
|
|
333
|
+
afterPhase = "pending";
|
|
334
|
+
return [value];
|
|
335
|
+
});
|
|
336
|
+
return Effect.andThen(runAfter, Cause.done());
|
|
337
|
+
});
|
|
338
|
+
return Stream.fromPull(Effect.succeed(pull)).pipe(Stream.ensuring(Effect.exit(Effect.interruptible(runAfter)).pipe(Effect.flatMap((exit) => {
|
|
339
|
+
if (Exit.isSuccess(exit)) return Effect.void;
|
|
340
|
+
return reportDerivativeCause(exit.cause);
|
|
341
|
+
}))));
|
|
342
|
+
}));
|
|
343
|
+
//#endregion
|
|
344
|
+
//#region src/engine/internal/tool-exposure.ts
|
|
345
|
+
const invalid = (message) => ModelProtocolError.make({ message });
|
|
346
|
+
const decodeSelection = (value) => Schema.decodeUnknownEffect(Selection)(value).pipe(Effect.mapError(() => invalid("Invalid Tool exposure selection")));
|
|
347
|
+
const decodeSnapshot = (value) => Schema.decodeUnknownEffect(Snapshot)(value).pipe(Effect.mapError(() => invalid("Invalid recorded Tool exposure snapshot")), Effect.map((snapshot) => Object.freeze(Snapshot.make({
|
|
348
|
+
exposedToolNames: Object.freeze([...snapshot.exposedToolNames]),
|
|
349
|
+
...snapshot.selection === void 0 ? {} : { selection: Object.freeze(Selection.make({ toolNames: Object.freeze([...snapshot.selection.toolNames]) })) }
|
|
350
|
+
}))));
|
|
351
|
+
const eligibleCatalog = Effect.fn("ToolExposure.eligibleCatalog")(function* (definition, request, grant, depth) {
|
|
352
|
+
const visibility = yield* RunToolVisibility;
|
|
353
|
+
const entries = [];
|
|
354
|
+
for (const tool of Object.values(definition.toolkit.tools)) {
|
|
355
|
+
if (Context.get(tool.annotations, DiscoveryTool) && (getToolExecutionKind(tool.annotations) !== "ordinary" || getToolExecutionClass(tool) !== "readonly")) return yield* invalid("Discovery requires ordinary readonly Tools");
|
|
356
|
+
const namespace = Context.get(tool.annotations, ToolNamespace);
|
|
357
|
+
entries.push({
|
|
358
|
+
kind: "native",
|
|
359
|
+
tool,
|
|
360
|
+
nativeToolName: tool.name,
|
|
361
|
+
...namespace === void 0 ? {} : { namespace }
|
|
362
|
+
});
|
|
363
|
+
for (const extra of Context.get(tool.annotations, AdditionalToolCatalog)) entries.push({
|
|
364
|
+
kind: "code-mode",
|
|
365
|
+
...extra,
|
|
366
|
+
nativeToolName: tool.name
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
const names = [...new Set(entries.map((entry) => entry.tool.name))];
|
|
370
|
+
const visible = visibility === void 0 ? names : yield* visibility.visible({
|
|
371
|
+
...request,
|
|
372
|
+
toolNames: names
|
|
373
|
+
});
|
|
374
|
+
const checked = visibility === void 0 ? visible : yield* Schema.decodeEffect(ToolNames)(visible).pipe(Effect.mapError(() => invalid("Host Tool visibility returned invalid names")));
|
|
375
|
+
if (checked.some((name) => !names.includes(name))) return yield* invalid("Host Tool visibility returned an unregistered name");
|
|
376
|
+
const allowed = new Set(checked);
|
|
377
|
+
const permits = (tool) => allowed.has(tool.name) && isSubagentToolAllowed(grant, depth, tool.name, tool.annotations);
|
|
378
|
+
for (const outer of Object.values(definition.toolkit.tools)) if (permits(outer) && Context.get(outer.annotations, IncludesCatalogDocumentation) && Context.get(outer.annotations, AdditionalToolCatalog).some((entry) => !permits(entry.tool))) return yield* invalid("A Tool description includes hidden catalogue declarations; configure includeDeclarations: false");
|
|
379
|
+
return Object.freeze(entries.filter((entry) => {
|
|
380
|
+
const outer = definition.toolkit.tools[entry.nativeToolName];
|
|
381
|
+
return outer !== void 0 && permits(outer) && permits(entry.tool);
|
|
382
|
+
}).map((entry) => Object.freeze(entry)));
|
|
383
|
+
});
|
|
384
|
+
const validateSelection = Effect.fn("ToolExposure.validateSelection")(function* (value, definition, entries, requireEligible = true) {
|
|
385
|
+
const selection = yield* decodeSelection(value);
|
|
386
|
+
const eligible = new Set(entries.filter((entry) => entry.kind === "native").map((entry) => entry.nativeToolName));
|
|
387
|
+
if (selection.toolNames.some((name) => !Object.hasOwn(definition.toolkit.tools, name) || requireEligible && !eligible.has(name))) return yield* invalid("Tool selection names a Tool outside the eligible registered toolkit");
|
|
388
|
+
return Selection.make({ toolNames: Object.freeze([...selection.toolNames]) });
|
|
389
|
+
});
|
|
390
|
+
const exposureSnapshot = Effect.fn("ToolExposure.exposureSnapshot")(function* (definition, selection, entries, transformer, only) {
|
|
391
|
+
const native = entries.filter((entry) => entry.kind === "native").map((entry) => entry.tool);
|
|
392
|
+
const progressive = definition.toolExposure !== void 0 || selection !== void 0;
|
|
393
|
+
const mandatory = Object.values(definition.toolkit.tools).filter((tool) => Context.get(tool.annotations, DiscoveryTool) || Context.get(tool.annotations, ContextRolloverTool) || definition.completion?.required === true && definition.completion.tool === tool.name);
|
|
394
|
+
if (progressive && mandatory.some((tool) => !native.some((candidate) => candidate.name === tool.name))) return yield* invalid("A mandatory Tool is excluded by host visibility or the inherited grant");
|
|
395
|
+
const pinned = native.filter((tool) => Context.get(tool.annotations, PinnedTool) || mandatory.includes(tool));
|
|
396
|
+
if (selection !== void 0) yield* validateSelection(selection, definition, entries, false);
|
|
397
|
+
const selected = selection === void 0 ? void 0 : new Set(selection.toolNames);
|
|
398
|
+
const names = native.filter((tool) => selected === void 0 || selected.has(tool.name) || pinned.includes(tool)).map((tool) => tool.name).filter((name) => only === void 0 || only.includes(name));
|
|
399
|
+
if (progressive) {
|
|
400
|
+
const limits = yield* Schema.decodeEffect(Limits)({
|
|
401
|
+
maxTools: definition.toolExposure?.maxTools ?? 64,
|
|
402
|
+
maxSchemaBytes: definition.toolExposure?.maxSchemaBytes ?? 262144
|
|
403
|
+
}).pipe(Effect.mapError(() => invalid("Invalid Tool exposure bounds")));
|
|
404
|
+
if (names.length > limits.maxTools) return yield* invalid("Tool exposure exceeds the configured Tool count bound");
|
|
405
|
+
if ((yield* Effect.try({
|
|
406
|
+
try: () => utf8ByteLength(JSON.stringify(names.map((name) => {
|
|
407
|
+
const tool = definition.toolkit.tools[name];
|
|
408
|
+
if (tool === void 0) throw new Error("Selected Tool is unavailable");
|
|
409
|
+
return Tool.isProviderDefined(tool) ? {
|
|
410
|
+
type: tool.providerName,
|
|
411
|
+
args: tool.args
|
|
412
|
+
} : {
|
|
413
|
+
name,
|
|
414
|
+
description: Tool.getDescription(tool),
|
|
415
|
+
parameters: Tool.getJsonSchema(tool, { transformer })
|
|
416
|
+
};
|
|
417
|
+
}))),
|
|
418
|
+
catch: () => invalid("A selected Tool schema cannot be rendered")
|
|
419
|
+
})) > limits.maxSchemaBytes) return yield* invalid("Tool exposure exceeds the configured schema byte bound");
|
|
420
|
+
}
|
|
421
|
+
return yield* decodeSnapshot({
|
|
422
|
+
exposedToolNames: names,
|
|
423
|
+
...selection === void 0 ? {} : { selection }
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
//#endregion
|
|
427
|
+
//#region src/engine/internal/tool-telemetry.ts
|
|
428
|
+
/**
|
|
429
|
+
* @internal Payload-free control signal used only to close the canonical Tool span with a failed
|
|
430
|
+
* Exit. It is removed immediately outside that span and never enters the public error channel.
|
|
431
|
+
*/
|
|
432
|
+
var ToolSpanFailure = class ToolSpanFailure extends AiError.AiError.extend("@effect-agent/engine/ToolSpanFailure")({}) {
|
|
433
|
+
name = "ToolCallFailed";
|
|
434
|
+
/** Schema-safe factory retaining the generated constructor required by Effect Schema. */
|
|
435
|
+
static marker() {
|
|
436
|
+
return ToolSpanFailure.make({
|
|
437
|
+
module: "effect-agent",
|
|
438
|
+
method: "execute_tool",
|
|
439
|
+
reason: AiError.UnknownError.make({ description: "Tool execution reached a failed terminal state" })
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
const TOOL_SPAN_FAILURE_MARKER_ATTRIBUTE = "@effect-agent/engine/ToolSpanFailureMarker";
|
|
444
|
+
const terminalOutcomeTokens = /* @__PURE__ */ new WeakMap();
|
|
445
|
+
/** @internal Annotate one completed attempt and hand its existing marker to the isolated span. */
|
|
446
|
+
const annotateToolSpanTerminalOutcome = (outcome, failureMarker, failureHandling, failureMode) => {
|
|
447
|
+
const token = {};
|
|
448
|
+
terminalOutcomeTokens.set(token, {
|
|
449
|
+
outcome,
|
|
450
|
+
failureMarker,
|
|
451
|
+
failureHandling,
|
|
452
|
+
failureMode
|
|
453
|
+
});
|
|
454
|
+
return Effect.annotateCurrentSpan({
|
|
455
|
+
"effect_agent.tool.outcome": outcome,
|
|
456
|
+
[TOOL_SPAN_FAILURE_MARKER_ATTRIBUTE]: token
|
|
457
|
+
});
|
|
458
|
+
};
|
|
459
|
+
const stripToolSpanFailures = (cause, marker) => {
|
|
460
|
+
if (!cause.reasons.some((reason) => Cause.isFailReason(reason) && reason.error === marker)) return {
|
|
461
|
+
found: false,
|
|
462
|
+
residual: cause
|
|
463
|
+
};
|
|
464
|
+
const reasons = cause.reasons.filter((reason) => {
|
|
465
|
+
if (Cause.isFailReason(reason) && reason.error === marker) return false;
|
|
466
|
+
return true;
|
|
467
|
+
});
|
|
468
|
+
return {
|
|
469
|
+
found: true,
|
|
470
|
+
residual: Cause.fromReasons(reasons)
|
|
471
|
+
};
|
|
472
|
+
};
|
|
473
|
+
/** @internal Remove only the private marker and restore the saved Cause plus every residual. */
|
|
474
|
+
const restoreToolSpanFailureCause = (cause, marker, original) => {
|
|
475
|
+
const { found, residual } = stripToolSpanFailures(cause, marker);
|
|
476
|
+
return {
|
|
477
|
+
found,
|
|
478
|
+
restored: original === void 0 ? residual : Cause.combine(original, residual)
|
|
479
|
+
};
|
|
480
|
+
};
|
|
481
|
+
const MAX_REPORTED_SPAN_LIFECYCLE_DEFECTS = 16;
|
|
482
|
+
/** Content-free marker used only to close a delegate that cannot be wrapped safely. */
|
|
483
|
+
var ToolSpanLifecycleFailure = class extends Error {
|
|
484
|
+
name = "ToolSpanLifecycleFailure";
|
|
485
|
+
constructor() {
|
|
486
|
+
super("Tool span lifecycle failed");
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
/**
|
|
490
|
+
* @internal Guard one host span so synchronous exporter defects cannot alter Tool execution.
|
|
491
|
+
* The wrapper retains the span contract for Effect while forwarding every measurement to the host.
|
|
492
|
+
*/
|
|
493
|
+
var IsolatedToolSpan = class {
|
|
494
|
+
_tag = "Span";
|
|
495
|
+
name;
|
|
496
|
+
spanId;
|
|
497
|
+
traceId;
|
|
498
|
+
parent;
|
|
499
|
+
annotations;
|
|
500
|
+
attributes = /* @__PURE__ */ new Map();
|
|
501
|
+
links;
|
|
502
|
+
sampled;
|
|
503
|
+
kind;
|
|
504
|
+
status;
|
|
505
|
+
#delegate;
|
|
506
|
+
#recordDefect;
|
|
507
|
+
#terminalOutcome;
|
|
508
|
+
#terminalFailure;
|
|
509
|
+
#terminalFailureHandling;
|
|
510
|
+
#terminalFailureMode;
|
|
511
|
+
constructor(delegate, options, recordDefect) {
|
|
512
|
+
this.#delegate = delegate;
|
|
513
|
+
this.#recordDefect = recordDefect;
|
|
514
|
+
this.name = options.name;
|
|
515
|
+
this.spanId = delegate.spanId;
|
|
516
|
+
this.traceId = delegate.traceId;
|
|
517
|
+
this.parent = options.parent;
|
|
518
|
+
this.annotations = options.annotations;
|
|
519
|
+
this.links = [...options.links];
|
|
520
|
+
this.sampled = delegate.sampled;
|
|
521
|
+
this.kind = options.kind;
|
|
522
|
+
this.status = {
|
|
523
|
+
_tag: "Started",
|
|
524
|
+
startTime: options.startTime
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
end(endTime, exit) {
|
|
528
|
+
if (this.status._tag === "Ended") return;
|
|
529
|
+
const exportedExit = this.#terminalOutcome === "failure" ? Exit.fail(this.#terminalFailure ?? new ToolSpanLifecycleFailure()) : this.#terminalOutcome === "success" ? Exit.succeed(void 0) : exit;
|
|
530
|
+
this.status = {
|
|
531
|
+
_tag: "Ended",
|
|
532
|
+
startTime: this.status.startTime,
|
|
533
|
+
endTime,
|
|
534
|
+
exit: exportedExit
|
|
535
|
+
};
|
|
536
|
+
if (this.#terminalOutcome !== void 0) {
|
|
537
|
+
this.attributes.set("effect_agent.tool.outcome", this.#terminalOutcome);
|
|
538
|
+
try {
|
|
539
|
+
this.#delegate.attribute("effect_agent.tool.outcome", this.#terminalOutcome);
|
|
540
|
+
} catch (defect) {
|
|
541
|
+
this.#recordDefect(defect);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
for (const [key, value] of [["effect_agent.tool.failure_mode", this.#terminalFailureMode], ["effect_agent.tool.failure_handling", this.#terminalFailureHandling]]) if (value !== void 0) {
|
|
545
|
+
this.attributes.set(key, value);
|
|
546
|
+
try {
|
|
547
|
+
this.#delegate.attribute(key, value);
|
|
548
|
+
} catch (defect) {
|
|
549
|
+
this.#recordDefect(defect);
|
|
550
|
+
}
|
|
551
|
+
} else if (key === "effect_agent.tool.failure_handling") this.attributes.delete(key);
|
|
552
|
+
try {
|
|
553
|
+
this.#delegate.end(endTime, exportedExit);
|
|
554
|
+
} catch (defect) {
|
|
555
|
+
this.#recordDefect(defect);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
attribute(key, value) {
|
|
559
|
+
if (key === TOOL_SPAN_FAILURE_MARKER_ATTRIBUTE) {
|
|
560
|
+
if (typeof value === "object" && value !== null) {
|
|
561
|
+
const terminal = terminalOutcomeTokens.get(value);
|
|
562
|
+
if (terminal !== void 0) {
|
|
563
|
+
this.#terminalOutcome = terminal.outcome;
|
|
564
|
+
this.#terminalFailure = terminal.failureMarker;
|
|
565
|
+
this.#terminalFailureHandling = terminal.failureHandling;
|
|
566
|
+
this.#terminalFailureMode = terminal.failureMode;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (key === "effect_agent.tool.failure_handling") return;
|
|
572
|
+
this.attributes.set(key, value);
|
|
573
|
+
try {
|
|
574
|
+
this.#delegate.attribute(key, value);
|
|
575
|
+
} catch (defect) {
|
|
576
|
+
this.#recordDefect(defect);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
event(name, startTime, attributes) {
|
|
580
|
+
try {
|
|
581
|
+
this.#delegate.event(name, startTime, attributes);
|
|
582
|
+
} catch (defect) {
|
|
583
|
+
this.#recordDefect(defect);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
addLinks(links) {
|
|
587
|
+
this.links.push(...links);
|
|
588
|
+
try {
|
|
589
|
+
this.#delegate.addLinks(links);
|
|
590
|
+
} catch (defect) {
|
|
591
|
+
this.#recordDefect(defect);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
/**
|
|
596
|
+
* @internal Isolate the complete canonical span lifecycle. A creation defect receives a local,
|
|
597
|
+
* non-exported span so the handler still runs; delegate method/finalization defects are retained
|
|
598
|
+
* as bounded reporter diagnostics and never enter the handler stream Cause.
|
|
599
|
+
*/
|
|
600
|
+
const makeIsolatedToolTracer = (delegate) => {
|
|
601
|
+
const defects = [];
|
|
602
|
+
const recordDefect = (defect) => {
|
|
603
|
+
if (defects.length < MAX_REPORTED_SPAN_LIFECYCLE_DEFECTS) defects.push(defect);
|
|
604
|
+
};
|
|
605
|
+
let delegateContext;
|
|
606
|
+
try {
|
|
607
|
+
delegateContext = delegate.context?.bind(delegate);
|
|
608
|
+
} catch (defect) {
|
|
609
|
+
recordDefect(defect);
|
|
610
|
+
delegateContext = void 0;
|
|
611
|
+
}
|
|
612
|
+
const context = delegateContext === void 0 ? void 0 : (primitive, fiber) => {
|
|
613
|
+
let evaluation = { _tag: "Pending" };
|
|
614
|
+
const currentEvaluation = () => evaluation;
|
|
615
|
+
const evaluate = (currentFiber) => {
|
|
616
|
+
switch (evaluation._tag) {
|
|
617
|
+
case "Succeeded": return evaluation.value;
|
|
618
|
+
case "Failed": throw evaluation.error;
|
|
619
|
+
case "Pending": try {
|
|
620
|
+
const value = primitive["~effect/Effect/evaluate"](currentFiber);
|
|
621
|
+
evaluation = {
|
|
622
|
+
_tag: "Succeeded",
|
|
623
|
+
value
|
|
624
|
+
};
|
|
625
|
+
return value;
|
|
626
|
+
} catch (error) {
|
|
627
|
+
evaluation = {
|
|
628
|
+
_tag: "Failed",
|
|
629
|
+
error
|
|
630
|
+
};
|
|
631
|
+
throw error;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
const guardedPrimitive = { ["~effect/Effect/evaluate"]: evaluate };
|
|
636
|
+
try {
|
|
637
|
+
delegateContext(guardedPrimitive, fiber);
|
|
638
|
+
} catch (defect) {
|
|
639
|
+
const observed = currentEvaluation();
|
|
640
|
+
switch (observed._tag) {
|
|
641
|
+
case "Failed": throw observed.error;
|
|
642
|
+
case "Succeeded":
|
|
643
|
+
recordDefect(defect);
|
|
644
|
+
return observed.value;
|
|
645
|
+
case "Pending":
|
|
646
|
+
recordDefect(defect);
|
|
647
|
+
return evaluate(fiber);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const observed = currentEvaluation();
|
|
651
|
+
switch (observed._tag) {
|
|
652
|
+
case "Failed": throw observed.error;
|
|
653
|
+
case "Succeeded": return observed.value;
|
|
654
|
+
case "Pending": return evaluate(fiber);
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
return {
|
|
658
|
+
tracer: Tracer.make({
|
|
659
|
+
context,
|
|
660
|
+
span(options) {
|
|
661
|
+
let delegateSpan;
|
|
662
|
+
try {
|
|
663
|
+
delegateSpan = delegate.span(options);
|
|
664
|
+
} catch (defect) {
|
|
665
|
+
recordDefect(defect);
|
|
666
|
+
return new Tracer.NativeSpan(options);
|
|
667
|
+
}
|
|
668
|
+
try {
|
|
669
|
+
return new IsolatedToolSpan(delegateSpan, options, recordDefect);
|
|
670
|
+
} catch (defect) {
|
|
671
|
+
recordDefect(defect);
|
|
672
|
+
try {
|
|
673
|
+
delegateSpan.end(options.startTime, Exit.fail(new ToolSpanLifecycleFailure()));
|
|
674
|
+
} catch (closeDefect) {
|
|
675
|
+
recordDefect(closeDefect);
|
|
676
|
+
}
|
|
677
|
+
return new Tracer.NativeSpan(options);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}),
|
|
681
|
+
reportLifecycleDefects: Effect.suspend(() => {
|
|
682
|
+
const pending = defects.splice(0);
|
|
683
|
+
return Effect.forEach(pending, (defect) => isolateToolDerivative(ErrorReporter.report(Cause.die(defect))), { discard: true });
|
|
684
|
+
})
|
|
685
|
+
};
|
|
686
|
+
};
|
|
687
|
+
/**
|
|
688
|
+
* @internal Engine-owned span-lifecycle policy. The AgentRuntime composition boundary builds this
|
|
689
|
+
* capability from the host's ambient Tracer; Tool execution consumes it without selecting,
|
|
690
|
+
* decorating, or locally providing a Tracer implementation.
|
|
691
|
+
*/
|
|
692
|
+
var ToolSpanTelemetry = class ToolSpanTelemetry extends Context.Service()("@effect-agent/engine/ToolSpanTelemetry") {
|
|
693
|
+
static layer = Layer.effect(ToolSpanTelemetry, Effect.map(Tracer.Tracer, (delegate) => ToolSpanTelemetry.of({
|
|
694
|
+
isolateEffectSpanLifecycle: (effect) => Effect.suspend(() => {
|
|
695
|
+
const isolated = makeIsolatedToolTracer(delegate);
|
|
696
|
+
return effect.pipe(Effect.provideService(Tracer.Tracer, isolated.tracer), Effect.ensuring(isolated.reportLifecycleDefects));
|
|
697
|
+
}),
|
|
698
|
+
isolateToolkitHandle: (effect) => Effect.currentSpan.pipe(Effect.option, Effect.flatMap((parentOption) => {
|
|
699
|
+
if (Option.isNone(parentOption)) return effect;
|
|
700
|
+
const parent = parentOption.value;
|
|
701
|
+
const local = new Tracer.NativeSpan({
|
|
702
|
+
name: "AgentRuntime.toolkit.handle",
|
|
703
|
+
parent: Option.some(parent),
|
|
704
|
+
annotations: Context.add(Context.empty(), Tracer.DisablePropagation, true),
|
|
705
|
+
links: [],
|
|
706
|
+
startTime: 0n,
|
|
707
|
+
kind: "internal",
|
|
708
|
+
sampled: parent.sampled
|
|
709
|
+
});
|
|
710
|
+
return effect.pipe(Effect.withParentSpan(local), Effect.onExit((exit) => Effect.sync(() => {
|
|
711
|
+
local.end(0n, exit);
|
|
712
|
+
})));
|
|
713
|
+
})),
|
|
714
|
+
isolateSpanLifecycle: (stream) => Stream.unwrap(Effect.sync(() => {
|
|
715
|
+
const isolated = makeIsolatedToolTracer(delegate);
|
|
716
|
+
return stream.pipe(Stream.provideService(Tracer.Tracer, isolated.tracer), Stream.ensuring(isolated.reportLifecycleDefects));
|
|
717
|
+
}))
|
|
718
|
+
})));
|
|
719
|
+
};
|
|
720
|
+
//#endregion
|
|
721
|
+
//#region src/engine/internal/error-diagnostic.ts
|
|
722
|
+
const DIAGNOSTIC_MESSAGE_LIMIT = 4096;
|
|
723
|
+
const DIAGNOSTIC_TAG_LIMIT = 128;
|
|
724
|
+
const boundedDiagnostic = (value, maxCharacters) => value.length <= maxCharacters ? value : value.slice(0, maxCharacters);
|
|
725
|
+
const ownStringDiagnostic = (input, key, maxCharacters) => {
|
|
726
|
+
if (input === null || typeof input !== "object" && typeof input !== "function") return;
|
|
727
|
+
try {
|
|
728
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
729
|
+
return descriptor !== void 0 && "value" in descriptor && typeof descriptor.value === "string" ? boundedDiagnostic(descriptor.value, maxCharacters) : void 0;
|
|
730
|
+
} catch {
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
/** Total, bounded projection for opaque failures. It never invokes arbitrary coercion hooks. */
|
|
735
|
+
const errorMessage = (error) => {
|
|
736
|
+
const message = ownStringDiagnostic(error, "message", DIAGNOSTIC_MESSAGE_LIMIT);
|
|
737
|
+
if (message !== void 0) return message;
|
|
738
|
+
return typeof error === "string" ? boundedDiagnostic(error, DIAGNOSTIC_MESSAGE_LIMIT) : "Unknown error";
|
|
739
|
+
};
|
|
740
|
+
/** Total, bounded tag projection for opaque failures. */
|
|
741
|
+
const errorTag = (error) => ownStringDiagnostic(error, "_tag", DIAGNOSTIC_TAG_LIMIT) ?? "UnknownError";
|
|
742
|
+
//#endregion
|
|
743
|
+
//#region src/engine/internal/agent-runtime.ts
|
|
744
|
+
/** Schema factory for the terminal value produced by reducing a completed agent event stream. */
|
|
745
|
+
const AgentResultSchema = (output) => Schema.Struct({
|
|
746
|
+
output,
|
|
747
|
+
threadId: ThreadId,
|
|
748
|
+
runId: RunId,
|
|
749
|
+
turns: Schema.Int.check(Schema.isGreaterThan(0)),
|
|
750
|
+
finishReason: Schema.Literals([
|
|
751
|
+
"completed",
|
|
752
|
+
"model-stop",
|
|
753
|
+
"budget-exhausted"
|
|
754
|
+
]),
|
|
755
|
+
/** Dimension that bound when the Run settled budget-exhausted (RUN-025; the grant-flow marker). */
|
|
756
|
+
exhausted: Schema.optionalKey(Schema.Literals([
|
|
757
|
+
"tokens",
|
|
758
|
+
"tool-calls",
|
|
759
|
+
"turns"
|
|
760
|
+
])),
|
|
761
|
+
/** Schema-encoded application disposition declared for an ordinary completed Run. */
|
|
762
|
+
runDisposition: Schema.optionalKey(Schema.Json),
|
|
763
|
+
/** Cumulative spend for the Run, as reported on its terminal event. */
|
|
764
|
+
usage: Schema.optionalKey(RunTotals),
|
|
765
|
+
/** Disjoint usage of attached descendants, including nested and failed children. */
|
|
766
|
+
delegatedUsage: Schema.optionalKey(RunTotals)
|
|
767
|
+
}).check(Schema.makeFilter((result) => !("runDisposition" in result) || result.runDisposition === void 0 || !("finishReason" in result) || result.finishReason !== "budget-exhausted", { expected: "runDisposition only when finishReason is not budget-exhausted" }));
|
|
768
|
+
/** Direct usage only: budget counters never absorb descendant consumption. */
|
|
769
|
+
const runTotalsOf = (context) => RunTotals.make({
|
|
770
|
+
modelCalls: context.modelCalls,
|
|
771
|
+
inputTokens: context.inputTokens,
|
|
772
|
+
outputTokens: context.outputTokens,
|
|
773
|
+
costMicrousd: context.costMicrousd,
|
|
774
|
+
usageStatus: context.usageStatus,
|
|
775
|
+
pricingStatus: context.pricingStatus,
|
|
776
|
+
unobservedModelCalls: context.unobservedModelCalls
|
|
777
|
+
});
|
|
778
|
+
const usageReportOf = Effect.fn("AgentRuntime.usageReport")(function* (context) {
|
|
779
|
+
const contributions = [];
|
|
780
|
+
for (const read of context.childUsage.values()) {
|
|
781
|
+
const report = yield* read;
|
|
782
|
+
contributions.push(report.usage, report.delegatedUsage);
|
|
783
|
+
}
|
|
784
|
+
const delegatedUsage = yield* sumRunTotals(contributions).pipe(Effect.catchTag("UsageAggregationError", () => Effect.succeed(unknownRunTotals())));
|
|
785
|
+
return RunUsageReport.make({
|
|
786
|
+
usage: runTotalsOf(context),
|
|
787
|
+
delegatedUsage
|
|
788
|
+
});
|
|
789
|
+
});
|
|
790
|
+
const noteIncompleteUsage = Effect.fn("AgentRuntime.noteIncompleteUsage")(function* (context, turn) {
|
|
791
|
+
const accounting = yield* ModelUsageAccounting;
|
|
792
|
+
context.unobservedModelCalls += 1;
|
|
793
|
+
context.usageStatus = context.usageStatus === "unknown" || context.modelCalls === 0 ? "unknown" : "partial";
|
|
794
|
+
context.pricingStatus = context.pricingStatus === "unknown" || context.modelCalls === 0 ? "unknown" : "partial";
|
|
795
|
+
yield* accounting.noteIncompleteUsage(turn);
|
|
796
|
+
});
|
|
797
|
+
const runCounter = Metric.counter("effect_agent_runs_total", { description: "Agent runs started; no content or high-cardinality identifiers are recorded." });
|
|
798
|
+
const modelCounter = Metric.counter("effect_agent_model_calls_total", { description: "Agent model calls started; no content or high-cardinality identifiers are recorded." });
|
|
799
|
+
const toolCounter = Metric.counter("effect_agent_tool_calls_total", { description: "Agent tool handlers started; no content or high-cardinality identifiers are recorded." });
|
|
800
|
+
const MAX_STAGED_PROVIDER_EVENTS = 256;
|
|
801
|
+
const MAX_STAGED_PROVIDER_BYTES = 1048576;
|
|
802
|
+
const DEFAULT_RUN_BUFFER_LIMITS = {
|
|
803
|
+
maxModelResponseParts: 16384,
|
|
804
|
+
maxModelResponseBytes: 8388608,
|
|
805
|
+
maxRunEvents: 65536,
|
|
806
|
+
maxToolProgressBytes: 8388608,
|
|
807
|
+
maxSubagentEventsPerBatch: 1024
|
|
808
|
+
};
|
|
809
|
+
const tighteningBufferLimit = (configured, ceiling, minimum) => configured === void 0 || !Number.isFinite(configured) ? ceiling : Math.min(ceiling, Math.max(minimum, Math.floor(configured)));
|
|
810
|
+
const effectiveRunBufferLimits = (configured) => ({
|
|
811
|
+
maxModelResponseParts: tighteningBufferLimit(configured?.maxModelResponseParts, DEFAULT_RUN_BUFFER_LIMITS.maxModelResponseParts, 1),
|
|
812
|
+
maxModelResponseBytes: tighteningBufferLimit(configured?.maxModelResponseBytes, DEFAULT_RUN_BUFFER_LIMITS.maxModelResponseBytes, 1),
|
|
813
|
+
maxRunEvents: tighteningBufferLimit(configured?.maxRunEvents, DEFAULT_RUN_BUFFER_LIMITS.maxRunEvents, 2),
|
|
814
|
+
maxToolProgressBytes: tighteningBufferLimit(configured?.maxToolProgressBytes, DEFAULT_RUN_BUFFER_LIMITS.maxToolProgressBytes, 1),
|
|
815
|
+
maxSubagentEventsPerBatch: tighteningBufferLimit(configured?.maxSubagentEventsPerBatch, DEFAULT_RUN_BUFFER_LIMITS.maxSubagentEventsPerBatch, 1)
|
|
816
|
+
});
|
|
817
|
+
const structuredCloneFunction = Reflect.get(globalThis, "structuredClone");
|
|
818
|
+
const knownSafeModelResponsePrototypes = /* @__PURE__ */ new Set([Response.Usage.prototype]);
|
|
819
|
+
const schemaClassPrototype = (schema) => {
|
|
820
|
+
if (typeof schema !== "function" || !Schema.isSchema(schema)) return void 0;
|
|
821
|
+
const descriptor = Object.getOwnPropertyDescriptor(schema, "prototype");
|
|
822
|
+
return descriptor !== void 0 && "value" in descriptor && descriptor.value !== null && typeof descriptor.value === "object" ? descriptor.value : void 0;
|
|
823
|
+
};
|
|
824
|
+
/**
|
|
825
|
+
* Build the bounded preflight view for a decoded Tool payload whose root is an
|
|
826
|
+
* application-selected Schema class. The class instance is deliberately omitted:
|
|
827
|
+
* its complete canonical encoding is measured before cloning, and it is never
|
|
828
|
+
* retained in the Turn trace. Every other response field remains in the preflight.
|
|
829
|
+
*/
|
|
830
|
+
const schemaClassToolPartPreflight = (part, toolkit) => {
|
|
831
|
+
try {
|
|
832
|
+
if (part === null || typeof part !== "object") return void 0;
|
|
833
|
+
const read = (key) => {
|
|
834
|
+
const descriptor = Object.getOwnPropertyDescriptor(part, key);
|
|
835
|
+
if (descriptor === void 0 || !("value" in descriptor)) throw new TypeError(`response part ${key} must be an own data property`);
|
|
836
|
+
return descriptor.value;
|
|
837
|
+
};
|
|
838
|
+
const type = read("type");
|
|
839
|
+
if (type !== "tool-call" && type !== "tool-result") return void 0;
|
|
840
|
+
const name = read("name");
|
|
841
|
+
if (typeof name !== "string" || !hasTool(toolkit.tools, name)) return void 0;
|
|
842
|
+
const tool = toolkit.tools[name];
|
|
843
|
+
const payload = read(type === "tool-call" ? "params" : "result");
|
|
844
|
+
if (payload === null || typeof payload !== "object") return void 0;
|
|
845
|
+
const payloadPrototype = Object.getPrototypeOf(payload);
|
|
846
|
+
if (!(type === "tool-call" ? [tool.parametersSchema] : [tool.successSchema, tool.failureSchema]).some((schema) => schemaClassPrototype(schema) === payloadPrototype)) return;
|
|
847
|
+
const preflight = {};
|
|
848
|
+
const retainedKeys = type === "tool-call" ? [
|
|
849
|
+
"type",
|
|
850
|
+
"id",
|
|
851
|
+
"name",
|
|
852
|
+
"providerExecuted",
|
|
853
|
+
"metadata"
|
|
854
|
+
] : [
|
|
855
|
+
"type",
|
|
856
|
+
"id",
|
|
857
|
+
"name",
|
|
858
|
+
"isFailure",
|
|
859
|
+
"providerExecuted",
|
|
860
|
+
"preliminary",
|
|
861
|
+
"metadata",
|
|
862
|
+
"encodedResult"
|
|
863
|
+
];
|
|
864
|
+
for (const key of retainedKeys) preflight[key] = read(key);
|
|
865
|
+
return preflight;
|
|
866
|
+
} catch {
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
const inspectModelResponsePartCapacity = (usage, part, limits, knownSafePrototypes = knownSafeModelResponsePrototypes) => Effect.suspend(() => {
|
|
871
|
+
const bytes = boundedValueFootprint(part, limits.maxModelResponseBytes - usage.responsePartBytes, knownSafePrototypes);
|
|
872
|
+
return bytes === void 0 ? Effect.fail(ModelProtocolError.make({ message: `Model response exceeded the ${limits.maxModelResponseBytes}-byte retained response limit` })) : Effect.succeed(bytes);
|
|
873
|
+
});
|
|
874
|
+
/**
|
|
875
|
+
* `disableToolCallResolution` keeps model Tool Call parameters in their
|
|
876
|
+
* encoded wire form. Build the response ownership codec from those encoded
|
|
877
|
+
* parameter Schemas; the engine decodes and canonically re-encodes each call
|
|
878
|
+
* against the Definition-owned Schema before it enters history or executes.
|
|
879
|
+
*/
|
|
880
|
+
const encodedToolParameterToolkit = (toolkit) => Toolkit.make(...Object.values(toolkit.tools).map((tool) => tool.setParameters(Schema.toEncoded(tool.parametersSchema))));
|
|
881
|
+
const makeModelResponseCodec = (toolkit) => Schema.toCodecJson(Response.StreamPart(encodedToolParameterToolkit(toolkit)));
|
|
882
|
+
const modelResponseCodecs = /* @__PURE__ */ new WeakMap();
|
|
883
|
+
const modelResponseCodecFor = (toolkit) => {
|
|
884
|
+
const cached = modelResponseCodecs.get(toolkit);
|
|
885
|
+
if (cached !== void 0) return cached;
|
|
886
|
+
const codec = makeModelResponseCodec(toolkit);
|
|
887
|
+
modelResponseCodecs.set(toolkit, codec);
|
|
888
|
+
return codec;
|
|
889
|
+
};
|
|
890
|
+
const ownModelResponsePartGeneral = Effect.fn("AgentRuntime.ownModelResponsePart")(function* (part, toolkit, usage, limits) {
|
|
891
|
+
if (usage.responsePartCount >= limits.maxModelResponseParts) return yield* ModelProtocolError.make({ message: `Model response exceeded the ${limits.maxModelResponseParts}-part response limit` });
|
|
892
|
+
if (boundedValueFootprint(part, limits.maxModelResponseBytes - usage.responsePartBytes, knownSafeModelResponsePrototypes) === void 0) {
|
|
893
|
+
const preflight = schemaClassToolPartPreflight(part, toolkit);
|
|
894
|
+
yield* inspectModelResponsePartCapacity(usage, preflight ?? part, limits);
|
|
895
|
+
}
|
|
896
|
+
const codec = modelResponseCodecFor(toolkit);
|
|
897
|
+
const encodingFailure = ModelProtocolError.make({ message: "Model response part failed canonical encoding" });
|
|
898
|
+
const encoded = yield* Schema.encodeUnknownEffect(codec)(part).pipe(Effect.mapError(() => encodingFailure));
|
|
899
|
+
const retainedBytes = yield* inspectModelResponsePartCapacity(usage, encoded, limits);
|
|
900
|
+
const ownedEncoded = yield* Effect.try({
|
|
901
|
+
try: () => {
|
|
902
|
+
if (typeof structuredCloneFunction !== "function") throw new TypeError("structuredClone is unavailable");
|
|
903
|
+
return Reflect.apply(structuredCloneFunction, globalThis, [encoded]);
|
|
904
|
+
},
|
|
905
|
+
catch: () => ModelProtocolError.make({ message: "Model response part could not be converted into engine-owned data" })
|
|
906
|
+
});
|
|
907
|
+
const decodingFailure = ModelProtocolError.make({ message: "Model response part failed canonical decoding" });
|
|
908
|
+
return {
|
|
909
|
+
ownedPart: yield* Schema.decodeUnknownEffect(codec)(ownedEncoded).pipe(Effect.mapError(() => decodingFailure)),
|
|
910
|
+
retainedBytes
|
|
911
|
+
};
|
|
912
|
+
});
|
|
913
|
+
const ownModelResponsePart = (part, toolkit, usage, limits) => Effect.suspend(() => {
|
|
914
|
+
const primitive = usage.responsePartCount < limits.maxModelResponseParts ? ownPrimitiveDelta(part, limits.maxModelResponseBytes - usage.responsePartBytes) : void 0;
|
|
915
|
+
return primitive === void 0 ? ownModelResponsePartGeneral(part, toolkit, usage, limits) : Effect.succeed(primitive);
|
|
916
|
+
});
|
|
917
|
+
const consumeModelResponsePart = (usage, retainedBytes, limits) => Effect.suspend(() => {
|
|
918
|
+
if (usage.responsePartCount >= limits.maxModelResponseParts || !Number.isSafeInteger(retainedBytes) || retainedBytes < 0 || retainedBytes > limits.maxModelResponseBytes - usage.responsePartBytes) return Effect.fail(ModelProtocolError.make({ message: usage.responsePartCount >= limits.maxModelResponseParts ? `Model response exceeded the ${limits.maxModelResponseParts}-part response limit` : `Model response exceeded the ${limits.maxModelResponseBytes}-byte retained response limit` }));
|
|
919
|
+
return Effect.sync(() => {
|
|
920
|
+
usage.responsePartCount += 1;
|
|
921
|
+
usage.responsePartBytes += retainedBytes;
|
|
922
|
+
});
|
|
923
|
+
});
|
|
924
|
+
const withSemaphorePermit = (semaphore, stream) => Stream.scoped(Stream.fromEffect(Effect.acquireRelease(semaphore.take(1), (permits) => semaphore.release(permits).pipe(Effect.asVoid))).pipe(Stream.flatMap(() => stream)));
|
|
925
|
+
const hasTool = (tools, name) => Object.hasOwn(tools, name);
|
|
926
|
+
const startPart = (parts, id, description) => {
|
|
927
|
+
if (parts.has(id)) return Effect.fail(ModelProtocolError.make({ message: `Model response repeated ${description} start for ${id}` }));
|
|
928
|
+
parts.set(id, "open");
|
|
929
|
+
return Effect.void;
|
|
930
|
+
};
|
|
931
|
+
const continuePart = (parts, id, description) => parts.get(id) === "open" ? Effect.void : Effect.fail(ModelProtocolError.make({ message: `Model response emitted ${description} for inactive part ${id}` }));
|
|
932
|
+
const endPart = (parts, id, description) => continuePart(parts, id, `${description} end`).pipe(Effect.tap(() => Effect.sync(() => parts.set(id, "closed"))));
|
|
933
|
+
const firstOpenPart = (trace) => {
|
|
934
|
+
for (const [id, state] of trace.textParts) if (state === "open") return `text part ${id}`;
|
|
935
|
+
for (const [id, state] of trace.reasoningParts) if (state === "open") return `reasoning part ${id}`;
|
|
936
|
+
for (const [id, part] of trace.toolParameterParts) if (part.state === "open") return `Tool parameter part ${id}`;
|
|
937
|
+
};
|
|
938
|
+
/** Canonically encode already-decoded Tool Call parameters for history and execution. */
|
|
939
|
+
const encodeToolCallParameters = (tool, toolName, decodedParams) => {
|
|
940
|
+
return Schema.encodeUnknownEffect(tool.parametersSchema)(decodedParams).pipe(Effect.mapError((cause) => ModelProtocolError.make({ message: `Invalid parameters for Tool ${toolName}: ${cause.message}` })));
|
|
941
|
+
};
|
|
942
|
+
/**
|
|
943
|
+
* Decode canonically recorded Tool parameters for policy and authorization.
|
|
944
|
+
* The private assertion restores the correlation lost by dynamic record
|
|
945
|
+
* lookup only around a successful Schema decode.
|
|
946
|
+
*/
|
|
947
|
+
const decodeToolCallParameters = (tool, toolName, encodedParams, boundary = "execution") => {
|
|
948
|
+
return Schema.decodeUnknownEffect(tool.parametersSchema)(encodedParams).pipe(Effect.mapError((cause) => ModelProtocolError.make({ message: `Recorded parameters for Tool ${toolName} failed validation${boundary === "resume" ? " on resume" : ""}: ${cause.message}` })));
|
|
949
|
+
};
|
|
950
|
+
/**
|
|
951
|
+
* Decode canonical parameters for approval while retaining their encoded
|
|
952
|
+
* form for the native `Toolkit.handle` boundary.
|
|
953
|
+
*/
|
|
954
|
+
const prepareToolCall = (toolkit, call, declarationIndex) => {
|
|
955
|
+
const name = call.name;
|
|
956
|
+
if (!hasTool(toolkit.tools, name)) return Effect.fail(ModelProtocolError.make({ message: `Model requested unknown Tool ${call.name}` }));
|
|
957
|
+
const tool = toolkit.tools[name];
|
|
958
|
+
return decodeToolCallId(call.id).pipe(Effect.flatMap((toolCallId) => decodeToolCallParameters(tool, call.name, call.params).pipe(Effect.map((decodedParams) => ({
|
|
959
|
+
call,
|
|
960
|
+
name,
|
|
961
|
+
toolCallId,
|
|
962
|
+
decodedParams,
|
|
963
|
+
tool,
|
|
964
|
+
declarationIndex
|
|
965
|
+
})))));
|
|
966
|
+
};
|
|
967
|
+
/**
|
|
968
|
+
* Re-validate one canonically recorded Tool Call's encoded parameters through
|
|
969
|
+
* the owning parameter Schema before a resumed batch may execute anything.
|
|
970
|
+
* Declarations were validated when they entered the log (RUN-004), but a
|
|
971
|
+
* resumed Attempt re-validates on read (STORE-006); a decode failure is a
|
|
972
|
+
* strict no-start boundary. The same private assertion contract as
|
|
973
|
+
* `encodeToolCallParameters` applies: correlation lost by dynamic record
|
|
974
|
+
* lookup is restored only around a successful Schema decode.
|
|
975
|
+
*/
|
|
976
|
+
/**
|
|
977
|
+
* Effective Run bounds (RUN-021): per-Run allowances tighten the Agent
|
|
978
|
+
* Policy's Turn and Tool Call ceilings but can never widen them — the
|
|
979
|
+
* effective limit is `min(policy bound, max(1, floor(allowance)))`. The
|
|
980
|
+
* `onExhaustion` resolution (RUN-018/RUN-019) keys off these effective
|
|
981
|
+
* limits, which is how an orchestrator grants a delegated child a budget
|
|
982
|
+
* extension by re-invoking with a larger allowance below the Definition's
|
|
983
|
+
* ceiling.
|
|
984
|
+
*/
|
|
985
|
+
const CurrentSubagentAuthority = Context.Reference("@effect-agent/engine/internal/CurrentSubagentAuthority", { defaultValue: () => void 0 });
|
|
986
|
+
const boundedAllowance = (policyBound, allowance) => allowance === void 0 || !Number.isFinite(allowance) ? policyBound : Math.min(policyBound, Math.max(1, Math.floor(allowance)));
|
|
987
|
+
const effectiveRunBounds = (policy, options) => ({
|
|
988
|
+
maxTurns: boundedAllowance(policy.maxTurns, options.turnAllowance),
|
|
989
|
+
maxToolCalls: boundedAllowance(policy.maxToolCalls, options.toolCallAllowance)
|
|
990
|
+
});
|
|
991
|
+
const decodeResumedSettledCall = Effect.fn("AgentRuntime.decodeResumedSettledCall")((input, maxResultBytes, providerCallIds) => Effect.gen(function* () {
|
|
992
|
+
const raw = yield* Effect.try({
|
|
993
|
+
try: () => {
|
|
994
|
+
if (input === null || typeof input !== "object") throw new TypeError("settled Tool Call must be an object");
|
|
995
|
+
const readOwnDataProperty = (key) => {
|
|
996
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
997
|
+
if (descriptor === void 0 || !("value" in descriptor)) throw new TypeError(`settled Tool Call ${key} must be an own data property`);
|
|
998
|
+
return descriptor.value;
|
|
999
|
+
};
|
|
1000
|
+
const selection = Object.getOwnPropertyDescriptor(input, "toolSelection");
|
|
1001
|
+
if (selection !== void 0 && !("value" in selection)) throw new TypeError("settled Tool selection must be an own data property");
|
|
1002
|
+
const rejected = Object.getOwnPropertyDescriptor(input, "budgetRejected");
|
|
1003
|
+
if (rejected !== void 0 && !("value" in rejected)) throw new TypeError("settled Tool Call budgetRejected must be an own data property");
|
|
1004
|
+
return {
|
|
1005
|
+
id: readOwnDataProperty("id"),
|
|
1006
|
+
result: readOwnDataProperty("result"),
|
|
1007
|
+
isFailure: readOwnDataProperty("isFailure"),
|
|
1008
|
+
...rejected === void 0 ? {} : { budgetRejected: rejected.value },
|
|
1009
|
+
...selection === void 0 ? {} : { toolSelection: selection.value }
|
|
1010
|
+
};
|
|
1011
|
+
},
|
|
1012
|
+
catch: () => ModelProtocolError.make({ message: "Turn resume contains an invalid settled Tool Call" })
|
|
1013
|
+
});
|
|
1014
|
+
const result = boundedCanonicalJsonSnapshot(raw.result, typeof raw.id === "string" && providerCallIds.has(raw.id) ? MAX_STAGED_PROVIDER_BYTES : maxResultBytes);
|
|
1015
|
+
if (result === void 0) return yield* ModelProtocolError.make({ message: "Turn resume settled Tool result is not bounded canonical JSON" });
|
|
1016
|
+
return yield* Schema.decodeUnknownEffect(RunTurnResumeSettledCallSchema)({
|
|
1017
|
+
...raw,
|
|
1018
|
+
result: result.value
|
|
1019
|
+
}).pipe(Effect.mapError(() => ModelProtocolError.make({ message: "Turn resume contains an invalid settled Tool Call" })));
|
|
1020
|
+
}));
|
|
1021
|
+
const snapshotResumedSettledCalls = Effect.fn("AgentRuntime.snapshotResumedSettledCalls")((resume, maximum) => Effect.try({
|
|
1022
|
+
try: () => {
|
|
1023
|
+
if (resume === null || typeof resume !== "object") throw new TypeError("Turn resume must be an object");
|
|
1024
|
+
const settledDescriptor = Object.getOwnPropertyDescriptor(resume, "settled");
|
|
1025
|
+
if (settledDescriptor === void 0 || !("value" in settledDescriptor)) throw new TypeError("Turn resume settled must be an own data property");
|
|
1026
|
+
const settled = settledDescriptor.value;
|
|
1027
|
+
if (!Array.isArray(settled)) throw new TypeError("Turn resume settled must be an array");
|
|
1028
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(settled, "length");
|
|
1029
|
+
if (lengthDescriptor === void 0 || !("value" in lengthDescriptor) || !Number.isSafeInteger(lengthDescriptor.value) || lengthDescriptor.value < 0 || lengthDescriptor.value > maximum) throw new TypeError("Turn resume settled has an invalid length");
|
|
1030
|
+
const snapshot = [];
|
|
1031
|
+
for (let index = 0; index < lengthDescriptor.value; index += 1) {
|
|
1032
|
+
const entryDescriptor = Object.getOwnPropertyDescriptor(settled, String(index));
|
|
1033
|
+
if (entryDescriptor === void 0 || !("value" in entryDescriptor)) throw new TypeError("Turn resume settled entries must be own data properties");
|
|
1034
|
+
snapshot.push(entryDescriptor.value);
|
|
1035
|
+
}
|
|
1036
|
+
return snapshot;
|
|
1037
|
+
},
|
|
1038
|
+
catch: () => ModelProtocolError.make({ message: "Turn resume contains an invalid settled Tool Call collection" })
|
|
1039
|
+
}));
|
|
1040
|
+
const decodeResumeUsage = Effect.fn("AgentRuntime.decodeResumeUsage")((input) => Effect.gen(function* () {
|
|
1041
|
+
const snapshot = yield* Effect.try({
|
|
1042
|
+
try: () => {
|
|
1043
|
+
if (input === null || typeof input !== "object") throw new TypeError("Run resume usage must be an object");
|
|
1044
|
+
const read = (key, optional = false) => {
|
|
1045
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
1046
|
+
if (descriptor === void 0 && optional) return void 0;
|
|
1047
|
+
if (descriptor === void 0 || !("value" in descriptor)) throw new TypeError(`Run resume usage ${key} must be an own data property`);
|
|
1048
|
+
return descriptor.value;
|
|
1049
|
+
};
|
|
1050
|
+
return {
|
|
1051
|
+
...Object.fromEntries([
|
|
1052
|
+
"usageStatus",
|
|
1053
|
+
"pricingStatus",
|
|
1054
|
+
"unobservedModelCalls",
|
|
1055
|
+
"children"
|
|
1056
|
+
].map((key) => [key, read(key, true)]).filter(([, value]) => value !== void 0)),
|
|
1057
|
+
modelCalls: read("modelCalls"),
|
|
1058
|
+
inputTokens: read("inputTokens"),
|
|
1059
|
+
outputTokens: read("outputTokens"),
|
|
1060
|
+
lastInputTokens: read("lastInputTokens"),
|
|
1061
|
+
lastOutputTokens: read("lastOutputTokens"),
|
|
1062
|
+
costMicrousd: read("costMicrousd"),
|
|
1063
|
+
committedTurns: read("committedTurns"),
|
|
1064
|
+
toolCalls: read("toolCalls"),
|
|
1065
|
+
programmaticToolCalls: read("programmaticToolCalls"),
|
|
1066
|
+
consecutiveToolFailures: read("consecutiveToolFailures"),
|
|
1067
|
+
finalizationUsed: read("finalizationUsed")
|
|
1068
|
+
};
|
|
1069
|
+
},
|
|
1070
|
+
catch: () => ModelProtocolError.make({ message: "Run resume usage requires own data properties with non-negative safe-integer totals and last-call tokens no greater than their cumulative totals" })
|
|
1071
|
+
});
|
|
1072
|
+
return yield* Schema.decodeUnknownEffect(RunResumeUsageSchema)(snapshot).pipe(Effect.mapError(() => ModelProtocolError.make({ message: "Run resume usage requires own data properties with non-negative safe-integer totals and last-call tokens no greater than their cumulative totals" })));
|
|
1073
|
+
}));
|
|
1074
|
+
const makeToolFailedEvent = Effect.fn("AgentRuntime.makeToolFailedEvent")(function* (context, turnId, call, error, failureHandling, budgetRejected) {
|
|
1075
|
+
const toolCallId = yield* decodeToolCallId(call.id);
|
|
1076
|
+
const tools = context.definition.toolkit.tools;
|
|
1077
|
+
return ToolCallFailed.make({
|
|
1078
|
+
...yield* eventBase(context),
|
|
1079
|
+
turnId,
|
|
1080
|
+
toolCallId,
|
|
1081
|
+
toolName: call.name,
|
|
1082
|
+
errorTag: errorTag(error),
|
|
1083
|
+
message: errorMessage(error),
|
|
1084
|
+
providerExecuted: false,
|
|
1085
|
+
failureHandling,
|
|
1086
|
+
...Object.hasOwn(tools, call.name) ? { failureMode: tools[call.name].failureMode } : {},
|
|
1087
|
+
...budgetRejected === void 0 ? {} : { budgetRejected }
|
|
1088
|
+
});
|
|
1089
|
+
});
|
|
1090
|
+
/**
|
|
1091
|
+
* Settle a rejected Tool batch without starting any handler: every open
|
|
1092
|
+
* application call receives the encoded failure as its model-visible result.
|
|
1093
|
+
* Only budget rejections (RUN-018) are exempt from repeated-failure folding;
|
|
1094
|
+
* correctable model declarations count as ordinary failed calls. Emit one
|
|
1095
|
+
* `ToolCallFailed` per call with no `ToolCallStarted` (the
|
|
1096
|
+
* approval-denied precedent). The caller hands the trace to
|
|
1097
|
+
* `toolBatchContinuation`, so the synthetic results advance history through
|
|
1098
|
+
* the ordinary tool message and — under a durable coordinator that never saw
|
|
1099
|
+
* a response commit for this Turn — settle canonically via the single-batch
|
|
1100
|
+
* Turn commit.
|
|
1101
|
+
*/
|
|
1102
|
+
const settleRejectedBatch = Effect.fn("AgentRuntime.settleRejectedBatch")(function* (context, turnId, trace, error, alreadySettled) {
|
|
1103
|
+
const budgetRejected = error._tag === "AgentPolicyError" ? true : void 0;
|
|
1104
|
+
const encodedResult = {
|
|
1105
|
+
_tag: error._tag,
|
|
1106
|
+
...error._tag === "AgentPolicyError" ? { limit: error.limit } : {},
|
|
1107
|
+
message: error.message
|
|
1108
|
+
};
|
|
1109
|
+
const events = [];
|
|
1110
|
+
for (const [index, call] of trace.applicationToolCalls.entries()) {
|
|
1111
|
+
if (alreadySettled?.has(call.id) === true) continue;
|
|
1112
|
+
trace.finalToolResultIds.add(call.id);
|
|
1113
|
+
trace.applicationToolResults[index] = {
|
|
1114
|
+
id: call.id,
|
|
1115
|
+
name: call.name,
|
|
1116
|
+
encodedResult,
|
|
1117
|
+
isFailure: true,
|
|
1118
|
+
...budgetRejected === void 0 ? {} : { budgetRejected }
|
|
1119
|
+
};
|
|
1120
|
+
events.push(yield* makeToolFailedEvent(context, turnId, call, error, "returned-to-model", budgetRejected));
|
|
1121
|
+
}
|
|
1122
|
+
return events;
|
|
1123
|
+
});
|
|
1124
|
+
const stampSubagentEvent = Effect.fn("AgentRuntime.stampSubagentEvent")(function* (context, turnId, payload) {
|
|
1125
|
+
if (payload._tag === "AgentUpdateEmitted") return AgentUpdateEmitted.make({
|
|
1126
|
+
...yield* eventBase(context),
|
|
1127
|
+
turnId,
|
|
1128
|
+
update: payload.update
|
|
1129
|
+
});
|
|
1130
|
+
const shared = {
|
|
1131
|
+
...yield* eventBase(context),
|
|
1132
|
+
turnId,
|
|
1133
|
+
toolCallId: payload.toolCallId,
|
|
1134
|
+
delegationId: payload.delegationId,
|
|
1135
|
+
childThreadId: payload.childThreadId,
|
|
1136
|
+
childRunId: payload.childRunId,
|
|
1137
|
+
targetAgentId: payload.targetAgentId,
|
|
1138
|
+
depth: payload.depth
|
|
1139
|
+
};
|
|
1140
|
+
const report = "usage" in payload ? {
|
|
1141
|
+
...payload.usage === void 0 ? {} : { usage: payload.usage },
|
|
1142
|
+
...payload.delegatedUsage === void 0 ? {} : { delegatedUsage: payload.delegatedUsage }
|
|
1143
|
+
} : {};
|
|
1144
|
+
switch (payload._tag) {
|
|
1145
|
+
case "SubagentRequested": return SubagentRequested.make(shared);
|
|
1146
|
+
case "SubagentStarted": return SubagentStarted.make(shared);
|
|
1147
|
+
case "SubagentProgress": return SubagentProgress.make({
|
|
1148
|
+
...shared,
|
|
1149
|
+
summary: payload.summary
|
|
1150
|
+
});
|
|
1151
|
+
case "SubagentCompleted": return SubagentCompleted.make({
|
|
1152
|
+
...shared,
|
|
1153
|
+
turns: payload.turns,
|
|
1154
|
+
finishReason: payload.finishReason,
|
|
1155
|
+
...payload.exhausted !== void 0 ? { exhausted: payload.exhausted } : {},
|
|
1156
|
+
...report
|
|
1157
|
+
});
|
|
1158
|
+
case "SubagentFailed": return SubagentFailed.make({
|
|
1159
|
+
...shared,
|
|
1160
|
+
...report,
|
|
1161
|
+
errorTag: payload.errorTag,
|
|
1162
|
+
message: payload.message
|
|
1163
|
+
});
|
|
1164
|
+
case "SubagentInterrupted": return SubagentInterrupted.make({
|
|
1165
|
+
...shared,
|
|
1166
|
+
...report,
|
|
1167
|
+
reason: payload.reason
|
|
1168
|
+
});
|
|
1169
|
+
case "SubagentJoined": return SubagentJoined.make({
|
|
1170
|
+
...shared,
|
|
1171
|
+
...report
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
1175
|
+
const approvalDecision = (context, turnId, prepared, options) => Effect.gen(function* () {
|
|
1176
|
+
const approval = prepared.tool.needsApproval;
|
|
1177
|
+
if (approval === void 0 || approval === false) return { required: false };
|
|
1178
|
+
if (!(typeof approval === "function" ? yield* Effect.suspend(() => {
|
|
1179
|
+
const result = approval(prepared.decodedParams, {
|
|
1180
|
+
toolCallId: prepared.call.id,
|
|
1181
|
+
messages: context.history.content
|
|
1182
|
+
});
|
|
1183
|
+
return Effect.isEffect(result) ? result : Effect.succeed(result);
|
|
1184
|
+
}) : approval)) return { required: false };
|
|
1185
|
+
const request = Response.makePart("tool-approval-request", {
|
|
1186
|
+
approvalId: `${context.runId}:${prepared.call.id}`,
|
|
1187
|
+
toolCallId: prepared.call.id
|
|
1188
|
+
});
|
|
1189
|
+
if (options.approval === void 0) return {
|
|
1190
|
+
required: true,
|
|
1191
|
+
request,
|
|
1192
|
+
decision: {
|
|
1193
|
+
_tag: "unresolved",
|
|
1194
|
+
reason: "No approval decision hook is available"
|
|
1195
|
+
}
|
|
1196
|
+
};
|
|
1197
|
+
const toolCallId = yield* decodeToolCallId(prepared.call.id);
|
|
1198
|
+
return {
|
|
1199
|
+
required: true,
|
|
1200
|
+
request,
|
|
1201
|
+
decision: yield* options.approval.request({
|
|
1202
|
+
request,
|
|
1203
|
+
threadId: context.threadId,
|
|
1204
|
+
runId: context.runId,
|
|
1205
|
+
turnId,
|
|
1206
|
+
toolCallId,
|
|
1207
|
+
toolName: prepared.call.name,
|
|
1208
|
+
parameters: prepared.decodedParams
|
|
1209
|
+
})
|
|
1210
|
+
};
|
|
1211
|
+
});
|
|
1212
|
+
/**
|
|
1213
|
+
* Resolve every native Effect AI approval before the handler scheduler starts.
|
|
1214
|
+
* Concatenating this preflight stream ahead of handler streams makes denied or
|
|
1215
|
+
* unresolved batches a strict no-start boundary.
|
|
1216
|
+
*/
|
|
1217
|
+
const preflightApproval = (context, turnId, prepared, options) => Stream.unwrap(approvalDecision(context, turnId, prepared, options).pipe(Effect.flatMap((approval) => {
|
|
1218
|
+
if (!approval.required) return Effect.succeed(Stream.empty);
|
|
1219
|
+
return Effect.gen(function* () {
|
|
1220
|
+
const toolCallId = yield* decodeToolCallId(prepared.call.id);
|
|
1221
|
+
const requested = ApprovalRequested.make({
|
|
1222
|
+
...yield* eventBase(context),
|
|
1223
|
+
turnId,
|
|
1224
|
+
toolCallId,
|
|
1225
|
+
toolName: prepared.call.name
|
|
1226
|
+
});
|
|
1227
|
+
switch (approval.decision._tag) {
|
|
1228
|
+
case "approved": return Stream.succeed(requested);
|
|
1229
|
+
case "denied": {
|
|
1230
|
+
const denied = AgentApprovalDenied.make({
|
|
1231
|
+
toolCallId: prepared.call.id,
|
|
1232
|
+
toolName: prepared.call.name,
|
|
1233
|
+
message: approval.decision.reason ?? "Tool approval was denied"
|
|
1234
|
+
});
|
|
1235
|
+
const failed = ToolCallFailed.make({
|
|
1236
|
+
...yield* eventBase(context),
|
|
1237
|
+
turnId,
|
|
1238
|
+
toolCallId,
|
|
1239
|
+
toolName: prepared.call.name,
|
|
1240
|
+
errorTag: denied._tag,
|
|
1241
|
+
message: denied.message,
|
|
1242
|
+
providerExecuted: false,
|
|
1243
|
+
failureMode: prepared.tool.failureMode,
|
|
1244
|
+
failureHandling: "propagated"
|
|
1245
|
+
});
|
|
1246
|
+
return Stream.fromIterable([requested, failed]).pipe(Stream.concat(Stream.fail(denied)));
|
|
1247
|
+
}
|
|
1248
|
+
case "unresolved": {
|
|
1249
|
+
const pending = AgentApprovalPending.make({
|
|
1250
|
+
approvalId: approval.request.approvalId,
|
|
1251
|
+
toolCallId: prepared.call.id,
|
|
1252
|
+
toolName: prepared.call.name,
|
|
1253
|
+
message: approval.decision.reason ?? "Tool approval remains unresolved"
|
|
1254
|
+
});
|
|
1255
|
+
return Stream.succeed(requested).pipe(Stream.concat(Stream.fail(pending)));
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
})));
|
|
1260
|
+
/**
|
|
1261
|
+
* Recheck host-owned Tool authority after approval and before durable preparation or scheduling.
|
|
1262
|
+
* Every call in the executable batch is authorized in declaration order before ANY Handler may
|
|
1263
|
+
* start, so a later denial leaves this Attempt's complete batch at zero application side effects.
|
|
1264
|
+
*/
|
|
1265
|
+
const preflightToolAuthorization = (context, turnId, turn, call, options, annotations) => {
|
|
1266
|
+
const authorization = isSubagentToolAllowed(options.subagentGrant, options.delegationDepth ?? options.parentLink?.depth ?? 0, call.toolName, annotations) ? options.toolAuthorization : { authorize: () => Effect.succeed({
|
|
1267
|
+
_tag: "denied",
|
|
1268
|
+
reason: "Tool exceeds the inherited subagent grant"
|
|
1269
|
+
}) };
|
|
1270
|
+
if (authorization === void 0) return Stream.empty;
|
|
1271
|
+
return Stream.unwrap(authorization.authorize({
|
|
1272
|
+
threadId: context.threadId,
|
|
1273
|
+
runId: context.runId,
|
|
1274
|
+
turnId,
|
|
1275
|
+
turn,
|
|
1276
|
+
input: context.input,
|
|
1277
|
+
call
|
|
1278
|
+
}).pipe(Effect.map((decision) => {
|
|
1279
|
+
if (decision._tag === "allowed") return Stream.empty;
|
|
1280
|
+
const denied = AgentToolAuthorizationDenied.make({
|
|
1281
|
+
toolCallId: call.toolCallId,
|
|
1282
|
+
toolName: call.toolName,
|
|
1283
|
+
message: decision.reason
|
|
1284
|
+
});
|
|
1285
|
+
return Stream.fromEffect(Effect.map(eventBase(context), (base) => ToolCallFailed.make({
|
|
1286
|
+
...base,
|
|
1287
|
+
turnId,
|
|
1288
|
+
toolCallId: call.toolCallId,
|
|
1289
|
+
toolName: call.toolName,
|
|
1290
|
+
errorTag: denied._tag,
|
|
1291
|
+
message: denied.message,
|
|
1292
|
+
providerExecuted: false,
|
|
1293
|
+
failureMode: context.definition.toolkit.tools[call.toolName].failureMode,
|
|
1294
|
+
failureHandling: "propagated"
|
|
1295
|
+
}))).pipe(Stream.concat(Stream.fail(denied)));
|
|
1296
|
+
})));
|
|
1297
|
+
};
|
|
1298
|
+
const ProviderResponsePartId = Schema.String.check(Schema.isMaxLength(128), Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/));
|
|
1299
|
+
const ProviderToolCallId = ToolCallId.check(Schema.isMaxLength(128), Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/));
|
|
1300
|
+
const isTelemetryToolCallId = Schema.is(ProviderToolCallId);
|
|
1301
|
+
/** One bounded identity surface shared by canonical Tool spans and terminal logs. */
|
|
1302
|
+
const toolTelemetryAttributes = (descriptor) => ({
|
|
1303
|
+
"gen_ai.operation.name": "execute_tool",
|
|
1304
|
+
"gen_ai.tool.name": descriptor.toolName,
|
|
1305
|
+
"gen_ai.tool.type": "function",
|
|
1306
|
+
...descriptor.toolCallId === void 0 ? {} : {
|
|
1307
|
+
"gen_ai.tool.call.id": descriptor.toolCallId,
|
|
1308
|
+
toolCallId: descriptor.toolCallId
|
|
1309
|
+
},
|
|
1310
|
+
"gen_ai.agent.name": descriptor.context.agentId,
|
|
1311
|
+
"gen_ai.conversation.id": descriptor.context.threadId,
|
|
1312
|
+
"effect_agent.tool.execution_class": descriptor.executionClass,
|
|
1313
|
+
"effect_agent.tool.invocation_kind": descriptor.invocationKind,
|
|
1314
|
+
"effect_agent.tool.failure_mode": descriptor.failureMode,
|
|
1315
|
+
...descriptor.parentToolCallId === void 0 ? {} : {
|
|
1316
|
+
"effect_agent.tool.parent_call.id": descriptor.parentToolCallId,
|
|
1317
|
+
parentToolCallId: descriptor.parentToolCallId
|
|
1318
|
+
},
|
|
1319
|
+
...descriptor.sequenceIndex === void 0 ? {} : {
|
|
1320
|
+
"effect_agent.tool.sequence_index": descriptor.sequenceIndex,
|
|
1321
|
+
sequenceIndex: descriptor.sequenceIndex
|
|
1322
|
+
},
|
|
1323
|
+
agentId: descriptor.context.agentId,
|
|
1324
|
+
threadId: descriptor.context.threadId,
|
|
1325
|
+
runId: descriptor.context.runId,
|
|
1326
|
+
turnId: descriptor.turnId,
|
|
1327
|
+
toolName: descriptor.toolName
|
|
1328
|
+
});
|
|
1329
|
+
/** Canonical content-free terminal signal for any application Tool handler attempt. */
|
|
1330
|
+
const terminalToolTelemetry = (descriptor, outcome, failureMarker, failureHandling) => annotateToolSpanTerminalOutcome(outcome, failureMarker, failureHandling, descriptor.failureMode).pipe(Effect.andThen((outcome === "success" ? Effect.logInfo("agent tool execution completed") : Effect.logWarning("agent tool execution failed")).pipe(Effect.annotateLogs({
|
|
1331
|
+
...toolTelemetryAttributes(descriptor),
|
|
1332
|
+
"effect_agent.tool.outcome": outcome,
|
|
1333
|
+
...failureHandling === void 0 ? {} : { "effect_agent.tool.failure_handling": failureHandling },
|
|
1334
|
+
toolExecutionClass: descriptor.executionClass,
|
|
1335
|
+
toolOutcome: outcome
|
|
1336
|
+
}))));
|
|
1337
|
+
/** Bound only the observer's diagnostic; broker/public diagnostic policy is a separate contract. */
|
|
1338
|
+
const toolFailureMessage = (message) => {
|
|
1339
|
+
let bytes = 0;
|
|
1340
|
+
let end = 0;
|
|
1341
|
+
for (const character of message) {
|
|
1342
|
+
const size = utf8ByteLength(character);
|
|
1343
|
+
if (bytes + size > 4096) break;
|
|
1344
|
+
bytes += size;
|
|
1345
|
+
end += character.length;
|
|
1346
|
+
}
|
|
1347
|
+
return message.slice(0, end);
|
|
1348
|
+
};
|
|
1349
|
+
const executePreparedToolCall = (context, turnId, toolkit, prepared, trace, resultBounds, onWaiting) => {
|
|
1350
|
+
const call = prepared.call;
|
|
1351
|
+
const observer = context.toolFailureObserver;
|
|
1352
|
+
const telemetryToolCallId = isTelemetryToolCallId(call.id) ? call.id : void 0;
|
|
1353
|
+
const executionClass = getToolExecutionClass(prepared.tool);
|
|
1354
|
+
const telemetryDescriptor = {
|
|
1355
|
+
context,
|
|
1356
|
+
turnId,
|
|
1357
|
+
toolCallId: telemetryToolCallId,
|
|
1358
|
+
toolName: call.name,
|
|
1359
|
+
executionClass,
|
|
1360
|
+
invocationKind: "model",
|
|
1361
|
+
failureMode: prepared.tool.failureMode
|
|
1362
|
+
};
|
|
1363
|
+
const toolSpanFailure = ToolSpanFailure.marker();
|
|
1364
|
+
let terminal = false;
|
|
1365
|
+
let terminalResultCommitted = false;
|
|
1366
|
+
let terminalOutcome;
|
|
1367
|
+
let terminalResult;
|
|
1368
|
+
let propagatedFailure;
|
|
1369
|
+
let failureObservation;
|
|
1370
|
+
const terminalTelemetry = (outcome, failureHandling) => terminalToolTelemetry(telemetryDescriptor, outcome, outcome === "failure" ? toolSpanFailure : void 0, failureHandling);
|
|
1371
|
+
const isolatedTerminalTelemetry = (outcome, failureHandling) => isolateToolDerivative(terminalTelemetry(outcome, failureHandling));
|
|
1372
|
+
/**
|
|
1373
|
+
* The authoritative event reaches the downstream Run stream before derivative work starts.
|
|
1374
|
+
* Telemetry and observation share one owner across the next-pull/early-close race, under this
|
|
1375
|
+
* call's batch permit. A returned failure still fails a normal second pull with the span marker.
|
|
1376
|
+
*/
|
|
1377
|
+
const terminalEventThenAfter = (event, outcome, failSpan, failureHandling) => {
|
|
1378
|
+
const telemetry = isolatedTerminalTelemetry(outcome, failureHandling);
|
|
1379
|
+
const after = observer === void 0 ? telemetry : telemetry.pipe(Effect.andThen(Effect.suspend(() => failureObservation === void 0 ? Effect.void : deliverToolFailure(observer, failureObservation))));
|
|
1380
|
+
return emitThenAfter(event, after.pipe(Effect.andThen(failSpan ? Effect.fail(toolSpanFailure) : Effect.void)));
|
|
1381
|
+
};
|
|
1382
|
+
const started = Stream.fromEffect(Effect.gen(function* () {
|
|
1383
|
+
const toolCallId = yield* decodeToolCallId(call.id);
|
|
1384
|
+
yield* Effect.logDebug("agent tool handler started").pipe(Effect.annotateLogs({
|
|
1385
|
+
agentId: context.agentId,
|
|
1386
|
+
runId: context.runId,
|
|
1387
|
+
turnId,
|
|
1388
|
+
...telemetryToolCallId === void 0 ? {} : { toolCallId: telemetryToolCallId },
|
|
1389
|
+
toolName: call.name
|
|
1390
|
+
}));
|
|
1391
|
+
yield* Metric.update(toolCounter, 1);
|
|
1392
|
+
return ToolCallStarted.make({
|
|
1393
|
+
...yield* eventBase(context),
|
|
1394
|
+
turnId,
|
|
1395
|
+
toolCallId,
|
|
1396
|
+
toolName: call.name
|
|
1397
|
+
});
|
|
1398
|
+
}).pipe(Effect.withLogSpan("AgentRuntime.tool")));
|
|
1399
|
+
const handle = toolkit.handle;
|
|
1400
|
+
const results = Stream.unwrap(Effect.flatMap(ToolSpanTelemetry, ({ isolateToolkitHandle }) => isolateToolkitHandle(handle.call(toolkit, prepared.name, call.params, call.id)))).pipe(Stream.mapEffect((result) => Effect.gen(function* () {
|
|
1401
|
+
if (terminal || trace.finalToolResultIds.has(call.id)) return yield* ModelProtocolError.make({ message: `Tool Call ${call.id} produced more than one terminal result` });
|
|
1402
|
+
const toolCallId = yield* decodeToolCallId(call.id);
|
|
1403
|
+
if (result.preliminary) {
|
|
1404
|
+
const owned = yield* ownApplicationToolProgress(context, result.encodedResult);
|
|
1405
|
+
return ToolProgress.make({
|
|
1406
|
+
...yield* eventBase(context),
|
|
1407
|
+
turnId,
|
|
1408
|
+
toolCallId,
|
|
1409
|
+
toolName: call.name,
|
|
1410
|
+
result: owned,
|
|
1411
|
+
providerExecuted: false
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
terminal = true;
|
|
1415
|
+
terminalOutcome = result.isFailure ? "failure" : "success";
|
|
1416
|
+
terminalResult = {
|
|
1417
|
+
encodedResult: result.encodedResult,
|
|
1418
|
+
isFailure: result.isFailure,
|
|
1419
|
+
result: result.result
|
|
1420
|
+
};
|
|
1421
|
+
})), Stream.filter((event) => event !== void 0));
|
|
1422
|
+
const commitTerminalResult = Effect.suspend(() => {
|
|
1423
|
+
if (!terminal || terminalOutcome === void 0 || terminalResult === void 0) return Effect.fail(ModelProtocolError.make({ message: `Tool Call ${call.id} completed without a terminal result` }));
|
|
1424
|
+
const result = terminalResult;
|
|
1425
|
+
return Effect.gen(function* () {
|
|
1426
|
+
const toolCallId = yield* decodeToolCallId(call.id);
|
|
1427
|
+
let toolSelection;
|
|
1428
|
+
if (!result.isFailure && Context.get(prepared.tool.annotations, DiscoveryTool)) {
|
|
1429
|
+
toolSelection = yield* validateSelection(result.result, context.definition, context.toolCatalog);
|
|
1430
|
+
yield* exposureSnapshot(context.definition, toolSelection, context.toolCatalog, context.toolSchemaTransformer);
|
|
1431
|
+
}
|
|
1432
|
+
const encodedResult = boundEncodedToolResult(result.encodedResult, resultBounds);
|
|
1433
|
+
const event = result.isFailure ? ToolCallFailed.make({
|
|
1434
|
+
...yield* eventBase(context),
|
|
1435
|
+
turnId,
|
|
1436
|
+
toolCallId,
|
|
1437
|
+
toolName: call.name,
|
|
1438
|
+
errorTag: errorTag(result.result),
|
|
1439
|
+
message: errorMessage(result.result),
|
|
1440
|
+
providerExecuted: false,
|
|
1441
|
+
failureMode: prepared.tool.failureMode,
|
|
1442
|
+
failureHandling: "returned-to-model"
|
|
1443
|
+
}) : ToolCallSucceeded.make({
|
|
1444
|
+
...yield* eventBase(context),
|
|
1445
|
+
turnId,
|
|
1446
|
+
toolCallId,
|
|
1447
|
+
toolName: call.name,
|
|
1448
|
+
result: yield* decodeEventJson(encodedResult, "Tool result"),
|
|
1449
|
+
...toolSelection === void 0 ? {} : { toolSelection },
|
|
1450
|
+
providerExecuted: false
|
|
1451
|
+
});
|
|
1452
|
+
trace.finalToolResultIds.add(call.id);
|
|
1453
|
+
trace.applicationToolResults[prepared.declarationIndex] = {
|
|
1454
|
+
...toolSelection === void 0 ? {} : { toolSelection },
|
|
1455
|
+
id: call.id,
|
|
1456
|
+
name: call.name,
|
|
1457
|
+
encodedResult,
|
|
1458
|
+
isFailure: result.isFailure
|
|
1459
|
+
};
|
|
1460
|
+
terminalResultCommitted = true;
|
|
1461
|
+
if (observer !== void 0 && event._tag === "ToolCallFailed") failureObservation = {
|
|
1462
|
+
_tag: "ModelToolFailure",
|
|
1463
|
+
kind: "declared-failure",
|
|
1464
|
+
agentId: context.agentId,
|
|
1465
|
+
threadId: context.threadId,
|
|
1466
|
+
runId: context.runId,
|
|
1467
|
+
turnId,
|
|
1468
|
+
toolCallId,
|
|
1469
|
+
toolName: call.name,
|
|
1470
|
+
executionClass,
|
|
1471
|
+
tag: event.errorTag
|
|
1472
|
+
};
|
|
1473
|
+
return event;
|
|
1474
|
+
});
|
|
1475
|
+
});
|
|
1476
|
+
const finalizeTerminalResult = Stream.unwrap(Effect.sync(() => terminalEventThenAfter(commitTerminalResult, terminalOutcome ?? "failure", terminalOutcome === "failure", terminalOutcome === "failure" ? "returned-to-model" : void 0)));
|
|
1477
|
+
const failTerminalResult = (cause) => {
|
|
1478
|
+
terminal = true;
|
|
1479
|
+
terminalOutcome = "failure";
|
|
1480
|
+
terminalResult = void 0;
|
|
1481
|
+
propagatedFailure = cause;
|
|
1482
|
+
return terminalEventThenAfter(makeToolFailedEvent(context, turnId, call, Cause.squash(cause), "propagated").pipe(Effect.tap(() => Effect.sync(() => {
|
|
1483
|
+
trace.finalToolResultIds.add(call.id);
|
|
1484
|
+
terminalResultCommitted = true;
|
|
1485
|
+
}))), "failure", true, "propagated");
|
|
1486
|
+
};
|
|
1487
|
+
const measured = started.pipe(Stream.concat(results), Stream.concat(finalizeTerminalResult), Stream.catchCause((cause) => {
|
|
1488
|
+
const { found: hasToolSpanFailure, residual: toolCause } = stripToolSpanFailures(cause, toolSpanFailure);
|
|
1489
|
+
if (hasToolSpanFailure) return Stream.failCause(cause);
|
|
1490
|
+
if (terminalResultCommitted) return Stream.failCause(toolCause);
|
|
1491
|
+
if (toolCause.reasons.length > 0 && toolCause.reasons.every(Cause.isInterruptReason)) return Stream.failCause(toolCause);
|
|
1492
|
+
const waiting = waitingFromCause(toolCause);
|
|
1493
|
+
if (waiting !== void 0) {
|
|
1494
|
+
if (terminal || trace.finalToolResultIds.has(call.id)) return failTerminalResult(Cause.fail(ModelProtocolError.make({ message: `Tool Call ${call.id} raised the waiting signal after its terminal result` })));
|
|
1495
|
+
onWaiting(waiting);
|
|
1496
|
+
return Stream.empty;
|
|
1497
|
+
}
|
|
1498
|
+
return failTerminalResult(toolCause);
|
|
1499
|
+
}), Stream.withSpan(`execute_tool ${call.name}`, {
|
|
1500
|
+
kind: "internal",
|
|
1501
|
+
attributes: toolTelemetryAttributes(telemetryDescriptor)
|
|
1502
|
+
}));
|
|
1503
|
+
return Stream.unwrap(Effect.map(ToolSpanTelemetry, ({ isolateSpanLifecycle }) => isolateSpanLifecycle(measured))).pipe(Stream.catchCause((cause) => {
|
|
1504
|
+
const { found, restored } = restoreToolSpanFailureCause(cause, toolSpanFailure, propagatedFailure);
|
|
1505
|
+
if (!found) return Stream.failCause(restored);
|
|
1506
|
+
return restored.reasons.length === 0 ? Stream.empty : Stream.failCause(restored);
|
|
1507
|
+
}));
|
|
1508
|
+
};
|
|
1509
|
+
/**
|
|
1510
|
+
* Execute a wholly preflighted native Toolkit batch under one finite engine
|
|
1511
|
+
* semaphore.
|
|
1512
|
+
*
|
|
1513
|
+
* The engine provides this batch's `RunEventSink` locally to the handler
|
|
1514
|
+
* streams, bound to the batch's Turn. Sink emissions are drained into the
|
|
1515
|
+
* batch stream and stamped through `eventBase`; the batch settles (including
|
|
1516
|
+
* by failure) only after every already-emitted Subagent event has surfaced.
|
|
1517
|
+
* The public exclusion of engine-provided services happens once at the Run
|
|
1518
|
+
* boundary in `stream`, so this signature keeps the naked handler services.
|
|
1519
|
+
*/
|
|
1520
|
+
const executeToolBatch = (context, turnId, turn, toolkit, calls, trace, concurrency, options, brokerAccounting, resultBounds, settledCallIds) => Stream.unwrap(Effect.gen(function* () {
|
|
1521
|
+
const exposed = context.toolExposure?.exposedToolNames;
|
|
1522
|
+
const visibility = yield* RunToolVisibility;
|
|
1523
|
+
const eligible = new Set(context.toolCatalog.filter((entry) => entry.kind === "native").map((entry) => entry.nativeToolName));
|
|
1524
|
+
if (calls.some((call) => exposed !== void 0 && !exposed.includes(call.name) || visibility !== void 0 && !settledCallIds?.has(call.id) && !eligible.has(call.name))) return yield* ModelProtocolError.make({ message: "Tool batch calls a Tool outside its request exposure or current visibility" });
|
|
1525
|
+
const prepared = yield* Effect.forEach(calls, (call, declarationIndex) => prepareToolCall(toolkit, call, declarationIndex));
|
|
1526
|
+
if (prepared.some((call) => Context.get(call.tool.annotations, ContextRolloverTool)) && trace.toolCalls.size !== 1) return yield* ModelProtocolError.make({ message: "A context rollover Tool must be the only Tool Call in its batch" });
|
|
1527
|
+
const semaphore = yield* Semaphore.make(concurrency);
|
|
1528
|
+
const approvalPreflight = prepared.reduce((stream, call) => stream.pipe(Stream.concat(preflightApproval(context, turnId, call, options))), Stream.empty);
|
|
1529
|
+
const descriptors = trace.applicationCallDescriptors;
|
|
1530
|
+
const durability = options.durability;
|
|
1531
|
+
const hookServices = yield* Effect.context();
|
|
1532
|
+
const executable = settledCallIds === void 0 ? prepared : prepared.filter((call) => !settledCallIds.has(call.call.id));
|
|
1533
|
+
const authorizationPreflight = (settledCallIds === void 0 ? descriptors : descriptors.filter((call) => !settledCallIds.has(call.toolCallId))).reduce((stream, call) => stream.pipe(Stream.concat(preflightToolAuthorization(context, turnId, turn, call, options, toolkit.tools[call.toolName]?.annotations ?? Context.empty()))), Stream.empty);
|
|
1534
|
+
const preparation = durability === void 0 ? Stream.empty : Stream.fromEffect(Effect.suspend(() => {
|
|
1535
|
+
const preparedDescriptors = descriptors.filter((call) => call.executionClass !== "readonly" || call.executionKind !== "ordinary");
|
|
1536
|
+
return preparedDescriptors.length === 0 ? Effect.void : durability.prepareToolCalls(preparedDescriptors);
|
|
1537
|
+
})).pipe(Stream.drain);
|
|
1538
|
+
const stepServiceFor = (call) => durability === void 0 ? passthroughDurableStep() : makeDurableStepService(call.toolCallId, durability.step, hookServices);
|
|
1539
|
+
const subagentHook = options.subagent;
|
|
1540
|
+
const batchSubagentDurability = subagentHook === void 0 ? ephemeralSubagentDurability : makeSubagentDurabilityService(subagentHook, hookServices);
|
|
1541
|
+
const waitingByDeclaration = /* @__PURE__ */ new Map();
|
|
1542
|
+
const scheduling = options.scheduling ?? (yield* RunToolScheduling);
|
|
1543
|
+
const groups = [];
|
|
1544
|
+
let parallel = [];
|
|
1545
|
+
for (const call of executable) if (scheduling.toolRequiresSequential?.(call.name) === true) {
|
|
1546
|
+
if (parallel.length > 0) {
|
|
1547
|
+
groups.push(parallel);
|
|
1548
|
+
parallel = [];
|
|
1549
|
+
}
|
|
1550
|
+
groups.push([call]);
|
|
1551
|
+
} else parallel.push(call);
|
|
1552
|
+
if (parallel.length > 0) groups.push(parallel);
|
|
1553
|
+
const handlers = groups.reduce((stream, group) => {
|
|
1554
|
+
const channels = group.map((call) => withSemaphorePermit(semaphore, Stream.unwrap(Effect.gen(function* () {
|
|
1555
|
+
const subagentHost = yield* SubagentHost.forTool;
|
|
1556
|
+
const messagingHost = yield* MessagingHost.forTool;
|
|
1557
|
+
const source = {
|
|
1558
|
+
_tag: "tool",
|
|
1559
|
+
agentId: context.agentId,
|
|
1560
|
+
threadId: context.threadId,
|
|
1561
|
+
runId: context.runId,
|
|
1562
|
+
toolCallId: call.toolCallId
|
|
1563
|
+
};
|
|
1564
|
+
const broker = yield* makeToolBrokerService({
|
|
1565
|
+
context,
|
|
1566
|
+
turnId,
|
|
1567
|
+
outerToolCallId: call.toolCallId,
|
|
1568
|
+
turn,
|
|
1569
|
+
maxToolCalls: brokerAccounting.maxToolCalls,
|
|
1570
|
+
declaredToolCalls: brokerAccounting.declaredToolCalls,
|
|
1571
|
+
budget: options.budget,
|
|
1572
|
+
reservePolicyUsage: options.durability?.reservePolicyUsage,
|
|
1573
|
+
hookServices
|
|
1574
|
+
});
|
|
1575
|
+
return executePreparedToolCall(context, turnId, toolkit, call, trace, resultBounds, (waiting) => {
|
|
1576
|
+
waitingByDeclaration.set(call.declarationIndex, waiting);
|
|
1577
|
+
}).pipe(Stream.provideService(DurableStep, stepServiceFor(call)), Stream.provideService(SubagentHost, subagentHost(source)), Stream.provideService(MessagingHost, messagingHost(source)), Stream.provideService(ToolBroker, broker.service), Stream.provideService(CurrentToolCatalog, { entries: context.toolCatalog }), Stream.ensuring(Effect.sync(() => broker.close())));
|
|
1578
|
+
}))).channel);
|
|
1579
|
+
const next = Stream.fromChannel(Channel.mergeAll(Channel.fromIterable(channels), { concurrency }));
|
|
1580
|
+
return stream.pipe(Stream.concat(next));
|
|
1581
|
+
}, Stream.empty);
|
|
1582
|
+
const sinkQueue = yield* Queue.bounded(context.bufferLimits.maxSubagentEventsPerBatch);
|
|
1583
|
+
const batchSink = { emit: (payload) => ![
|
|
1584
|
+
"SubagentRequested",
|
|
1585
|
+
"SubagentStarted",
|
|
1586
|
+
"SubagentProgress",
|
|
1587
|
+
"SubagentCompleted",
|
|
1588
|
+
"SubagentFailed",
|
|
1589
|
+
"SubagentInterrupted",
|
|
1590
|
+
"SubagentJoined"
|
|
1591
|
+
].includes(payload._tag) ? Effect.fail(new RunEventSinkClosedError({ message: "Unsupported Tool event payload" })) : Queue.offer(sinkQueue, payload).pipe(Effect.flatMap((accepted) => accepted ? Effect.sync(() => {
|
|
1592
|
+
if (context.liveChildren.has(payload.childRunId)) return;
|
|
1593
|
+
if ("usage" in payload && payload.usage !== void 0) context.childUsage.set(payload.childRunId, Effect.succeed(RunUsageReport.make({
|
|
1594
|
+
usage: payload.usage,
|
|
1595
|
+
delegatedUsage: payload.delegatedUsage ?? unknownRunTotals()
|
|
1596
|
+
})));
|
|
1597
|
+
else if (payload._tag === "SubagentStarted" && !context.childUsage.has(payload.childRunId)) context.childUsage.set(payload.childRunId, Effect.succeed(RunUsageReport.make({
|
|
1598
|
+
usage: unknownRunTotals(),
|
|
1599
|
+
delegatedUsage: unknownRunTotals()
|
|
1600
|
+
})));
|
|
1601
|
+
}) : Effect.fail(RunEventSinkClosedError.make({ message: `Subagent event ${payload._tag} was emitted after its Tool batch settled` })))) };
|
|
1602
|
+
const updateAcceptance = yield* AgentUpdateAcceptance;
|
|
1603
|
+
let updatesOpen = true;
|
|
1604
|
+
const updateEmitter = { emit: (request) => context.updatePermits.withPermit(Effect.gen(function* () {
|
|
1605
|
+
if (!updatesOpen) return yield* new UpdateError({ reason: "unavailable" });
|
|
1606
|
+
if (request.target.id !== context.agentId || request.target.updates !== context.definition.updates || context.definition.updates === void 0) return yield* new UpdateError({ reason: "identity" });
|
|
1607
|
+
const updateId = yield* Schema.decodeEffect(IdempotencyKey)(request.updateId).pipe(Effect.mapError(() => new UpdateError({ reason: "validation" })));
|
|
1608
|
+
const value = yield* Schema.decodeEffect(Schema.fromJsonString(Schema.Json))(JSON.stringify(request.value)).pipe(Effect.mapError(() => new UpdateError({ reason: "validation" })));
|
|
1609
|
+
yield* context.validateUpdate(value);
|
|
1610
|
+
const existing = context.updates.get(updateId);
|
|
1611
|
+
if (existing !== void 0) {
|
|
1612
|
+
if (!Schema.toEquivalence(Schema.Json)(existing.value, value)) return yield* new UpdateError({ reason: "conflict" });
|
|
1613
|
+
return existing;
|
|
1614
|
+
}
|
|
1615
|
+
const bytes = utf8ByteLength(JSON.stringify(value));
|
|
1616
|
+
const maxCount = options.updates?.maxCount ?? 32;
|
|
1617
|
+
const maxBytes = options.updates?.maxBytes ?? 16384;
|
|
1618
|
+
if (!Number.isSafeInteger(maxCount) || maxCount < 1 || !Number.isSafeInteger(maxBytes) || maxBytes < 1 || context.updates.size >= maxCount || context.updateBytes + bytes > maxBytes) return yield* new UpdateError({ reason: "capacity" });
|
|
1619
|
+
const snapshot = boundedCanonicalJsonSnapshot(value, bytes);
|
|
1620
|
+
if (snapshot === void 0) return yield* new UpdateError({ reason: "validation" });
|
|
1621
|
+
const accepted = yield* updateAcceptance.accept(Object.freeze(Update.make({
|
|
1622
|
+
schemaVersion: 1,
|
|
1623
|
+
agentId: context.agentId,
|
|
1624
|
+
threadId: context.threadId,
|
|
1625
|
+
runId: context.runId,
|
|
1626
|
+
updateId,
|
|
1627
|
+
sequence: context.updates.size + 1,
|
|
1628
|
+
value: snapshot.value
|
|
1629
|
+
})));
|
|
1630
|
+
const validated = yield* Schema.decodeEffect(Update)(accepted).pipe(Effect.mapError(() => new UpdateError({ reason: "validation" })));
|
|
1631
|
+
if (validated.agentId !== context.agentId || validated.threadId !== context.threadId || validated.runId !== context.runId || validated.updateId !== request.updateId || !Schema.toEquivalence(Schema.Json)(validated.value, value)) return yield* new UpdateError({ reason: "identity" });
|
|
1632
|
+
const update = Object.freeze(Update.make({
|
|
1633
|
+
...validated,
|
|
1634
|
+
value: snapshot.value
|
|
1635
|
+
}));
|
|
1636
|
+
context.updates.set(request.updateId, update);
|
|
1637
|
+
context.updateBytes += bytes;
|
|
1638
|
+
if (!(yield* Queue.offer(sinkQueue, {
|
|
1639
|
+
_tag: "AgentUpdateEmitted",
|
|
1640
|
+
update
|
|
1641
|
+
}))) return yield* new UpdateError({ reason: "unavailable" });
|
|
1642
|
+
return update;
|
|
1643
|
+
})) };
|
|
1644
|
+
let settledCause;
|
|
1645
|
+
const settling = handlers.pipe(Stream.provideService(RunEventSink, batchSink), Stream.provideService(Emitter, updateEmitter), Stream.provideService(SubagentDurability, batchSubagentDurability), Stream.catchCause((cause) => {
|
|
1646
|
+
settledCause = cause;
|
|
1647
|
+
return Stream.empty;
|
|
1648
|
+
}), Stream.ensuring(Effect.sync(() => {
|
|
1649
|
+
updatesOpen = false;
|
|
1650
|
+
}).pipe(Effect.andThen(Queue.end(sinkQueue)))));
|
|
1651
|
+
const sinkEvents = Stream.fromQueue(sinkQueue).pipe(Stream.mapEffect((payload) => stampSubagentEvent(context, turnId, payload)));
|
|
1652
|
+
const settled = Stream.merge(settling, sinkEvents).pipe(Stream.concat(Stream.fromEffect(Effect.suspend(() => {
|
|
1653
|
+
if (settledCause !== void 0) return Effect.failCause(settledCause);
|
|
1654
|
+
const waiting = [...waitingByDeclaration.entries()].sort(([left], [right]) => left - right).map(([, signal]) => signal);
|
|
1655
|
+
const [first, ...rest] = waiting;
|
|
1656
|
+
if (first === void 0) return Effect.void;
|
|
1657
|
+
const child = (signal) => ({
|
|
1658
|
+
toolCallId: signal.toolCallId,
|
|
1659
|
+
childThreadId: signal.childThreadId,
|
|
1660
|
+
childSubmissionId: signal.childSubmissionId,
|
|
1661
|
+
childRunId: signal.childRunId
|
|
1662
|
+
});
|
|
1663
|
+
return Effect.fail(AgentChildPending.make({
|
|
1664
|
+
children: [child(first), ...rest.map(child)],
|
|
1665
|
+
message: `${waiting.length} durable delegation ${waiting.length === 1 ? "call is" : "calls are"} waiting on attached children; the Run suspended without settling`
|
|
1666
|
+
}));
|
|
1667
|
+
})).pipe(Stream.drain)));
|
|
1668
|
+
return approvalPreflight.pipe(Stream.concat(authorizationPreflight), Stream.concat(preparation), Stream.concat(settled));
|
|
1669
|
+
}));
|
|
1670
|
+
const schedulingConcurrency = Effect.fn("AgentRuntime.schedulingConcurrency")(function* (configured, explicit) {
|
|
1671
|
+
const override = (explicit ?? (yield* RunToolScheduling)).runOverride;
|
|
1672
|
+
if (override === void 0) return configured;
|
|
1673
|
+
if (override.mode === "sequential") return 1;
|
|
1674
|
+
if (!Number.isInteger(override.concurrency) || override.concurrency <= 0) return yield* AgentPolicyError.make({
|
|
1675
|
+
limit: "usage",
|
|
1676
|
+
message: "Run Tool concurrency override must be a positive integer"
|
|
1677
|
+
});
|
|
1678
|
+
return Math.min(configured, override.concurrency);
|
|
1679
|
+
});
|
|
1680
|
+
/**
|
|
1681
|
+
* One terminal outcome per declared Tool Call of the completed Turn, in
|
|
1682
|
+
* declaration order. Provider-executed results are read from the response
|
|
1683
|
+
* parts; application results from the settled batch.
|
|
1684
|
+
*/
|
|
1685
|
+
const turnToolFailures = (trace) => {
|
|
1686
|
+
const outcomes = [];
|
|
1687
|
+
for (const [id, call] of trace.toolCalls) {
|
|
1688
|
+
if (call.providerExecuted) {
|
|
1689
|
+
for (const part of trace.parts) if (part.type === "tool-result" && part.id === id && part.preliminary !== true) {
|
|
1690
|
+
outcomes.push(part.isFailure);
|
|
1691
|
+
break;
|
|
1692
|
+
}
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
const result = trace.applicationToolResults.find((candidate) => candidate?.id === id);
|
|
1696
|
+
if (result !== void 0 && result.budgetRejected !== true) outcomes.push(result.isFailure);
|
|
1697
|
+
}
|
|
1698
|
+
return outcomes;
|
|
1699
|
+
};
|
|
1700
|
+
/**
|
|
1701
|
+
* Fold the completed Turn's terminal Tool outcomes into the Run's
|
|
1702
|
+
* consecutive-failure counter in declaration order, then enforce the
|
|
1703
|
+
* repeated-failure Stop Policy before the next model request. A terminal
|
|
1704
|
+
* success resets the counter; a `repeatedFailureLimit` of `0` disables the
|
|
1705
|
+
* bound.
|
|
1706
|
+
*/
|
|
1707
|
+
const applyRepeatedFailurePolicy = (context, trace, repeatedFailureLimit) => Effect.suspend(() => {
|
|
1708
|
+
for (const isFailure of turnToolFailures(trace)) context.consecutiveToolFailures = isFailure ? context.consecutiveToolFailures + 1 : 0;
|
|
1709
|
+
if (repeatedFailureLimit > 0 && context.consecutiveToolFailures >= repeatedFailureLimit) return Effect.fail(AgentPolicyError.make({
|
|
1710
|
+
limit: "repeated-failures",
|
|
1711
|
+
message: `Agent reached its ${repeatedFailureLimit} consecutive Tool Call failure limit`
|
|
1712
|
+
}));
|
|
1713
|
+
return Effect.void;
|
|
1714
|
+
});
|
|
1715
|
+
const inputsToPrompt = (inputs) => Effect.try({
|
|
1716
|
+
try: () => Prompt.fromMessages(inputs.flatMap((input) => Prompt.make(input).content)),
|
|
1717
|
+
catch: (cause) => AgentInputError.make({ message: `Unable to materialize queued Run input: ${errorMessage(cause)}` })
|
|
1718
|
+
});
|
|
1719
|
+
/** Validate live reference context before it reaches budgeting or provider I/O. */
|
|
1720
|
+
const transientInputToPrompt = (input) => Effect.try({
|
|
1721
|
+
try: () => Prompt.make(input),
|
|
1722
|
+
catch: (cause) => AgentInputError.make({ message: `Unable to materialize transient Run context: ${errorMessage(cause)}` })
|
|
1723
|
+
}).pipe(Effect.flatMap((prompt) => Schema.decodeEffect(Prompt.Prompt)({ content: prompt.content }).pipe(Effect.mapError((cause) => AgentInputError.make({ message: `Unable to materialize transient Run context: ${errorMessage(cause)}` })))));
|
|
1724
|
+
const advanceHistory = (context, history, options) => Effect.gen(function* () {
|
|
1725
|
+
context.history = history;
|
|
1726
|
+
if (options.onHistory !== void 0) yield* options.onHistory(history);
|
|
1727
|
+
});
|
|
1728
|
+
const drainInputs = (context, options) => Effect.gen(function* () {
|
|
1729
|
+
if (options.input === void 0) return [];
|
|
1730
|
+
const commands = yield* options.input.drain(options.commandDrainPolicy ?? "one");
|
|
1731
|
+
const steering = [];
|
|
1732
|
+
for (const command of commands) if (command.kind === "steering") steering.push(command.input);
|
|
1733
|
+
else context.pendingFollowUps.push(command.input);
|
|
1734
|
+
return steering;
|
|
1735
|
+
});
|
|
1736
|
+
const takeFollowUps = (context, policy) => {
|
|
1737
|
+
if (context.pendingFollowUps.length === 0) return [];
|
|
1738
|
+
if (policy === "one") {
|
|
1739
|
+
const input = context.pendingFollowUps.shift();
|
|
1740
|
+
return input === void 0 ? [] : [input];
|
|
1741
|
+
}
|
|
1742
|
+
return context.pendingFollowUps.splice(0, context.pendingFollowUps.length);
|
|
1743
|
+
};
|
|
1744
|
+
const appendInputs = (context, source, inputs, options) => Effect.gen(function* () {
|
|
1745
|
+
if (inputs.length === 0) return source;
|
|
1746
|
+
const additions = yield* inputsToPrompt(inputs);
|
|
1747
|
+
const history = Prompt.fromMessages([...source.content, ...additions.content]);
|
|
1748
|
+
yield* advanceHistory(context, history, options);
|
|
1749
|
+
return history;
|
|
1750
|
+
});
|
|
1751
|
+
/**
|
|
1752
|
+
* RUN-022: bound one application Tool result at the settle seam. The bounded
|
|
1753
|
+
* value is what official history, the settled record, and the success event
|
|
1754
|
+
* all carry, so every downstream surface agrees; a within-bounds result
|
|
1755
|
+
* passes through with its identity preserved.
|
|
1756
|
+
*/
|
|
1757
|
+
const boundEncodedToolResult = (encodedResult, bounds) => {
|
|
1758
|
+
let text;
|
|
1759
|
+
try {
|
|
1760
|
+
text = JSON.stringify(encodedResult);
|
|
1761
|
+
} catch (cause) {
|
|
1762
|
+
return unserializableToolResult(cause);
|
|
1763
|
+
}
|
|
1764
|
+
if (text === void 0) return unserializableToolResult("the encoded result is not a JSON value");
|
|
1765
|
+
const bounded = applyToolResultBounds(text, bounds);
|
|
1766
|
+
try {
|
|
1767
|
+
return Schema.decodeSync(Schema.fromJsonString(Schema.Unknown))(bounded);
|
|
1768
|
+
} catch (cause) {
|
|
1769
|
+
return unserializableToolResult(cause);
|
|
1770
|
+
}
|
|
1771
|
+
};
|
|
1772
|
+
const RUN_STATUS_WARNING = " · WARNING: approaching limits — converge and deliver your final result now.";
|
|
1773
|
+
/** Fraction checks use `consumed * 5 >= limit * 4` so the 80% threshold stays integer-exact. */
|
|
1774
|
+
const nearingLimit = (consumed, limit) => consumed * 5 >= limit * 4;
|
|
1775
|
+
/**
|
|
1776
|
+
* RUN-024: render the derived run-status message appended to each outgoing
|
|
1777
|
+
* model request. The format is pinned here — tests target this function.
|
|
1778
|
+
*/
|
|
1779
|
+
const formatRunStatus = (view) => {
|
|
1780
|
+
const researchBudget = view.tokenBudget === void 0 ? void 0 : Math.max(0, view.tokenBudget - (view.completionReserveTokens ?? 0));
|
|
1781
|
+
const remainingResearch = researchBudget === void 0 ? void 0 : Math.max(0, researchBudget - view.tokensConsumed);
|
|
1782
|
+
const warn = nearingLimit(view.turn, view.maxTurns) || nearingLimit(view.toolCallsUsed, view.maxToolCalls) || researchBudget !== void 0 && (nearingLimit(view.tokensConsumed, researchBudget) || view.lastInputTokens > 0 && (remainingResearch ?? 0) <= view.lastInputTokens) || nearingLimit(view.elapsedSeconds, view.maxDurationSeconds);
|
|
1783
|
+
const reserveStatus = view.tokenBudget === void 0 ? "" : ` · research-remaining ${remainingResearch} · completion-reserve ${view.completionReserveTokens ?? 0}`;
|
|
1784
|
+
return `<run-status>turn ${view.turn}/${view.maxTurns} · tool-calls ${view.toolCallsUsed}/${view.maxToolCalls} · tokens ${view.tokensConsumed}/${view.tokenBudget ?? "unbounded"}${reserveStatus} · last-context ${view.lastInputTokens} · elapsed ${view.elapsedSeconds}s/${view.maxDurationSeconds}s${warn ? RUN_STATUS_WARNING : ""}</run-status>`;
|
|
1785
|
+
};
|
|
1786
|
+
/**
|
|
1787
|
+
* The run-status message is derived per request and appended only to the
|
|
1788
|
+
* OUTGOING prompt: official history and durable commits never carry it, so it
|
|
1789
|
+
* can never accumulate or replay.
|
|
1790
|
+
*/
|
|
1791
|
+
const outgoingModelPrompt = (policy, context, prepared, turn, declaredToolCalls) => Effect.gen(function* () {
|
|
1792
|
+
if (policy.runStatus !== "appended") return prepared;
|
|
1793
|
+
const now = yield* Clock.currentTimeMillis;
|
|
1794
|
+
const status = formatRunStatus({
|
|
1795
|
+
turn,
|
|
1796
|
+
maxTurns: policy.maxTurns,
|
|
1797
|
+
toolCallsUsed: declaredToolCalls + context.programmaticToolCalls,
|
|
1798
|
+
maxToolCalls: policy.maxToolCalls,
|
|
1799
|
+
tokensConsumed: context.inputTokens + context.outputTokens,
|
|
1800
|
+
tokenBudget: policy.tokenBudget,
|
|
1801
|
+
completionReserveTokens: policy.completionReserveTokens,
|
|
1802
|
+
lastInputTokens: context.lastInputTokens,
|
|
1803
|
+
elapsedSeconds: Math.max(0, Math.floor((now - context.startedAtMillis) / 1e3)),
|
|
1804
|
+
maxDurationSeconds: Math.floor(Duration.toMillis(policy.maxDuration) / 1e3)
|
|
1805
|
+
});
|
|
1806
|
+
return Prompt.fromMessages([...prepared.content, Prompt.makeMessage("user", { content: [Prompt.makePart("text", { text: status })] })]);
|
|
1807
|
+
});
|
|
1808
|
+
const ProviderUsage = Schema.Struct({
|
|
1809
|
+
inputTokens: Schema.Struct({
|
|
1810
|
+
uncached: Schema.optional(Schema.Natural),
|
|
1811
|
+
total: Schema.optional(Schema.Natural),
|
|
1812
|
+
cacheRead: Schema.optional(Schema.Natural),
|
|
1813
|
+
cacheWrite: Schema.optional(Schema.Natural)
|
|
1814
|
+
}),
|
|
1815
|
+
outputTokens: Schema.Struct({
|
|
1816
|
+
total: Schema.optional(Schema.Natural),
|
|
1817
|
+
text: Schema.optional(Schema.Natural),
|
|
1818
|
+
reasoning: Schema.optional(Schema.Natural)
|
|
1819
|
+
})
|
|
1820
|
+
});
|
|
1821
|
+
const invalidProviderUsage = () => ModelProtocolError.make({ message: "Model response usage fields and derived totals must be non-negative safe integers" });
|
|
1822
|
+
const decodeProviderUsageTotal = (value) => Schema.decodeEffect(Schema.Natural)(value).pipe(Effect.mapError(() => invalidProviderUsage()));
|
|
1823
|
+
const consumeUsage = (agent, context, usage, toolCallCount, turn, options, response = {}) => Effect.gen(function* () {
|
|
1824
|
+
if (usage === void 0) return yield* AgentPolicyError.make({
|
|
1825
|
+
limit: "usage",
|
|
1826
|
+
message: "A completed model response did not report usage"
|
|
1827
|
+
});
|
|
1828
|
+
const providerUsage = yield* Schema.decodeEffect(ProviderUsage)(usage).pipe(Effect.mapError(() => invalidProviderUsage()));
|
|
1829
|
+
const reportedUncached = providerUsage.inputTokens.uncached ?? 0;
|
|
1830
|
+
const cacheRead = providerUsage.inputTokens.cacheRead ?? 0;
|
|
1831
|
+
const cacheWrite = providerUsage.inputTokens.cacheWrite ?? 0;
|
|
1832
|
+
const reportedText = providerUsage.outputTokens.text ?? 0;
|
|
1833
|
+
const reasoning = providerUsage.outputTokens.reasoning ?? 0;
|
|
1834
|
+
const reportedInputTotal = providerUsage.inputTokens.total;
|
|
1835
|
+
const reportedInputWithoutWrite = yield* decodeProviderUsageTotal(reportedUncached + cacheRead);
|
|
1836
|
+
const disjointReportedUncached = reportedUncached - (reportedInputTotal !== void 0 && providerUsage.inputTokens.uncached !== void 0 && providerUsage.inputTokens.cacheWrite !== void 0 && cacheWrite <= reportedUncached && reportedInputWithoutWrite <= reportedInputTotal && cacheWrite > reportedInputTotal - reportedInputWithoutWrite ? cacheWrite : 0);
|
|
1837
|
+
const reportedInputComponents = yield* decodeProviderUsageTotal(disjointReportedUncached + cacheRead + cacheWrite);
|
|
1838
|
+
const allInputComponentsReported = providerUsage.inputTokens.uncached !== void 0 && providerUsage.inputTokens.cacheRead !== void 0 && providerUsage.inputTokens.cacheWrite !== void 0;
|
|
1839
|
+
if (reportedInputTotal !== void 0 && (reportedInputTotal < reportedInputComponents || allInputComponentsReported && reportedInputTotal !== reportedInputComponents)) return yield* invalidProviderUsage();
|
|
1840
|
+
const inputTokens = reportedInputTotal ?? reportedInputComponents;
|
|
1841
|
+
const reportedOutputComponents = yield* decodeProviderUsageTotal(reportedText + reasoning);
|
|
1842
|
+
const reportedOutputTotal = providerUsage.outputTokens.total;
|
|
1843
|
+
const allOutputComponentsReported = providerUsage.outputTokens.text !== void 0 && providerUsage.outputTokens.reasoning !== void 0;
|
|
1844
|
+
if (reportedOutputTotal !== void 0 && (reportedOutputTotal < reportedOutputComponents || allOutputComponentsReported && reportedOutputTotal !== reportedOutputComponents)) return yield* invalidProviderUsage();
|
|
1845
|
+
const outputTokens = reportedOutputTotal ?? reportedOutputComponents;
|
|
1846
|
+
const inputRemainder = inputTokens - reportedInputComponents;
|
|
1847
|
+
const outputRemainder = outputTokens - reportedOutputComponents;
|
|
1848
|
+
const uncached = disjointReportedUncached + (providerUsage.inputTokens.uncached === void 0 ? inputRemainder : 0);
|
|
1849
|
+
const normalizedCacheRead = cacheRead + (providerUsage.inputTokens.uncached !== void 0 && providerUsage.inputTokens.cacheRead === void 0 ? inputRemainder : 0);
|
|
1850
|
+
const normalizedCacheWrite = cacheWrite + (providerUsage.inputTokens.uncached !== void 0 && providerUsage.inputTokens.cacheRead !== void 0 && providerUsage.inputTokens.cacheWrite === void 0 ? inputRemainder : 0);
|
|
1851
|
+
const text = reportedText + (providerUsage.outputTokens.text === void 0 ? outputRemainder : 0);
|
|
1852
|
+
const normalizedReasoning = reasoning + (providerUsage.outputTokens.text !== void 0 && providerUsage.outputTokens.reasoning === void 0 ? outputRemainder : 0);
|
|
1853
|
+
const totalTokens = yield* decodeProviderUsageTotal(inputTokens + outputTokens);
|
|
1854
|
+
const provider = yield* Model.ProviderName;
|
|
1855
|
+
const model = yield* Model.ModelName;
|
|
1856
|
+
const estimate = options.estimateCostMicrousd === void 0 ? 0 : yield* options.estimateCostMicrousd(usage, {
|
|
1857
|
+
provider,
|
|
1858
|
+
model,
|
|
1859
|
+
usage,
|
|
1860
|
+
...response
|
|
1861
|
+
});
|
|
1862
|
+
const costMicrousd = typeof estimate === "number" ? estimate : estimate.costMicrousd;
|
|
1863
|
+
if (!Number.isSafeInteger(costMicrousd) || costMicrousd < 0) return yield* AgentPolicyError.make({
|
|
1864
|
+
limit: "cost",
|
|
1865
|
+
message: "Model cost estimation must produce a non-negative integer number of microdollars"
|
|
1866
|
+
});
|
|
1867
|
+
const serviceTier = typeof estimate === "number" ? void 0 : estimate.serviceTier;
|
|
1868
|
+
const pricingVersion = typeof estimate === "number" ? void 0 : estimate.pricingVersion;
|
|
1869
|
+
const pricingStatus = options.estimateCostMicrousd === void 0 ? "unknown" : typeof estimate === "number" ? "estimated" : estimate.pricingStatus ?? "estimated";
|
|
1870
|
+
const validPricingIdentity = (value) => value === void 0 || value.length > 0 && value.length <= 256;
|
|
1871
|
+
if (!validPricingIdentity(serviceTier) || !validPricingIdentity(pricingVersion)) return yield* AgentPolicyError.make({
|
|
1872
|
+
limit: "cost",
|
|
1873
|
+
message: "Model cost estimation returned an invalid service tier or pricing version"
|
|
1874
|
+
});
|
|
1875
|
+
const modelUsage = ModelCallUsage.make({
|
|
1876
|
+
provider,
|
|
1877
|
+
model,
|
|
1878
|
+
...response.response === void 0 ? {} : { response: ModelResponseIdentity.make(response.response) },
|
|
1879
|
+
purpose: response.purpose ?? "turn",
|
|
1880
|
+
usageStatus: Object.values(providerUsage.inputTokens).every((value) => value === void 0) && Object.values(providerUsage.outputTokens).every((value) => value === void 0) ? "unknown" : [
|
|
1881
|
+
providerUsage.inputTokens.uncached,
|
|
1882
|
+
providerUsage.inputTokens.cacheRead,
|
|
1883
|
+
providerUsage.inputTokens.cacheWrite,
|
|
1884
|
+
providerUsage.outputTokens.text,
|
|
1885
|
+
providerUsage.outputTokens.reasoning
|
|
1886
|
+
].every((value) => value !== void 0) ? "complete" : "partial",
|
|
1887
|
+
pricingStatus,
|
|
1888
|
+
...serviceTier === void 0 ? {} : { serviceTier },
|
|
1889
|
+
...pricingVersion === void 0 ? {} : { pricingVersion },
|
|
1890
|
+
inputTokens: InputTokenUsage.make({
|
|
1891
|
+
total: inputTokens,
|
|
1892
|
+
uncached,
|
|
1893
|
+
cacheRead: normalizedCacheRead,
|
|
1894
|
+
cacheWrite: normalizedCacheWrite
|
|
1895
|
+
}),
|
|
1896
|
+
outputTokens: OutputTokenUsage.make({
|
|
1897
|
+
total: outputTokens,
|
|
1898
|
+
text,
|
|
1899
|
+
reasoning: normalizedReasoning
|
|
1900
|
+
}),
|
|
1901
|
+
costMicrousd
|
|
1902
|
+
});
|
|
1903
|
+
const modelCalls = yield* decodeProviderUsageTotal(context.modelCalls + 1);
|
|
1904
|
+
const cumulativeInputTokens = yield* decodeProviderUsageTotal(context.inputTokens + inputTokens);
|
|
1905
|
+
const cumulativeOutputTokens = yield* decodeProviderUsageTotal(context.outputTokens + outputTokens);
|
|
1906
|
+
const cumulativeCostMicrousd = context.costMicrousd + costMicrousd;
|
|
1907
|
+
if (!Number.isSafeInteger(cumulativeCostMicrousd)) return yield* AgentPolicyError.make({
|
|
1908
|
+
limit: "cost",
|
|
1909
|
+
message: "Cumulative model cost exceeds safe-integer accounting capacity"
|
|
1910
|
+
});
|
|
1911
|
+
const firstObserved = context.modelCalls === 0 && context.unobservedModelCalls === 0;
|
|
1912
|
+
const combineStatus = (prior, next) => firstObserved && prior === "complete" ? next : prior === next ? prior : "partial";
|
|
1913
|
+
context.usageStatus = combineStatus(context.usageStatus, modelUsage.usageStatus ?? "unknown");
|
|
1914
|
+
context.pricingStatus = combineStatus(context.pricingStatus, pricingStatus === "estimated" ? "complete" : "unknown");
|
|
1915
|
+
context.modelCalls = modelCalls;
|
|
1916
|
+
context.inputTokens = cumulativeInputTokens;
|
|
1917
|
+
context.outputTokens = cumulativeOutputTokens;
|
|
1918
|
+
context.lastInputTokens = inputTokens;
|
|
1919
|
+
context.lastOutputTokens = outputTokens;
|
|
1920
|
+
context.costMicrousd = cumulativeCostMicrousd;
|
|
1921
|
+
context.lastCostMicrousd = costMicrousd;
|
|
1922
|
+
if (options.durability !== void 0) yield* options.durability.noteTurnUsage({
|
|
1923
|
+
turn,
|
|
1924
|
+
usage: modelUsage
|
|
1925
|
+
});
|
|
1926
|
+
const policy = agent.definition.policy;
|
|
1927
|
+
const consumedTokens = context.inputTokens + context.outputTokens;
|
|
1928
|
+
const tokenBudget = policy.tokenBudget;
|
|
1929
|
+
let breach;
|
|
1930
|
+
if (!context.finalizing && !context.tokenExhausted && tokenBudget !== void 0 && consumedTokens > tokenBudget) {
|
|
1931
|
+
breach = AgentPolicyError.make({
|
|
1932
|
+
limit: "tokens",
|
|
1933
|
+
message: `Agent exceeded its ${tokenBudget} token budget`
|
|
1934
|
+
});
|
|
1935
|
+
if (policy.onExhaustion === "fail") return yield* breach;
|
|
1936
|
+
context.tokenExhausted = true;
|
|
1937
|
+
context.exhaustedDimension ??= "tokens";
|
|
1938
|
+
}
|
|
1939
|
+
const costBudget = policy.costBudgetMicrousd;
|
|
1940
|
+
if (costBudget !== void 0) {
|
|
1941
|
+
if (options.estimateCostMicrousd === void 0) return yield* AgentPolicyError.make({
|
|
1942
|
+
limit: "cost",
|
|
1943
|
+
message: "Agent cost budget requires a model cost estimator"
|
|
1944
|
+
});
|
|
1945
|
+
if (pricingStatus === "unknown") return yield* AgentPolicyError.make({
|
|
1946
|
+
limit: "cost",
|
|
1947
|
+
message: "Agent cost budget requires a known model cost estimate"
|
|
1948
|
+
});
|
|
1949
|
+
if (context.costMicrousd > costBudget) return yield* AgentPolicyError.make({
|
|
1950
|
+
limit: "cost",
|
|
1951
|
+
message: `Agent exceeded its ${costBudget} microdollar cost budget`
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
if (options.budget !== void 0) {
|
|
1955
|
+
const delta = {
|
|
1956
|
+
modelCalls: 1,
|
|
1957
|
+
inputTokens,
|
|
1958
|
+
outputTokens,
|
|
1959
|
+
totalTokens,
|
|
1960
|
+
toolCalls: toolCallCount,
|
|
1961
|
+
costMicrousd,
|
|
1962
|
+
usage,
|
|
1963
|
+
modelUsage
|
|
1964
|
+
};
|
|
1965
|
+
yield* options.budget.consume(delta);
|
|
1966
|
+
}
|
|
1967
|
+
const warnings = [];
|
|
1968
|
+
if (tokenBudget !== void 0 && !context.warnedLimits.has("tokens") && nearingLimit(consumedTokens, tokenBudget)) {
|
|
1969
|
+
context.warnedLimits.add("tokens");
|
|
1970
|
+
warnings.push(BudgetWarning.make({
|
|
1971
|
+
...yield* eventBase(context),
|
|
1972
|
+
limit: "tokens",
|
|
1973
|
+
consumed: consumedTokens,
|
|
1974
|
+
limitValue: tokenBudget
|
|
1975
|
+
}));
|
|
1976
|
+
}
|
|
1977
|
+
return {
|
|
1978
|
+
breach,
|
|
1979
|
+
warnings,
|
|
1980
|
+
modelUsage
|
|
1981
|
+
};
|
|
1982
|
+
});
|
|
1983
|
+
const eventBaseFor = Effect.fnUntraced(function* (context, terminal) {
|
|
1984
|
+
const ceiling = terminal ? context.bufferLimits.maxRunEvents : context.bufferLimits.maxRunEvents - 1;
|
|
1985
|
+
if (context.sequence >= ceiling) return yield* ModelProtocolError.make({ message: `Run exceeded the ${context.bufferLimits.maxRunEvents}-event buffer limit` });
|
|
1986
|
+
const timestamp = DateTime.makeUnsafe(yield* Clock.currentTimeMillis);
|
|
1987
|
+
const sequence = context.sequence;
|
|
1988
|
+
context.sequence += 1;
|
|
1989
|
+
return {
|
|
1990
|
+
eventVersion: 1,
|
|
1991
|
+
runId: context.runId,
|
|
1992
|
+
threadId: context.threadId,
|
|
1993
|
+
agentId: context.agentId,
|
|
1994
|
+
sequence,
|
|
1995
|
+
timestamp
|
|
1996
|
+
};
|
|
1997
|
+
});
|
|
1998
|
+
const eventBase = (context) => eventBaseFor(context, false);
|
|
1999
|
+
/** The ordinary event budget always reserves one slot for this typed terminal projection. */
|
|
2000
|
+
const terminalEventBase = (context) => eventBaseFor(context, true);
|
|
2001
|
+
const toolProgressLimitError = (context) => ModelProtocolError.make({ message: `Run exceeded the ${context.bufferLimits.maxToolProgressBytes}-byte Tool progress limit or received invalid progress JSON` });
|
|
2002
|
+
const admitToolProgress = (context, snapshot) => Effect.suspend(() => {
|
|
2003
|
+
if (snapshot.bytes > context.bufferLimits.maxToolProgressBytes - context.toolProgressBytes) return Effect.fail(toolProgressLimitError(context));
|
|
2004
|
+
context.toolProgressBytes += snapshot.bytes;
|
|
2005
|
+
return Effect.succeed(snapshot.value);
|
|
2006
|
+
});
|
|
2007
|
+
const ownApplicationToolProgress = (context, result) => Effect.suspend(() => {
|
|
2008
|
+
const snapshot = boundedCanonicalJsonSnapshot(result, context.bufferLimits.maxToolProgressBytes - context.toolProgressBytes);
|
|
2009
|
+
return snapshot === void 0 ? Effect.fail(toolProgressLimitError(context)) : admitToolProgress(context, snapshot);
|
|
2010
|
+
});
|
|
2011
|
+
const snapshotStagedProviderEvent = (trace, payload) => Effect.suspend(() => {
|
|
2012
|
+
if (trace.providerResultPayloads.length + (trace.turnCompletion === void 0 ? 0 : 1) >= MAX_STAGED_PROVIDER_EVENTS) return Effect.fail(ModelProtocolError.make({ message: `Model response exceeded the ${MAX_STAGED_PROVIDER_EVENTS}-event staged provider event limit` }));
|
|
2013
|
+
const snapshot = boundedJsonSnapshot(payload, MAX_STAGED_PROVIDER_BYTES - trace.providerStagedPayloadBytes);
|
|
2014
|
+
if (snapshot === void 0) return Effect.fail(ModelProtocolError.make({ message: `Model response exceeded the ${MAX_STAGED_PROVIDER_BYTES}-byte staged provider event limit` }));
|
|
2015
|
+
return Effect.succeed(snapshot);
|
|
2016
|
+
});
|
|
2017
|
+
const stageProviderResultPayload = (trace, payload) => Effect.gen(function* () {
|
|
2018
|
+
const snapshot = yield* snapshotStagedProviderEvent(trace, payload);
|
|
2019
|
+
const snapshotObject = snapshot.value;
|
|
2020
|
+
if (snapshotObject === null || typeof snapshotObject !== "object" || Array.isArray(snapshotObject) || Object.getPrototypeOf(snapshotObject) !== null) return yield* ModelProtocolError.make({ message: "Provider Tool result could not be normalized as JSON" });
|
|
2021
|
+
const resultDescriptor = Object.getOwnPropertyDescriptor(snapshotObject, "result");
|
|
2022
|
+
const normalizedResult = resultDescriptor !== void 0 && "value" in resultDescriptor ? Schema.decodeUnknownOption(Schema.Json)(resultDescriptor.value) : Option.none();
|
|
2023
|
+
if (payload._tag !== "ToolCallFailed" && Option.isNone(normalizedResult)) return yield* ModelProtocolError.make({ message: "Provider Tool result could not be normalized as JSON" });
|
|
2024
|
+
const normalized = payload._tag === "ToolCallFailed" ? Object.freeze({ ...payload }) : Object.freeze({
|
|
2025
|
+
...payload,
|
|
2026
|
+
result: Option.getOrThrow(normalizedResult)
|
|
2027
|
+
});
|
|
2028
|
+
trace.providerResultPayloads.push(normalized);
|
|
2029
|
+
trace.providerStagedPayloadBytes += snapshot.bytes;
|
|
2030
|
+
});
|
|
2031
|
+
const ProviderToolResultSnapshot = Schema.Struct({
|
|
2032
|
+
result: Schema.Json,
|
|
2033
|
+
metadata: Schema.Record(Schema.String, Schema.Json)
|
|
2034
|
+
});
|
|
2035
|
+
const snapshotProviderToolResultPart = Effect.fnUntraced(function* (trace, part) {
|
|
2036
|
+
const snapshot = boundedJsonSnapshot({
|
|
2037
|
+
result: part.encodedResult,
|
|
2038
|
+
metadata: part.metadata
|
|
2039
|
+
}, MAX_STAGED_PROVIDER_BYTES - trace.providerStagedPayloadBytes);
|
|
2040
|
+
if (snapshot === void 0) return yield* ModelProtocolError.make({ message: `Model response exceeded the ${MAX_STAGED_PROVIDER_BYTES}-byte staged provider event limit` });
|
|
2041
|
+
const normalized = yield* Schema.decodeUnknownEffect(ProviderToolResultSnapshot)(snapshot.value).pipe(Effect.mapError(() => ModelProtocolError.make({ message: "Provider Tool result could not be normalized as JSON" })));
|
|
2042
|
+
trace.providerStagedPayloadBytes += snapshot.bytes;
|
|
2043
|
+
return normalized;
|
|
2044
|
+
});
|
|
2045
|
+
const stampProviderResultEvent = (context, turnId, payload) => Effect.gen(function* () {
|
|
2046
|
+
if (payload._tag === "ToolProgress") {
|
|
2047
|
+
const text = yield* Schema.encodeEffect(Schema.fromJsonString(Schema.Json))(payload.result).pipe(Effect.mapError(() => toolProgressLimitError(context)));
|
|
2048
|
+
yield* admitToolProgress(context, {
|
|
2049
|
+
value: payload.result,
|
|
2050
|
+
bytes: utf8ByteLength(text)
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
2053
|
+
const base = yield* eventBase(context);
|
|
2054
|
+
switch (payload._tag) {
|
|
2055
|
+
case "ToolProgress": return ToolProgress.make({
|
|
2056
|
+
...base,
|
|
2057
|
+
turnId,
|
|
2058
|
+
...payload
|
|
2059
|
+
});
|
|
2060
|
+
case "ToolCallSucceeded": return ToolCallSucceeded.make({
|
|
2061
|
+
...base,
|
|
2062
|
+
turnId,
|
|
2063
|
+
...payload
|
|
2064
|
+
});
|
|
2065
|
+
case "ToolCallFailed": return ToolCallFailed.make({
|
|
2066
|
+
...base,
|
|
2067
|
+
turnId,
|
|
2068
|
+
...payload,
|
|
2069
|
+
failureHandling: "returned-to-model"
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
2072
|
+
});
|
|
2073
|
+
/**
|
|
2074
|
+
* RUN-026: deterministic estimate of the NEXT model call's live context.
|
|
2075
|
+
* Anchored on the last provider-reported call when the view has only grown
|
|
2076
|
+
* since then; a fresh Run or a just-compacted view falls back to the full
|
|
2077
|
+
* chars/4 estimate.
|
|
2078
|
+
*/
|
|
2079
|
+
const estimateContextTokens = Effect.fn("AgentRuntime.estimateContextTokens")(function* (messages, messageTokenEstimator) {
|
|
2080
|
+
const compactor = yield* ContextCompactor;
|
|
2081
|
+
const estimate = messageTokenEstimator === void 0 ? Option.some(compactor.estimate(messages)) : evaluateMessageTokenEstimates(messageTokenEstimator, (estimate) => estimatePromptTokens(messages, estimate));
|
|
2082
|
+
if (Option.isNone(estimate)) return yield* CompactionError.make({ message: "Message token estimator returned an invalid token count" });
|
|
2083
|
+
return yield* Schema.decodeEffect(Schema.Natural)(estimate.value).pipe(Effect.mapError((cause) => CompactionError.make({
|
|
2084
|
+
message: "Compactor returned an invalid token estimate",
|
|
2085
|
+
cause
|
|
2086
|
+
})));
|
|
2087
|
+
});
|
|
2088
|
+
const nextContextEstimate = Effect.fn("AgentRuntime.nextContextEstimate")(function* (context, view) {
|
|
2089
|
+
const state = context.compaction;
|
|
2090
|
+
if (context.lastInputTokens > 0 && state.lastViewLength >= 0 && state.lastViewLength <= view.length) return context.lastInputTokens + context.lastOutputTokens + (yield* estimateContextTokens(view.slice(state.lastViewLength)));
|
|
2091
|
+
return yield* estimateContextTokens(view);
|
|
2092
|
+
});
|
|
2093
|
+
const snapshotCompactionMessages = Effect.fnUntraced(function* (messages) {
|
|
2094
|
+
return yield* Effect.try({
|
|
2095
|
+
try: () => messages.map((message) => Schema.decodeUnknownSync(Schema.Json)(JSON.parse(JSON.stringify(Schema.encodeSync(Prompt.Message)(message))))),
|
|
2096
|
+
catch: (cause) => CompactionError.make({
|
|
2097
|
+
message: "Could not snapshot prepared compaction history",
|
|
2098
|
+
cause
|
|
2099
|
+
})
|
|
2100
|
+
});
|
|
2101
|
+
});
|
|
2102
|
+
/** Text a provider overflow classification matches against (message + reason). */
|
|
2103
|
+
const overflowText = (error) => `${error.message} ${error.reason.message}`;
|
|
2104
|
+
/**
|
|
2105
|
+
* Consume the installed strategy under engine-owned bounds. Validate each decision, commit it,
|
|
2106
|
+
* then update the disposable view. Summary model calls retain the Run's response-buffer and
|
|
2107
|
+
* budget guards; default summaries also obey the captured Model's input allowance. Pricing
|
|
2108
|
+
* reads the selected Model's identity inside the same provision scope.
|
|
2109
|
+
*/
|
|
2110
|
+
const compactContext = (agent, context, source, turn, options, resolvedModelInputLimit, messageTokenEstimator, targetTokens, trigger, modelCallAllowed = true, requested) => Effect.gen(function* () {
|
|
2111
|
+
const state = context.compaction;
|
|
2112
|
+
const events = [];
|
|
2113
|
+
const messages = source.content;
|
|
2114
|
+
const allowance = context.compactionTurn;
|
|
2115
|
+
const preparedSource = context.preparedCompactionSource;
|
|
2116
|
+
const preparedSnapshot = preparedSource === void 0 ? void 0 : yield* snapshotCompactionMessages(messages);
|
|
2117
|
+
if (preparedSource !== void 0 && preparedSnapshot !== void 0) {
|
|
2118
|
+
let start = -1;
|
|
2119
|
+
let end = 0;
|
|
2120
|
+
let mapped = true;
|
|
2121
|
+
const positions = [];
|
|
2122
|
+
const originalPositions = preparedSource.protectedReferences.map((message) => messages.indexOf(message));
|
|
2123
|
+
const originalMapping = preparedSource.protectedReferences.every((message, position) => {
|
|
2124
|
+
const index = originalPositions[position];
|
|
2125
|
+
return index !== void 0 && index >= 0 && index > (originalPositions[position - 1] ?? -1) && messages.lastIndexOf(message) === index && Equal.equals(preparedSnapshot[index], preparedSource.protectedMessages[position]);
|
|
2126
|
+
});
|
|
2127
|
+
for (let position = 0; !originalMapping && position < preparedSource.protectedMessages.length; position += 1) {
|
|
2128
|
+
const protectedMessage = preparedSource.protectedMessages[position];
|
|
2129
|
+
const index = preparedSnapshot.findIndex((message, index) => index >= end && Equal.equals(message, protectedMessage));
|
|
2130
|
+
if (index < 0) {
|
|
2131
|
+
mapped = false;
|
|
2132
|
+
break;
|
|
2133
|
+
}
|
|
2134
|
+
if (start < 0) start = index;
|
|
2135
|
+
end = index + 1;
|
|
2136
|
+
positions.push(index);
|
|
2137
|
+
}
|
|
2138
|
+
let reverseEnd = preparedSnapshot.length;
|
|
2139
|
+
for (let position = preparedSource.protectedMessages.length - 1; !originalMapping && mapped && position >= 0; position -= 1) {
|
|
2140
|
+
const protectedMessage = preparedSource.protectedMessages[position];
|
|
2141
|
+
const index = preparedSnapshot.findLastIndex((message, index) => index < reverseEnd && Equal.equals(message, protectedMessage));
|
|
2142
|
+
if (index !== positions[position]) mapped = false;
|
|
2143
|
+
reverseEnd = index;
|
|
2144
|
+
}
|
|
2145
|
+
if (originalMapping) {
|
|
2146
|
+
mapped = true;
|
|
2147
|
+
start = originalPositions[0] ?? -1;
|
|
2148
|
+
end = (originalPositions.at(-1) ?? -1) + 1;
|
|
2149
|
+
}
|
|
2150
|
+
if (!mapped) return yield* CompactionError.make({ message: "Prepared context cannot map the protected instructions and input for compaction" });
|
|
2151
|
+
state.protectedStart = mapped ? start : -1;
|
|
2152
|
+
state.protectedEnd = mapped ? end : -1;
|
|
2153
|
+
state.protectSystemMessages = true;
|
|
2154
|
+
}
|
|
2155
|
+
const resolvedRequest = requested === void 0 ? void 0 : {
|
|
2156
|
+
...requested,
|
|
2157
|
+
through: requested.through ?? Math.max(0, state.protectedStart)
|
|
2158
|
+
};
|
|
2159
|
+
if (requested !== void 0 && requested.through === void 0 && resolvedRequest !== void 0 && (resolvedRequest.through <= (state.replacement?.through ?? 0) || collectCoveredMessages(messages, state, resolvedRequest.through).length === 0)) return { events };
|
|
2160
|
+
if (allowance.turn !== turn) {
|
|
2161
|
+
allowance.turn = turn;
|
|
2162
|
+
allowance.summaryCalls = 0;
|
|
2163
|
+
allowance.applied.clear();
|
|
2164
|
+
}
|
|
2165
|
+
if (allowance.applied.has("summarize") || allowance.applied.has("rollover")) return yield* CompactionError.make({ message: "Compaction already replaced this Turn's context" });
|
|
2166
|
+
const before = yield* estimateContextTokens(buildCompactedView(messages, state), messageTokenEstimator);
|
|
2167
|
+
const summarize = (summarizerPrompt, model) => {
|
|
2168
|
+
const generate = Effect.gen(function* () {
|
|
2169
|
+
if (allowance.summaryCalls++ > 0 || !modelCallAllowed) return yield* CompactionError.make({ message: "Compaction exceeded its summary-call allowance" });
|
|
2170
|
+
if (model === void 0 && resolvedModelInputLimit !== void 0 && (yield* estimateContextTokens(summarizerPrompt.content, messageTokenEstimator)) > resolvedModelInputLimit) return yield* CompactionError.make({ message: "Compaction summary request exceeds the resolved model input limit" });
|
|
2171
|
+
const pieces = [];
|
|
2172
|
+
const responseUsage = {
|
|
2173
|
+
responsePartCount: 0,
|
|
2174
|
+
responsePartBytes: 0
|
|
2175
|
+
};
|
|
2176
|
+
let summaryUsage;
|
|
2177
|
+
let summaryResponse;
|
|
2178
|
+
let summaryFinishMetadata;
|
|
2179
|
+
let summaryFinished = false;
|
|
2180
|
+
let summaryFailure;
|
|
2181
|
+
const textParts = /* @__PURE__ */ new Map();
|
|
2182
|
+
const reasoningParts = /* @__PURE__ */ new Map();
|
|
2183
|
+
const summaryExit = yield* guardBudgetStream(LanguageModel.streamText({ prompt: summarizerPrompt }), options.budget).pipe(Stream.runForEach((part) => Effect.gen(function* () {
|
|
2184
|
+
const owned = yield* ownModelResponsePart(part, Toolkit.empty, responseUsage, context.bufferLimits);
|
|
2185
|
+
yield* consumeModelResponsePart(responseUsage, owned.retainedBytes, context.bufferLimits);
|
|
2186
|
+
const ownedPart = owned.ownedPart;
|
|
2187
|
+
if (ownedPart.type === "text-delta") pieces.push(ownedPart.delta);
|
|
2188
|
+
else if (ownedPart.type === "finish") {
|
|
2189
|
+
if (summaryUsage === void 0) {
|
|
2190
|
+
summaryUsage = ownedPart.usage;
|
|
2191
|
+
summaryFinishMetadata = ownedPart.metadata;
|
|
2192
|
+
}
|
|
2193
|
+
} else if (ownedPart.type === "response-metadata") summaryResponse = yield* responseIdentity(ownedPart, summaryResponse);
|
|
2194
|
+
yield* Effect.gen(function* () {
|
|
2195
|
+
if (summaryFinished) return yield* ModelProtocolError.make({ message: "Compaction response emitted content after its finish part" });
|
|
2196
|
+
switch (ownedPart.type) {
|
|
2197
|
+
case "text-start": return yield* startPart(textParts, ownedPart.id, "compaction text");
|
|
2198
|
+
case "text-delta": return yield* continuePart(textParts, ownedPart.id, "compaction text delta");
|
|
2199
|
+
case "text-end": return yield* endPart(textParts, ownedPart.id, "compaction text");
|
|
2200
|
+
case "reasoning-start": return yield* startPart(reasoningParts, ownedPart.id, "compaction reasoning");
|
|
2201
|
+
case "reasoning-delta": return yield* continuePart(reasoningParts, ownedPart.id, "compaction reasoning delta");
|
|
2202
|
+
case "reasoning-end": return yield* endPart(reasoningParts, ownedPart.id, "compaction reasoning");
|
|
2203
|
+
case "finish":
|
|
2204
|
+
summaryFinished = true;
|
|
2205
|
+
if (ownedPart.reason !== "stop" || [...textParts.values(), ...reasoningParts.values()].includes("open")) return yield* ModelProtocolError.make({ message: "Compaction response did not finish with complete text and a stop reason" });
|
|
2206
|
+
return;
|
|
2207
|
+
case "tool-params-start":
|
|
2208
|
+
case "tool-params-delta":
|
|
2209
|
+
case "tool-params-end":
|
|
2210
|
+
case "tool-approval-request":
|
|
2211
|
+
case "error": return yield* ModelProtocolError.make({ message: `Compaction response contained an unusable ${ownedPart.type} part` });
|
|
2212
|
+
case "file":
|
|
2213
|
+
case "response-metadata":
|
|
2214
|
+
case "source": return;
|
|
2215
|
+
}
|
|
2216
|
+
}).pipe(Effect.catch((error) => Effect.sync(() => {
|
|
2217
|
+
summaryFailure ??= error;
|
|
2218
|
+
})));
|
|
2219
|
+
})), Effect.exit);
|
|
2220
|
+
if (summaryUsage === void 0) {
|
|
2221
|
+
yield* noteIncompleteUsage(context, turn);
|
|
2222
|
+
if (Exit.isFailure(summaryExit)) return yield* Effect.failCause(summaryExit.cause);
|
|
2223
|
+
if (!summaryFinished) return yield* ModelProtocolError.make({ message: "Compaction response ended without a finish part" });
|
|
2224
|
+
}
|
|
2225
|
+
const wasFinalizing = context.finalizing;
|
|
2226
|
+
const priorSummaryModelCalls = context.modelCalls;
|
|
2227
|
+
context.finalizing = true;
|
|
2228
|
+
const consumedExit = yield* consumeUsage(agent, context, summaryUsage, 0, turn, options, {
|
|
2229
|
+
response: summaryResponse,
|
|
2230
|
+
finishMetadata: summaryFinishMetadata,
|
|
2231
|
+
purpose: "summary"
|
|
2232
|
+
}).pipe(Effect.tapCause(() => summaryUsage !== void 0 && context.modelCalls === priorSummaryModelCalls ? noteIncompleteUsage(context, turn) : Effect.void), Effect.ensuring(Effect.sync(() => {
|
|
2233
|
+
context.finalizing = wasFinalizing;
|
|
2234
|
+
})), Effect.exit);
|
|
2235
|
+
if (Exit.isFailure(summaryExit)) return yield* Effect.failCause(summaryExit.cause);
|
|
2236
|
+
if (Exit.isFailure(consumedExit)) return yield* Effect.failCause(consumedExit.cause);
|
|
2237
|
+
events.push(...consumedExit.value.warnings);
|
|
2238
|
+
if (!summaryFinished) return yield* ModelProtocolError.make({ message: "Compaction response ended without a finish part" });
|
|
2239
|
+
const summary = pieces.join("").trim();
|
|
2240
|
+
if (summaryFailure !== void 0) return yield* summaryFailure;
|
|
2241
|
+
if (summary.length === 0) return yield* ModelProtocolError.make({ message: "Compaction response requires non-whitespace text and a valid finish part" });
|
|
2242
|
+
return summary;
|
|
2243
|
+
});
|
|
2244
|
+
return model === void 0 ? generate : Effect.provide(generate, model);
|
|
2245
|
+
};
|
|
2246
|
+
const applied = allowance.applied;
|
|
2247
|
+
yield* (yield* ContextCompactor).compact({
|
|
2248
|
+
source,
|
|
2249
|
+
state: Object.freeze({
|
|
2250
|
+
...state,
|
|
2251
|
+
replacement: state.replacement === void 0 ? void 0 : Object.freeze({ ...state.replacement })
|
|
2252
|
+
}),
|
|
2253
|
+
policy: agent.definition.policy.compaction,
|
|
2254
|
+
targetTokens,
|
|
2255
|
+
threadId: context.threadId,
|
|
2256
|
+
runId: context.runId,
|
|
2257
|
+
turn,
|
|
2258
|
+
trigger,
|
|
2259
|
+
modelCallAllowed,
|
|
2260
|
+
...messageTokenEstimator === void 0 ? {} : { estimateMessageTokens: messageTokenEstimator },
|
|
2261
|
+
...resolvedRequest === void 0 ? {} : { requested: resolvedRequest },
|
|
2262
|
+
summarize
|
|
2263
|
+
}).pipe(Stream.runForEach((candidate) => Effect.gen(function* () {
|
|
2264
|
+
const decision = yield* Schema.decodeEffect(CompactionDecision)(candidate).pipe(Effect.mapError((cause) => CompactionError.make({
|
|
2265
|
+
message: "Invalid compaction decision",
|
|
2266
|
+
cause
|
|
2267
|
+
})));
|
|
2268
|
+
if (applied.has(decision.kind) || applied.has("summarize") || applied.has("rollover")) return yield* CompactionError.make({ message: "Compaction exceeded its decision allowance" });
|
|
2269
|
+
const next = {
|
|
2270
|
+
...state,
|
|
2271
|
+
lastViewLength: -1
|
|
2272
|
+
};
|
|
2273
|
+
if (resolvedRequest !== void 0 && (decision.kind !== "rollover" || decision.through !== resolvedRequest.through)) return yield* CompactionError.make({ message: "Requested rollover must cover exactly its selected source boundary" });
|
|
2274
|
+
if (decision.kind === "rollover") {
|
|
2275
|
+
if (decision.through <= (state.replacement?.through ?? 0) || decision.through > messages.length || messages[decision.through]?.role === "tool" || collectCoveredMessages(messages, state, decision.through).length === 0) return yield* CompactionError.make({ message: "Rollover must advance coverage without splitting Tool pairs or discarding protected input" });
|
|
2276
|
+
next.replacement = {
|
|
2277
|
+
kind: "rollover",
|
|
2278
|
+
through: decision.through,
|
|
2279
|
+
windowId: contextWindowId(context.runId, turn),
|
|
2280
|
+
...decision.handoff === void 0 ? {} : { handoff: decision.handoff }
|
|
2281
|
+
};
|
|
2282
|
+
} else if (decision.kind === "summarize") {
|
|
2283
|
+
if (utf8ByteLength(decision.summary) > context.bufferLimits.maxModelResponseBytes) return yield* CompactionError.make({ message: "Compaction summary exceeded the response-buffer limit" });
|
|
2284
|
+
if (decision.through <= (state.replacement?.through ?? 0) || decision.through >= messages.length || messages[decision.through]?.role === "tool" || collectCoveredMessages(messages, state, decision.through).length === 0) return yield* CompactionError.make({ message: "Compaction must advance coverage without splitting Tool pairs or removing the recent tail" });
|
|
2285
|
+
next.replacement = {
|
|
2286
|
+
kind: "summarize",
|
|
2287
|
+
through: decision.through,
|
|
2288
|
+
summary: decision.summary
|
|
2289
|
+
};
|
|
2290
|
+
} else {
|
|
2291
|
+
const newestTool = messages.findLastIndex((message) => message.role === "tool");
|
|
2292
|
+
if (decision.through <= state.clearedThrough || decision.through > newestTool) return yield* CompactionError.make({ message: "Compaction must advance pruning while retaining the newest Tool result" });
|
|
2293
|
+
next.clearedThrough = decision.through;
|
|
2294
|
+
}
|
|
2295
|
+
const after = yield* estimateContextTokens(buildCompactedView(messages, next), messageTokenEstimator);
|
|
2296
|
+
if (decision.kind === "rollover" && trigger !== "requested" && after >= before) return yield* CompactionError.make({ message: "Automatic rollover did not reduce context" });
|
|
2297
|
+
const commit = {
|
|
2298
|
+
turn,
|
|
2299
|
+
source,
|
|
2300
|
+
through: decision.through,
|
|
2301
|
+
kind: decision.kind,
|
|
2302
|
+
...decision.kind === "summarize" ? { summary: decision.summary } : {},
|
|
2303
|
+
...decision.kind === "rollover" && decision.handoff !== void 0 ? { handoff: decision.handoff } : {},
|
|
2304
|
+
tokensBeforeEstimate: before,
|
|
2305
|
+
tokensAfterEstimate: after
|
|
2306
|
+
};
|
|
2307
|
+
if (options.durability !== void 0) yield* options.durability.commitCompaction(commit);
|
|
2308
|
+
Object.assign(state, next);
|
|
2309
|
+
if (next.replacement?.kind === "rollover") context.windowId = next.replacement.windowId;
|
|
2310
|
+
if (preparedSource !== void 0 && preparedSnapshot !== void 0) preparedSource.prefix = preparedSnapshot.slice(0, Math.max(next.clearedThrough, next.replacement?.through ?? 0));
|
|
2311
|
+
applied.add(decision.kind);
|
|
2312
|
+
events.push(CompactionPerformed.make({
|
|
2313
|
+
...yield* eventBase(context),
|
|
2314
|
+
turn,
|
|
2315
|
+
kind: decision.kind,
|
|
2316
|
+
tokensBeforeEstimate: before,
|
|
2317
|
+
tokensAfterEstimate: after
|
|
2318
|
+
}));
|
|
2319
|
+
})));
|
|
2320
|
+
if (requested !== void 0 && !applied.has("rollover")) return yield* CompactionError.make({ message: "Compaction strategy did not honor the requested rollover" });
|
|
2321
|
+
return { events };
|
|
2322
|
+
});
|
|
2323
|
+
const decodeInput = Effect.fn("AgentRuntime.decodeInput")((agent, input) => Schema.decodeUnknownEffect(agent.definition.input)(input).pipe(Effect.mapError((cause) => AgentInputError.make({ message: cause.message }))));
|
|
2324
|
+
const evaluateInstructions = Effect.fn("AgentRuntime.evaluateInstructions")((instructions, input) => Effect.suspend(() => {
|
|
2325
|
+
const result = typeof instructions === "function" ? instructions(input) : instructions;
|
|
2326
|
+
return Effect.isEffect(result) ? result : Effect.succeed(result);
|
|
2327
|
+
}));
|
|
2328
|
+
const encodeInput = Effect.fn("AgentRuntime.encodeInput")((agent, input) => Schema.encodeEffect(agent.definition.input)(input).pipe(Effect.mapError((cause) => AgentInputError.make({ message: `Unable to encode Agent input: ${cause.message}` }))));
|
|
2329
|
+
const renderInputPromptEffect = Effect.fn("AgentRuntime.renderInputPrompt")((inputPrompt, decodedInput, encodedInput) => inputPrompt === void 0 ? Effect.try({
|
|
2330
|
+
try: () => {
|
|
2331
|
+
const encoded = JSON.stringify(encodedInput);
|
|
2332
|
+
if (encoded === void 0) throw new Error("Agent input cannot be represented as JSON");
|
|
2333
|
+
return encoded;
|
|
2334
|
+
},
|
|
2335
|
+
catch: (cause) => AgentInputError.make({ message: `Unable to materialize Agent input: ${errorMessage(cause)}` })
|
|
2336
|
+
}) : Effect.suspend(() => {
|
|
2337
|
+
const result = inputPrompt(decodedInput);
|
|
2338
|
+
return Effect.isEffect(result) ? result : Effect.succeed(result);
|
|
2339
|
+
}));
|
|
2340
|
+
function renderInputPrompt(inputPrompt, decodedInput, encodedInput) {
|
|
2341
|
+
return renderInputPromptEffect(inputPrompt, decodedInput, encodedInput);
|
|
2342
|
+
}
|
|
2343
|
+
const makeInitialPrompt = Effect.fn("AgentRuntime.makeInitialPrompt")((instructions, inputPrompt, history) => Effect.try({
|
|
2344
|
+
try: () => {
|
|
2345
|
+
const instructionPrompt = typeof instructions === "string" ? Prompt.fromMessages([Prompt.makeMessage("system", { content: instructions })]) : Prompt.make(instructions);
|
|
2346
|
+
return Prompt.fromMessages([
|
|
2347
|
+
...history.content,
|
|
2348
|
+
...instructionPrompt.content,
|
|
2349
|
+
...Prompt.make(inputPrompt).content
|
|
2350
|
+
]);
|
|
2351
|
+
},
|
|
2352
|
+
catch: (cause) => AgentInputError.make({ message: `Unable to materialize Agent input: ${errorMessage(cause)}` })
|
|
2353
|
+
}));
|
|
2354
|
+
const decodeToolCallId = Effect.fn((id) => Schema.decodeEffect(ToolCallId)(id).pipe(Effect.mapError((cause) => ModelProtocolError.make({ message: `Invalid Tool Call ID: ${cause.message}` }))));
|
|
2355
|
+
const decodeProviderToolCallId = Effect.fn((id) => Schema.decodeEffect(ProviderToolCallId)(id).pipe(Effect.mapError(() => ModelProtocolError.make({ message: "Model supplied an invalid Tool Call ID; expected 1-128 ASCII letters, digits, dots, underscores, colons, or hyphens" }))));
|
|
2356
|
+
const decodeProviderResponsePartId = Effect.fn((id) => Schema.decodeEffect(ProviderResponsePartId)(id).pipe(Effect.mapError(() => ModelProtocolError.make({ message: "Model supplied an invalid response part ID; expected 1-128 ASCII letters, digits, dots, underscores, colons, or hyphens" }))));
|
|
2357
|
+
const validateProviderPartIdentifiers = Effect.fnUntraced(function* (part) {
|
|
2358
|
+
switch (part.type) {
|
|
2359
|
+
case "text-start":
|
|
2360
|
+
case "text-delta":
|
|
2361
|
+
case "text-end":
|
|
2362
|
+
case "reasoning-start":
|
|
2363
|
+
case "reasoning-delta":
|
|
2364
|
+
case "reasoning-end":
|
|
2365
|
+
case "source":
|
|
2366
|
+
yield* decodeProviderResponsePartId(part.id);
|
|
2367
|
+
return;
|
|
2368
|
+
case "response-metadata":
|
|
2369
|
+
if (part.id !== void 0) yield* decodeProviderResponsePartId(part.id);
|
|
2370
|
+
return;
|
|
2371
|
+
case "tool-params-start":
|
|
2372
|
+
case "tool-params-delta":
|
|
2373
|
+
case "tool-params-end":
|
|
2374
|
+
case "tool-call":
|
|
2375
|
+
case "tool-result":
|
|
2376
|
+
yield* decodeProviderToolCallId(part.id);
|
|
2377
|
+
return;
|
|
2378
|
+
case "tool-approval-request":
|
|
2379
|
+
yield* decodeProviderResponsePartId(part.approvalId);
|
|
2380
|
+
yield* decodeProviderToolCallId(part.toolCallId);
|
|
2381
|
+
return;
|
|
2382
|
+
case "error":
|
|
2383
|
+
case "file":
|
|
2384
|
+
case "finish":
|
|
2385
|
+
case "reasoning":
|
|
2386
|
+
case "text": return;
|
|
2387
|
+
}
|
|
2388
|
+
});
|
|
2389
|
+
const responseIdentity = (part, previous) => Schema.decodeEffect(ModelResponseIdentity)({
|
|
2390
|
+
...previous,
|
|
2391
|
+
...part.id === void 0 ? {} : { id: part.id },
|
|
2392
|
+
...part.modelId === void 0 ? {} : { model: part.modelId }
|
|
2393
|
+
}).pipe(Effect.mapError(() => ModelProtocolError.make({ message: "Invalid provider response identity" })), Effect.filterOrFail(() => !(previous?.id !== void 0 && part.id !== void 0 && previous.id !== part.id || previous?.model !== void 0 && part.modelId !== void 0 && previous.model !== part.modelId), () => ModelProtocolError.make({ message: "Provider response identity changed within one call" })));
|
|
2394
|
+
const decodeEventJson = Effect.fn("AgentRuntime.decodeEventJson")((value, label) => Schema.decodeUnknownEffect(Schema.Json)(value).pipe(Effect.mapError((cause) => ModelProtocolError.make({ message: `${label} is not JSON: ${cause.message}` }))));
|
|
2395
|
+
/**
|
|
2396
|
+
* Preserves provider-executed results as assistant content while application
|
|
2397
|
+
* handler results remain Tool messages for the next model request.
|
|
2398
|
+
*/
|
|
2399
|
+
const promptFromTurnParts = (trace) => {
|
|
2400
|
+
if (trace.replayedResponse !== void 0) return trace.replayedResponse;
|
|
2401
|
+
const responsePrompt = Prompt.fromResponseParts(trace.parts.filter((part) => !(part.type === "tool-result" && part.providerExecuted)));
|
|
2402
|
+
const providerResults = trace.parts.flatMap((part) => part.type === "tool-result" && part.providerExecuted && !part.preliminary ? [Prompt.makePart("tool-result", {
|
|
2403
|
+
id: part.id,
|
|
2404
|
+
name: part.name,
|
|
2405
|
+
isFailure: part.isFailure,
|
|
2406
|
+
result: part.encodedResult,
|
|
2407
|
+
providerExecuted: true
|
|
2408
|
+
})] : []);
|
|
2409
|
+
if (providerResults.length === 0) return responsePrompt;
|
|
2410
|
+
const messages = [];
|
|
2411
|
+
let attachedProviderResults = false;
|
|
2412
|
+
for (const message of responsePrompt.content) {
|
|
2413
|
+
if (message.role === "assistant") {
|
|
2414
|
+
messages.push(Prompt.makeMessage("assistant", {
|
|
2415
|
+
content: [...message.content, ...providerResults],
|
|
2416
|
+
options: message.options
|
|
2417
|
+
}));
|
|
2418
|
+
attachedProviderResults = true;
|
|
2419
|
+
continue;
|
|
2420
|
+
}
|
|
2421
|
+
if (message.role === "tool") continue;
|
|
2422
|
+
messages.push(message);
|
|
2423
|
+
}
|
|
2424
|
+
if (!attachedProviderResults) messages.push(Prompt.makeMessage("assistant", { content: providerResults }));
|
|
2425
|
+
return Prompt.fromMessages(messages);
|
|
2426
|
+
};
|
|
2427
|
+
const eventsForPart = Effect.fnUntraced(function* (context, turnId, turn, tools, trace, part, retainedBytes) {
|
|
2428
|
+
yield* consumeModelResponsePart(trace, retainedBytes, context.bufferLimits);
|
|
2429
|
+
if (part.type === "finish" && trace.usage === void 0) {
|
|
2430
|
+
trace.usage = part.usage;
|
|
2431
|
+
trace.finishMetadata = part.metadata;
|
|
2432
|
+
}
|
|
2433
|
+
if (trace.finished) return yield* ModelProtocolError.make({ message: "Model response emitted content after its finish part" });
|
|
2434
|
+
yield* validateProviderPartIdentifiers(part);
|
|
2435
|
+
if (part.type === "response-metadata") trace.response = yield* responseIdentity(part, trace.response);
|
|
2436
|
+
if (part.type !== "tool-call" && part.type !== "tool-result") trace.parts.push(part);
|
|
2437
|
+
switch (part.type) {
|
|
2438
|
+
case "text-start":
|
|
2439
|
+
yield* startPart(trace.textParts, part.id, "text");
|
|
2440
|
+
return [];
|
|
2441
|
+
case "text-delta":
|
|
2442
|
+
yield* continuePart(trace.textParts, part.id, "text delta");
|
|
2443
|
+
trace.text.push(part.delta);
|
|
2444
|
+
return [TextDelta.make({
|
|
2445
|
+
...yield* eventBase(context),
|
|
2446
|
+
turnId,
|
|
2447
|
+
text: part.delta
|
|
2448
|
+
})];
|
|
2449
|
+
case "text-end":
|
|
2450
|
+
yield* endPart(trace.textParts, part.id, "text");
|
|
2451
|
+
return [];
|
|
2452
|
+
case "reasoning-start":
|
|
2453
|
+
yield* startPart(trace.reasoningParts, part.id, "reasoning");
|
|
2454
|
+
return [];
|
|
2455
|
+
case "reasoning-delta":
|
|
2456
|
+
yield* continuePart(trace.reasoningParts, part.id, "reasoning delta");
|
|
2457
|
+
return [ReasoningDelta.make({
|
|
2458
|
+
...yield* eventBase(context),
|
|
2459
|
+
turnId,
|
|
2460
|
+
text: part.delta
|
|
2461
|
+
})];
|
|
2462
|
+
case "reasoning-end":
|
|
2463
|
+
yield* endPart(trace.reasoningParts, part.id, "reasoning");
|
|
2464
|
+
return [];
|
|
2465
|
+
case "tool-params-start":
|
|
2466
|
+
if (trace.toolParameterParts.has(part.id)) return yield* ModelProtocolError.make({ message: `Model response repeated Tool parameter start for ${part.id}` });
|
|
2467
|
+
trace.toolParameterParts.set(part.id, {
|
|
2468
|
+
name: part.name,
|
|
2469
|
+
providerExecuted: part.providerExecuted,
|
|
2470
|
+
state: "open"
|
|
2471
|
+
});
|
|
2472
|
+
return [];
|
|
2473
|
+
case "tool-params-delta":
|
|
2474
|
+
if (trace.toolParameterParts.get(part.id)?.state !== "open") return yield* ModelProtocolError.make({ message: `Model response emitted Tool parameter delta for inactive part ${part.id}` });
|
|
2475
|
+
return [];
|
|
2476
|
+
case "tool-params-end": {
|
|
2477
|
+
const parameterPart = trace.toolParameterParts.get(part.id);
|
|
2478
|
+
if (parameterPart?.state !== "open") return yield* ModelProtocolError.make({ message: `Model response emitted Tool parameter end for inactive part ${part.id}` });
|
|
2479
|
+
parameterPart.state = "closed";
|
|
2480
|
+
return [];
|
|
2481
|
+
}
|
|
2482
|
+
case "tool-call": {
|
|
2483
|
+
if (!hasTool(tools, part.name)) return yield* ModelProtocolError.make({ message: `Model requested unknown Tool ${part.name}` });
|
|
2484
|
+
if (trace.toolCalls.has(part.id)) return yield* ModelProtocolError.make({ message: `Model response repeated Tool Call ID ${part.id}` });
|
|
2485
|
+
const parameterPart = trace.toolParameterParts.get(part.id);
|
|
2486
|
+
if (parameterPart?.state === "open") return yield* ModelProtocolError.make({ message: `Model response declared Tool Call ${part.id} before its parameters completed` });
|
|
2487
|
+
if (parameterPart !== void 0 && (parameterPart.name !== part.name || parameterPart.providerExecuted !== part.providerExecuted)) return yield* ModelProtocolError.make({ message: `Completed Tool parameters did not match Tool Call ${part.id}` });
|
|
2488
|
+
const toolCallId = yield* decodeToolCallId(part.id);
|
|
2489
|
+
const tool = tools[part.name];
|
|
2490
|
+
const decodedParameters = yield* decodeToolCallParameters(tool, part.name, part.params);
|
|
2491
|
+
const encodedParameters = yield* encodeToolCallParameters(tool, part.name, decodedParameters);
|
|
2492
|
+
const parameters = yield* decodeEventJson(encodedParameters, "Tool parameters");
|
|
2493
|
+
const canonicalCall = Response.makePart("tool-call", {
|
|
2494
|
+
id: part.id,
|
|
2495
|
+
name: part.name,
|
|
2496
|
+
params: parameters,
|
|
2497
|
+
providerExecuted: part.providerExecuted,
|
|
2498
|
+
metadata: part.metadata
|
|
2499
|
+
});
|
|
2500
|
+
trace.parts.push(canonicalCall);
|
|
2501
|
+
trace.toolCalls.set(part.id, {
|
|
2502
|
+
name: part.name,
|
|
2503
|
+
providerExecuted: part.providerExecuted
|
|
2504
|
+
});
|
|
2505
|
+
if (!part.providerExecuted) {
|
|
2506
|
+
trace.applicationToolCalls.push(canonicalCall);
|
|
2507
|
+
trace.applicationCallDescriptors.push({
|
|
2508
|
+
toolCallId,
|
|
2509
|
+
toolName: part.name,
|
|
2510
|
+
parameters,
|
|
2511
|
+
executionClass: getToolExecutionClass(tool),
|
|
2512
|
+
executionKind: getToolExecutionKind(tool.annotations)
|
|
2513
|
+
});
|
|
2514
|
+
}
|
|
2515
|
+
return [ToolCallDeclared.make({
|
|
2516
|
+
...yield* eventBase(context),
|
|
2517
|
+
turnId,
|
|
2518
|
+
toolCallId,
|
|
2519
|
+
toolName: part.name,
|
|
2520
|
+
parameters,
|
|
2521
|
+
providerExecuted: part.providerExecuted
|
|
2522
|
+
})];
|
|
2523
|
+
}
|
|
2524
|
+
case "tool-result": {
|
|
2525
|
+
const declaredCall = trace.toolCalls.get(part.id);
|
|
2526
|
+
if (declaredCall === void 0) return yield* ModelProtocolError.make({ message: `Model response returned an unrequested Tool result ${part.id}` });
|
|
2527
|
+
if (declaredCall.name !== part.name) return yield* ModelProtocolError.make({ message: `Tool result name did not match Tool Call ${part.id}: expected ${declaredCall.name}, received ${part.name}` });
|
|
2528
|
+
if (declaredCall.providerExecuted !== part.providerExecuted) return yield* ModelProtocolError.make({ message: `Tool result execution boundary did not match Tool Call ${part.id}` });
|
|
2529
|
+
if (!part.providerExecuted) return yield* ModelProtocolError.make({ message: `Model response included an application Tool result before engine execution for ${part.id}` });
|
|
2530
|
+
const toolCallId = yield* decodeToolCallId(part.id);
|
|
2531
|
+
const normalized = yield* snapshotProviderToolResultPart(trace, part);
|
|
2532
|
+
const result = normalized.result;
|
|
2533
|
+
if (part.preliminary === true) {
|
|
2534
|
+
yield* stageProviderResultPayload(trace, {
|
|
2535
|
+
_tag: "ToolProgress",
|
|
2536
|
+
toolCallId,
|
|
2537
|
+
toolName: part.name,
|
|
2538
|
+
result,
|
|
2539
|
+
providerExecuted: true
|
|
2540
|
+
});
|
|
2541
|
+
trace.parts.push(Response.toolResultPart({
|
|
2542
|
+
id: part.id,
|
|
2543
|
+
name: part.name,
|
|
2544
|
+
isFailure: part.isFailure,
|
|
2545
|
+
result,
|
|
2546
|
+
encodedResult: result,
|
|
2547
|
+
providerExecuted: true,
|
|
2548
|
+
preliminary: true,
|
|
2549
|
+
metadata: normalized.metadata
|
|
2550
|
+
}));
|
|
2551
|
+
return [];
|
|
2552
|
+
}
|
|
2553
|
+
if (trace.finalToolResultIds.has(part.id)) return yield* ModelProtocolError.make({ message: `Tool Call ${part.id} produced more than one terminal result` });
|
|
2554
|
+
if (part.isFailure) yield* stageProviderResultPayload(trace, {
|
|
2555
|
+
_tag: "ToolCallFailed",
|
|
2556
|
+
toolCallId,
|
|
2557
|
+
toolName: part.name,
|
|
2558
|
+
errorTag: errorTag(result),
|
|
2559
|
+
message: errorMessage(result),
|
|
2560
|
+
providerExecuted: true
|
|
2561
|
+
});
|
|
2562
|
+
else yield* stageProviderResultPayload(trace, {
|
|
2563
|
+
_tag: "ToolCallSucceeded",
|
|
2564
|
+
toolCallId,
|
|
2565
|
+
toolName: part.name,
|
|
2566
|
+
result,
|
|
2567
|
+
providerExecuted: true
|
|
2568
|
+
});
|
|
2569
|
+
trace.finalToolResultIds.add(part.id);
|
|
2570
|
+
trace.parts.push(Response.toolResultPart({
|
|
2571
|
+
id: part.id,
|
|
2572
|
+
name: part.name,
|
|
2573
|
+
isFailure: part.isFailure,
|
|
2574
|
+
result,
|
|
2575
|
+
encodedResult: result,
|
|
2576
|
+
providerExecuted: true,
|
|
2577
|
+
preliminary: false,
|
|
2578
|
+
metadata: normalized.metadata
|
|
2579
|
+
}));
|
|
2580
|
+
return [];
|
|
2581
|
+
}
|
|
2582
|
+
case "finish": {
|
|
2583
|
+
const openPart = firstOpenPart(trace);
|
|
2584
|
+
if (openPart !== void 0) return yield* ModelProtocolError.make({ message: `Model response finished before completing ${openPart}` });
|
|
2585
|
+
trace.finished = true;
|
|
2586
|
+
trace.finishReason = part.reason;
|
|
2587
|
+
trace.usage = part.usage;
|
|
2588
|
+
if (Array.from(trace.toolCalls.values()).some(({ providerExecuted }) => providerExecuted)) {
|
|
2589
|
+
const turnCompletion = { finishReason: part.reason };
|
|
2590
|
+
const snapshot = yield* snapshotStagedProviderEvent(trace, {
|
|
2591
|
+
_tag: "TurnCompleted",
|
|
2592
|
+
...turnCompletion
|
|
2593
|
+
});
|
|
2594
|
+
trace.turnCompletion = turnCompletion;
|
|
2595
|
+
trace.providerStagedPayloadBytes += snapshot.bytes;
|
|
2596
|
+
return [];
|
|
2597
|
+
}
|
|
2598
|
+
return [TurnCompleted.make({
|
|
2599
|
+
...yield* eventBase(context),
|
|
2600
|
+
turnId,
|
|
2601
|
+
turn,
|
|
2602
|
+
finishReason: part.reason
|
|
2603
|
+
})];
|
|
2604
|
+
}
|
|
2605
|
+
case "error": return yield* ModelProtocolError.make({ message: `Model response failed: ${errorMessage(part.error)}` });
|
|
2606
|
+
case "file":
|
|
2607
|
+
case "reasoning":
|
|
2608
|
+
case "response-metadata":
|
|
2609
|
+
case "source":
|
|
2610
|
+
case "text":
|
|
2611
|
+
case "tool-approval-request": return [];
|
|
2612
|
+
}
|
|
2613
|
+
});
|
|
2614
|
+
const decodeFinalOutput = Effect.fn("AgentRuntime.decodeFinalOutput")(function* (agent, text) {
|
|
2615
|
+
const eventJson = isTextOutput(agent.definition.output) ? text : yield* Schema.decodeEffect(Schema.fromJsonString(Schema.Json))(text).pipe(Effect.mapError((cause) => AgentOutputError.make({ message: `Agent output is not valid JSON: ${cause.message}` })));
|
|
2616
|
+
const candidateOutput = eventJson;
|
|
2617
|
+
return {
|
|
2618
|
+
encoded: eventJson,
|
|
2619
|
+
decoded: yield* Schema.decodeUnknownEffect(agent.definition.output)(candidateOutput).pipe(Effect.mapError((cause) => AgentOutputError.make({ message: cause.message })))
|
|
2620
|
+
};
|
|
2621
|
+
});
|
|
2622
|
+
const encodeOutputCandidate = Effect.fn("AgentRuntime.encodeOutputCandidate")(function* (agent, candidate) {
|
|
2623
|
+
const encoded = yield* Schema.encodeUnknownEffect(agent.definition.output)(candidate).pipe(Effect.mapError((cause) => AgentOutputError.make({ message: `Completion Tool output failed Schema encoding: ${cause.message}` })));
|
|
2624
|
+
const decoded = yield* Schema.decodeUnknownEffect(agent.definition.output)(encoded).pipe(Effect.mapError((cause) => AgentOutputError.make({ message: `Completion Tool output failed canonical decoding: ${cause.message}` })));
|
|
2625
|
+
return {
|
|
2626
|
+
encoded: yield* Schema.decodeUnknownEffect(Schema.Json)(encoded).pipe(Effect.mapError((cause) => AgentOutputError.make({ message: `Completion Tool output did not encode as durable JSON: ${cause.message}` }))),
|
|
2627
|
+
decoded
|
|
2628
|
+
};
|
|
2629
|
+
});
|
|
2630
|
+
const projectToolResult = Effect.fn("AgentRuntime.projectToolResult")(function* (agent, declaration, parameters, result) {
|
|
2631
|
+
const tool = agent.definition.toolkit.tools[declaration.tool];
|
|
2632
|
+
if (tool === void 0) return yield* ModelProtocolError.make({ message: `Agent completion declaration references unknown Tool ${declaration.tool}` });
|
|
2633
|
+
const decodedParameters = yield* Schema.decodeUnknownEffect(tool.parametersSchema)(parameters).pipe(Effect.mapError((cause) => AgentOutputError.make({ message: `Completion Tool parameters failed canonical decoding: ${cause.message}` })));
|
|
2634
|
+
const decodedResult = yield* Schema.decodeUnknownEffect(tool.successSchema)(result).pipe(Effect.mapError((cause) => AgentOutputError.make({ message: `Completion Tool result failed canonical decoding: ${cause.message}` })));
|
|
2635
|
+
return yield* Effect.try({
|
|
2636
|
+
try: () => declaration.project({
|
|
2637
|
+
parameters: decodedParameters,
|
|
2638
|
+
result: decodedResult
|
|
2639
|
+
}),
|
|
2640
|
+
catch: (cause) => AgentOutputError.make({ message: `Completion Tool projector failed: ${cause instanceof Error ? cause.message : String(cause)}` })
|
|
2641
|
+
});
|
|
2642
|
+
});
|
|
2643
|
+
const projectCompletionOutput = Effect.fn("AgentRuntime.projectCompletionOutput")(function* (agent, declaration, parameters, result) {
|
|
2644
|
+
return yield* encodeOutputCandidate(agent, yield* projectToolResult(agent, declaration, parameters, result));
|
|
2645
|
+
});
|
|
2646
|
+
/** Reconstruct optional action completion identically for live execution and canonical recovery. */
|
|
2647
|
+
const projectCompletionFromToolOutput = Effect.fn("AgentRuntime.projectCompletionFromToolOutput")(function* (agent, declaration, parameters, result) {
|
|
2648
|
+
const projected = yield* projectToolResult(agent, declaration, parameters, result);
|
|
2649
|
+
if (!Option.isOption(projected)) return yield* AgentOutputError.make({ message: "Action completion projector did not return an Option" });
|
|
2650
|
+
if (Option.isNone(projected)) return Option.none();
|
|
2651
|
+
return Option.some(yield* encodeOutputCandidate(agent, projected.value));
|
|
2652
|
+
});
|
|
2653
|
+
const encodeRunDispositionCandidate = Effect.fn("AgentRuntime.encodeRunDisposition")(function* (declaration, output) {
|
|
2654
|
+
const selected = yield* Effect.try({
|
|
2655
|
+
try: () => declaration.fromOutput(output),
|
|
2656
|
+
catch: (cause) => AgentRunDispositionError.make({
|
|
2657
|
+
cause,
|
|
2658
|
+
message: "Run disposition selector failed"
|
|
2659
|
+
})
|
|
2660
|
+
});
|
|
2661
|
+
if (selected === void 0) return void 0;
|
|
2662
|
+
const encoded = yield* Schema.encodeUnknownEffect(declaration.schema)(selected).pipe(Effect.mapError((cause) => AgentRunDispositionError.make({
|
|
2663
|
+
cause,
|
|
2664
|
+
message: "Run disposition failed Schema encoding"
|
|
2665
|
+
})));
|
|
2666
|
+
return yield* Schema.decodeUnknownEffect(Schema.Json)(encoded).pipe(Effect.mapError((cause) => AgentRunDispositionError.make({
|
|
2667
|
+
cause,
|
|
2668
|
+
message: "Run disposition did not encode as durable JSON"
|
|
2669
|
+
})));
|
|
2670
|
+
});
|
|
2671
|
+
function encodeRunDisposition(agent, output) {
|
|
2672
|
+
const declaration = agent.definition.runDisposition;
|
|
2673
|
+
return declaration === void 0 ? Effect.void : encodeRunDispositionCandidate(declaration, output);
|
|
2674
|
+
}
|
|
2675
|
+
const decodeRunDispositionCandidate = Effect.fn("AgentRuntime.decodeRunDisposition")(function* (declaration, encoded) {
|
|
2676
|
+
return yield* Schema.decodeEffect(declaration.schema)(encoded).pipe(Effect.mapError((cause) => AgentRunDispositionError.make({
|
|
2677
|
+
cause,
|
|
2678
|
+
message: cause.message
|
|
2679
|
+
})));
|
|
2680
|
+
});
|
|
2681
|
+
function decodeRunDisposition(agent, encoded) {
|
|
2682
|
+
const declaration = agent.definition.runDisposition;
|
|
2683
|
+
return declaration === void 0 ? Effect.fail(ModelProtocolError.make({ message: "RunCompleted declared a run disposition without a definition-owned Schema" })) : decodeRunDispositionCandidate(declaration, encoded);
|
|
2684
|
+
}
|
|
2685
|
+
const nextTurn = (prompt, turn, toolCalls) => Stream.succeed({
|
|
2686
|
+
_tag: "NextTurn",
|
|
2687
|
+
prompt,
|
|
2688
|
+
turn,
|
|
2689
|
+
toolCalls
|
|
2690
|
+
});
|
|
2691
|
+
const makeTurn = (agent, context, prompt, turn, priorToolCalls, options) => Stream.unwrap(Effect.gen(function* () {
|
|
2692
|
+
const policy = agent.definition.policy;
|
|
2693
|
+
const bounds = effectiveRunBounds(policy, options);
|
|
2694
|
+
if (options.beforeTurn !== void 0) yield* options.beforeTurn();
|
|
2695
|
+
if ((yield* Clock.currentTimeMillis) >= context.durationDeadlineMillis) return failRunEventStream(durationLimitError(agent.definition.policy));
|
|
2696
|
+
if (policy.onExhaustion === "fail" && turn > bounds.maxTurns) return failRunEventStream(AgentPolicyError.make({
|
|
2697
|
+
limit: "turns",
|
|
2698
|
+
message: `Agent exceeded its ${bounds.maxTurns} Turn limit`
|
|
2699
|
+
}));
|
|
2700
|
+
if (context.finalizationUsed) return failRunEventStream(AgentPolicyError.make({
|
|
2701
|
+
limit: turn > bounds.maxTurns ? "turns" : priorToolCalls + context.programmaticToolCalls > bounds.maxToolCalls ? "tool-calls" : "tokens",
|
|
2702
|
+
message: "Agent already used its one grace finalization"
|
|
2703
|
+
}));
|
|
2704
|
+
const turnId = yield* (yield* IdGenerator$1).nextTurnId;
|
|
2705
|
+
const outputContract = outputSchemaContract(agent.definition);
|
|
2706
|
+
const outputContractMessage = outputContract._tag === "rendered" ? outputContract.message : void 0;
|
|
2707
|
+
const contextRequest = {
|
|
2708
|
+
threadId: context.threadId,
|
|
2709
|
+
runId: context.runId,
|
|
2710
|
+
turnId,
|
|
2711
|
+
turn,
|
|
2712
|
+
source: prompt,
|
|
2713
|
+
...outputContractMessage === void 0 ? {} : { outputContract: outputContractMessage }
|
|
2714
|
+
};
|
|
2715
|
+
const modelContext = options.context === void 0 ? { prompt } : yield* options.context.prepare(contextRequest);
|
|
2716
|
+
const callContext = modelContext.modelCall === void 0 ? void 0 : yield* Schema.decodeEffect(ModelCallContext)(modelContext.modelCall.context).pipe(Effect.mapError((cause) => CompactionError.make({
|
|
2717
|
+
message: "Invalid resolved model context bounds",
|
|
2718
|
+
cause
|
|
2719
|
+
})));
|
|
2720
|
+
const contextTokenLimit = callContext === void 0 ? policy.contextTokenLimit : Math.max(0, Math.min(policy.contextTokenLimit ?? Infinity, callContext.maxInputTokens ?? Infinity, callContext.contextCapacity - callContext.outputReserveTokens) - callContext.uncountedOverheadTokens);
|
|
2721
|
+
context.windowContextTokenLimit = contextTokenLimit;
|
|
2722
|
+
const toolSchemaTransformer = modelContext.modelCall?.toolSchemaTransformer;
|
|
2723
|
+
const messageTokenEstimator = modelContext.modelCall?.estimateMessageTokens;
|
|
2724
|
+
const visibility = yield* RunToolVisibility;
|
|
2725
|
+
const catalog = yield* eligibleCatalog(agent.definition, {
|
|
2726
|
+
threadId: context.threadId,
|
|
2727
|
+
runId: context.runId,
|
|
2728
|
+
turn,
|
|
2729
|
+
input: context.input
|
|
2730
|
+
}, options.subagentGrant, options.delegationDepth ?? options.parentLink?.depth ?? 0);
|
|
2731
|
+
if (modelContext.toolSelection !== void 0) context.toolSelection = yield* validateSelection(modelContext.toolSelection, agent.definition, catalog);
|
|
2732
|
+
let snapshot = agent.definition.toolExposure === void 0 && context.toolSelection === void 0 && visibility === void 0 ? void 0 : yield* exposureSnapshot(agent.definition, context.toolSelection, catalog, toolSchemaTransformer);
|
|
2733
|
+
const selectionSnapshot = snapshot;
|
|
2734
|
+
context.toolSchemaTransformer = toolSchemaTransformer;
|
|
2735
|
+
context.toolCatalog = catalog;
|
|
2736
|
+
context.toolExposure = snapshot;
|
|
2737
|
+
const modelToolkit = Toolkit.make(...snapshot === void 0 ? catalog.filter((entry) => entry.kind === "native").map((entry) => entry.tool) : catalog.filter((entry) => entry.kind === "native" && snapshot?.exposedToolNames.includes(entry.nativeToolName)).map((entry) => entry.tool));
|
|
2738
|
+
if (options.durability !== void 0 && snapshot !== void 0) {
|
|
2739
|
+
if (options.durability.noteToolExposure === void 0 && context.toolSelection !== void 0) return yield* ModelProtocolError.make({ message: "Durable Tool exposure requires a request snapshot staging hook" });
|
|
2740
|
+
}
|
|
2741
|
+
const estimateCallTokens = (messages) => estimateContextTokens(messages, messageTokenEstimator);
|
|
2742
|
+
const modelServices = modelContext.modelCall === void 0 ? void 0 : yield* Layer.build(Layer.fresh(modelContext.modelCall.model));
|
|
2743
|
+
const withCallModel = (operation) => modelServices === void 0 ? operation : Effect.provide(operation, modelServices);
|
|
2744
|
+
const priorCompactionPrefix = context.preparedCompactionSource?.prefix;
|
|
2745
|
+
if (priorCompactionPrefix !== void 0 && priorCompactionPrefix.length > 0) {
|
|
2746
|
+
const preparedPrefix = yield* snapshotCompactionMessages(modelContext.prompt.content.slice(0, priorCompactionPrefix.length));
|
|
2747
|
+
if (!Equal.equals(preparedPrefix, priorCompactionPrefix)) return yield* CompactionError.make({ message: "Prepared context changed a prefix already covered by compaction" });
|
|
2748
|
+
}
|
|
2749
|
+
const trace = {
|
|
2750
|
+
responsePartCount: 0,
|
|
2751
|
+
responsePartBytes: 0,
|
|
2752
|
+
parts: [],
|
|
2753
|
+
text: [],
|
|
2754
|
+
textParts: /* @__PURE__ */ new Map(),
|
|
2755
|
+
reasoningParts: /* @__PURE__ */ new Map(),
|
|
2756
|
+
toolParameterParts: /* @__PURE__ */ new Map(),
|
|
2757
|
+
toolCalls: /* @__PURE__ */ new Map(),
|
|
2758
|
+
finalToolResultIds: /* @__PURE__ */ new Set(),
|
|
2759
|
+
providerResultPayloads: [],
|
|
2760
|
+
providerStagedPayloadBytes: 0,
|
|
2761
|
+
turnCompletion: void 0,
|
|
2762
|
+
applicationToolCalls: [],
|
|
2763
|
+
applicationCallDescriptors: [],
|
|
2764
|
+
applicationToolResults: [],
|
|
2765
|
+
finished: false,
|
|
2766
|
+
finishReason: void 0,
|
|
2767
|
+
usage: void 0
|
|
2768
|
+
};
|
|
2769
|
+
const started = Stream.fromEffect(Effect.gen(function* () {
|
|
2770
|
+
yield* Metric.update(modelCounter, 1);
|
|
2771
|
+
yield* Effect.logDebug("agent model call started").pipe(Effect.annotateLogs({
|
|
2772
|
+
agentId: context.agentId,
|
|
2773
|
+
runId: context.runId,
|
|
2774
|
+
turnId
|
|
2775
|
+
}));
|
|
2776
|
+
return [TurnStarted.make({
|
|
2777
|
+
...yield* eventBase(context),
|
|
2778
|
+
turnId,
|
|
2779
|
+
turn
|
|
2780
|
+
}), ModelStarted.make({
|
|
2781
|
+
...yield* eventBase(context),
|
|
2782
|
+
turnId,
|
|
2783
|
+
turn
|
|
2784
|
+
})];
|
|
2785
|
+
}).pipe(Effect.withLogSpan("AgentRuntime.model"))).pipe(Stream.flatMap(Stream.fromIterable));
|
|
2786
|
+
let finalAnswerOnly = policy.onExhaustion !== "fail" && (turn > bounds.maxTurns || priorToolCalls + context.programmaticToolCalls > bounds.maxToolCalls || context.tokenExhausted);
|
|
2787
|
+
const modelToolChoice = () => {
|
|
2788
|
+
return finalAnswerOnly || agent.definition.completion?.required === true && policy.onExhaustion === "fail" && turn === bounds.maxTurns ? agent.definition.completion === void 0 || agent.definition.completion.required !== true && !catalog.some((entry) => entry.kind === "native" && entry.nativeToolName === agent.definition.completion?.tool) ? "none" : agent.definition.completion.required === true ? { tool: agent.definition.completion.tool } : {
|
|
2789
|
+
mode: "auto",
|
|
2790
|
+
oneOf: [agent.definition.completion.tool]
|
|
2791
|
+
} : agent.definition.completion?.required === true ? "required" : void 0;
|
|
2792
|
+
};
|
|
2793
|
+
if (finalAnswerOnly && context.exhaustedDimension === void 0) context.exhaustedDimension = turn > bounds.maxTurns ? "turns" : priorToolCalls + context.programmaticToolCalls > bounds.maxToolCalls ? "tool-calls" : "tokens";
|
|
2794
|
+
if (outputContract._tag === "unrenderable" && turn === 1) yield* Effect.logWarning("Agent output schema cannot render to JSON Schema; the model-visible final output contract is omitted").pipe(Effect.annotateLogs({
|
|
2795
|
+
agentId: context.agentId,
|
|
2796
|
+
runId: context.runId,
|
|
2797
|
+
reason: outputContract.reason
|
|
2798
|
+
}));
|
|
2799
|
+
const admissionRequired = contextTokenLimit !== void 0 || !context.finalizing && !finalAnswerOnly && policy.tokenBudget !== void 0;
|
|
2800
|
+
const outputContractTokens = !admissionRequired || outputContractMessage === void 0 ? 0 : yield* estimateCallTokens([Prompt.makeMessage("system", { content: outputContractMessage })]);
|
|
2801
|
+
const canonicalDecoration = !admissionRequired ? Prompt.empty : yield* outgoingModelPrompt(policy, context, Prompt.empty, turn, priorToolCalls);
|
|
2802
|
+
const estimateToolSchemaTokens = Effect.suspend(() => {
|
|
2803
|
+
const choice = modelToolChoice();
|
|
2804
|
+
const tools = typeof choice === "object" && "oneOf" in choice ? catalog.filter((entry) => entry.kind === "native" && choice.oneOf.includes(entry.nativeToolName)).map((entry) => entry.tool) : Object.values(modelToolkit.tools);
|
|
2805
|
+
return callContext === void 0 || tools.length === 0 ? Effect.succeed(0) : Effect.try({
|
|
2806
|
+
try: () => JSON.stringify(tools.map((tool) => Tool.isProviderDefined(tool) ? {
|
|
2807
|
+
type: tool.providerName,
|
|
2808
|
+
args: tool.args
|
|
2809
|
+
} : {
|
|
2810
|
+
name: tool.name,
|
|
2811
|
+
description: Tool.getDescription(tool),
|
|
2812
|
+
parameters: Tool.getJsonSchema(tool, { transformer: toolSchemaTransformer })
|
|
2813
|
+
})),
|
|
2814
|
+
catch: (cause) => CompactionError.make({
|
|
2815
|
+
message: "Could not estimate native Tool schemas",
|
|
2816
|
+
cause
|
|
2817
|
+
})
|
|
2818
|
+
}).pipe(Effect.flatMap((content) => estimateCallTokens([Prompt.makeMessage("system", { content })])));
|
|
2819
|
+
});
|
|
2820
|
+
let toolSchemaTokens = yield* estimateToolSchemaTokens;
|
|
2821
|
+
const canonicalDecorationPromptTokens = !admissionRequired ? 0 : outputContractTokens + (yield* estimateCallTokens(canonicalDecoration.content));
|
|
2822
|
+
const canonicalDecorationTokens = () => toolSchemaTokens + canonicalDecorationPromptTokens;
|
|
2823
|
+
const estimateSourceContext = (view) => options.context === void 0 && options.transientContext === void 0 ? nextContextEstimate(context, view) : estimateCallTokens(view);
|
|
2824
|
+
let prepared = buildCompactedView(modelContext.prompt.content, context.compaction);
|
|
2825
|
+
let sourceTokens;
|
|
2826
|
+
const preparedSourceTokens = Effect.suspend(() => sourceTokens === void 0 ? estimateSourceContext(prepared).pipe(Effect.tap((tokens) => Effect.sync(() => {
|
|
2827
|
+
sourceTokens = tokens;
|
|
2828
|
+
}))) : Effect.succeed(sourceTokens));
|
|
2829
|
+
const refreshPrepared = () => {
|
|
2830
|
+
prepared = buildCompactedView(modelContext.prompt.content, context.compaction);
|
|
2831
|
+
sourceTokens = void 0;
|
|
2832
|
+
};
|
|
2833
|
+
let preEvents = [];
|
|
2834
|
+
if (modelContext.rollover !== void 0) {
|
|
2835
|
+
if (context.pendingContextToolCallId !== void 0) return yield* CompactionError.make({ message: "Host and Tool rollover requests cannot share a Turn" });
|
|
2836
|
+
const requested = yield* Schema.decodeEffect(ContextRolloverSelection)(modelContext.rollover).pipe(Effect.mapError((cause) => CompactionError.make({
|
|
2837
|
+
message: "Invalid host context rollover",
|
|
2838
|
+
cause
|
|
2839
|
+
})));
|
|
2840
|
+
const outcome = yield* compactContext(agent, context, modelContext.prompt, turn, options, callContext === void 0 ? void 0 : contextTokenLimit, messageTokenEstimator, contextTokenLimit === void 0 ? void 0 : Math.max(0, contextTokenLimit - canonicalDecorationTokens()), "requested", false, requested).pipe(withCallModel);
|
|
2841
|
+
if (outcome.events.some((event) => event._tag === "CompactionPerformed")) context.compaction.lastCompactionTurn = turn;
|
|
2842
|
+
preEvents = outcome.events;
|
|
2843
|
+
refreshPrepared();
|
|
2844
|
+
}
|
|
2845
|
+
const lastToolIndex = context.pendingContextToolCallId === void 0 ? -1 : modelContext.prompt.content.findLastIndex((message) => message.role === "tool" && message.content.some((part) => part.type === "tool-result" && part.id === context.pendingContextToolCallId));
|
|
2846
|
+
if (context.pendingContextToolCallId !== void 0 && lastToolIndex < 0) return yield* CompactionError.make({ message: "Prepared context omitted the pending context Tool result" });
|
|
2847
|
+
const lastTool = modelContext.prompt.content[lastToolIndex];
|
|
2848
|
+
if (context.pendingContextToolCallId !== void 0 && (lastTool?.role !== "tool" || lastTool.content.length !== 1)) return yield* CompactionError.make({ message: "Prepared context changed the pending context Tool batch" });
|
|
2849
|
+
if (lastTool?.role === "tool" && lastTool.content.length === 1) {
|
|
2850
|
+
const result = lastTool.content[0];
|
|
2851
|
+
if (result?.type === "tool-result" && !result.isFailure && result.providerExecuted !== true && result.id === context.pendingContextToolCallId && hasTool(agent.definition.toolkit.tools, result.name) && Context.get(agent.definition.toolkit.tools[result.name].annotations, ContextRolloverTool)) {
|
|
2852
|
+
const request = yield* Schema.decodeUnknownEffect(ContextRolloverRequest, { onExcessProperty: "error" })(result.result).pipe(Effect.mapError((cause) => CompactionError.make({
|
|
2853
|
+
message: "Invalid context rollover Tool result",
|
|
2854
|
+
cause
|
|
2855
|
+
})));
|
|
2856
|
+
const outcome = yield* compactContext(agent, context, modelContext.prompt, turn, options, callContext === void 0 ? void 0 : contextTokenLimit, messageTokenEstimator, contextTokenLimit === void 0 ? void 0 : Math.max(0, contextTokenLimit - canonicalDecorationTokens()), "requested", false, {
|
|
2857
|
+
...request,
|
|
2858
|
+
through: lastToolIndex + 1
|
|
2859
|
+
}).pipe(withCallModel);
|
|
2860
|
+
context.compaction.lastCompactionTurn = turn;
|
|
2861
|
+
preEvents = outcome.events;
|
|
2862
|
+
refreshPrepared();
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
context.pendingContextToolCallId = void 0;
|
|
2866
|
+
if (!context.finalizing && admissionRequired) {
|
|
2867
|
+
const consumedTokens = context.inputTokens + context.outputTokens;
|
|
2868
|
+
const tokenCallTarget = policy.tokenBudget === void 0 || finalAnswerOnly ? void 0 : Math.max(0, policy.tokenBudget - consumedTokens - policy.completionReserveTokens);
|
|
2869
|
+
const contextCallTarget = contextTokenLimit;
|
|
2870
|
+
const fullTarget = tokenCallTarget === void 0 ? contextCallTarget : contextCallTarget === void 0 ? tokenCallTarget : Math.min(tokenCallTarget, contextCallTarget);
|
|
2871
|
+
const sourceTarget = fullTarget === void 0 ? void 0 : Math.max(0, fullTarget - canonicalDecorationTokens());
|
|
2872
|
+
const estimate = (yield* preparedSourceTokens) + canonicalDecorationTokens();
|
|
2873
|
+
const contextPressure = contextCallTarget !== void 0 && estimate > contextCallTarget;
|
|
2874
|
+
const tokenPressure = tokenCallTarget !== void 0 && estimate > tokenCallTarget;
|
|
2875
|
+
if ((contextPressure || tokenPressure) && sourceTarget !== void 0 && sourceTarget > 0 && context.compaction.lastCompactionTurn !== turn) {
|
|
2876
|
+
context.compaction.lastCompactionTurn = turn;
|
|
2877
|
+
const outcome = yield* compactContext(agent, context, modelContext.prompt, turn, options, callContext === void 0 ? void 0 : contextTokenLimit, messageTokenEstimator, sourceTarget, "pressure", !tokenPressure).pipe(withCallModel);
|
|
2878
|
+
preEvents = [...preEvents, ...outcome.events];
|
|
2879
|
+
if (outcome.events.some((event) => event._tag === "CompactionPerformed")) refreshPrepared();
|
|
2880
|
+
}
|
|
2881
|
+
if (context.tokenExhausted) {
|
|
2882
|
+
finalAnswerOnly = true;
|
|
2883
|
+
toolSchemaTokens = yield* estimateToolSchemaTokens;
|
|
2884
|
+
}
|
|
2885
|
+
const preparedTokenCallTarget = policy.tokenBudget === void 0 || finalAnswerOnly ? void 0 : Math.max(0, policy.tokenBudget - (context.inputTokens + context.outputTokens) - policy.completionReserveTokens);
|
|
2886
|
+
let preparedEstimate = (yield* preparedSourceTokens) + canonicalDecorationTokens();
|
|
2887
|
+
if ((callContext !== void 0 || contextCallTarget === void 0 || preparedEstimate <= contextCallTarget) && preparedTokenCallTarget !== void 0 && preparedEstimate > preparedTokenCallTarget) {
|
|
2888
|
+
const error = AgentPolicyError.make({
|
|
2889
|
+
limit: "tokens",
|
|
2890
|
+
message: `The next research call would consume this Run's ${policy.completionReserveTokens} token completion reserve`
|
|
2891
|
+
});
|
|
2892
|
+
if (policy.onExhaustion === "fail") return yield* error;
|
|
2893
|
+
context.tokenExhausted = true;
|
|
2894
|
+
context.exhaustedDimension ??= "tokens";
|
|
2895
|
+
finalAnswerOnly = true;
|
|
2896
|
+
toolSchemaTokens = yield* estimateToolSchemaTokens;
|
|
2897
|
+
preparedEstimate = (yield* preparedSourceTokens) + canonicalDecorationTokens();
|
|
2898
|
+
}
|
|
2899
|
+
if (contextCallTarget !== void 0 && preparedEstimate > contextCallTarget) return yield* ContextBudgetError.make({
|
|
2900
|
+
message: `Compaction could not fit the next model prompt inside the ${contextCallTarget} token context target`,
|
|
2901
|
+
estimatedTokens: preparedEstimate,
|
|
2902
|
+
targetTokens: contextCallTarget,
|
|
2903
|
+
completionReserveTokens: policy.completionReserveTokens
|
|
2904
|
+
});
|
|
2905
|
+
}
|
|
2906
|
+
const transientContext = options.transientContext === void 0 ? Prompt.empty : yield* options.transientContext.load(contextRequest).pipe(Effect.flatMap(transientInputToPrompt));
|
|
2907
|
+
const derivedPrompt = options.transientContext === void 0 ? canonicalDecoration : yield* outgoingModelPrompt(policy, context, transientContext, turn, priorToolCalls);
|
|
2908
|
+
const derivedPromptContentTokens = !admissionRequired ? 0 : options.transientContext === void 0 ? canonicalDecorationPromptTokens : outputContractTokens + (yield* estimateCallTokens(derivedPrompt.content));
|
|
2909
|
+
const derivedPromptTokens = () => toolSchemaTokens + derivedPromptContentTokens;
|
|
2910
|
+
if (!context.finalizing && admissionRequired && options.transientContext !== void 0) {
|
|
2911
|
+
const tokenCallTarget = policy.tokenBudget === void 0 || finalAnswerOnly ? void 0 : Math.max(0, policy.tokenBudget - (context.inputTokens + context.outputTokens) - policy.completionReserveTokens);
|
|
2912
|
+
const fullTarget = tokenCallTarget === void 0 ? contextTokenLimit : contextTokenLimit === void 0 ? tokenCallTarget : Math.min(tokenCallTarget, contextTokenLimit);
|
|
2913
|
+
const sourceTarget = fullTarget === void 0 ? void 0 : Math.max(0, fullTarget - derivedPromptTokens());
|
|
2914
|
+
let preparedEstimate = (yield* preparedSourceTokens) + derivedPromptTokens();
|
|
2915
|
+
const contextPressure = contextTokenLimit !== void 0 && preparedEstimate > contextTokenLimit;
|
|
2916
|
+
const tokenPressure = tokenCallTarget !== void 0 && preparedEstimate > tokenCallTarget;
|
|
2917
|
+
const currentCompactionAllowance = context.compactionTurn.turn === turn;
|
|
2918
|
+
const summarizedThisTurn = currentCompactionAllowance && (context.compactionTurn.applied.has("summarize") || context.compactionTurn.applied.has("rollover"));
|
|
2919
|
+
const prunedThisTurn = currentCompactionAllowance && context.compactionTurn.applied.has("clear-tool-results");
|
|
2920
|
+
const canCompact = !summarizedThisTurn && (!prunedThisTurn || !tokenPressure && policy.compaction.mode !== "prune");
|
|
2921
|
+
if ((contextPressure || tokenPressure) && sourceTarget !== void 0 && sourceTarget > 0 && canCompact) {
|
|
2922
|
+
context.compaction.lastCompactionTurn = turn;
|
|
2923
|
+
const outcome = yield* compactContext(agent, context, modelContext.prompt, turn, options, callContext === void 0 ? void 0 : contextTokenLimit, messageTokenEstimator, sourceTarget, "pressure", !tokenPressure).pipe(withCallModel);
|
|
2924
|
+
preEvents = [...preEvents, ...outcome.events];
|
|
2925
|
+
if (outcome.events.some((event) => event._tag === "CompactionPerformed")) refreshPrepared();
|
|
2926
|
+
preparedEstimate = (yield* preparedSourceTokens) + derivedPromptTokens();
|
|
2927
|
+
}
|
|
2928
|
+
if (context.tokenExhausted) {
|
|
2929
|
+
finalAnswerOnly = true;
|
|
2930
|
+
toolSchemaTokens = yield* estimateToolSchemaTokens;
|
|
2931
|
+
}
|
|
2932
|
+
const preparedTokenCallTarget = policy.tokenBudget === void 0 || finalAnswerOnly ? void 0 : Math.max(0, policy.tokenBudget - (context.inputTokens + context.outputTokens) - policy.completionReserveTokens);
|
|
2933
|
+
if ((callContext !== void 0 || contextTokenLimit === void 0 || preparedEstimate <= contextTokenLimit) && preparedTokenCallTarget !== void 0 && preparedEstimate > preparedTokenCallTarget) {
|
|
2934
|
+
const error = AgentPolicyError.make({
|
|
2935
|
+
limit: "tokens",
|
|
2936
|
+
message: `The next research call would consume this Run's ${policy.completionReserveTokens} token completion reserve`
|
|
2937
|
+
});
|
|
2938
|
+
if (policy.onExhaustion === "fail") return yield* error;
|
|
2939
|
+
context.tokenExhausted = true;
|
|
2940
|
+
context.exhaustedDimension ??= "tokens";
|
|
2941
|
+
finalAnswerOnly = true;
|
|
2942
|
+
toolSchemaTokens = yield* estimateToolSchemaTokens;
|
|
2943
|
+
}
|
|
2944
|
+
preparedEstimate = (yield* preparedSourceTokens) + derivedPromptTokens();
|
|
2945
|
+
if (contextTokenLimit !== void 0 && preparedEstimate > contextTokenLimit) return yield* ContextBudgetError.make({
|
|
2946
|
+
message: `Transient context could not fit the next model prompt inside the ${contextTokenLimit} token context target`,
|
|
2947
|
+
estimatedTokens: preparedEstimate,
|
|
2948
|
+
targetTokens: contextTokenLimit,
|
|
2949
|
+
completionReserveTokens: policy.completionReserveTokens
|
|
2950
|
+
});
|
|
2951
|
+
}
|
|
2952
|
+
if (finalAnswerOnly) yield* context.policyReservations.withPermit(Effect.gen(function* () {
|
|
2953
|
+
context.finalizationUsed = true;
|
|
2954
|
+
if (options.durability?.reservePolicyUsage !== void 0) yield* options.durability.reservePolicyUsage({
|
|
2955
|
+
programmaticToolCalls: context.programmaticToolCalls,
|
|
2956
|
+
finalizationUsed: true
|
|
2957
|
+
});
|
|
2958
|
+
}));
|
|
2959
|
+
/** The model-visible view of the Turn basis under current compaction state. */
|
|
2960
|
+
const compactedOutgoing = () => {
|
|
2961
|
+
context.compaction.lastViewLength = prepared.length;
|
|
2962
|
+
return Prompt.fromMessages(prepared);
|
|
2963
|
+
};
|
|
2964
|
+
const consumeTurnUsage = (toolCallCount) => Effect.suspend(() => {
|
|
2965
|
+
trace.usageConsumed = true;
|
|
2966
|
+
const priorModelCalls = context.modelCalls;
|
|
2967
|
+
return consumeUsage(agent, context, trace.usage, toolCallCount, turn, options, {
|
|
2968
|
+
response: trace.response,
|
|
2969
|
+
finishMetadata: trace.finishMetadata,
|
|
2970
|
+
purpose: "turn"
|
|
2971
|
+
}).pipe(withCallModel, Effect.tapCause(() => context.modelCalls === priorModelCalls ? noteIncompleteUsage(context, turn) : Effect.void));
|
|
2972
|
+
});
|
|
2973
|
+
const retainFailedUsage = Effect.fn("AgentRuntime.retainFailedUsage")(function* () {
|
|
2974
|
+
if (trace.usageConsumed) return;
|
|
2975
|
+
trace.usageConsumed = true;
|
|
2976
|
+
if (trace.usage === void 0) return yield* noteIncompleteUsage(context, turn);
|
|
2977
|
+
yield* consumeTurnUsage(0).pipe(Effect.exit);
|
|
2978
|
+
});
|
|
2979
|
+
const attempt = (basis) => Stream.unwrap(outgoingModelPrompt(policy, context, Prompt.fromMessages([...basis.content, ...transientContext.content]), turn, priorToolCalls).pipe(Effect.flatMap((outgoing) => Effect.gen(function* () {
|
|
2980
|
+
const toolChoice = modelToolChoice();
|
|
2981
|
+
let requestToolkit = modelToolkit;
|
|
2982
|
+
snapshot = selectionSnapshot;
|
|
2983
|
+
if (selectionSnapshot !== void 0 && typeof toolChoice === "object" && "oneOf" in toolChoice) {
|
|
2984
|
+
const finalSelection = yield* validateSelection({ toolNames: toolChoice.oneOf }, agent.definition, catalog);
|
|
2985
|
+
const selected = yield* exposureSnapshot(agent.definition, finalSelection, catalog, toolSchemaTransformer, toolChoice.oneOf);
|
|
2986
|
+
requestToolkit = Toolkit.make(...catalog.filter((entry) => entry.kind === "native" && selected.exposedToolNames.includes(entry.nativeToolName)).map((entry) => entry.tool));
|
|
2987
|
+
snapshot = Object.freeze(Snapshot.make({
|
|
2988
|
+
...selectionSnapshot,
|
|
2989
|
+
exposedToolNames: selected.exposedToolNames
|
|
2990
|
+
}));
|
|
2991
|
+
}
|
|
2992
|
+
context.toolExposure = snapshot;
|
|
2993
|
+
const providerPrompt = outputContract._tag !== "rendered" ? outgoing : insertOutputContract(outgoing, outputContract.part);
|
|
2994
|
+
return yield* estimateCallTokens(providerPrompt.content).pipe(Effect.map((tokens) => tokens + toolSchemaTokens), Effect.tap((estimatedTokens) => contextTokenLimit !== void 0 && (options.context !== void 0 || options.transientContext !== void 0) && estimatedTokens > contextTokenLimit ? ContextBudgetError.make({
|
|
2995
|
+
message: `Prepared context could not fit the next model prompt inside the ${contextTokenLimit} token context target`,
|
|
2996
|
+
estimatedTokens,
|
|
2997
|
+
targetTokens: contextTokenLimit,
|
|
2998
|
+
completionReserveTokens: policy.completionReserveTokens
|
|
2999
|
+
}) : Effect.void), Effect.tap((tokens) => Effect.sync(() => {
|
|
3000
|
+
context.windowTokens = tokens;
|
|
3001
|
+
})), Effect.tap(() => snapshot === void 0 || options.durability?.noteToolExposure === void 0 ? Effect.void : options.durability.noteToolExposure(turn, snapshot)), Effect.as(guardBudgetStream(LanguageModel.streamText({
|
|
3002
|
+
prompt: providerPrompt,
|
|
3003
|
+
toolkit: requestToolkit,
|
|
3004
|
+
disableToolCallResolution: true,
|
|
3005
|
+
...toolChoice === void 0 ? {} : { toolChoice }
|
|
3006
|
+
}), options.budget).pipe(Stream.onStart(Effect.sync(() => {
|
|
3007
|
+
trace.usageConsumed = false;
|
|
3008
|
+
})), Stream.mapEffect((part) => ownModelResponsePart(part, agent.definition.toolkit, trace, context.bufferLimits).pipe(Effect.flatMap((owned) => eventsForPart(context, turnId, turn, agent.definition.toolkit.tools, trace, owned.ownedPart, owned.retainedBytes)))), Stream.flatMap(Stream.fromIterable), Stream.tapCause(() => retainFailedUsage()))));
|
|
3009
|
+
}))));
|
|
3010
|
+
const response = attempt(compactedOutgoing()).pipe(Stream.catch((error) => {
|
|
3011
|
+
if (!(error instanceof AiError.AiError) || !isContextOverflowMessage(overflowText(error))) return Stream.fail(error);
|
|
3012
|
+
const message = overflowText(error);
|
|
3013
|
+
if (trace.parts.length > 0 || contextTokenLimit === void 0) return Stream.fail(ContextOverflowError.make({
|
|
3014
|
+
message,
|
|
3015
|
+
retried: false
|
|
3016
|
+
}));
|
|
3017
|
+
if (context.compaction.overflowRetryTurn === turn) return Stream.fail(ContextOverflowError.make({
|
|
3018
|
+
message,
|
|
3019
|
+
retried: true
|
|
3020
|
+
}));
|
|
3021
|
+
context.compaction.overflowRetryTurn = turn;
|
|
3022
|
+
context.compaction.lastCompactionTurn = turn;
|
|
3023
|
+
return Stream.unwrap(Effect.gen(function* () {
|
|
3024
|
+
const outcome = yield* compactContext(agent, context, modelContext.prompt, turn, options, callContext === void 0 ? void 0 : contextTokenLimit, messageTokenEstimator, Math.max(0, contextTokenLimit - derivedPromptTokens()), "overflow").pipe(withCallModel).pipe(Effect.mapError((inner) => inner instanceof AiError.AiError && isContextOverflowMessage(overflowText(inner)) ? ContextOverflowError.make({
|
|
3025
|
+
message: overflowText(inner),
|
|
3026
|
+
retried: true
|
|
3027
|
+
}) : inner));
|
|
3028
|
+
if (outcome.events.some((event) => event._tag === "CompactionPerformed")) refreshPrepared();
|
|
3029
|
+
const retryEstimate = (yield* preparedSourceTokens) + derivedPromptTokens();
|
|
3030
|
+
if (retryEstimate > contextTokenLimit) return yield* ContextBudgetError.make({
|
|
3031
|
+
message: `Overflow compaction could not fit the retry inside the ${contextTokenLimit} token context target`,
|
|
3032
|
+
estimatedTokens: retryEstimate,
|
|
3033
|
+
targetTokens: contextTokenLimit,
|
|
3034
|
+
completionReserveTokens: policy.completionReserveTokens
|
|
3035
|
+
});
|
|
3036
|
+
const retried = attempt(compactedOutgoing()).pipe(Stream.catch((again) => again instanceof AiError.AiError && isContextOverflowMessage(overflowText(again)) ? Stream.fail(ContextOverflowError.make({
|
|
3037
|
+
message: overflowText(again),
|
|
3038
|
+
retried: true
|
|
3039
|
+
})) : Stream.fail(again)));
|
|
3040
|
+
return Stream.fromIterable(outcome.events).pipe(Stream.concat(retried));
|
|
3041
|
+
}));
|
|
3042
|
+
}), Stream.withSpan("AgentRuntime.model", { attributes: {
|
|
3043
|
+
agentId: context.agentId,
|
|
3044
|
+
runId: context.runId,
|
|
3045
|
+
turnId
|
|
3046
|
+
} }));
|
|
3047
|
+
const continuation = Stream.unwrap(Effect.sync(() => {
|
|
3048
|
+
if (!trace.finished) return failRunEventStream(ModelProtocolError.make({ message: "Model response ended without a finish part" }));
|
|
3049
|
+
const hasProviderCalls = Array.from(trace.toolCalls.values()).some(({ providerExecuted }) => providerExecuted);
|
|
3050
|
+
const turnCompletion = trace.turnCompletion;
|
|
3051
|
+
if (hasProviderCalls && turnCompletion === void 0) return failRunEventStream(ModelProtocolError.make({ message: "Model response omitted staged Turn completion" }));
|
|
3052
|
+
const completionTool = agent.definition.completion?.tool;
|
|
3053
|
+
const declaresCompletion = completionTool !== void 0 && trace.applicationToolCalls.some((call) => call.name === completionTool);
|
|
3054
|
+
const declaresActionCompletion = trace.applicationToolCalls.some((call) => agent.definition.completionFromTools?.some((declaration) => declaration.tool === call.name));
|
|
3055
|
+
if (trace.applicationToolCalls.some((call) => hasTool(agent.definition.toolkit.tools, call.name) && Context.get(agent.definition.toolkit.tools[call.name].annotations, ContextRolloverTool)) && (trace.toolCalls.size !== 1 || declaresCompletion || declaresActionCompletion)) return failRunEventStream(ModelProtocolError.make({ message: "A context rollover Tool must be the only Tool Call and cannot complete the Run" }));
|
|
3056
|
+
const completionBatchError = (declaresCompletion || declaresActionCompletion) && trace.applicationToolCalls.length !== 1 ? ModelProtocolError.make({ message: declaresCompletion ? `Completion Tool ${completionTool} must be the only application Tool Call in its batch` : "An action completion Tool must be the only application Tool Call in its batch" }) : void 0;
|
|
3057
|
+
if (completionBatchError !== void 0 && finalAnswerOnly) return failRunEventStream(completionBatchError);
|
|
3058
|
+
const completionBatch = declaresCompletion && trace.applicationToolCalls.length === 1;
|
|
3059
|
+
if (finalAnswerOnly && trace.toolCalls.size > 0 && (hasProviderCalls || !completionBatch)) return failRunEventStream(ModelProtocolError.make({ message: completionTool === void 0 ? `Model declared ${trace.toolCalls.size} Tool Call(s) under toolChoice "none" after budget exhaustion` : `Model declared ${trace.toolCalls.size} non-completion Tool Call(s) after budget exhaustion` }));
|
|
3060
|
+
const providerOnly = trace.toolCalls.size > 0 && Array.from(trace.toolCalls.values()).every(({ providerExecuted }) => providerExecuted);
|
|
3061
|
+
const missingProviderResult = Array.from(trace.toolCalls.entries()).find(([id, call]) => call.providerExecuted && !trace.finalToolResultIds.has(id));
|
|
3062
|
+
if (missingProviderResult !== void 0) return failRunEventStream(ModelProtocolError.make({ message: `Provider-executed Tool Call ${missingProviderResult[0]} completed without a terminal result` }));
|
|
3063
|
+
const toolCalls = priorToolCalls + trace.toolCalls.size;
|
|
3064
|
+
const overToolBudget = toolCalls + context.programmaticToolCalls > bounds.maxToolCalls;
|
|
3065
|
+
if (overToolBudget && policy.onExhaustion === "fail") return failRunEventStream(AgentPolicyError.make({
|
|
3066
|
+
limit: "tool-calls",
|
|
3067
|
+
message: `Agent exceeded its ${bounds.maxToolCalls} Tool Call limit`
|
|
3068
|
+
}));
|
|
3069
|
+
if (agent.definition.completion?.required === true && trace.toolCalls.size === 0) return failRunEventStream(ModelProtocolError.make({ message: `Model stopped without required completion Tool ${agent.definition.completion.tool}` }));
|
|
3070
|
+
const stagedResponse = Stream.fromIterable(trace.providerResultPayloads).pipe(Stream.mapEffect((payload) => stampProviderResultEvent(context, turnId, payload)), Stream.concat(turnCompletion === void 0 ? Stream.empty : Stream.fromEffect(Effect.map(eventBase(context), (base) => TurnCompleted.make({
|
|
3071
|
+
...base,
|
|
3072
|
+
turnId,
|
|
3073
|
+
turn,
|
|
3074
|
+
finishReason: turnCompletion.finishReason
|
|
3075
|
+
})))));
|
|
3076
|
+
/** Official history advanced through this Turn's response, plus any Tool message. */
|
|
3077
|
+
const historyWithResponse = (...additions) => Prompt.fromMessages([
|
|
3078
|
+
...prompt.content,
|
|
3079
|
+
...promptFromTurnParts(trace).content,
|
|
3080
|
+
...additions
|
|
3081
|
+
]);
|
|
3082
|
+
const afterValidatedResponse = (next) => stagedResponse.pipe(Stream.concat(Stream.unwrap(Effect.gen(function* () {
|
|
3083
|
+
const consumed = yield* consumeTurnUsage(trace.toolCalls.size);
|
|
3084
|
+
const pre = [...consumed.warnings];
|
|
3085
|
+
if (!context.warnedLimits.has("tool-calls") && nearingLimit(toolCalls + context.programmaticToolCalls, bounds.maxToolCalls)) {
|
|
3086
|
+
context.warnedLimits.add("tool-calls");
|
|
3087
|
+
pre.push(BudgetWarning.make({
|
|
3088
|
+
...yield* eventBase(context),
|
|
3089
|
+
limit: "tool-calls",
|
|
3090
|
+
consumed: toolCalls + context.programmaticToolCalls,
|
|
3091
|
+
limitValue: bounds.maxToolCalls
|
|
3092
|
+
}));
|
|
3093
|
+
}
|
|
3094
|
+
if (!context.warnedLimits.has("turns") && nearingLimit(turn, bounds.maxTurns)) {
|
|
3095
|
+
context.warnedLimits.add("turns");
|
|
3096
|
+
pre.push(BudgetWarning.make({
|
|
3097
|
+
...yield* eventBase(context),
|
|
3098
|
+
limit: "turns",
|
|
3099
|
+
consumed: turn,
|
|
3100
|
+
limitValue: bounds.maxTurns
|
|
3101
|
+
}));
|
|
3102
|
+
}
|
|
3103
|
+
const emitThen = (nextStream) => pre.length === 0 ? nextStream : Stream.fromIterable(pre).pipe(Stream.concat(nextStream));
|
|
3104
|
+
if (consumed.breach !== void 0) {
|
|
3105
|
+
if (agent.definition.completion?.required !== true && trace.applicationToolCalls.length === 0 && trace.finishReason === "stop") {
|
|
3106
|
+
const output = yield* decodeFinalOutput(agent, trace.text.join("")).pipe(Effect.map(Option.some), Effect.catch(() => Effect.succeed(Option.none())));
|
|
3107
|
+
if (Option.isSome(output)) {
|
|
3108
|
+
yield* advanceHistory(context, historyWithResponse(), options);
|
|
3109
|
+
const completionUsage = yield* usageReportOf(context);
|
|
3110
|
+
return emitThen(Stream.fromEffect(Effect.map(eventBase(context), (base) => RunCompleted.make({
|
|
3111
|
+
...base,
|
|
3112
|
+
output: output.value.encoded,
|
|
3113
|
+
turns: turn,
|
|
3114
|
+
finishReason: "budget-exhausted",
|
|
3115
|
+
exhausted: "tokens",
|
|
3116
|
+
...completionUsage
|
|
3117
|
+
}))));
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
if (trace.applicationToolCalls.length > 0 && !completionBatch) {
|
|
3121
|
+
const rejection = yield* settleRejectedBatch(context, turnId, trace, AgentPolicyError.make({
|
|
3122
|
+
limit: "tokens",
|
|
3123
|
+
message: `Token budget exhausted: this Run's ${policy.tokenBudget ?? 0} token budget was reached, so this call was rejected without executing. Do not request more tools; produce your final answer now from the information you already have.`
|
|
3124
|
+
}));
|
|
3125
|
+
return emitThen(Stream.fromIterable(rejection).pipe(Stream.concat(toolBatchContinuation(agent, context, trace, prompt, turn, toolCalls, options))));
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
return emitThen(yield* next);
|
|
3129
|
+
}))));
|
|
3130
|
+
/**
|
|
3131
|
+
* Advance official history for a Turn that always continues, drain
|
|
3132
|
+
* steering at the safe seam, and start the next Turn. The `maxTurns`
|
|
3133
|
+
* guard already ran for these Tool Call sites.
|
|
3134
|
+
*/
|
|
3135
|
+
const continueTurn = (history) => Effect.gen(function* () {
|
|
3136
|
+
yield* advanceHistory(context, history, options);
|
|
3137
|
+
const steering = yield* drainInputs(context, options);
|
|
3138
|
+
const nextPrompt = yield* appendInputs(context, history, steering, options);
|
|
3139
|
+
return nextTurn(nextPrompt, turn + 1, toolCalls);
|
|
3140
|
+
});
|
|
3141
|
+
/**
|
|
3142
|
+
* The otherwise-stop seam: advance official history, drain steering,
|
|
3143
|
+
* fall back to buffered follow-ups, then either start the next Turn
|
|
3144
|
+
* under the `maxTurns` and repeated-failure Stop Policy bounds or
|
|
3145
|
+
* complete the Run with the Turn's decoded final output.
|
|
3146
|
+
*/
|
|
3147
|
+
const settleOrFollowUp = (history) => Effect.gen(function* () {
|
|
3148
|
+
yield* advanceHistory(context, history, options);
|
|
3149
|
+
const steering = yield* drainInputs(context, options);
|
|
3150
|
+
const queued = steering.length > 0 ? steering : takeFollowUps(context, options.commandDrainPolicy ?? "one");
|
|
3151
|
+
if (queued.length > 0) {
|
|
3152
|
+
if (policy.onExhaustion === "fail" ? turn >= bounds.maxTurns : turn > bounds.maxTurns) return failRunEventStream(AgentPolicyError.make({
|
|
3153
|
+
limit: "turns",
|
|
3154
|
+
message: `Agent exceeded its ${bounds.maxTurns} Turn limit`
|
|
3155
|
+
}));
|
|
3156
|
+
yield* applyRepeatedFailurePolicy(context, trace, agent.definition.policy.repeatedFailureLimit);
|
|
3157
|
+
const nextPrompt = yield* appendInputs(context, history, queued, options);
|
|
3158
|
+
return nextTurn(nextPrompt, turn + 1, toolCalls);
|
|
3159
|
+
}
|
|
3160
|
+
const output = yield* decodeFinalOutput(agent, trace.text.join(""));
|
|
3161
|
+
const declaration = agent.definition.runDisposition;
|
|
3162
|
+
const runDisposition = finalAnswerOnly || declaration === void 0 ? void 0 : yield* encodeRunDisposition(agent, output.decoded);
|
|
3163
|
+
const completionUsage = yield* usageReportOf(context);
|
|
3164
|
+
return Stream.fromEffect(Effect.map(eventBase(context), (base) => RunCompleted.make({
|
|
3165
|
+
...base,
|
|
3166
|
+
output: output.encoded,
|
|
3167
|
+
...runDisposition === void 0 ? {} : { runDisposition },
|
|
3168
|
+
turns: turn,
|
|
3169
|
+
finishReason: finalAnswerOnly ? "budget-exhausted" : "model-stop",
|
|
3170
|
+
...finalAnswerOnly && context.exhaustedDimension !== void 0 ? { exhausted: context.exhaustedDimension } : {},
|
|
3171
|
+
...completionUsage
|
|
3172
|
+
})));
|
|
3173
|
+
});
|
|
3174
|
+
if (providerOnly && trace.finishReason === "stop") {
|
|
3175
|
+
if (agent.definition.completion?.required === true) {
|
|
3176
|
+
if (policy.onExhaustion === "fail" ? turn >= bounds.maxTurns : turn > bounds.maxTurns) return failRunEventStream(AgentPolicyError.make({
|
|
3177
|
+
limit: "turns",
|
|
3178
|
+
message: `Agent exceeded its ${bounds.maxTurns} Turn limit`
|
|
3179
|
+
}));
|
|
3180
|
+
}
|
|
3181
|
+
return afterValidatedResponse(agent.definition.completion?.required === true ? continueTurn(historyWithResponse()) : settleOrFollowUp(historyWithResponse()));
|
|
3182
|
+
}
|
|
3183
|
+
if (trace.toolCalls.size > 0) {
|
|
3184
|
+
if (trace.finishReason !== "tool-calls") return failRunEventStream(ModelProtocolError.make({ message: `Model declared Tool Calls with incompatible finish reason ${trace.finishReason}` }));
|
|
3185
|
+
if (turn > bounds.maxTurns && !(turn === bounds.maxTurns + 1 && finalAnswerOnly && completionBatch) || policy.onExhaustion === "fail" && turn === bounds.maxTurns && !completionBatch) return failRunEventStream(AgentPolicyError.make({
|
|
3186
|
+
limit: "turns",
|
|
3187
|
+
message: `Agent exceeded its ${bounds.maxTurns} Turn limit`
|
|
3188
|
+
}));
|
|
3189
|
+
const batchRejection = overToolBudget && trace.applicationToolCalls.length > 0 && !(completionBatch && policy.onExhaustion === "final-answer") ? AgentPolicyError.make({
|
|
3190
|
+
limit: "tool-calls",
|
|
3191
|
+
message: `Tool Call budget exhausted: this Run's ${bounds.maxToolCalls} Tool Call limit was reached, so this call was rejected without executing. Do not request more tools; produce your final answer now from the information you already have.`
|
|
3192
|
+
}) : completionBatchError === void 0 ? void 0 : ModelProtocolError.make({ message: `${completionBatchError.message}. ` + (hasProviderCalls ? "The application batch was rejected before execution; none of its application tools ran. Completed provider results are retained. " : "The entire batch was rejected before execution; none of its tools ran. ") + "Request any needed ordinary tools first, wait for their results, then call a completion tool alone." });
|
|
3193
|
+
if (batchRejection !== void 0) return afterValidatedResponse(Effect.gen(function* () {
|
|
3194
|
+
const rejection = yield* settleRejectedBatch(context, turnId, trace, batchRejection);
|
|
3195
|
+
return Stream.fromIterable(rejection).pipe(Stream.concat(toolBatchContinuation(agent, context, trace, prompt, turn, toolCalls, options)));
|
|
3196
|
+
}));
|
|
3197
|
+
if (trace.applicationToolCalls.length === 0) return afterValidatedResponse(Effect.gen(function* () {
|
|
3198
|
+
const history = historyWithResponse();
|
|
3199
|
+
yield* advanceHistory(context, history, options);
|
|
3200
|
+
yield* applyRepeatedFailurePolicy(context, trace, agent.definition.policy.repeatedFailureLimit);
|
|
3201
|
+
const steering = yield* drainInputs(context, options);
|
|
3202
|
+
const nextPrompt = yield* appendInputs(context, history, steering, options);
|
|
3203
|
+
return nextTurn(nextPrompt, turn + 1, toolCalls);
|
|
3204
|
+
}));
|
|
3205
|
+
return afterValidatedResponse(Effect.gen(function* () {
|
|
3206
|
+
const toolkit = yield* agent.definition.toolkit;
|
|
3207
|
+
const concurrency = yield* schedulingConcurrency(agent.definition.policy.toolConcurrency, options.scheduling);
|
|
3208
|
+
if (options.durability !== void 0) yield* options.durability.commitResponse({
|
|
3209
|
+
turn,
|
|
3210
|
+
turnId,
|
|
3211
|
+
responseMessages: promptFromTurnParts(trace),
|
|
3212
|
+
calls: trace.applicationCallDescriptors,
|
|
3213
|
+
toolExposure: snapshot
|
|
3214
|
+
});
|
|
3215
|
+
return guardBudgetStream(executeToolBatch(context, turnId, turn, toolkit, trace.applicationToolCalls, trace, concurrency, options, {
|
|
3216
|
+
maxToolCalls: bounds.maxToolCalls,
|
|
3217
|
+
declaredToolCalls: toolCalls
|
|
3218
|
+
}, agent.definition.policy.toolResultBounds), options.budget).pipe(Stream.concat(toolBatchContinuation(agent, context, trace, prompt, turn, toolCalls, options)));
|
|
3219
|
+
}));
|
|
3220
|
+
}
|
|
3221
|
+
if (trace.finishReason !== "stop") return failRunEventStream(ModelProtocolError.make({ message: `Model stopped without a final answer (${trace.finishReason})` }));
|
|
3222
|
+
return afterValidatedResponse(settleOrFollowUp(historyWithResponse()));
|
|
3223
|
+
}));
|
|
3224
|
+
const events = Stream.fromIterable(preEvents).pipe(Stream.concat(started), Stream.concat(response), Stream.concat(continuation.pipe(Stream.tapCause(() => retainFailedUsage()))));
|
|
3225
|
+
return modelServices === void 0 ? events : Stream.provideContext(events, modelServices);
|
|
3226
|
+
}));
|
|
3227
|
+
/**
|
|
3228
|
+
* Settle a completed application Tool batch and continue the Run: verify one
|
|
3229
|
+
* final result per declared call, fold outcomes into the repeated-failure
|
|
3230
|
+
* Stop Policy, advance official history with the Tool message in declaration
|
|
3231
|
+
* order, drain steering at the safe seam, and start the next Turn. Shared by
|
|
3232
|
+
* the ordinary model-declared path (`makeTurn`) and the durable batch-resume
|
|
3233
|
+
* path (`makeResumeTurn`).
|
|
3234
|
+
*/
|
|
3235
|
+
const toolBatchContinuation = (agent, context, trace, prompt, turn, toolCalls, options) => Stream.unwrap(Effect.gen(function* () {
|
|
3236
|
+
if (trace.finalToolResultIds.size !== trace.toolCalls.size) return failRunEventStream(ModelProtocolError.make({ message: "A Tool Call turn completed without one final result per Tool Call" }));
|
|
3237
|
+
const orderedResults = [];
|
|
3238
|
+
for (const call of trace.applicationToolCalls) {
|
|
3239
|
+
const result = trace.applicationToolResults.find((candidate) => candidate?.id === call.id);
|
|
3240
|
+
if (result === void 0) return failRunEventStream(ModelProtocolError.make({ message: "Tool batch did not settle completely" }));
|
|
3241
|
+
orderedResults.push(result);
|
|
3242
|
+
}
|
|
3243
|
+
for (const result of orderedResults) if (!result.isFailure && result.toolSelection !== void 0) context.toolSelection = result.toolSelection;
|
|
3244
|
+
const toolMessage = Prompt.makeMessage("tool", { content: orderedResults.map((result) => Prompt.makePart("tool-result", {
|
|
3245
|
+
id: result.id,
|
|
3246
|
+
name: result.name,
|
|
3247
|
+
result: result.encodedResult,
|
|
3248
|
+
isFailure: result.isFailure,
|
|
3249
|
+
providerExecuted: false
|
|
3250
|
+
})) });
|
|
3251
|
+
const history = Prompt.fromMessages([
|
|
3252
|
+
...prompt.content,
|
|
3253
|
+
...promptFromTurnParts(trace).content,
|
|
3254
|
+
toolMessage
|
|
3255
|
+
]);
|
|
3256
|
+
yield* advanceHistory(context, history, options);
|
|
3257
|
+
yield* applyRepeatedFailurePolicy(context, trace, agent.definition.policy.repeatedFailureLimit);
|
|
3258
|
+
const rolloverResult = orderedResults.length === 1 ? orderedResults[0] : void 0;
|
|
3259
|
+
if (rolloverResult !== void 0 && !rolloverResult.isFailure && hasTool(agent.definition.toolkit.tools, rolloverResult.name) && Context.get(agent.definition.toolkit.tools[rolloverResult.name].annotations, ContextRolloverTool)) context.pendingContextToolCallId = rolloverResult.id;
|
|
3260
|
+
const completion = agent.definition.completion;
|
|
3261
|
+
const successfulResult = trace.applicationToolCalls.length === 1 && orderedResults.length === 1 && orderedResults[0]?.isFailure === false ? orderedResults[0] : void 0;
|
|
3262
|
+
const actionCompletion = agent.definition.completionFromTools?.find((declaration) => declaration.tool === successfulResult?.name);
|
|
3263
|
+
let selectedOutput = Option.none();
|
|
3264
|
+
if (successfulResult !== void 0 && (successfulResult.name === completion?.tool || actionCompletion !== void 0)) {
|
|
3265
|
+
const call = trace.applicationCallDescriptors[0];
|
|
3266
|
+
if (call === void 0) return failRunEventStream(ModelProtocolError.make({ message: "Completion Tool has no canonical call descriptor" }));
|
|
3267
|
+
if (actionCompletion !== void 0) selectedOutput = yield* projectCompletionFromToolOutput(agent, actionCompletion, call.parameters, successfulResult.encodedResult);
|
|
3268
|
+
else if (completion !== void 0) selectedOutput = Option.some(yield* projectCompletionOutput(agent, completion, call.parameters, successfulResult.encodedResult));
|
|
3269
|
+
}
|
|
3270
|
+
if (Option.isSome(selectedOutput)) {
|
|
3271
|
+
const output = selectedOutput.value;
|
|
3272
|
+
const bounds = effectiveRunBounds(agent.definition.policy, options);
|
|
3273
|
+
const exhausted = context.tokenExhausted ? "tokens" : turn > bounds.maxTurns ? "turns" : toolCalls + context.programmaticToolCalls > bounds.maxToolCalls ? "tool-calls" : context.exhaustedDimension;
|
|
3274
|
+
if (exhausted !== void 0 && context.exhaustedDimension === void 0) context.exhaustedDimension = exhausted;
|
|
3275
|
+
const declaration = agent.definition.runDisposition;
|
|
3276
|
+
const runDisposition = exhausted !== void 0 || declaration === void 0 ? void 0 : yield* encodeRunDisposition(agent, output.decoded);
|
|
3277
|
+
const completionUsage = yield* usageReportOf(context);
|
|
3278
|
+
return Stream.fromEffect(Effect.map(eventBase(context), (base) => RunCompleted.make({
|
|
3279
|
+
...base,
|
|
3280
|
+
output: output.encoded,
|
|
3281
|
+
...runDisposition === void 0 ? {} : { runDisposition },
|
|
3282
|
+
turns: turn,
|
|
3283
|
+
finishReason: exhausted === void 0 ? "completed" : "budget-exhausted",
|
|
3284
|
+
...exhausted === void 0 ? {} : { exhausted },
|
|
3285
|
+
...completionUsage
|
|
3286
|
+
})));
|
|
3287
|
+
}
|
|
3288
|
+
const steering = yield* drainInputs(context, options);
|
|
3289
|
+
const nextPrompt = yield* appendInputs(context, history, steering, options);
|
|
3290
|
+
return nextTurn(nextPrompt, turn + 1, toolCalls);
|
|
3291
|
+
}));
|
|
3292
|
+
/**
|
|
3293
|
+
* Resume one canonically declared Tool batch without re-invoking the model
|
|
3294
|
+
* (the durable batch-resume seam consumed via `RunOptions.resume`).
|
|
3295
|
+
*
|
|
3296
|
+
* The declared calls are re-validated through their Tool parameter Schemas
|
|
3297
|
+
* before anything executes; a decode failure is a strict no-start boundary
|
|
3298
|
+
* (RUN-004/STORE-006). The resumed Turn's response messages are rebuilt in
|
|
3299
|
+
* canonical encoded form so official history matches the recorded response.
|
|
3300
|
+
* Calls listed in `settled` are injected as final results without starting
|
|
3301
|
+
* their handlers — recorded Tool outcomes never rerun — while approval
|
|
3302
|
+
* preflight and durable preparation still cover the complete declared batch.
|
|
3303
|
+
* No model request is made and no usage is consumed for the resumed Turn.
|
|
3304
|
+
*/
|
|
3305
|
+
const makeResumeTurn = (agent, context, prompt, resume, countedToolCalls, options) => Stream.unwrap(Effect.gen(function* () {
|
|
3306
|
+
const tools = agent.definition.toolkit.tools;
|
|
3307
|
+
const turn = resume.turn;
|
|
3308
|
+
const turnId = resume.turnId;
|
|
3309
|
+
const visibility = yield* RunToolVisibility;
|
|
3310
|
+
if (resume.toolExposure === void 0 && (agent.definition.toolExposure !== void 0 || context.toolSelection !== void 0 || visibility !== void 0)) return yield* ModelProtocolError.make({ message: "Resumed progressive Turn is missing its original Tool exposure" });
|
|
3311
|
+
context.toolCatalog = yield* eligibleCatalog(agent.definition, {
|
|
3312
|
+
threadId: context.threadId,
|
|
3313
|
+
runId: context.runId,
|
|
3314
|
+
turn,
|
|
3315
|
+
input: context.input
|
|
3316
|
+
}, options.subagentGrant, options.delegationDepth ?? options.parentLink?.depth ?? 0);
|
|
3317
|
+
context.toolExposure = resume.toolExposure === void 0 ? void 0 : yield* decodeSnapshot(resume.toolExposure);
|
|
3318
|
+
const originalExposure = context.toolExposure;
|
|
3319
|
+
if (originalExposure !== void 0) {
|
|
3320
|
+
yield* validateSelection({ toolNames: originalExposure.exposedToolNames }, agent.definition, context.toolCatalog, false);
|
|
3321
|
+
if (originalExposure.selection !== void 0) yield* validateSelection(originalExposure.selection, agent.definition, context.toolCatalog, false);
|
|
3322
|
+
if (resume.calls.some((call) => !originalExposure.exposedToolNames.includes(call.name))) return yield* ModelProtocolError.make({ message: "Resumed Tool call was not exposed in its original model request" });
|
|
3323
|
+
context.toolSelection = originalExposure.selection;
|
|
3324
|
+
}
|
|
3325
|
+
if (!Number.isInteger(turn) || turn <= 0) return failRunEventStream(ModelProtocolError.make({ message: "Turn resume requires a positive integer turn number" }));
|
|
3326
|
+
if (resume.calls.length === 0) return failRunEventStream(ModelProtocolError.make({ message: "Turn resume requires at least one declared Tool Call" }));
|
|
3327
|
+
const trace = {
|
|
3328
|
+
replayedResponse: resume.responseMessages,
|
|
3329
|
+
responsePartCount: 0,
|
|
3330
|
+
responsePartBytes: 0,
|
|
3331
|
+
parts: [],
|
|
3332
|
+
text: [],
|
|
3333
|
+
textParts: /* @__PURE__ */ new Map(),
|
|
3334
|
+
reasoningParts: /* @__PURE__ */ new Map(),
|
|
3335
|
+
toolParameterParts: /* @__PURE__ */ new Map(),
|
|
3336
|
+
toolCalls: /* @__PURE__ */ new Map(),
|
|
3337
|
+
finalToolResultIds: /* @__PURE__ */ new Set(),
|
|
3338
|
+
providerResultPayloads: [],
|
|
3339
|
+
providerStagedPayloadBytes: 0,
|
|
3340
|
+
turnCompletion: void 0,
|
|
3341
|
+
applicationToolCalls: [],
|
|
3342
|
+
applicationCallDescriptors: [],
|
|
3343
|
+
applicationToolResults: [],
|
|
3344
|
+
finished: true,
|
|
3345
|
+
finishReason: "tool-calls",
|
|
3346
|
+
usage: void 0
|
|
3347
|
+
};
|
|
3348
|
+
const declarationByCallId = /* @__PURE__ */ new Map();
|
|
3349
|
+
for (const call of resume.calls) {
|
|
3350
|
+
if (!hasTool(tools, call.name)) return failRunEventStream(ModelProtocolError.make({ message: `Turn resume declared unknown Tool ${call.name}` }));
|
|
3351
|
+
if (trace.toolCalls.has(call.id)) return failRunEventStream(ModelProtocolError.make({ message: `Turn resume repeated Tool Call ID ${call.id}` }));
|
|
3352
|
+
const tool = tools[call.name];
|
|
3353
|
+
const toolCallId = yield* decodeToolCallId(call.id);
|
|
3354
|
+
yield* decodeToolCallParameters(tool, call.name, call.params, "resume");
|
|
3355
|
+
const parameters = yield* decodeEventJson(call.params, "Tool parameters");
|
|
3356
|
+
const providerExecuted = call.providerExecuted === true;
|
|
3357
|
+
declarationByCallId.set(call.id, {
|
|
3358
|
+
index: trace.applicationToolCalls.length,
|
|
3359
|
+
name: call.name,
|
|
3360
|
+
providerExecuted
|
|
3361
|
+
});
|
|
3362
|
+
trace.parts.push(Response.makePart("tool-call", {
|
|
3363
|
+
id: call.id,
|
|
3364
|
+
name: call.name,
|
|
3365
|
+
params: parameters,
|
|
3366
|
+
providerExecuted
|
|
3367
|
+
}));
|
|
3368
|
+
trace.toolCalls.set(call.id, {
|
|
3369
|
+
name: call.name,
|
|
3370
|
+
providerExecuted
|
|
3371
|
+
});
|
|
3372
|
+
if (providerExecuted) continue;
|
|
3373
|
+
trace.applicationToolCalls.push(Response.makePart("tool-call", {
|
|
3374
|
+
id: call.id,
|
|
3375
|
+
name: call.name,
|
|
3376
|
+
params: parameters,
|
|
3377
|
+
providerExecuted: false
|
|
3378
|
+
}));
|
|
3379
|
+
trace.applicationCallDescriptors.push({
|
|
3380
|
+
toolCallId,
|
|
3381
|
+
toolName: call.name,
|
|
3382
|
+
parameters,
|
|
3383
|
+
executionClass: getToolExecutionClass(tool),
|
|
3384
|
+
executionKind: getToolExecutionKind(tool.annotations)
|
|
3385
|
+
});
|
|
3386
|
+
}
|
|
3387
|
+
const completionTool = agent.definition.completion?.tool;
|
|
3388
|
+
const actionCompletionCall = trace.applicationToolCalls.find((call) => agent.definition.completionFromTools?.some((declaration) => declaration.tool === call.name));
|
|
3389
|
+
if ((actionCompletionCall !== void 0 || completionTool !== void 0 && trace.applicationToolCalls.some((call) => call.name === completionTool)) && trace.applicationToolCalls.length !== 1) return failRunEventStream(ModelProtocolError.make({ message: "A completion Tool must be the only application Tool Call in its batch" }));
|
|
3390
|
+
const completionBatch = completionTool !== void 0 && trace.applicationToolCalls.length === 1 && trace.applicationToolCalls[0]?.name === completionTool;
|
|
3391
|
+
if (completionBatch && completionTool !== void 0 && Context.get(tools[completionTool].annotations, ContextRolloverTool) || actionCompletionCall !== void 0 && Context.get(tools[actionCompletionCall.name].annotations, ContextRolloverTool)) return failRunEventStream(ModelProtocolError.make({ message: "A context rollover Tool cannot complete the Run" }));
|
|
3392
|
+
if (context.finalizationUsed && (!completionBatch || trace.toolCalls.size !== 1)) return failRunEventStream(ModelProtocolError.make({ message: "A resumed grace finalization may only execute the completion Tool" }));
|
|
3393
|
+
const settledIds = /* @__PURE__ */ new Set();
|
|
3394
|
+
const providerCallIds = new Set([...declarationByCallId].filter(([, call]) => call.providerExecuted).map(([id]) => id));
|
|
3395
|
+
const settledInputs = yield* snapshotResumedSettledCalls(resume, declarationByCallId.size);
|
|
3396
|
+
for (const settledInput of settledInputs) {
|
|
3397
|
+
const settledCall = yield* decodeResumedSettledCall(settledInput, agent.definition.policy.toolResultBounds.maxBytes, providerCallIds);
|
|
3398
|
+
const declared = declarationByCallId.get(settledCall.id);
|
|
3399
|
+
if (declared === void 0) return failRunEventStream(ModelProtocolError.make({ message: `Turn resume settled an undeclared Tool Call ${settledCall.id}` }));
|
|
3400
|
+
if (settledIds.has(settledCall.id)) return failRunEventStream(ModelProtocolError.make({ message: `Turn resume settled Tool Call ${settledCall.id} more than once` }));
|
|
3401
|
+
settledIds.add(settledCall.id);
|
|
3402
|
+
trace.finalToolResultIds.add(settledCall.id);
|
|
3403
|
+
if (declared.providerExecuted) {
|
|
3404
|
+
trace.parts.push(Response.makePart("tool-result", {
|
|
3405
|
+
id: settledCall.id,
|
|
3406
|
+
name: declared.name,
|
|
3407
|
+
result: settledCall.result,
|
|
3408
|
+
encodedResult: settledCall.result,
|
|
3409
|
+
isFailure: settledCall.isFailure,
|
|
3410
|
+
providerExecuted: true,
|
|
3411
|
+
preliminary: false
|
|
3412
|
+
}));
|
|
3413
|
+
continue;
|
|
3414
|
+
}
|
|
3415
|
+
trace.applicationToolResults[declared.index] = {
|
|
3416
|
+
...settledCall.toolSelection === void 0 ? {} : { toolSelection: yield* validateSelection(settledCall.toolSelection, agent.definition, context.toolCatalog, false) },
|
|
3417
|
+
id: settledCall.id,
|
|
3418
|
+
name: declared.name,
|
|
3419
|
+
encodedResult: settledCall.result,
|
|
3420
|
+
isFailure: settledCall.isFailure,
|
|
3421
|
+
...settledCall.budgetRejected === void 0 ? {} : { budgetRejected: settledCall.budgetRejected }
|
|
3422
|
+
};
|
|
3423
|
+
}
|
|
3424
|
+
const policy = agent.definition.policy;
|
|
3425
|
+
const bounds = effectiveRunBounds(policy, options);
|
|
3426
|
+
const toolCalls = countedToolCalls;
|
|
3427
|
+
for (const [id, call] of declarationByCallId) if (call.providerExecuted && !settledIds.has(id)) return failRunEventStream(ModelProtocolError.make({ message: `Turn resume lacks the canonical provider result for ${id}` }));
|
|
3428
|
+
const overToolBudget = toolCalls + context.programmaticToolCalls > bounds.maxToolCalls;
|
|
3429
|
+
if (overToolBudget && policy.onExhaustion === "fail") return failRunEventStream(AgentPolicyError.make({
|
|
3430
|
+
limit: "tool-calls",
|
|
3431
|
+
message: `Agent exceeded its ${bounds.maxToolCalls} Tool Call limit`
|
|
3432
|
+
}));
|
|
3433
|
+
if (turn > bounds.maxTurns && !(policy.onExhaustion === "final-answer" && turn === bounds.maxTurns + 1 && context.finalizationUsed && completionBatch) || policy.onExhaustion === "fail" && turn === bounds.maxTurns && !completionBatch) return failRunEventStream(AgentPolicyError.make({
|
|
3434
|
+
limit: "turns",
|
|
3435
|
+
message: `Agent exceeded its ${bounds.maxTurns} Turn limit`
|
|
3436
|
+
}));
|
|
3437
|
+
const toolkit = yield* agent.definition.toolkit;
|
|
3438
|
+
const concurrency = yield* schedulingConcurrency(agent.definition.policy.toolConcurrency, options.scheduling);
|
|
3439
|
+
const leading = resume.leadingMessages;
|
|
3440
|
+
const resumedPrompt = leading === void 0 || leading.content.length === 0 ? prompt : Prompt.fromMessages([...prompt.content, ...leading.content]);
|
|
3441
|
+
if (resumedPrompt !== prompt) yield* advanceHistory(context, resumedPrompt, options);
|
|
3442
|
+
const started = Stream.fromEffect(Effect.gen(function* () {
|
|
3443
|
+
yield* Effect.logDebug("agent resumed a declared Tool batch").pipe(Effect.annotateLogs({
|
|
3444
|
+
agentId: context.agentId,
|
|
3445
|
+
runId: context.runId,
|
|
3446
|
+
turnId
|
|
3447
|
+
}));
|
|
3448
|
+
return [TurnStarted.make({
|
|
3449
|
+
...yield* eventBase(context),
|
|
3450
|
+
turnId,
|
|
3451
|
+
turn
|
|
3452
|
+
}), TurnCompleted.make({
|
|
3453
|
+
...yield* eventBase(context),
|
|
3454
|
+
turnId,
|
|
3455
|
+
turn,
|
|
3456
|
+
finishReason: "tool-calls"
|
|
3457
|
+
})];
|
|
3458
|
+
}).pipe(Effect.withLogSpan("AgentRuntime.resume"))).pipe(Stream.flatMap(Stream.fromIterable));
|
|
3459
|
+
const continueAfterBatch = () => toolBatchContinuation(agent, context, trace, resumedPrompt, turn, toolCalls, options);
|
|
3460
|
+
if (overToolBudget && !(completionBatch && policy.onExhaustion === "final-answer")) {
|
|
3461
|
+
const rejection = yield* settleRejectedBatch(context, turnId, trace, AgentPolicyError.make({
|
|
3462
|
+
limit: "tool-calls",
|
|
3463
|
+
message: `Tool Call budget exhausted: this Run's ${bounds.maxToolCalls} Tool Call limit was reached, so this call was rejected without executing. Do not request more tools; produce your final answer now from the information you already have.`
|
|
3464
|
+
}), settledIds);
|
|
3465
|
+
return started.pipe(Stream.concat(Stream.fromIterable(rejection)), Stream.concat(continueAfterBatch()));
|
|
3466
|
+
}
|
|
3467
|
+
const toolResults = guardBudgetStream(executeToolBatch(context, turnId, turn, toolkit, trace.applicationToolCalls, trace, concurrency, options, {
|
|
3468
|
+
maxToolCalls: bounds.maxToolCalls,
|
|
3469
|
+
declaredToolCalls: toolCalls
|
|
3470
|
+
}, agent.definition.policy.toolResultBounds, settledIds), options.budget);
|
|
3471
|
+
return started.pipe(Stream.concat(toolResults), Stream.concat(continueAfterBatch()));
|
|
3472
|
+
}));
|
|
3473
|
+
const failRunEventStream = (error) => Stream.fail(error);
|
|
3474
|
+
const durationLimitError = (policy) => AgentPolicyError.make({
|
|
3475
|
+
limit: "duration",
|
|
3476
|
+
message: `Agent exceeded its ${Duration.format(policy.maxDuration)} duration limit`
|
|
3477
|
+
});
|
|
3478
|
+
const enforceDurationDeadline = (execution, durationDeadlineMillis, durationLimit) => Stream.unwrap(Effect.gen(function* () {
|
|
3479
|
+
const remaining = durationDeadlineMillis - (yield* Clock.currentTimeMillis);
|
|
3480
|
+
if (remaining <= 0) return Stream.fail(durationLimit);
|
|
3481
|
+
return execution.pipe(Stream.interruptWhen(Effect.sleep(remaining).pipe(Effect.andThen(Effect.fail(durationLimit)))));
|
|
3482
|
+
}));
|
|
3483
|
+
const guardBudgetStream = (stream, budget) => budget === void 0 ? stream : Stream.transformPull(stream, (pull) => Effect.succeed(budget.guard(pull)));
|
|
3484
|
+
function streamWithCompletion(agentValue, input, runOptions = {}, onCompleted, onUsage) {
|
|
3485
|
+
const agent = { definition: "definition" in agentValue ? agentValue.definition : agentValue };
|
|
3486
|
+
const model = "definition" in agentValue ? agentValue.model : void 0;
|
|
3487
|
+
return Stream.unwrap(Effect.gen(function* () {
|
|
3488
|
+
const history = yield* ThreadHistory;
|
|
3489
|
+
const preparation = yield* Effect.serviceOption(RunContextPreparation).pipe(Effect.map(Option.getOrElse(() => RunContextPreparation.of({}))));
|
|
3490
|
+
const authorization = yield* Effect.serviceOption(RunToolAuthorization).pipe(Effect.map(Option.getOrUndefined));
|
|
3491
|
+
const visibility = yield* RunToolVisibility;
|
|
3492
|
+
const ids = yield* IdGenerator$1;
|
|
3493
|
+
const threadId = runOptions.threadId ?? (yield* ids.nextThreadId);
|
|
3494
|
+
const runId = runOptions.runId ?? (yield* ids.nextRunId);
|
|
3495
|
+
const retained = history.retention === "incremental" && runOptions.durability !== void 0 ? void 0 : yield* history.open({
|
|
3496
|
+
threadId,
|
|
3497
|
+
runId
|
|
3498
|
+
});
|
|
3499
|
+
if (history.retention === "on-success" && (runOptions.history !== void 0 || runOptions.onHistory !== void 0 || runOptions.input !== void 0 || runOptions.durability !== void 0 || runOptions.subagent !== void 0 || runOptions.resume !== void 0 || runOptions.resumeUsage !== void 0)) return yield* ThreadHistoryError.make({
|
|
3500
|
+
threadId,
|
|
3501
|
+
reason: "incompatible",
|
|
3502
|
+
message: "Provided history cannot share ownership with explicit history, input queues, or durable recovery hooks"
|
|
3503
|
+
});
|
|
3504
|
+
const initialHistory = runOptions.history ?? retained?.prompt;
|
|
3505
|
+
if (retained !== void 0 && runOptions.history !== void 0) yield* retained.stageHistory(runOptions.history);
|
|
3506
|
+
const options = {
|
|
3507
|
+
...runOptions,
|
|
3508
|
+
context: runOptions.context ?? preparation.hook,
|
|
3509
|
+
transientContext: runOptions.transientContext ?? preparation.transientContext,
|
|
3510
|
+
toolAuthorization: runOptions.toolAuthorization ?? authorization,
|
|
3511
|
+
threadId,
|
|
3512
|
+
runId,
|
|
3513
|
+
...retained === void 0 ? {} : {
|
|
3514
|
+
history: initialHistory,
|
|
3515
|
+
onHistory: (next) => retained.stageHistory(next).pipe(Effect.andThen(() => runOptions.onHistory?.(next) ?? Effect.void))
|
|
3516
|
+
}
|
|
3517
|
+
};
|
|
3518
|
+
const programmaticAuthorization = Layer.effect(ProgrammaticToolAuthorization, Effect.gen(function* () {
|
|
3519
|
+
const services = yield* Effect.context();
|
|
3520
|
+
const selected = options.toolAuthorization;
|
|
3521
|
+
return ProgrammaticToolAuthorization.of({ authorize: (request) => selected === void 0 ? Effect.succeed({ _tag: "allowed" }) : provideHookServices(selected.authorize(request), services).pipe(Effect.catchCauseFilter(Cause.findError, (error, cause) => Effect.fail(new BrokerCallFailure(error, cause)))) });
|
|
3522
|
+
}));
|
|
3523
|
+
const interpreted = Stream.unwrap(Effect.gen(function* () {
|
|
3524
|
+
const resumed = options.resume === void 0 ? void 0 : {
|
|
3525
|
+
batch: options.resume,
|
|
3526
|
+
usage: yield* decodeResumeUsage(options.resumeUsage)
|
|
3527
|
+
};
|
|
3528
|
+
const resumeUsage = resumed?.usage ?? (options.resumeUsage === void 0 ? void 0 : yield* decodeResumeUsage(options.resumeUsage));
|
|
3529
|
+
if (resumed !== void 0 && (resumed.usage.committedTurns !== resumed.batch.turn || resumed.usage.toolCalls < resumed.batch.calls.length || resumed.usage.consecutiveToolFailures > resumed.usage.toolCalls - resumed.batch.calls.length)) return yield* ModelProtocolError.make({ message: "Run resume accounting conflicts with the pending Turn and declared Tool Calls" });
|
|
3530
|
+
const attemptStartedAtMillis = yield* Clock.currentTimeMillis;
|
|
3531
|
+
const attemptDeadlineMillis = attemptStartedAtMillis + Duration.toMillis(agent.definition.policy.maxDuration);
|
|
3532
|
+
const durationDeadlineMillis = options.durationDeadline === void 0 ? attemptDeadlineMillis : Math.min(attemptDeadlineMillis, DateTime.toEpochMillis(options.durationDeadline));
|
|
3533
|
+
const startedAtMillis = options.runStartedAt === void 0 ? attemptStartedAtMillis : DateTime.toEpochMillis(options.runStartedAt);
|
|
3534
|
+
const compactor = yield* Effect.serviceOption(ContextCompactor).pipe(Effect.flatMap(Option.match({
|
|
3535
|
+
onSome: Effect.succeed,
|
|
3536
|
+
onNone: () => Effect.provide(ContextCompactor, ContextCompactor.layer)
|
|
3537
|
+
})));
|
|
3538
|
+
const initialToolSelection = options.toolSelection ?? (agent.definition.toolExposure === void 0 ? void 0 : { toolNames: agent.definition.toolExposure.initialToolNames ?? [] });
|
|
3539
|
+
const updateSchema = agent.definition.updates;
|
|
3540
|
+
const updateContext = yield* Effect.context();
|
|
3541
|
+
const context = {
|
|
3542
|
+
validateUpdate: updateSchema === void 0 ? () => Effect.fail(new UpdateError({ reason: "unavailable" })) : (value) => Schema.decodeEffect(updateSchema)(value).pipe(Effect.provide(updateContext), Effect.asVoid, Effect.mapError(() => new UpdateError({ reason: "validation" }))),
|
|
3543
|
+
updates: /* @__PURE__ */ new Map(),
|
|
3544
|
+
updateBytes: 0,
|
|
3545
|
+
updatePermits: yield* Semaphore.make(1),
|
|
3546
|
+
definition: agent.definition,
|
|
3547
|
+
toolSelection: initialToolSelection === void 0 ? void 0 : yield* decodeSelection(initialToolSelection),
|
|
3548
|
+
toolCatalog: [],
|
|
3549
|
+
toolExposure: void 0,
|
|
3550
|
+
toolSchemaTransformer: void 0,
|
|
3551
|
+
agentId: agent.definition.id,
|
|
3552
|
+
threadId,
|
|
3553
|
+
runId,
|
|
3554
|
+
toolFailureObserver: yield* CurrentToolFailureObserver,
|
|
3555
|
+
input: void 0,
|
|
3556
|
+
pendingFollowUps: [],
|
|
3557
|
+
startedAtMillis,
|
|
3558
|
+
durationDeadlineMillis,
|
|
3559
|
+
history: options.history ?? Prompt.empty,
|
|
3560
|
+
modelCalls: resumeUsage?.modelCalls ?? 0,
|
|
3561
|
+
usageStatus: resumeUsage?.usageStatus ?? (resumeUsage === void 0 ? "complete" : "unknown"),
|
|
3562
|
+
pricingStatus: resumeUsage?.pricingStatus ?? (resumeUsage === void 0 ? "complete" : "unknown"),
|
|
3563
|
+
unobservedModelCalls: resumeUsage?.unobservedModelCalls ?? 0,
|
|
3564
|
+
childUsage: new Map((resumeUsage?.children ?? []).map((child) => [child.runId, Effect.succeed(child.report)])),
|
|
3565
|
+
liveChildren: /* @__PURE__ */ new Set(),
|
|
3566
|
+
consecutiveToolFailures: resumeUsage?.consecutiveToolFailures ?? 0,
|
|
3567
|
+
inputTokens: resumeUsage?.inputTokens ?? 0,
|
|
3568
|
+
outputTokens: resumeUsage?.outputTokens ?? 0,
|
|
3569
|
+
lastInputTokens: resumeUsage?.lastInputTokens ?? 0,
|
|
3570
|
+
lastOutputTokens: resumeUsage?.lastOutputTokens ?? 0,
|
|
3571
|
+
costMicrousd: resumeUsage?.costMicrousd ?? 0,
|
|
3572
|
+
lastCostMicrousd: 0,
|
|
3573
|
+
warnedLimits: /* @__PURE__ */ new Set(),
|
|
3574
|
+
finalizing: false,
|
|
3575
|
+
tokenExhausted: false,
|
|
3576
|
+
exhaustedDimension: void 0,
|
|
3577
|
+
compaction: initialCompactionState(),
|
|
3578
|
+
preparedCompactionSource: void 0,
|
|
3579
|
+
compactionTurn: {
|
|
3580
|
+
turn: 0,
|
|
3581
|
+
summaryCalls: 0,
|
|
3582
|
+
applied: /* @__PURE__ */ new Set()
|
|
3583
|
+
},
|
|
3584
|
+
windowId: options.initialContextWindowId ?? contextWindowId(runId, 0),
|
|
3585
|
+
windowTokens: resumeUsage?.lastInputTokens ?? 0,
|
|
3586
|
+
windowContextTokenLimit: agent.definition.policy.contextTokenLimit,
|
|
3587
|
+
pendingContextToolCallId: options.pendingContextToolCallId,
|
|
3588
|
+
bufferLimits: effectiveRunBufferLimits(options.bufferLimits),
|
|
3589
|
+
sequence: 0,
|
|
3590
|
+
toolProgressBytes: 0,
|
|
3591
|
+
programmaticToolCalls: resumeUsage?.programmaticToolCalls ?? 0,
|
|
3592
|
+
finalizationUsed: resumeUsage?.finalizationUsed ?? false,
|
|
3593
|
+
policyReservations: yield* Semaphore.make(1)
|
|
3594
|
+
};
|
|
3595
|
+
onUsage?.(usageReportOf(context));
|
|
3596
|
+
if (resumeUsage !== void 0) {
|
|
3597
|
+
const bounds = effectiveRunBounds(agent.definition.policy, options);
|
|
3598
|
+
if (context.finalizationUsed) context.exhaustedDimension = resumeUsage.committedTurns > bounds.maxTurns ? "turns" : resumeUsage.toolCalls + context.programmaticToolCalls > bounds.maxToolCalls ? "tool-calls" : "tokens";
|
|
3599
|
+
if (agent.definition.policy.onExhaustion === "fail" && resumeUsage.toolCalls + context.programmaticToolCalls > bounds.maxToolCalls) return failRunEventStream(AgentPolicyError.make({
|
|
3600
|
+
limit: "tool-calls",
|
|
3601
|
+
message: `Agent exceeded its ${bounds.maxToolCalls} Tool Call limit`
|
|
3602
|
+
}));
|
|
3603
|
+
const failureLimit = agent.definition.policy.repeatedFailureLimit;
|
|
3604
|
+
if (failureLimit > 0 && context.consecutiveToolFailures >= failureLimit) return failRunEventStream(AgentPolicyError.make({
|
|
3605
|
+
limit: "repeated-failures",
|
|
3606
|
+
message: `Agent reached its ${failureLimit} consecutive Tool Call failure limit`
|
|
3607
|
+
}));
|
|
3608
|
+
const seededCostBudget = agent.definition.policy.costBudgetMicrousd;
|
|
3609
|
+
if (seededCostBudget !== void 0 && context.costMicrousd > seededCostBudget) return failRunEventStream(AgentPolicyError.make({
|
|
3610
|
+
limit: "cost",
|
|
3611
|
+
message: `Agent exceeded its ${seededCostBudget} microdollar cost budget`
|
|
3612
|
+
}));
|
|
3613
|
+
const seededBudget = agent.definition.policy.tokenBudget;
|
|
3614
|
+
if (seededBudget !== void 0 && context.inputTokens + context.outputTokens > seededBudget) {
|
|
3615
|
+
if (agent.definition.policy.onExhaustion === "fail") return failRunEventStream(AgentPolicyError.make({
|
|
3616
|
+
limit: "tokens",
|
|
3617
|
+
message: `Agent exceeded its ${seededBudget} token budget`
|
|
3618
|
+
}));
|
|
3619
|
+
context.tokenExhausted = true;
|
|
3620
|
+
context.exhaustedDimension ??= "tokens";
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
if (options.input?.start !== void 0) yield* options.input.start();
|
|
3624
|
+
const started = Stream.fromEffect(Effect.gen(function* () {
|
|
3625
|
+
yield* Metric.update(runCounter, 1);
|
|
3626
|
+
yield* Effect.logDebug("agent run started").pipe(Effect.annotateLogs({
|
|
3627
|
+
agentId: context.agentId,
|
|
3628
|
+
runId: context.runId
|
|
3629
|
+
}));
|
|
3630
|
+
return yield* eventBase(context).pipe(Effect.map((base) => RunStarted.make(base)));
|
|
3631
|
+
}).pipe(Effect.withLogSpan("AgentRuntime.run")));
|
|
3632
|
+
const execution = Stream.unwrap(Effect.gen(function* () {
|
|
3633
|
+
const decodedInput = yield* decodeInput(agent, input);
|
|
3634
|
+
const instructions = yield* evaluateInstructions(agent.definition.instructions, decodedInput);
|
|
3635
|
+
const encodedInput = yield* encodeInput(agent, decodedInput);
|
|
3636
|
+
context.input = encodedInput;
|
|
3637
|
+
if (retained !== void 0) yield* retained.stageInput(encodedInput);
|
|
3638
|
+
const inputPrompt = options.frameworkMessage === void 0 ? yield* renderInputPrompt(agent.definition.inputPrompt, decodedInput, encodedInput) : yield* Schema.encodeEffect(Schema.fromJsonString(FrameworkMessage))(options.frameworkMessage).pipe(Effect.mapError(() => AgentInputError.make({ message: "Invalid worker message" })));
|
|
3639
|
+
const priorHistoryLength = context.history.content.length;
|
|
3640
|
+
const prompt = yield* makeInitialPrompt(instructions, inputPrompt, context.history);
|
|
3641
|
+
if (options.context === void 0) {
|
|
3642
|
+
context.compaction.protectedStart = priorHistoryLength;
|
|
3643
|
+
context.compaction.protectedEnd = prompt.content.length;
|
|
3644
|
+
} else context.preparedCompactionSource = {
|
|
3645
|
+
protectedReferences: options.protectedContext?.content ?? prompt.content.slice(priorHistoryLength),
|
|
3646
|
+
protectedMessages: yield* snapshotCompactionMessages(options.protectedContext?.content ?? prompt.content.slice(priorHistoryLength)),
|
|
3647
|
+
prefix: []
|
|
3648
|
+
};
|
|
3649
|
+
yield* advanceHistory(context, prompt, options);
|
|
3650
|
+
let pending;
|
|
3651
|
+
if (resumed !== void 0) pending = {
|
|
3652
|
+
prompt,
|
|
3653
|
+
turn: resumed.batch.turn,
|
|
3654
|
+
toolCalls: resumed.usage.toolCalls,
|
|
3655
|
+
resume: resumed.batch
|
|
3656
|
+
};
|
|
3657
|
+
else {
|
|
3658
|
+
const steering = yield* drainInputs(context, options);
|
|
3659
|
+
pending = {
|
|
3660
|
+
prompt: yield* appendInputs(context, prompt, steering, options),
|
|
3661
|
+
turn: (resumeUsage?.committedTurns ?? 0) + 1,
|
|
3662
|
+
toolCalls: resumeUsage?.toolCalls ?? 0
|
|
3663
|
+
};
|
|
3664
|
+
}
|
|
3665
|
+
return Stream.fromEffectRepeat(Effect.suspend(() => {
|
|
3666
|
+
const current = pending;
|
|
3667
|
+
pending = void 0;
|
|
3668
|
+
return current === void 0 ? Cause.done() : Effect.succeed(current);
|
|
3669
|
+
})).pipe(Stream.flatMap((request) => request.resume === void 0 ? makeTurn(agent, context, request.prompt, request.turn, request.toolCalls, options) : makeResumeTurn(agent, context, request.prompt, request.resume, request.toolCalls, options)), Stream.filterMapEffect((output) => Effect.sync(() => {
|
|
3670
|
+
if (output._tag === "NextTurn") {
|
|
3671
|
+
pending = output;
|
|
3672
|
+
return Result.fail(void 0);
|
|
3673
|
+
}
|
|
3674
|
+
return Result.succeed(output);
|
|
3675
|
+
})));
|
|
3676
|
+
}));
|
|
3677
|
+
const durationLimit = durationLimitError(agent.definition.policy);
|
|
3678
|
+
const deadline = enforceDurationDeadline(execution, durationDeadlineMillis, durationLimit);
|
|
3679
|
+
const nativeUpdateTool = agent.definition.toolkit.tools.emit_update;
|
|
3680
|
+
const updateHandlers = agent.definition.updates === void 0 || nativeUpdateTool === void 0 ? Context.empty() : yield* Toolkit.make(nativeUpdateTool).toHandlers({ emit_update: (parameters, call) => Effect.gen(function* () {
|
|
3681
|
+
if (call.toolCallId === void 0) return yield* new UpdateError({ reason: "unavailable" });
|
|
3682
|
+
const decoded = yield* Schema.decodeUnknownEffect(Schema.Struct({ value: Schema.Unknown }))(parameters).pipe(Effect.mapError(() => new UpdateError({ reason: "validation" })));
|
|
3683
|
+
const value = yield* Schema.encodeUnknownEffect(agent.definition.updates)(decoded.value).pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Json)), Effect.mapError(() => new UpdateError({ reason: "validation" })));
|
|
3684
|
+
const updateId = yield* Schema.decodeEffect(IdempotencyKey)(`tool:${context.runId}:${call.toolCallId}`).pipe(Effect.mapError(() => new UpdateError({ reason: "validation" })));
|
|
3685
|
+
yield* (yield* Emitter).emit({
|
|
3686
|
+
target: agent.definition,
|
|
3687
|
+
updateId,
|
|
3688
|
+
value
|
|
3689
|
+
});
|
|
3690
|
+
}) });
|
|
3691
|
+
const engineToolServices = Context.make(AgentSpawner, makeAgentSpawner({
|
|
3692
|
+
agentId: context.agentId,
|
|
3693
|
+
threadId: context.threadId,
|
|
3694
|
+
runId: context.runId
|
|
3695
|
+
}, options.delegationDepth ?? options.parentLink?.depth ?? 0, history, preparation, agent.definition.policy, options.subagentGrant, options.subagentBudget, options.subagentBudgetScope, (childRunId, read) => {
|
|
3696
|
+
context.liveChildren.add(childRunId);
|
|
3697
|
+
context.childUsage.set(childRunId, read);
|
|
3698
|
+
})).pipe(Context.add(ContextWindow, { status: Effect.sync(() => {
|
|
3699
|
+
const estimatedTokens = Math.min(Number.MAX_SAFE_INTEGER, context.windowTokens + context.lastOutputTokens);
|
|
3700
|
+
const contextTokenLimit = context.windowContextTokenLimit ?? null;
|
|
3701
|
+
return ContextWindowStatus.make({
|
|
3702
|
+
threadId: context.threadId,
|
|
3703
|
+
runId: context.runId,
|
|
3704
|
+
windowId: context.windowId,
|
|
3705
|
+
estimatedTokens,
|
|
3706
|
+
contextTokenLimit,
|
|
3707
|
+
remainingTokens: contextTokenLimit === null ? null : Math.max(0, contextTokenLimit - estimatedTokens)
|
|
3708
|
+
});
|
|
3709
|
+
}) }), Context.merge(updateHandlers), Context.add(Emitter, { emit: () => Effect.fail(new UpdateError({ reason: "unavailable" })) }), Context.add(CurrentToolCatalog, { entries: [] }), Context.add(RunToolVisibility, visibility), Context.add(RunEventSink, closedRunEventSink), Context.add(DurableStep, closedDurableStep), Context.add(SubagentDurability, closedSubagentDurability), Context.add(SubagentHost, SubagentHost.unavailable), Context.add(MessagingHost, MessagingHost.unavailable), Context.add(CurrentSubagentAuthority, options.subagentGrant === void 0 ? void 0 : {
|
|
3710
|
+
grant: options.subagentGrant,
|
|
3711
|
+
depth: options.delegationDepth ?? options.parentLink?.depth ?? 0
|
|
3712
|
+
}), Context.add(ToolBroker, closedToolBroker));
|
|
3713
|
+
return started.pipe(Stream.concat(deadline), Stream.catch((error) => {
|
|
3714
|
+
return Stream.fromEffect(Effect.gen(function* () {
|
|
3715
|
+
if (error instanceof AgentApprovalPending || error instanceof AgentChildPending) return RunSuspended.make({
|
|
3716
|
+
...yield* terminalEventBase(context),
|
|
3717
|
+
reason: error.message,
|
|
3718
|
+
...yield* usageReportOf(context)
|
|
3719
|
+
});
|
|
3720
|
+
return RunFailed.make({
|
|
3721
|
+
...yield* terminalEventBase(context),
|
|
3722
|
+
...yield* usageReportOf(context),
|
|
3723
|
+
errorTag: errorTag(error),
|
|
3724
|
+
message: errorMessage(error)
|
|
3725
|
+
});
|
|
3726
|
+
})).pipe(Stream.concat(Stream.fail(error)));
|
|
3727
|
+
}), Stream.withSpan("AgentRuntime.run", { attributes: {
|
|
3728
|
+
agentId: context.agentId,
|
|
3729
|
+
runId: context.runId
|
|
3730
|
+
} }), Stream.provide(engineToolServices), Stream.provideService(ContextCompactor, compactor));
|
|
3731
|
+
}));
|
|
3732
|
+
const finalized = options.input?.end === void 0 ? interpreted : interpreted.pipe(Stream.ensuring(options.input.end()));
|
|
3733
|
+
const events = (model === void 0 ? finalized : finalized.pipe(Stream.provide(model, { local: true }))).pipe(Stream.provide(ToolSpanTelemetry.layer), Stream.provide(programmaticAuthorization));
|
|
3734
|
+
if (retained === void 0 && onCompleted === void 0) return events;
|
|
3735
|
+
let completed;
|
|
3736
|
+
return events.pipe(Stream.filter((event) => {
|
|
3737
|
+
if (event._tag !== "RunCompleted") return true;
|
|
3738
|
+
completed = event;
|
|
3739
|
+
return false;
|
|
3740
|
+
}), Stream.scoped, Stream.concat(Stream.unwrap(Effect.gen(function* () {
|
|
3741
|
+
const terminal = completed;
|
|
3742
|
+
if (terminal === void 0) return yield* ModelProtocolError.make({ message: "Agent stream ended without RunCompleted" });
|
|
3743
|
+
return Stream.fromEffect(Effect.gen(function* () {
|
|
3744
|
+
if (onCompleted !== void 0) yield* onCompleted(terminal);
|
|
3745
|
+
if (retained !== void 0) yield* retained.commit(terminal);
|
|
3746
|
+
return terminal;
|
|
3747
|
+
})).pipe(Stream.catch((error) => Stream.make(RunFailed.make({
|
|
3748
|
+
eventVersion: terminal.eventVersion,
|
|
3749
|
+
threadId: terminal.threadId,
|
|
3750
|
+
runId: terminal.runId,
|
|
3751
|
+
agentId: terminal.agentId,
|
|
3752
|
+
usage: terminal.usage,
|
|
3753
|
+
delegatedUsage: terminal.delegatedUsage,
|
|
3754
|
+
sequence: terminal.sequence,
|
|
3755
|
+
timestamp: terminal.timestamp,
|
|
3756
|
+
errorTag: errorTag(error),
|
|
3757
|
+
message: errorMessage(error)
|
|
3758
|
+
})).pipe(Stream.concat(Stream.fail(error)))));
|
|
3759
|
+
}))));
|
|
3760
|
+
}));
|
|
3761
|
+
}
|
|
3762
|
+
/** Validate the terminal result before the stream commits history and publishes completion. */
|
|
3763
|
+
const reduceRunEvents = (agent, events) => Effect.gen(function* () {
|
|
3764
|
+
let result;
|
|
3765
|
+
yield* Stream.runDrain(events((completed) => Effect.gen(function* () {
|
|
3766
|
+
const candidateOutput = completed.output;
|
|
3767
|
+
const output = yield* Schema.decodeUnknownEffect(agent.definition.output)(candidateOutput).pipe(Effect.mapError((cause) => AgentOutputError.make({ message: cause.message })));
|
|
3768
|
+
const declaration = agent.definition.runDisposition;
|
|
3769
|
+
const runDisposition = completed.runDisposition === void 0 ? void 0 : completed.finishReason === "budget-exhausted" ? yield* ModelProtocolError.make({ message: "A budget-exhausted RunCompleted event cannot declare a run disposition" }) : declaration === void 0 ? yield* ModelProtocolError.make({ message: "RunCompleted declared a run disposition without a definition-owned Schema" }) : yield* decodeRunDisposition(agent, completed.runDisposition);
|
|
3770
|
+
result = {
|
|
3771
|
+
output,
|
|
3772
|
+
threadId: completed.threadId,
|
|
3773
|
+
runId: completed.runId,
|
|
3774
|
+
turns: completed.turns,
|
|
3775
|
+
finishReason: completed.finishReason,
|
|
3776
|
+
...completed.exhausted !== void 0 ? { exhausted: completed.exhausted } : {},
|
|
3777
|
+
...runDisposition === void 0 ? {} : { runDisposition: completed.runDisposition },
|
|
3778
|
+
...completed.usage === void 0 ? {} : { usage: completed.usage },
|
|
3779
|
+
...completed.delegatedUsage === void 0 ? {} : { delegatedUsage: completed.delegatedUsage }
|
|
3780
|
+
};
|
|
3781
|
+
})));
|
|
3782
|
+
if (result === void 0) return yield* ModelProtocolError.make({ message: "Agent stream ended without RunCompleted" });
|
|
3783
|
+
return result;
|
|
3784
|
+
});
|
|
3785
|
+
/**
|
|
3786
|
+
* Complete execution and close run-owned resources before returning the decoded result.
|
|
3787
|
+
* Caller-contributed requirements, including Scope, remain visible; services supplied by
|
|
3788
|
+
* application Layers retain their application's lifetime.
|
|
3789
|
+
*/
|
|
3790
|
+
const runProgram = Effect.fn("AgentRuntime.run")(function* (agent, events) {
|
|
3791
|
+
return yield* reduceRunEvents(agent, events);
|
|
3792
|
+
});
|
|
3793
|
+
const startProgram = Effect.fn("AgentRuntime.start")(function* (agent, events, options) {
|
|
3794
|
+
yield* Scope.Scope;
|
|
3795
|
+
const bufferLimits = Object.freeze(effectiveRunBufferLimits(options.bufferLimits));
|
|
3796
|
+
const executionOptionDescriptors = {
|
|
3797
|
+
...Object.getOwnPropertyDescriptors(options),
|
|
3798
|
+
bufferLimits: {
|
|
3799
|
+
configurable: false,
|
|
3800
|
+
enumerable: true,
|
|
3801
|
+
value: bufferLimits,
|
|
3802
|
+
writable: false
|
|
3803
|
+
}
|
|
3804
|
+
};
|
|
3805
|
+
const executionOptions = Object.create(Object.getPrototypeOf(options), executionOptionDescriptors);
|
|
3806
|
+
const captured = [];
|
|
3807
|
+
const observationCapacity = bufferLimits.maxRunEvents + 1;
|
|
3808
|
+
const pubsub = yield* PubSub.dropping({
|
|
3809
|
+
capacity: observationCapacity,
|
|
3810
|
+
replay: observationCapacity
|
|
3811
|
+
});
|
|
3812
|
+
yield* Effect.addFinalizer(() => PubSub.shutdown(pubsub));
|
|
3813
|
+
let readUsage = Effect.succeed(RunUsageReport.make({
|
|
3814
|
+
usage: emptyRunTotals(),
|
|
3815
|
+
delegatedUsage: emptyRunTotals()
|
|
3816
|
+
}));
|
|
3817
|
+
const fiber = yield* reduceRunEvents(agent, (onCompleted) => events(executionOptions, onCompleted, (read) => {
|
|
3818
|
+
readUsage = read;
|
|
3819
|
+
}).pipe(Stream.tap((event) => Effect.suspend(() => {
|
|
3820
|
+
captured.push(event);
|
|
3821
|
+
return PubSub.publish(pubsub, [event]);
|
|
3822
|
+
})))).pipe(Effect.onExit(() => Effect.suspend(() => readUsage).pipe(Effect.flatMap((report) => Effect.sync(() => {
|
|
3823
|
+
readUsage = Effect.succeed(report);
|
|
3824
|
+
})))), Effect.ensuring(PubSub.publish(pubsub, Exit.void))).pipe(Effect.forkScoped);
|
|
3825
|
+
return {
|
|
3826
|
+
await: Fiber.join(fiber),
|
|
3827
|
+
events: Fiber.await(fiber).pipe(Effect.andThen(Effect.sync(() => captured.slice()))),
|
|
3828
|
+
observe: Stream.fromPubSubTake(pubsub),
|
|
3829
|
+
usageReport: Effect.suspend(() => readUsage)
|
|
3830
|
+
};
|
|
3831
|
+
});
|
|
3832
|
+
/** Decode external input before instructions or model execution. */
|
|
3833
|
+
const streamUnknown = (agent, input, options) => streamWithCompletion(agent, input, options).pipe(Stream.provide(Layer.mergeAll(ModelUsageAccounting.layerEphemeral, AgentUpdateAcceptance.layerEphemeral)));
|
|
3834
|
+
/**
|
|
3835
|
+
* Host interpreter entry point with Attempt-local usage accounting and update acceptance in R.
|
|
3836
|
+
* Durable coordinators provide these services alongside their recovery hooks;
|
|
3837
|
+
* ordinary callers use streamUnknown's explicit ephemeral composition.
|
|
3838
|
+
*/
|
|
3839
|
+
const streamWithUsageAccountingUnknown = (agent, input, options) => streamWithCompletion(agent, input, options);
|
|
3840
|
+
/** Accept schema-encoded input, retaining runtime validation. Use streamUnknown for external data. */
|
|
3841
|
+
const stream = (agent, input, options) => streamUnknown(agent, input, options);
|
|
3842
|
+
function runUnknown(agent, input, options = {}) {
|
|
3843
|
+
const program = "definition" in agent ? agent : { definition: agent };
|
|
3844
|
+
return runProgram(program, (onCompleted) => streamWithCompletion(agent, input, options, onCompleted).pipe(Stream.provide(Layer.mergeAll(ModelUsageAccounting.layerEphemeral, AgentUpdateAcceptance.layerEphemeral))));
|
|
3845
|
+
}
|
|
3846
|
+
/** Accept schema-encoded input, retaining runtime validation. Use runUnknown for external data. */
|
|
3847
|
+
const run = (agent, input, options) => runUnknown(agent, input, options);
|
|
3848
|
+
function startUnknown(agent, input, options = {}) {
|
|
3849
|
+
const program = "definition" in agent ? agent : { definition: agent };
|
|
3850
|
+
return startProgram(program, (executionOptions, onCompleted, onUsage) => streamWithCompletion(agent, input, executionOptions, onCompleted, onUsage).pipe(Stream.provide(Layer.mergeAll(ModelUsageAccounting.layerEphemeral, AgentUpdateAcceptance.layerEphemeral))), options);
|
|
3851
|
+
}
|
|
3852
|
+
/** Accept schema-encoded input, retaining runtime validation. Use startUnknown for external data. */
|
|
3853
|
+
const start = (agent, input, options) => startUnknown(agent, input, options);
|
|
3854
|
+
/**
|
|
3855
|
+
* Fail-closed Run-level `RunEventSink` default. Each Tool batch shadows it
|
|
3856
|
+
* with a live sink; any emission outside an active Tool batch fails with the
|
|
3857
|
+
* same typed error as emitting after a batch settled.
|
|
3858
|
+
*/
|
|
3859
|
+
const closedRunEventSink = { emit: (payload) => Effect.fail(RunEventSinkClosedError.make({ message: `Subagent event ${payload._tag} was emitted outside an active Tool batch` })) };
|
|
3860
|
+
/**
|
|
3861
|
+
* Fail-closed Run-level `DurableStep` default. Every executable Tool Call
|
|
3862
|
+
* shadows it with a live per-call service; a Step executed outside an active
|
|
3863
|
+
* Tool Call fails typed instead of silently running unrecorded.
|
|
3864
|
+
*/
|
|
3865
|
+
const closedDurableStep = { do: (name) => Effect.fail(DurableStepError.make({
|
|
3866
|
+
stepName: name,
|
|
3867
|
+
reason: "no-active-tool-call",
|
|
3868
|
+
message: "Durable Step was executed outside an active Tool Call"
|
|
3869
|
+
})) };
|
|
3870
|
+
/**
|
|
3871
|
+
* Fail-closed Run-level `ToolBroker` default. Every executable Tool Call
|
|
3872
|
+
* shadows it with a live per-call service bound to that call's identity and
|
|
3873
|
+
* held permit; opening a pass outside an active Tool Call fails typed.
|
|
3874
|
+
*/
|
|
3875
|
+
const closedToolBroker = { openPass: () => Effect.fail(ToolBrokerUnavailableError.make({ message: "The programmatic Tool broker was used outside an active Tool Call" })) };
|
|
3876
|
+
const programmaticOutcomeError = (index, tag, message) => ({
|
|
3877
|
+
_tag: "ProgrammaticCallError",
|
|
3878
|
+
index,
|
|
3879
|
+
errorTag: tag,
|
|
3880
|
+
message
|
|
3881
|
+
});
|
|
3882
|
+
/** A call's typed failure captured as a value so defects stay defects. */
|
|
3883
|
+
var BrokerCallFailure = class {
|
|
3884
|
+
error;
|
|
3885
|
+
cause;
|
|
3886
|
+
constructor(error, cause) {
|
|
3887
|
+
this.error = error;
|
|
3888
|
+
this.cause = cause;
|
|
3889
|
+
}
|
|
3890
|
+
};
|
|
3891
|
+
/** Run-owned authorization with typed override failures preserved for broker projection. */
|
|
3892
|
+
var ProgrammaticToolAuthorization = class extends Context.Service()("@effect-agent/engine/internal/ProgrammaticToolAuthorization") {};
|
|
3893
|
+
/**
|
|
3894
|
+
* Measure one started programmatic handler with the same canonical span semantics as a
|
|
3895
|
+
* model-declared handler. A private marker gives value-level failures a failed exported status;
|
|
3896
|
+
* the marker is removed before the broker outcome returns, while an original Effect Cause is
|
|
3897
|
+
* restored unchanged outside the span.
|
|
3898
|
+
*/
|
|
3899
|
+
const stripProgrammaticToolSpanFailure = (cause, marker) => {
|
|
3900
|
+
let found = false;
|
|
3901
|
+
const residual = [];
|
|
3902
|
+
for (const reason of cause.reasons) if (Cause.isFailReason(reason)) {
|
|
3903
|
+
if (reason.error === marker) found = true;
|
|
3904
|
+
else residual.push(Cause.makeDieReason(reason.error));
|
|
3905
|
+
} else residual.push(reason);
|
|
3906
|
+
return {
|
|
3907
|
+
found,
|
|
3908
|
+
residual: Cause.fromReasons(residual)
|
|
3909
|
+
};
|
|
3910
|
+
};
|
|
3911
|
+
const measureProgrammaticToolCall = (telemetry, descriptor, effect) => Effect.suspend(() => {
|
|
3912
|
+
const marker = ToolSpanFailure.marker();
|
|
3913
|
+
let terminalResult;
|
|
3914
|
+
let propagatedFailure;
|
|
3915
|
+
const measured = Effect.exit(effect).pipe(Effect.flatMap((exit) => {
|
|
3916
|
+
if (Exit.isFailure(exit)) {
|
|
3917
|
+
if (exit.cause.reasons.length > 0 && exit.cause.reasons.every(Cause.isInterruptReason)) return Effect.failCause(exit.cause);
|
|
3918
|
+
propagatedFailure = exit.cause;
|
|
3919
|
+
return isolateToolDerivative(terminalToolTelemetry(descriptor, "failure", marker, "propagated")).pipe(Effect.andThen(Effect.fail(marker)));
|
|
3920
|
+
}
|
|
3921
|
+
terminalResult = exit.value;
|
|
3922
|
+
const outcome = exit.value._tag === "ProgrammaticCallSuccess" ? "success" : "failure";
|
|
3923
|
+
return isolateToolDerivative(terminalToolTelemetry(descriptor, outcome, outcome === "failure" ? marker : void 0, outcome === "failure" ? "returned-to-caller" : void 0)).pipe(Effect.andThen(outcome === "failure" ? Effect.fail(marker) : Effect.succeed(exit.value)));
|
|
3924
|
+
}), Effect.withSpan(`execute_tool ${descriptor.toolName}`, {
|
|
3925
|
+
kind: "internal",
|
|
3926
|
+
attributes: toolTelemetryAttributes(descriptor)
|
|
3927
|
+
}));
|
|
3928
|
+
return telemetry.isolateEffectSpanLifecycle(measured).pipe(Effect.catchCause((cause) => {
|
|
3929
|
+
const { found, residual } = stripProgrammaticToolSpanFailure(cause, marker);
|
|
3930
|
+
const restored = propagatedFailure === void 0 ? residual : Cause.combine(propagatedFailure, residual);
|
|
3931
|
+
if (!found) return Effect.failCause(restored);
|
|
3932
|
+
if (restored.reasons.length > 0) return Effect.failCause(restored);
|
|
3933
|
+
return terminalResult === void 0 ? Effect.die("Programmatic Tool telemetry completed without a terminal result") : Effect.succeed(terminalResult);
|
|
3934
|
+
}));
|
|
3935
|
+
});
|
|
3936
|
+
const brokerSerializeJson = (value) => {
|
|
3937
|
+
try {
|
|
3938
|
+
const text = JSON.stringify(value);
|
|
3939
|
+
return text === void 0 ? void 0 : {
|
|
3940
|
+
text,
|
|
3941
|
+
bytes: utf8ByteLength(text)
|
|
3942
|
+
};
|
|
3943
|
+
} catch {
|
|
3944
|
+
return;
|
|
3945
|
+
}
|
|
3946
|
+
};
|
|
3947
|
+
const emptyProgrammaticUsage = Response.Usage.make({
|
|
3948
|
+
inputTokens: {},
|
|
3949
|
+
outputTokens: {}
|
|
3950
|
+
});
|
|
3951
|
+
/** Hostile values can throw from trap getters mid-decode; stay fail-closed. */
|
|
3952
|
+
const brokerDecodeJson = (value) => {
|
|
3953
|
+
try {
|
|
3954
|
+
return Schema.decodeUnknownOption(Schema.Json)(value);
|
|
3955
|
+
} catch {
|
|
3956
|
+
return Option.none();
|
|
3957
|
+
}
|
|
3958
|
+
};
|
|
3959
|
+
/**
|
|
3960
|
+
* Live per-outer-call `ToolBroker` (runtime spec §12.1; RUN-016, RUN-017).
|
|
3961
|
+
* The pass executes under the outer Tool Call's already-held scheduling
|
|
3962
|
+
* permit — invocations run in caller-owned structured fibers and acquire a finite
|
|
3963
|
+
* per-pass Semaphore, never the outer batch permit again. Execution indices are
|
|
3964
|
+
* allocated in invocation order before preflight, with gaps for rejected calls,
|
|
3965
|
+
* and every started call consumes the Run's Tool-call budgets before its
|
|
3966
|
+
* handler is invoked. Inner calls produce no Run events and no Canonical
|
|
3967
|
+
* Records. Trusted applications may explicitly observe non-propagating failures (RUN-036).
|
|
3968
|
+
*/
|
|
3969
|
+
const makeToolBrokerService = Effect.fnUntraced(function* (binding) {
|
|
3970
|
+
const toolSpanTelemetry = yield* ToolSpanTelemetry;
|
|
3971
|
+
const authorization = yield* ProgrammaticToolAuthorization;
|
|
3972
|
+
const lifecycle = { closed: false };
|
|
3973
|
+
const observer = binding.context.toolFailureObserver;
|
|
3974
|
+
const preflightObserver = observer === void 0 ? void 0 : {
|
|
3975
|
+
observer,
|
|
3976
|
+
permits: Semaphore.makeUnsafe(1)
|
|
3977
|
+
};
|
|
3978
|
+
return {
|
|
3979
|
+
service: { openPass: (toolkit, passOptions) => Effect.gen(function* () {
|
|
3980
|
+
const inheritedAuthority = yield* CurrentSubagentAuthority;
|
|
3981
|
+
if (lifecycle.closed) return yield* ToolBrokerUnavailableError.make({ message: "The outer Tool Call for this broker has already settled" });
|
|
3982
|
+
if (passOptions === void 0 || !Number.isSafeInteger(passOptions.maxResultBytes) || passOptions.maxResultBytes <= 0) return yield* ToolBrokerConfigurationError.make({ message: `maxResultBytes must be a positive safe integer; received ${String(passOptions?.maxResultBytes)}` });
|
|
3983
|
+
const handlerServices = yield* Effect.context();
|
|
3984
|
+
const concurrency = passOptions.concurrency ?? 4;
|
|
3985
|
+
if (!Number.isSafeInteger(concurrency) || concurrency < 1 || concurrency > 64) return yield* ToolBrokerConfigurationError.make({ message: "concurrency must be an integer between 1 and 64" });
|
|
3986
|
+
const permits = yield* Semaphore.make(concurrency);
|
|
3987
|
+
const records = [];
|
|
3988
|
+
const preflightFailure = (input, kind, tag, message, cause) => {
|
|
3989
|
+
const outcome = programmaticOutcomeError(void 0, tag, message);
|
|
3990
|
+
if (preflightObserver === void 0) return Effect.succeed(outcome);
|
|
3991
|
+
return deliverToolFailure(preflightObserver.observer, {
|
|
3992
|
+
_tag: "ProgrammaticPreflightFailure",
|
|
3993
|
+
agentId: binding.context.agentId,
|
|
3994
|
+
threadId: binding.context.threadId,
|
|
3995
|
+
runId: binding.context.runId,
|
|
3996
|
+
turnId: binding.turnId,
|
|
3997
|
+
parentToolCallId: binding.outerToolCallId,
|
|
3998
|
+
toolName: input.toolName,
|
|
3999
|
+
...hasTool(toolkit.tools, input.toolName) ? { executionClass: getToolExecutionClass(toolkit.tools[input.toolName]) } : {},
|
|
4000
|
+
kind,
|
|
4001
|
+
tag,
|
|
4002
|
+
message: toolFailureMessage(message),
|
|
4003
|
+
...cause === void 0 ? {} : { cause }
|
|
4004
|
+
}).pipe(preflightObserver.permits.withPermits(1), Effect.as(outcome));
|
|
4005
|
+
};
|
|
4006
|
+
const body = (input, sequenceIndex) => Effect.gen(function* () {
|
|
4007
|
+
if (!hasTool(toolkit.tools, input.toolName)) return yield* preflightFailure(input, "infrastructure", "ProgrammaticToolUnknownError", `Tool ${input.toolName} is not part of this pass's allowlisted Toolkit`);
|
|
4008
|
+
const tool = toolkit.tools[input.toolName];
|
|
4009
|
+
if (Object.values(binding.context.definition.toolkit.tools).some((outer) => Context.get(outer.annotations, AdditionalToolCatalog).some((entry) => entry.tool.name === input.toolName)) && !binding.context.toolCatalog.some((entry) => entry.kind === "code-mode" && entry.tool.name === input.toolName)) return yield* preflightFailure(input, "infrastructure", "ProgrammaticToolAuthorizationDenied", "Tool is excluded by the current host visibility policy");
|
|
4010
|
+
if (!isSubagentToolAllowed(inheritedAuthority?.grant, inheritedAuthority?.depth ?? 0, input.toolName, tool.annotations)) return yield* preflightFailure(input, "infrastructure", "ProgrammaticToolAuthorizationDenied", "Tool exceeds the inherited subagent grant");
|
|
4011
|
+
if (Context.get(tool.annotations, ContextRolloverTool)) return yield* preflightFailure(input, "infrastructure", "ProgrammaticContextRolloverUnsupportedError", "Context rollover requires a direct, singleton model Tool Call");
|
|
4012
|
+
const approval = tool.needsApproval;
|
|
4013
|
+
if (approval !== void 0 && approval !== false) return yield* preflightFailure(input, "infrastructure", "ProgrammaticApprovalUnsupportedError", `Tool ${input.toolName} requires approval; approval-requiring Tools never start programmatically in the ephemeral slice`);
|
|
4014
|
+
const invalidParameters = yield* Schema.decodeUnknownEffect(tool.parametersSchema)(input.encodedArguments).pipe(Effect.map(() => void 0), Effect.catchCauseFilter(Cause.findError, (error, cause) => preflightFailure(input, "protocol", "ModelProtocolError", `Invalid parameters for Tool ${input.toolName}: ${error.message}`, cause)));
|
|
4015
|
+
if (invalidParameters !== void 0) return invalidParameters;
|
|
4016
|
+
const handleId = ToolCallId.make(`${binding.outerToolCallId}#${sequenceIndex}`);
|
|
4017
|
+
const denied = yield* authorization.authorize({
|
|
4018
|
+
threadId: binding.context.threadId,
|
|
4019
|
+
runId: binding.context.runId,
|
|
4020
|
+
turnId: binding.turnId,
|
|
4021
|
+
turn: binding.turn,
|
|
4022
|
+
input: binding.context.input,
|
|
4023
|
+
programmatic: {
|
|
4024
|
+
parentToolCallId: binding.outerToolCallId,
|
|
4025
|
+
sequenceIndex
|
|
4026
|
+
},
|
|
4027
|
+
call: {
|
|
4028
|
+
toolCallId: handleId,
|
|
4029
|
+
toolName: input.toolName,
|
|
4030
|
+
parameters: input.encodedArguments,
|
|
4031
|
+
executionClass: getToolExecutionClass(tool),
|
|
4032
|
+
executionKind: getToolExecutionKind(tool.annotations)
|
|
4033
|
+
}
|
|
4034
|
+
}).pipe(Effect.flatMap((decision) => decision._tag === "allowed" ? Effect.succeed(void 0) : preflightFailure(input, "infrastructure", "ProgrammaticToolAuthorizationDenied", decision.reason)), Effect.catch((failure) => preflightFailure(input, "infrastructure", errorTag(failure.error), errorMessage(failure.error), failure.cause)));
|
|
4035
|
+
if (denied !== void 0) return denied;
|
|
4036
|
+
const rejected = yield* binding.context.policyReservations.withPermit(Effect.gen(function* () {
|
|
4037
|
+
if (binding.declaredToolCalls + binding.context.programmaticToolCalls + 1 > binding.maxToolCalls) return yield* preflightFailure(input, "infrastructure", "AgentPolicyError", `Agent exceeded its ${binding.maxToolCalls} Tool Call limit`);
|
|
4038
|
+
if (binding.budget !== void 0) {
|
|
4039
|
+
const exhausted = yield* provideHookServices(binding.budget.consume({
|
|
4040
|
+
modelCalls: 0,
|
|
4041
|
+
inputTokens: 0,
|
|
4042
|
+
outputTokens: 0,
|
|
4043
|
+
totalTokens: 0,
|
|
4044
|
+
toolCalls: 1,
|
|
4045
|
+
costMicrousd: 0,
|
|
4046
|
+
usage: emptyProgrammaticUsage
|
|
4047
|
+
}), binding.hookServices).pipe(Effect.map(() => void 0), Effect.catchCauseFilter(Cause.findError, (error, cause) => Effect.succeed({
|
|
4048
|
+
error,
|
|
4049
|
+
cause
|
|
4050
|
+
})));
|
|
4051
|
+
if (exhausted !== void 0) return yield* preflightFailure(input, "infrastructure", errorTag(exhausted.error), errorMessage(exhausted.error), exhausted.cause);
|
|
4052
|
+
}
|
|
4053
|
+
binding.context.programmaticToolCalls += 1;
|
|
4054
|
+
if (binding.reservePolicyUsage !== void 0) return yield* provideHookServices(binding.reservePolicyUsage({
|
|
4055
|
+
programmaticToolCalls: binding.context.programmaticToolCalls,
|
|
4056
|
+
finalizationUsed: binding.context.finalizationUsed
|
|
4057
|
+
}), binding.hookServices).pipe(Effect.as(void 0), Effect.catchCauseFilter(Cause.findError, (error, cause) => preflightFailure(input, "infrastructure", errorTag(error), errorMessage(error), cause)));
|
|
4058
|
+
}));
|
|
4059
|
+
if (rejected !== void 0) return rejected;
|
|
4060
|
+
const index = sequenceIndex;
|
|
4061
|
+
records[sequenceIndex] = {
|
|
4062
|
+
sequenceIndex,
|
|
4063
|
+
toolName: input.toolName,
|
|
4064
|
+
status: "uncertain"
|
|
4065
|
+
};
|
|
4066
|
+
const executionClass = getToolExecutionClass(tool);
|
|
4067
|
+
let failureObservation;
|
|
4068
|
+
const startedFailure = (kind, tag, message) => {
|
|
4069
|
+
const outcome = programmaticOutcomeError(index, tag, message);
|
|
4070
|
+
if (observer === void 0) return Effect.succeed(outcome);
|
|
4071
|
+
failureObservation = {
|
|
4072
|
+
_tag: "ProgrammaticToolFailure",
|
|
4073
|
+
agentId: binding.context.agentId,
|
|
4074
|
+
threadId: binding.context.threadId,
|
|
4075
|
+
runId: binding.context.runId,
|
|
4076
|
+
turnId: binding.turnId,
|
|
4077
|
+
toolCallId: handleId,
|
|
4078
|
+
parentToolCallId: binding.outerToolCallId,
|
|
4079
|
+
sequenceIndex: index,
|
|
4080
|
+
toolName: input.toolName,
|
|
4081
|
+
executionClass,
|
|
4082
|
+
kind,
|
|
4083
|
+
tag,
|
|
4084
|
+
message: toolFailureMessage(message)
|
|
4085
|
+
};
|
|
4086
|
+
return Effect.succeed(outcome);
|
|
4087
|
+
};
|
|
4088
|
+
const telemetryDescriptor = {
|
|
4089
|
+
context: binding.context,
|
|
4090
|
+
turnId: binding.turnId,
|
|
4091
|
+
toolCallId: handleId,
|
|
4092
|
+
toolName: input.toolName,
|
|
4093
|
+
executionClass,
|
|
4094
|
+
invocationKind: "programmatic",
|
|
4095
|
+
failureMode: tool.failureMode,
|
|
4096
|
+
parentToolCallId: binding.outerToolCallId,
|
|
4097
|
+
sequenceIndex: index
|
|
4098
|
+
};
|
|
4099
|
+
const recorded = measureProgrammaticToolCall(toolSpanTelemetry, telemetryDescriptor, Effect.gen(function* () {
|
|
4100
|
+
yield* Effect.logDebug("agent programmatic tool handler started").pipe(Effect.annotateLogs({
|
|
4101
|
+
agentId: binding.context.agentId,
|
|
4102
|
+
runId: binding.context.runId,
|
|
4103
|
+
turnId: binding.turnId,
|
|
4104
|
+
toolCallId: handleId,
|
|
4105
|
+
parentToolCallId: binding.outerToolCallId,
|
|
4106
|
+
toolName: input.toolName,
|
|
4107
|
+
sequenceIndex: index
|
|
4108
|
+
}));
|
|
4109
|
+
yield* Metric.update(toolCounter, 1);
|
|
4110
|
+
let terminal;
|
|
4111
|
+
let resultAfterTerminal = false;
|
|
4112
|
+
const handlerFailed = yield* Stream.unwrap(toolSpanTelemetry.isolateToolkitHandle(toolkit.handle(input.toolName, input.encodedArguments, handleId))).pipe(Stream.runForEach((result) => Effect.sync(() => {
|
|
4113
|
+
if (terminal !== void 0) {
|
|
4114
|
+
resultAfterTerminal = true;
|
|
4115
|
+
return;
|
|
4116
|
+
}
|
|
4117
|
+
if (!result.preliminary) terminal = {
|
|
4118
|
+
encodedResult: result.encodedResult,
|
|
4119
|
+
isFailure: result.isFailure,
|
|
4120
|
+
tag: observer === void 0 || !result.isFailure ? void 0 : errorTag(result.result)
|
|
4121
|
+
};
|
|
4122
|
+
})), Effect.map(() => void 0), Effect.catchCauseFilter(Cause.findError, (error, cause) => Effect.succeed(new BrokerCallFailure(error, cause))));
|
|
4123
|
+
if (handlerFailed instanceof BrokerCallFailure) {
|
|
4124
|
+
const tag = errorTag(handlerFailed.error);
|
|
4125
|
+
if (observer !== void 0) failureObservation = {
|
|
4126
|
+
_tag: "ProgrammaticToolFailure",
|
|
4127
|
+
agentId: binding.context.agentId,
|
|
4128
|
+
threadId: binding.context.threadId,
|
|
4129
|
+
runId: binding.context.runId,
|
|
4130
|
+
turnId: binding.turnId,
|
|
4131
|
+
toolCallId: handleId,
|
|
4132
|
+
parentToolCallId: binding.outerToolCallId,
|
|
4133
|
+
sequenceIndex: index,
|
|
4134
|
+
toolName: input.toolName,
|
|
4135
|
+
executionClass,
|
|
4136
|
+
kind: "handler-error",
|
|
4137
|
+
tag,
|
|
4138
|
+
cause: handlerFailed.cause
|
|
4139
|
+
};
|
|
4140
|
+
return programmaticOutcomeError(index, tag, errorMessage(handlerFailed.error));
|
|
4141
|
+
}
|
|
4142
|
+
if (resultAfterTerminal) return yield* startedFailure("protocol", "ModelProtocolError", `Tool Call ${handleId} produced more than one terminal result`);
|
|
4143
|
+
if (terminal === void 0) return yield* startedFailure("protocol", "ModelProtocolError", `Tool Call ${handleId} completed without a terminal result`);
|
|
4144
|
+
if (terminal.isFailure) {
|
|
4145
|
+
if (observer !== void 0) failureObservation = {
|
|
4146
|
+
_tag: "ProgrammaticToolFailure",
|
|
4147
|
+
agentId: binding.context.agentId,
|
|
4148
|
+
threadId: binding.context.threadId,
|
|
4149
|
+
runId: binding.context.runId,
|
|
4150
|
+
turnId: binding.turnId,
|
|
4151
|
+
toolCallId: handleId,
|
|
4152
|
+
parentToolCallId: binding.outerToolCallId,
|
|
4153
|
+
sequenceIndex: index,
|
|
4154
|
+
toolName: input.toolName,
|
|
4155
|
+
executionClass,
|
|
4156
|
+
kind: "declared-failure",
|
|
4157
|
+
tag: terminal.tag ?? "UnknownError"
|
|
4158
|
+
};
|
|
4159
|
+
return {
|
|
4160
|
+
_tag: "ProgrammaticCallFailure",
|
|
4161
|
+
index,
|
|
4162
|
+
encodedResult: terminal.encodedResult
|
|
4163
|
+
};
|
|
4164
|
+
}
|
|
4165
|
+
if (Option.isNone(brokerDecodeJson(terminal.encodedResult))) return yield* startedFailure("protocol", "ModelProtocolError", `Tool ${input.toolName} produced a success encoding outside JSON`);
|
|
4166
|
+
let encodedResult = terminal.encodedResult;
|
|
4167
|
+
if (passOptions.redactResult !== void 0) {
|
|
4168
|
+
const redacted = brokerDecodeJson(yield* passOptions.redactResult(encodedResult));
|
|
4169
|
+
if (Option.isNone(redacted)) return yield* startedFailure("protocol", "ModelProtocolError", `The redacted result for Tool ${input.toolName} is outside the JSON surface`);
|
|
4170
|
+
encodedResult = redacted.value;
|
|
4171
|
+
}
|
|
4172
|
+
const snapshot = brokerSerializeJson(encodedResult);
|
|
4173
|
+
if (snapshot === void 0 || snapshot.bytes > passOptions.maxResultBytes) return yield* startedFailure("infrastructure", "ProgrammaticResultLimitError", `Tool ${input.toolName} result of ${snapshot?.bytes ?? "unencodable"} bytes exceeds the ${passOptions.maxResultBytes}-byte broker bound`);
|
|
4174
|
+
const owned = Schema.decodeOption(Schema.fromJsonString(Schema.Json))(snapshot.text);
|
|
4175
|
+
if (Option.isNone(owned)) return yield* startedFailure("protocol", "ModelProtocolError", `Tool ${input.toolName} produced a success encoding outside JSON`);
|
|
4176
|
+
return {
|
|
4177
|
+
_tag: "ProgrammaticCallSuccess",
|
|
4178
|
+
index,
|
|
4179
|
+
encodedResult: owned.value
|
|
4180
|
+
};
|
|
4181
|
+
})).pipe(Effect.tap((outcome) => Effect.sync(() => {
|
|
4182
|
+
records[sequenceIndex] = {
|
|
4183
|
+
sequenceIndex,
|
|
4184
|
+
toolName: input.toolName,
|
|
4185
|
+
status: outcome._tag === "ProgrammaticCallSuccess" ? "succeeded" : outcome._tag === "ProgrammaticCallFailure" ? "failed" : "uncertain",
|
|
4186
|
+
...outcome._tag === "ProgrammaticCallError" ? { errorTag: outcome.errorTag } : {}
|
|
4187
|
+
};
|
|
4188
|
+
})));
|
|
4189
|
+
return yield* observer === void 0 ? recorded : recorded.pipe(Effect.tap(() => failureObservation === void 0 ? Effect.void : deliverToolFailure(observer, failureObservation)));
|
|
4190
|
+
}).pipe(Effect.provideContext(handlerServices));
|
|
4191
|
+
return {
|
|
4192
|
+
snapshot: Effect.sync(() => records.map((record) => ({ ...record }))),
|
|
4193
|
+
invoke: (input) => Effect.suspend(() => {
|
|
4194
|
+
const sequenceIndex = records.length;
|
|
4195
|
+
records.push({
|
|
4196
|
+
sequenceIndex,
|
|
4197
|
+
toolName: input.toolName,
|
|
4198
|
+
status: "not-started"
|
|
4199
|
+
});
|
|
4200
|
+
return permits.withPermit(Effect.suspend(() => lifecycle.closed ? preflightFailure(input, "infrastructure", "ToolBrokerUnavailableError", "The outer Tool Call for this pass has already settled") : body(input, sequenceIndex))).pipe(Effect.tap((outcome) => Effect.sync(() => {
|
|
4201
|
+
if (outcome._tag === "ProgrammaticCallError" && outcome.index === void 0) records[sequenceIndex] = {
|
|
4202
|
+
sequenceIndex,
|
|
4203
|
+
toolName: input.toolName,
|
|
4204
|
+
status: "not-started",
|
|
4205
|
+
errorTag: outcome.errorTag
|
|
4206
|
+
};
|
|
4207
|
+
})));
|
|
4208
|
+
})
|
|
4209
|
+
};
|
|
4210
|
+
}) },
|
|
4211
|
+
close: () => {
|
|
4212
|
+
lifecycle.closed = true;
|
|
4213
|
+
}
|
|
4214
|
+
};
|
|
4215
|
+
});
|
|
4216
|
+
/**
|
|
4217
|
+
* Ephemeral pass-through `DurableStep` provided when `RunOptions.durability`
|
|
4218
|
+
* is absent: each Step body executes exactly once in-process and nothing is
|
|
4219
|
+
* recorded. Durable Tools stay runnable on the ephemeral runtime with honest
|
|
4220
|
+
* (weaker) semantics — the durable claim attaches to the runtime, not the
|
|
4221
|
+
* Tool. Duplicate Step names remain the same typed identity conflict as under
|
|
4222
|
+
* the durable service so authoring bugs fail identically on both runtimes.
|
|
4223
|
+
*/
|
|
4224
|
+
const passthroughDurableStep = () => {
|
|
4225
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
4226
|
+
return { do: (name, _output, execute) => Effect.suspend(() => {
|
|
4227
|
+
if (usedNames.has(name)) return Effect.fail(DurableStepError.make({
|
|
4228
|
+
stepName: name,
|
|
4229
|
+
reason: "duplicate-step-name",
|
|
4230
|
+
message: "Durable Step name was reused within one Tool Call"
|
|
4231
|
+
}));
|
|
4232
|
+
usedNames.add(name);
|
|
4233
|
+
return execute;
|
|
4234
|
+
}) };
|
|
4235
|
+
};
|
|
4236
|
+
/**
|
|
4237
|
+
* Durable `DurableStep` service bound to one Tool Call over the coordinator's
|
|
4238
|
+
* `RunStepHook`.
|
|
4239
|
+
*
|
|
4240
|
+
* Semantics per durability §11: a committed result decodes through the
|
|
4241
|
+
* declared output Schema and returns without executing the body
|
|
4242
|
+
* (exactly-once-recorded); otherwise the body runs (at-least-once-executed —
|
|
4243
|
+
* a crash mid-body re-executes on the next Attempt and duplicate external
|
|
4244
|
+
* effects stay observable), the success is encoded through the Schema, and
|
|
4245
|
+
* only then committed. Failures are never recorded: a failing body fails the
|
|
4246
|
+
* Step call into the handler's error channel and re-entry re-executes it.
|
|
4247
|
+
* Step names must be deterministic and unique within one Tool Call; reuse is
|
|
4248
|
+
* a typed identity conflict because the second call would silently replay the
|
|
4249
|
+
* first call's recorded result. Hook failures and codec conflicts surface as
|
|
4250
|
+
* `DurableStepError` without widening the handler's error channel.
|
|
4251
|
+
*/
|
|
4252
|
+
/**
|
|
4253
|
+
* Provide a captured hook Context to a hook effect. TypeScript cannot reduce
|
|
4254
|
+
* the deferred conditional `Exclude<HookRequirements, HookRequirements>` on
|
|
4255
|
+
* an unresolved generic, so this private assertion pins the identity that
|
|
4256
|
+
* providing `Context<R>` to an `Effect<_, _, R>` leaves no requirements; it
|
|
4257
|
+
* never bypasses validation.
|
|
4258
|
+
*/
|
|
4259
|
+
const provideHookServices = (effect, services) => effect.pipe(Effect.provideContext(services));
|
|
4260
|
+
const makeDurableStepService = (toolCallId, hook, hookServices) => {
|
|
4261
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
4262
|
+
return { do: (name, output, execute) => Effect.gen(function* () {
|
|
4263
|
+
if (usedNames.has(name)) return yield* DurableStepError.make({
|
|
4264
|
+
toolCallId,
|
|
4265
|
+
stepName: name,
|
|
4266
|
+
reason: "duplicate-step-name",
|
|
4267
|
+
message: "Durable Step name was reused within one Tool Call"
|
|
4268
|
+
});
|
|
4269
|
+
usedNames.add(name);
|
|
4270
|
+
const key = {
|
|
4271
|
+
toolCallId,
|
|
4272
|
+
stepName: name
|
|
4273
|
+
};
|
|
4274
|
+
const recorded = yield* provideHookServices(hook.lookup(key), hookServices).pipe(Effect.mapError((cause) => DurableStepError.make({
|
|
4275
|
+
toolCallId,
|
|
4276
|
+
stepName: name,
|
|
4277
|
+
reason: "lookup-failed",
|
|
4278
|
+
message: "Durable Step lookup failed",
|
|
4279
|
+
cause
|
|
4280
|
+
})));
|
|
4281
|
+
if (Option.isSome(recorded)) return yield* Schema.decodeUnknownEffect(output)(recorded.value.encodedOutput).pipe(Effect.mapError(() => DurableStepError.make({
|
|
4282
|
+
toolCallId,
|
|
4283
|
+
stepName: name,
|
|
4284
|
+
reason: "recorded-result-invalid",
|
|
4285
|
+
message: "Recorded Durable Step result failed the declared output Schema"
|
|
4286
|
+
})));
|
|
4287
|
+
const value = yield* execute;
|
|
4288
|
+
const encodedOutput = yield* Schema.encodeEffect(output)(value).pipe(Effect.mapError(() => DurableStepError.make({
|
|
4289
|
+
toolCallId,
|
|
4290
|
+
stepName: name,
|
|
4291
|
+
reason: "output-encoding-failed",
|
|
4292
|
+
message: "Durable Step output failed the declared output Schema"
|
|
4293
|
+
})));
|
|
4294
|
+
yield* provideHookServices(hook.commit(key, encodedOutput), hookServices).pipe(Effect.mapError((cause) => DurableStepError.make({
|
|
4295
|
+
toolCallId,
|
|
4296
|
+
stepName: name,
|
|
4297
|
+
reason: "commit-failed",
|
|
4298
|
+
message: "Durable Step commit failed",
|
|
4299
|
+
cause
|
|
4300
|
+
})));
|
|
4301
|
+
return value;
|
|
4302
|
+
}) };
|
|
4303
|
+
};
|
|
4304
|
+
/**
|
|
4305
|
+
* Typed engine-owned suspension signal raised by a delegation Tool handler
|
|
4306
|
+
* through the per-batch `SubagentDurability` service when its durable child
|
|
4307
|
+
* is established but not settled (S2 plan §2). It is NOT a Tool failure: the
|
|
4308
|
+
* batch executor treats the raising call as "stays open" — no terminal
|
|
4309
|
+
* result, no `ToolCallFailed`, no batch failure policy, no sibling
|
|
4310
|
+
* interruption — and terminates the Run with `AgentChildPending` after every
|
|
4311
|
+
* non-waiting sibling handler settled.
|
|
4312
|
+
*/
|
|
4313
|
+
var ToolCallWaiting = class extends Schema.TaggedError()("ToolCallWaiting", {
|
|
4314
|
+
toolCallId: ToolCallId,
|
|
4315
|
+
childThreadId: ThreadId,
|
|
4316
|
+
childSubmissionId: SubmissionId,
|
|
4317
|
+
childRunId: RunId,
|
|
4318
|
+
receiptId: ReceiptId,
|
|
4319
|
+
message: Schema.String
|
|
4320
|
+
}) {};
|
|
4321
|
+
/**
|
|
4322
|
+
* The Run suspended `waitingForChild`: at least one durable delegation call
|
|
4323
|
+
* of the last Tool batch is waiting on its attached child.
|
|
4324
|
+
* Mirrors `AgentApprovalPending`: the engine emits
|
|
4325
|
+
* `RunSuspended` and then fails the Run stream with this error; the durable
|
|
4326
|
+
* coordinator catches it and ends the Attempt's ownership period without
|
|
4327
|
+
* settling. `children` is listed in declaration order, deterministically.
|
|
4328
|
+
*/
|
|
4329
|
+
var AgentChildPending = class extends Schema.TaggedError()("AgentChildPending", {
|
|
4330
|
+
children: Schema.NonEmptyArray(Schema.Struct({
|
|
4331
|
+
toolCallId: ToolCallId,
|
|
4332
|
+
childThreadId: ThreadId,
|
|
4333
|
+
childSubmissionId: SubmissionId,
|
|
4334
|
+
childRunId: RunId
|
|
4335
|
+
})),
|
|
4336
|
+
message: Schema.String
|
|
4337
|
+
}) {};
|
|
4338
|
+
/**
|
|
4339
|
+
* Typed failure of the engine-provided `SubagentDurability` operations.
|
|
4340
|
+
* `hook-failed` wraps a coordinator hook failure without leaking the hook's
|
|
4341
|
+
* error type into handler signatures (the `DurableStepError` precedent);
|
|
4342
|
+
* `no-active-tool-batch` is the fail-closed Run-level default — establishment
|
|
4343
|
+
* outside an active Tool batch never silently degrades to ephemeral spawning.
|
|
4344
|
+
*/
|
|
4345
|
+
var SubagentDurabilityError = class extends Schema.TaggedError()("SubagentDurabilityError", {
|
|
4346
|
+
operation: Schema.Literals([
|
|
4347
|
+
"establish",
|
|
4348
|
+
"join",
|
|
4349
|
+
"waiting"
|
|
4350
|
+
]),
|
|
4351
|
+
reason: Schema.Literals(["hook-failed", "no-active-tool-batch"]),
|
|
4352
|
+
message: Schema.String.check(Schema.isMaxLength(4096)),
|
|
4353
|
+
toolCallId: Schema.optionalKey(ToolCallId),
|
|
4354
|
+
/** Diagnostic cause for the live Effect only; Run events retain the fixed public message. */
|
|
4355
|
+
cause: Schema.optionalKey(Schema.Defect())
|
|
4356
|
+
}) {};
|
|
4357
|
+
/**
|
|
4358
|
+
* Engine-owned durable-Subagent seam provided locally to every Tool batch,
|
|
4359
|
+
* constructed from `RunOptions.subagent` when present and the explicit
|
|
4360
|
+
* ephemeral-mode default when absent. Like the other engine-provided Tool
|
|
4361
|
+
* services it is excluded from the runtime's public requirements and MUST NOT
|
|
4362
|
+
* be satisfied from an application Layer; handlers resolve it per call (the
|
|
4363
|
+
* S1 innermost re-provide pattern) so a Layer built inside another Run's
|
|
4364
|
+
* batch never captures a stale mode.
|
|
4365
|
+
*/
|
|
4366
|
+
var SubagentDurability = class extends Context.Service()("@effect-agent/engine/SubagentDurability") {};
|
|
4367
|
+
/** The one explicit ephemeral-mode value: absence of a durable coordinator is stated, not inferred. */
|
|
4368
|
+
const ephemeralSubagentDurability = { mode: "ephemeral" };
|
|
4369
|
+
const closedSubagentDurabilityFailure = (operation, toolCallId) => SubagentDurabilityError.make({
|
|
4370
|
+
operation,
|
|
4371
|
+
reason: "no-active-tool-batch",
|
|
4372
|
+
message: `Durable Subagent ${operation} was invoked outside an active Tool batch`,
|
|
4373
|
+
...toolCallId === void 0 ? {} : { toolCallId }
|
|
4374
|
+
});
|
|
4375
|
+
/**
|
|
4376
|
+
* Fail-closed Run-level `SubagentDurability` default. Each Tool batch shadows
|
|
4377
|
+
* it with its live per-batch service; resolving the seam outside an active
|
|
4378
|
+
* Tool batch fails typed instead of silently spawning an ephemeral child
|
|
4379
|
+
* under a durable coordinator.
|
|
4380
|
+
*/
|
|
4381
|
+
const closedSubagentDurability = {
|
|
4382
|
+
mode: "durable",
|
|
4383
|
+
establish: (request) => Effect.fail(closedSubagentDurabilityFailure("establish", request.toolCallId)),
|
|
4384
|
+
join: (request) => Effect.fail(closedSubagentDurabilityFailure("join", request.toolCallId)),
|
|
4385
|
+
waiting: (toolCallId) => Effect.fail(closedSubagentDurabilityFailure("waiting", toolCallId))
|
|
4386
|
+
};
|
|
4387
|
+
/**
|
|
4388
|
+
* Durable `SubagentDurability` service bound to one Tool batch over the
|
|
4389
|
+
* coordinator's `RunSubagentHook`. Hook failures surface as typed
|
|
4390
|
+
* `SubagentDurabilityError` values without widening handler error channels
|
|
4391
|
+
* (the coordinator keeps its own halt side channel, exactly like the Step
|
|
4392
|
+
* hook); `waiting` constructs the engine-owned `ToolCallWaiting` signal so a
|
|
4393
|
+
* handler can never counterfeit a foreign child identity shape.
|
|
4394
|
+
*/
|
|
4395
|
+
const makeSubagentDurabilityService = (hook, hookServices) => ({
|
|
4396
|
+
mode: "durable",
|
|
4397
|
+
establish: (request) => provideHookServices(hook.establish(request), hookServices).pipe(Effect.mapError((cause) => SubagentDurabilityError.make({
|
|
4398
|
+
operation: "establish",
|
|
4399
|
+
reason: "hook-failed",
|
|
4400
|
+
toolCallId: request.toolCallId,
|
|
4401
|
+
message: "Durable child establishment failed",
|
|
4402
|
+
cause
|
|
4403
|
+
}))),
|
|
4404
|
+
join: (request) => provideHookServices(hook.join(request), hookServices).pipe(Effect.mapError((cause) => SubagentDurabilityError.make({
|
|
4405
|
+
operation: "join",
|
|
4406
|
+
reason: "hook-failed",
|
|
4407
|
+
toolCallId: request.toolCallId,
|
|
4408
|
+
message: "Durable child join failed",
|
|
4409
|
+
cause
|
|
4410
|
+
}))),
|
|
4411
|
+
waiting: (toolCallId, child) => Effect.fail(ToolCallWaiting.make({
|
|
4412
|
+
toolCallId,
|
|
4413
|
+
childThreadId: child.childThreadId,
|
|
4414
|
+
childSubmissionId: child.childSubmissionId,
|
|
4415
|
+
childRunId: child.childRunId,
|
|
4416
|
+
receiptId: child.receiptId,
|
|
4417
|
+
message: `Tool Call ${toolCallId} is waiting on durable attached child ${child.childSubmissionId}`
|
|
4418
|
+
}))
|
|
4419
|
+
});
|
|
4420
|
+
/**
|
|
4421
|
+
* Extract the waiting suspension signal from a handler cause. It normally
|
|
4422
|
+
* travels as a typed failure through the native `Toolkit.handle` error
|
|
4423
|
+
* channel; the squash fallback also recognizes it inside a defect so a
|
|
4424
|
+
* wrapped signal still suspends instead of manufacturing a Tool failure.
|
|
4425
|
+
*/
|
|
4426
|
+
const waitingFromCause = (cause) => {
|
|
4427
|
+
const failure = Cause.findErrorOption(cause);
|
|
4428
|
+
if (Option.isSome(failure) && failure.value instanceof ToolCallWaiting) return failure.value;
|
|
4429
|
+
const squashed = Cause.squash(cause);
|
|
4430
|
+
return squashed instanceof ToolCallWaiting ? squashed : void 0;
|
|
4431
|
+
};
|
|
4432
|
+
/**
|
|
4433
|
+
* Run one child Agent Binding through the same interpreter as a top-level
|
|
4434
|
+
* Run.
|
|
4435
|
+
*
|
|
4436
|
+
* The spawner allocates a fresh child `ThreadId` and `RunId` through
|
|
4437
|
+
* `IdGenerator` (guaranteeing a fresh child Thread per invocation, with
|
|
4438
|
+
* no Thread reuse), constructs the immutable Parent Link at
|
|
4439
|
+
* `depth + 1`, and starts the child eagerly with `AgentRuntime.start` inside
|
|
4440
|
+
* the caller-provided Scope, so parent interruption always reaches the child
|
|
4441
|
+
* and its finalizers. Preflight policy (including S1's normative
|
|
4442
|
+
* subtree reservation and inherited-grant checks) belongs to the delegation capability and runs
|
|
4443
|
+
* before `spawn` is called. Children inherit the parent Run's provided history and context
|
|
4444
|
+
* services; delegation handlers do not select separate host policies.
|
|
4445
|
+
*/
|
|
4446
|
+
const spawnWithParent = (parent, depth, history, preparation, onChild) => Effect.fn("AgentSpawner.spawn")(function* (binding, input, delegation, options) {
|
|
4447
|
+
const ids = yield* IdGenerator$1;
|
|
4448
|
+
const threadId = yield* ids.nextThreadId;
|
|
4449
|
+
const runId = yield* ids.nextRunId;
|
|
4450
|
+
const childDepth = yield* Schema.decodeEffect(DelegationDepth)(depth + 1).pipe(Effect.orDie);
|
|
4451
|
+
const parentLink = SubagentParentLink.make({
|
|
4452
|
+
delegationId: delegation.delegationId,
|
|
4453
|
+
parentAgentId: parent.agentId,
|
|
4454
|
+
parentThreadId: parent.threadId,
|
|
4455
|
+
parentRunId: parent.runId,
|
|
4456
|
+
parentToolCallId: delegation.parentToolCallId,
|
|
4457
|
+
depth: childDepth
|
|
4458
|
+
});
|
|
4459
|
+
const child = yield* startUnknown(binding, input, {
|
|
4460
|
+
...options,
|
|
4461
|
+
threadId,
|
|
4462
|
+
runId,
|
|
4463
|
+
parentLink
|
|
4464
|
+
}).pipe(Effect.provide(Context.make(ThreadHistory, history).pipe(Context.add(RunContextPreparation, preparation))));
|
|
4465
|
+
onChild(runId, child.usageReport);
|
|
4466
|
+
return {
|
|
4467
|
+
...child,
|
|
4468
|
+
threadId,
|
|
4469
|
+
runId,
|
|
4470
|
+
parentLink
|
|
4471
|
+
};
|
|
4472
|
+
});
|
|
4473
|
+
/**
|
|
4474
|
+
* Engine-owned service contract through which a declared delegation Tool
|
|
4475
|
+
* runs an Attached Child on the same interpreter.
|
|
4476
|
+
*
|
|
4477
|
+
* The engine provides this service locally to every Run, bound to the Run's
|
|
4478
|
+
* immutable identity and delegation depth; it is never satisfied from an
|
|
4479
|
+
* application Layer, is excluded from the runtime's public requirements, and
|
|
4480
|
+
* exposes neither the engine's mutable Run state nor any root Layer Context.
|
|
4481
|
+
*/
|
|
4482
|
+
var AgentSpawner = class extends Context.Service()("@effect-agent/engine/AgentSpawner") {};
|
|
4483
|
+
const makeAgentSpawner = (parent, depth, history, preparation, policy, grant, budget, budgetScope, onChild = () => {}) => ({
|
|
4484
|
+
...grant === void 0 ? {} : { grant },
|
|
4485
|
+
...budget === void 0 ? {} : { budget },
|
|
4486
|
+
...budgetScope === void 0 ? {} : { budgetScope },
|
|
4487
|
+
policy,
|
|
4488
|
+
depth,
|
|
4489
|
+
parent,
|
|
4490
|
+
spawn: spawnWithParent(parent, depth, history, preparation, onChild)
|
|
4491
|
+
});
|
|
4492
|
+
/** Bound applied to the rendered defect message of `withTerminalDefectEvent` (SEC-013). */
|
|
4493
|
+
const DEFECT_MESSAGE_LIMIT = 2048;
|
|
4494
|
+
/**
|
|
4495
|
+
* Opt-in boundary combinator (P7 §7(h), decision point 8): the engine keeps defects as
|
|
4496
|
+
* defects — `RunFailed` covers EXPECTED failures only, and a defect still fails the event
|
|
4497
|
+
* stream with its full Cause (AGENTS.md: errors are never silently widened, and a defect is
|
|
4498
|
+
* never converted into a typed failure by default). A host boundary that forwards Run Events
|
|
4499
|
+
* to a UI or transport can wrap the stream with this helper to append ONE bounded terminal
|
|
4500
|
+
* `RunFailed { errorTag: "Defect" }` event before the original cause is rethrown, so a viewer
|
|
4501
|
+
* always observes a terminal event even when the Run dies.
|
|
4502
|
+
*
|
|
4503
|
+
* Event contract:
|
|
4504
|
+
*
|
|
4505
|
+
* - typed failures and interruptions pass through untouched — the engine already emitted
|
|
4506
|
+
* their terminal event, so nothing is duplicated;
|
|
4507
|
+
* - a cause carrying a defect first emits `RunFailed` with `errorTag: "Defect"` and a
|
|
4508
|
+
* bounded string rendering of the defect (never the raw value; hosts owning stricter
|
|
4509
|
+
* redaction apply it downstream), then RETHROWS the original cause unchanged;
|
|
4510
|
+
* - identity fields come from the last event already streamed (`sequence` advances by one);
|
|
4511
|
+
* a defect BEFORE the first event has no Run identity to attribute, so it is rethrown
|
|
4512
|
+
* without an event — the helper never fabricates identities.
|
|
4513
|
+
*/
|
|
4514
|
+
const withTerminalDefectEvent = (events) => Stream.suspend(() => {
|
|
4515
|
+
let last;
|
|
4516
|
+
return events.pipe(Stream.tap((event) => Effect.sync(() => {
|
|
4517
|
+
last = event;
|
|
4518
|
+
})), Stream.catchCause((cause) => {
|
|
4519
|
+
const base = last;
|
|
4520
|
+
if (base === void 0 || !Cause.hasDies(cause) || Cause.hasInterruptsOnly(cause)) return Stream.failCause(cause);
|
|
4521
|
+
return Stream.fromEffect(Effect.gen(function* () {
|
|
4522
|
+
const timestamp = DateTime.makeUnsafe(yield* Clock.currentTimeMillis);
|
|
4523
|
+
return RunFailed.make({
|
|
4524
|
+
eventVersion: 1,
|
|
4525
|
+
runId: base.runId,
|
|
4526
|
+
threadId: base.threadId,
|
|
4527
|
+
agentId: base.agentId,
|
|
4528
|
+
sequence: base.sequence + 1,
|
|
4529
|
+
timestamp,
|
|
4530
|
+
errorTag: "Defect",
|
|
4531
|
+
message: errorMessage(Cause.squash(cause)).slice(0, DEFECT_MESSAGE_LIMIT)
|
|
4532
|
+
});
|
|
4533
|
+
})).pipe(Stream.concat(Stream.failCause(cause)));
|
|
4534
|
+
}));
|
|
4535
|
+
});
|
|
4536
|
+
//#endregion
|
|
4537
|
+
//#region src/engine/AgentRuntime.ts
|
|
4538
|
+
var AgentRuntime_exports = /* @__PURE__ */ __exportAll({
|
|
4539
|
+
AgentChildPending: () => AgentChildPending,
|
|
4540
|
+
AgentResultSchema: () => AgentResultSchema,
|
|
4541
|
+
AgentSpawner: () => AgentSpawner,
|
|
4542
|
+
SubagentDurability: () => SubagentDurability,
|
|
4543
|
+
SubagentDurabilityError: () => SubagentDurabilityError,
|
|
4544
|
+
ToolCallWaiting: () => ToolCallWaiting,
|
|
4545
|
+
decodeFinalOutput: () => decodeFinalOutput,
|
|
4546
|
+
encodeRunDisposition: () => encodeRunDisposition,
|
|
4547
|
+
projectCompletionFromToolOutput: () => projectCompletionFromToolOutput,
|
|
4548
|
+
projectCompletionOutput: () => projectCompletionOutput,
|
|
4549
|
+
renderInputPrompt: () => renderInputPrompt,
|
|
4550
|
+
run: () => run,
|
|
4551
|
+
runUnknown: () => runUnknown,
|
|
4552
|
+
start: () => start,
|
|
4553
|
+
startUnknown: () => startUnknown,
|
|
4554
|
+
stream: () => stream,
|
|
4555
|
+
streamUnknown: () => streamUnknown,
|
|
4556
|
+
streamWithUsageAccountingUnknown: () => streamWithUsageAccountingUnknown,
|
|
4557
|
+
withTerminalDefectEvent: () => withTerminalDefectEvent
|
|
4558
|
+
});
|
|
4559
|
+
//#endregion
|
|
4560
|
+
export { stream as _, SubagentDurability as a, withTerminalDefectEvent as b, decodeFinalOutput as c, projectCompletionOutput as d, renderInputPrompt as f, startUnknown as g, start as h, AgentSpawner as i, encodeRunDisposition as l, runUnknown as m, AgentChildPending as n, SubagentDurabilityError as o, run as p, AgentResultSchema as r, ToolCallWaiting as s, AgentRuntime_exports as t, projectCompletionFromToolOutput as u, streamUnknown as v, streamWithUsageAccountingUnknown as y };
|
|
4561
|
+
|
|
4562
|
+
//# sourceMappingURL=AgentRuntime-BLyETCPo.mjs.map
|