@yolk-sdk/agent 0.1.0-canary.104 → 0.1.0-canary.106
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/classification/errors.d.mts +1 -1
- package/dist/classification/errors.mjs +1 -1
- package/dist/client/transport.d.mts +1 -1
- package/dist/client/transport.mjs +1 -1
- package/dist/loop/error.d.mts +1 -1
- package/dist/loop/error.mjs +1 -1
- package/dist/loop/llm-event.d.mts +1 -1
- package/dist/loop/llm-event.mjs +1 -1
- package/dist/loop/run.d.mts +33 -0
- package/dist/loop/run.d.mts.map +1 -1
- package/dist/loop/run.mjs +97 -21
- package/dist/loop/run.mjs.map +1 -1
- package/dist/providers/anthropic/conformance/cases.mjs +1 -1
- package/dist/providers/anthropic-messages-provider-internal.mjs +1 -1
- package/dist/providers/anthropic-provider-internal.mjs +1 -1
- package/dist/providers/openai/conformance/responses-cases-internal.mjs +1 -1
- package/dist/providers/openai/provider.d.mts +1 -1
- package/dist/providers/openai/provider.mjs +1 -1
- package/dist/providers/openai/realtime/client-codec.mjs +1 -1
- package/dist/providers/openai/realtime/session-config.d.mts +1 -1
- package/dist/providers/openai/realtime/session-config.mjs +1 -1
- package/dist/providers/openai-responses-provider-internal.d.mts +1 -1
- package/dist/providers/openai-responses-provider-internal.mjs +1 -1
- package/dist/providers/opencode/conformance/cases.mjs +1 -1
- package/dist/providers/vercel/ai-gateway-classifier.mjs +1 -1
- package/dist/react/chat-actions.d.mts +20 -20
- package/dist/react/chat-actions.d.mts.map +1 -1
- package/dist/react/chat-items.d.mts +13 -13
- package/dist/react/chat-items.mjs +1 -1
- package/dist/react/chat-messages.d.mts +12 -12
- package/dist/react/chat-session-events.d.mts +1 -1
- package/dist/react/chat-session-events.mjs +1 -1
- package/dist/runtime/error.d.mts +1 -1
- package/dist/runtime/error.mjs +1 -1
- package/dist/runtime/run-runtime.d.mts.map +1 -1
- package/dist/runtime/run-runtime.mjs +29 -6
- package/dist/runtime/run-runtime.mjs.map +1 -1
- package/dist/runtime/session-event-store.d.mts +1 -1
- package/dist/runtime/session-event-store.mjs +1 -1
- package/dist/skillset/merge.d.mts +3 -3
- package/dist/tools/arguments.d.mts +1 -1
- package/dist/tools/arguments.mjs +1 -1
- package/dist/tools/background.d.mts +1 -1
- package/dist/tools/background.mjs +1 -1
- package/dist/tools/change-preview.mjs +1 -1
- package/dist/tools/input.d.mts +1 -1
- package/dist/tools/input.mjs +1 -1
- package/dist/tools/interaction.d.mts +1 -1
- package/dist/tools/interaction.mjs +1 -1
- package/dist/tools/ledger.d.mts +1 -1
- package/dist/tools/ledger.mjs +1 -1
- package/dist/tools/plan-review.mjs +1 -1
- package/dist/tools/plan-runtime.mjs +1 -1
- package/dist/tools/plan.d.mts +1 -1
- package/dist/tools/plan.mjs +1 -1
- package/dist/tools/registry.d.mts +1 -1
- package/dist/tools/registry.mjs +1 -1
- package/dist/tools/subagent.mjs +1 -1
- package/dist/voice/projection.d.mts +1 -1
- package/dist/voice/projection.mjs +1 -1
- package/dist/voice/protocol.d.mts +1 -1
- package/dist/voice/protocol.mjs +1 -1
- package/dist/voice/session-log.d.mts +1 -1
- package/dist/voice/session-log.mjs +1 -1
- package/dist/voice/speech.d.mts +1 -1
- package/dist/voice/speech.mjs +1 -1
- package/dist/voice/tool-bridge.mjs +1 -1
- package/dist/voice/tool-server.mjs +1 -1
- package/package.json +2 -2
- package/src/loop/run.ts +203 -35
- package/src/runtime/run-runtime.ts +42 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ClassificationUsage } from "./schema.mjs";
|
|
2
|
-
import { ProviderErrorInfo } from "@yolk-sdk/agent/protocol";
|
|
3
2
|
import * as Schema from "effect/Schema";
|
|
3
|
+
import { ProviderErrorInfo } from "@yolk-sdk/agent/protocol";
|
|
4
4
|
//#region src/classification/errors.ts
|
|
5
5
|
/**
|
|
6
6
|
* Why an answer does not fit its question:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AgentTranscript } from "./state.mjs";
|
|
2
2
|
import { Cause, Effect, Layer, Stream } from "effect";
|
|
3
|
-
import { AgentEvent, AgentReasoningEffort, HitlResponse, InputResponse, InteractionResponse, QuestionResponse, ToolApprovalResponse } from "@yolk-sdk/agent/protocol";
|
|
4
3
|
import * as Schema$1 from "effect/Schema";
|
|
4
|
+
import { AgentEvent, AgentReasoningEffort, HitlResponse, InputResponse, InteractionResponse, QuestionResponse, ToolApprovalResponse } from "@yolk-sdk/agent/protocol";
|
|
5
5
|
import { HttpClient } from "effect/http";
|
|
6
6
|
|
|
7
7
|
//#region src/client/transport.d.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Cause, Channel, Data, Effect, Exit, Match, Predicate, Pull, Queue, Ref, Result, Scope, Stream } from "effect";
|
|
2
|
-
import { AgentEvent, AgentWebSocketServerMessage, InputResponseInput, InteractionResponseInput, QuestionResponseInput, ToolApprovalResponseInput, UserInput, isTerminalAgentEvent } from "@yolk-sdk/agent/protocol";
|
|
3
2
|
import * as Schema$1 from "effect/Schema";
|
|
3
|
+
import { AgentEvent, AgentWebSocketServerMessage, InputResponseInput, InteractionResponseInput, QuestionResponseInput, ToolApprovalResponseInput, UserInput, isTerminalAgentEvent } from "@yolk-sdk/agent/protocol";
|
|
4
4
|
import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/http";
|
|
5
5
|
//#region src/client/transport.ts
|
|
6
6
|
var AgentTransportError = class extends Schema$1.TaggedError()("AgentTransportError", {
|
package/dist/loop/error.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AgentError, ProviderErrorInfo } from "@yolk-sdk/agent/protocol";
|
|
2
1
|
import * as Schema from "effect/Schema";
|
|
2
|
+
import { AgentError, ProviderErrorInfo } from "@yolk-sdk/agent/protocol";
|
|
3
3
|
|
|
4
4
|
//#region src/loop/error.d.ts
|
|
5
5
|
declare const LLMResponseIssue: Schema.Literal<"missing_done">;
|
package/dist/loop/error.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Match } from "effect";
|
|
2
|
-
import { AgentError, ProviderErrorInfo } from "@yolk-sdk/agent/protocol";
|
|
3
2
|
import * as Schema$1 from "effect/Schema";
|
|
3
|
+
import { AgentError, ProviderErrorInfo } from "@yolk-sdk/agent/protocol";
|
|
4
4
|
//#region src/loop/error.ts
|
|
5
5
|
const LLMResponseIssue = Schema$1.Literal("missing_done");
|
|
6
6
|
var LLMError = class extends Schema$1.TaggedError()("LLMError", {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AgentUsage, ToolCall, ToolResult } from "@yolk-sdk/agent/protocol";
|
|
2
1
|
import * as Schema from "effect/Schema";
|
|
2
|
+
import { AgentUsage, ToolCall, ToolResult } from "@yolk-sdk/agent/protocol";
|
|
3
3
|
|
|
4
4
|
//#region src/loop/llm-event.d.ts
|
|
5
5
|
declare const LLMTextDelta_base: Schema.Class<LLMTextDelta, Schema.TaggedStruct<"TextDelta", {
|
package/dist/loop/llm-event.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AgentUsage, ToolCall, ToolResult } from "@yolk-sdk/agent/protocol";
|
|
2
1
|
import * as Schema from "effect/Schema";
|
|
2
|
+
import { AgentUsage, ToolCall, ToolResult } from "@yolk-sdk/agent/protocol";
|
|
3
3
|
//#region src/loop/llm-event.ts
|
|
4
4
|
var LLMTextDelta = class extends Schema.TaggedClass()("TextDelta", { text: Schema.String }) {};
|
|
5
5
|
var LLMReasoningDelta = class extends Schema.TaggedClass()("ReasoningDelta", { text: Schema.String }) {};
|
package/dist/loop/run.d.mts
CHANGED
|
@@ -23,6 +23,16 @@ type RunConfig = {
|
|
|
23
23
|
* ResolvedToolSet.approvalPreviews): pending approval requests carry their preview.
|
|
24
24
|
*/
|
|
25
25
|
readonly approvalPreviews?: Readonly<Record<string, ToolApprovalPreviewer>>;
|
|
26
|
+
/** The requests of the pause being resumed (`AgentAwaitingInput.requests`). An unanswered
|
|
27
|
+
* approval request listed here stays pending even when its tool no longer requires approval.
|
|
28
|
+
*/
|
|
29
|
+
readonly awaitingRequests?: ReadonlyArray<HitlRequest>;
|
|
30
|
+
/** Whether the transcript's pending tool calls resume a pause. Default: they do when
|
|
31
|
+
* `hitlResponses`, `awaitingRequests` or an `interactionHost` is given (otherwise a new model
|
|
32
|
+
* turn runs). `true` resumes them even without responses; `false` prepares them as a fresh
|
|
33
|
+
* batch (current policy, no recorded approval responses consulted).
|
|
34
|
+
*/
|
|
35
|
+
readonly resume?: boolean;
|
|
26
36
|
readonly model: string;
|
|
27
37
|
readonly reasoningEffort?: AgentReasoningEffort;
|
|
28
38
|
readonly capabilities?: AgentModelCapabilities;
|
|
@@ -41,6 +51,18 @@ type ToolBatchConfig = {
|
|
|
41
51
|
* ResolvedToolSet.approvalPreviews).
|
|
42
52
|
*/
|
|
43
53
|
readonly approvalPreviews?: Readonly<Record<string, ToolApprovalPreviewer>>;
|
|
54
|
+
/** The requests of the pause being resumed (`AgentAwaitingInput.requests`). An unanswered
|
|
55
|
+
* approval request listed here stays pending even when its tool no longer requires approval.
|
|
56
|
+
* A batch with `awaitingRequests` or any `hitlResponses` is a resume (recorded answers win
|
|
57
|
+
* over the current policy); pass only the responses of the batch being resumed, or set
|
|
58
|
+
* `resume`.
|
|
59
|
+
*/
|
|
60
|
+
readonly awaitingRequests?: ReadonlyArray<HitlRequest>;
|
|
61
|
+
/** Whether this batch resumes a pause (default: `awaitingRequests` given, or any
|
|
62
|
+
* `hitlResponses`). Set `false` for a new model turn's batch even when passing responses: a
|
|
63
|
+
* provider may reuse call ids, and only a resume lets recorded answers override the policy.
|
|
64
|
+
*/
|
|
65
|
+
readonly resume?: boolean;
|
|
44
66
|
readonly model?: string;
|
|
45
67
|
readonly createdMessages?: ReadonlyArray<AgentMessage>;
|
|
46
68
|
readonly turn?: number;
|
|
@@ -87,6 +109,17 @@ declare const prepareToolBatch: (input: {
|
|
|
87
109
|
* is pending, at most 8 at a time.
|
|
88
110
|
*/
|
|
89
111
|
readonly approvalPreviews?: Readonly<Record<string, ToolApprovalPreviewer>>;
|
|
112
|
+
/** The requests of the pause being resumed (`AgentAwaitingInput.requests`). An unanswered
|
|
113
|
+
* approval request listed here stays pending even when its tool no longer requires approval;
|
|
114
|
+
* a recorded answer always wins over the current policy.
|
|
115
|
+
*/
|
|
116
|
+
readonly awaitingRequests?: ReadonlyArray<HitlRequest>;
|
|
117
|
+
/** Whether this batch resumes a pause (default: `awaitingRequests` given, or any `responses`).
|
|
118
|
+
* Only a resume consults recorded approvals and the pause; a fresh batch (a new model turn,
|
|
119
|
+
* whose provider may reuse call ids) never settles an approval from `responses`: the current
|
|
120
|
+
* policy decides, and a gated call asks again.
|
|
121
|
+
*/
|
|
122
|
+
readonly resume?: boolean | undefined;
|
|
90
123
|
}) => Effect.Effect<PreparedToolBatch, ToolError>;
|
|
91
124
|
declare const runModelTurn: (config: ModelTurnConfig) => Stream.Stream<AgentEvent, AgentLoopError, ContextTransformer | LLMProvider | LoopConfig>;
|
|
92
125
|
declare const runToolBatch: (config: ToolBatchConfig) => Stream.Stream<AgentEvent, AgentLoopError, LoopConfig | ToolExecutor>;
|
package/dist/loop/run.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.mts","names":[],"sources":["../../src/loop/run.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"run.d.mts","names":[],"sources":["../../src/loop/run.ts"],"mappings":"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"}
|
package/dist/loop/run.mjs
CHANGED
|
@@ -8,9 +8,9 @@ import "../protocol/change-preview.mjs";
|
|
|
8
8
|
import { truncateCodePoints } from "../protocol/bounded-text.mjs";
|
|
9
9
|
import { validInteractionReceipt as validInteractionReceipt$1 } from "../protocol/tool.mjs";
|
|
10
10
|
import { withToolArgumentsErrorHint } from "../protocol/tool-argument-hints.mjs";
|
|
11
|
-
import { Clock, Data, Effect, Match, Option, Predicate, Ref, Stream } from "effect";
|
|
12
|
-
import { AgentAwaitingInput, AgentEnd, AgentRetry, AgentStart, AssistantMessageEvent, InputRequest, InputRequested, InteractionRequest, InteractionRequested, InteractionResponse, LLMReasoningDelta, LLMStreamEnd, LLMStreamStart, LLMTextDelta, ProviderToolResult, QuestionRequest, QuestionRequested, QuestionToolParams, SubagentCompleted, SubagentStarted, ToolApprovalRequest, ToolApprovalRequested, ToolExecutionAccepted, ToolExecutionCompleted, ToolExecutionError, ToolExecutionStarted, ToolInputDelta, ToolInputEnd, ToolInputStart, ToolResult, TurnEnd, TurnStart, UsageUpdate, addAgentUsage, assistantHostToolCalls, contentParts, contentPreview, decodeBackgroundToolInput, formatInputResponseContent, formatQuestionResponseContent, hitlResponseEvent, inputRequestId, inputResponseStructuredContent, interactionRequestId, makeSubagentRunId, providerToolDefs, questionResponseStructuredContent, toolResultMessageFromResult, zeroAgentUsage } from "@yolk-sdk/agent/protocol";
|
|
11
|
+
import { Array as Array$1, Clock, Data, Effect, Match, Option, Predicate, Ref, Stream, Struct } from "effect";
|
|
13
12
|
import * as Schema$1 from "effect/Schema";
|
|
13
|
+
import { AgentAwaitingInput, AgentEnd, AgentRetry, AgentStart, AssistantMessageEvent, InputRequest, InputRequested, InteractionRequest, InteractionRequested, InteractionResponse, LLMReasoningDelta, LLMStreamEnd, LLMStreamStart, LLMTextDelta, ProviderToolResult, QuestionRequest, QuestionRequested, QuestionToolParams, SubagentCompleted, SubagentStarted, ToolApprovalRequest, ToolApprovalRequested, ToolExecutionAccepted, ToolExecutionCompleted, ToolExecutionError, ToolExecutionStarted, ToolInputDelta, ToolInputEnd, ToolInputStart, ToolResult, TurnEnd, TurnStart, UsageUpdate, addAgentUsage, assistantHostToolCalls, contentParts, contentPreview, decodeBackgroundToolInput, formatInputResponseContent, formatQuestionResponseContent, hitlResponseEvent, inputRequestId, inputResponseStructuredContent, interactionRequestId, makeSubagentRunId, providerToolDefs, questionResponseStructuredContent, toolResultMessageFromResult, zeroAgentUsage } from "@yolk-sdk/agent/protocol";
|
|
14
14
|
//#region src/loop/run.ts
|
|
15
15
|
const objectField = (input, key) => {
|
|
16
16
|
if (!Predicate.isObjectOrArray(input)) return;
|
|
@@ -198,6 +198,26 @@ const questionRequestId = (call) => `question:${call.id}`;
|
|
|
198
198
|
const matchesApproval = (response, call, tools) => response.toolCallId === call.id && response.requestId === approvalRequestId(call, tools);
|
|
199
199
|
const matchesQuestion = (response, call) => response.toolCallId === call.id && response.requestId === questionRequestId(call);
|
|
200
200
|
const approvalResponseFor = (responses, call, tools) => responses.flatMap((response) => Predicate.isTagged(response, "ToolApprovalResponse") && matchesApproval(response, call, tools) ? [response] : [])[0];
|
|
201
|
+
/** Resume only: a denial for this call under another approval id of the same call (its tool's
|
|
202
|
+
* execution mode changed since the request) still denies. Approvals need the exact id.
|
|
203
|
+
*/
|
|
204
|
+
const callDenialFor = (responses, call) => responses.flatMap((response) => Predicate.isTagged(response, "ToolApprovalResponse") && response.decision === "denied" && response.toolCallId === call.id && (response.requestId === `approval:${call.id}` || response.requestId.startsWith(`approval:${call.id}:`)) ? [response] : [])[0];
|
|
205
|
+
const outstandingApprovalFor = (awaiting, call) => awaiting.flatMap((request) => Predicate.isTagged(request, "ToolApprovalRequest") && request.toolCallId === call.id ? [request] : [])[0];
|
|
206
|
+
const canonicalParams = (params) => Schema$1.decodeUnknownOption(Schema$1.Json)(params).pipe(Option.map(canonicalJson));
|
|
207
|
+
const sameParams = (left, right) => {
|
|
208
|
+
const leftJson = canonicalParams(left);
|
|
209
|
+
const rightJson = canonicalParams(right);
|
|
210
|
+
return Option.isSome(leftJson) && Option.isSome(rightJson) && leftJson.value === rightJson.value;
|
|
211
|
+
};
|
|
212
|
+
/** The outstanding request, when it still describes this call exactly, without its preview
|
|
213
|
+
* (recomputed only while the tool has a previewer).
|
|
214
|
+
*/
|
|
215
|
+
const reusableApprovalRequest = (outstanding, call, tools) => outstanding !== void 0 && outstanding.call.name === call.name && sameParams(outstanding.call.params, call.params) && outstanding.requestId === approvalRequestId(call, tools) ? ToolApprovalRequest.make({
|
|
216
|
+
requestId: outstanding.requestId,
|
|
217
|
+
toolCallId: outstanding.toolCallId,
|
|
218
|
+
call,
|
|
219
|
+
policy: outstanding.policy
|
|
220
|
+
}) : void 0;
|
|
201
221
|
const questionResponseFor = (responses, call) => responses.flatMap((response) => Predicate.isTagged(response, "QuestionResponse") && matchesQuestion(response, call) ? [response] : [])[0];
|
|
202
222
|
const toolApprovalRequest = (tools, call) => ToolApprovalRequest.make({
|
|
203
223
|
requestId: approvalRequestId(call, tools),
|
|
@@ -262,7 +282,23 @@ const prepareQuestionCall = (call, index, responses) => Effect.gen(function* ()
|
|
|
262
282
|
events: [QuestionRequested.make({ request })]
|
|
263
283
|
});
|
|
264
284
|
});
|
|
265
|
-
|
|
285
|
+
/**
|
|
286
|
+
* Approval of one call. A fresh batch (a new model turn) follows the current policy and never
|
|
287
|
+
* consults recorded answers: a provider may reuse call ids, so an earlier answer must never
|
|
288
|
+
* settle a new call (a gated call asks again). A resume batch binds the call to its pause
|
|
289
|
+
* instead, because hosts re-read tool policy at execution and the policy can change between the
|
|
290
|
+
* request and the answer:
|
|
291
|
+
*
|
|
292
|
+
* 1. A recorded answer wins: the first exact-match response settles the call (`denied`: the
|
|
293
|
+
* denied result, never executed; `approved`: executed). A denial for the same call under
|
|
294
|
+
* another approval id of that call (`approval:<id>` or `approval:<id>:…`) also denies.
|
|
295
|
+
* 2. Else an outstanding request from the resumed pause (`awaitingRequests`) stays pending: the
|
|
296
|
+
* same request while it still describes the call (name, canonical params, request id), else a
|
|
297
|
+
* fresh one.
|
|
298
|
+
* 3. Else the current policy decides: `manual` asks, anything else executes.
|
|
299
|
+
*/
|
|
300
|
+
const prepareApprovalCall = (input) => {
|
|
301
|
+
const { tools, call, index, responses } = input;
|
|
266
302
|
const envelope = backgroundEnvelope(tools, call);
|
|
267
303
|
if (envelope !== void 0 && Option.isNone(envelope)) return PreparedToolCall.Result({
|
|
268
304
|
index,
|
|
@@ -274,31 +310,37 @@ const prepareApprovalCall = (tools, call, index, responses) => {
|
|
|
274
310
|
content: "Expected exactly execution (foreground or background) and arguments."
|
|
275
311
|
})
|
|
276
312
|
});
|
|
277
|
-
if (!approvalRequired(tools, call)) return PreparedToolCall.Execute({
|
|
313
|
+
if (!input.resume && !approvalRequired(tools, call)) return PreparedToolCall.Execute({
|
|
278
314
|
index,
|
|
279
315
|
call,
|
|
280
316
|
events: []
|
|
281
317
|
});
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
if (response === void 0) return PreparedToolCall.Pending({
|
|
285
|
-
request,
|
|
286
|
-
events: [ToolApprovalRequested.make({
|
|
287
|
-
call,
|
|
288
|
-
request
|
|
289
|
-
})]
|
|
290
|
-
});
|
|
291
|
-
if (response.decision === "denied") return PreparedToolCall.Result({
|
|
318
|
+
const response = input.resume ? approvalResponseFor(responses, call, tools) ?? callDenialFor(responses, call) : void 0;
|
|
319
|
+
if (response?.decision === "denied") return PreparedToolCall.Result({
|
|
292
320
|
index,
|
|
293
321
|
call,
|
|
294
322
|
result: deniedToolResult(call, response),
|
|
295
323
|
events: [hitlResponseEvent(response)]
|
|
296
324
|
});
|
|
297
|
-
return PreparedToolCall.Execute({
|
|
325
|
+
if (response !== void 0) return PreparedToolCall.Execute({
|
|
298
326
|
index,
|
|
299
327
|
call,
|
|
300
328
|
events: [hitlResponseEvent(response)]
|
|
301
329
|
});
|
|
330
|
+
const outstanding = input.resume ? outstandingApprovalFor(input.awaiting, call) : void 0;
|
|
331
|
+
if (outstanding === void 0 && !approvalRequired(tools, call)) return PreparedToolCall.Execute({
|
|
332
|
+
index,
|
|
333
|
+
call,
|
|
334
|
+
events: []
|
|
335
|
+
});
|
|
336
|
+
const request = reusableApprovalRequest(outstanding, call, tools) ?? toolApprovalRequest(tools, call);
|
|
337
|
+
return PreparedToolCall.Pending({
|
|
338
|
+
request,
|
|
339
|
+
events: [ToolApprovalRequested.make({
|
|
340
|
+
call,
|
|
341
|
+
request
|
|
342
|
+
})]
|
|
343
|
+
});
|
|
302
344
|
};
|
|
303
345
|
/** Most approval change previews one batch computes at once. */
|
|
304
346
|
const approvalPreviewConcurrency = 8;
|
|
@@ -533,7 +575,14 @@ const prepareModelToolCall = (input) => !input.receipts.has(input.call.id) && to
|
|
|
533
575
|
content: "Question tool is unavailable",
|
|
534
576
|
isError: true
|
|
535
577
|
})
|
|
536
|
-
})) : Effect.succeed(prepareApprovalCall(
|
|
578
|
+
})) : Effect.succeed(prepareApprovalCall({
|
|
579
|
+
tools: input.tools,
|
|
580
|
+
call: input.call,
|
|
581
|
+
index: input.index,
|
|
582
|
+
responses: input.responses,
|
|
583
|
+
awaiting: input.awaiting,
|
|
584
|
+
resume: input.resume
|
|
585
|
+
}));
|
|
537
586
|
/** Preflight the entire batch before dispatching ANY call. Pending requests fence all execution.
|
|
538
587
|
* Fails (`ToolError` `unavailable`) only when host storage an interaction call validator reads is
|
|
539
588
|
* unavailable: the batch fails closed, like `loadInteractionReceipts`, and nothing runs.
|
|
@@ -542,12 +591,15 @@ const prepareToolBatch = (input) => Effect.gen(function* () {
|
|
|
542
591
|
const handlers = input.inputs ?? {};
|
|
543
592
|
const preflights = input.interactions ?? {};
|
|
544
593
|
const previewers = input.approvalPreviews ?? {};
|
|
594
|
+
const resume = input.resume ?? (input.awaitingRequests !== void 0 || input.responses.length > 0);
|
|
545
595
|
const planned = yield* Effect.forEach(input.calls, (call, index) => prepareToolCall({
|
|
546
596
|
tools: input.tools,
|
|
547
597
|
responses: input.responses,
|
|
548
598
|
handlers,
|
|
549
599
|
preflights,
|
|
550
600
|
receipts: input.interactionReceipts ?? /* @__PURE__ */ new Map(),
|
|
601
|
+
awaiting: resume ? input.awaitingRequests ?? [] : [],
|
|
602
|
+
resume,
|
|
551
603
|
call,
|
|
552
604
|
index
|
|
553
605
|
}));
|
|
@@ -574,10 +626,16 @@ const syntheticToolCompletionEvents = (prepared) => prepared.flatMap((item) => P
|
|
|
574
626
|
result: item.result
|
|
575
627
|
})] : []);
|
|
576
628
|
const toolResultIds = (messages) => new Set(messages.flatMap((message) => Predicate.isTagged(message, "ToolResult") ? [message.toolCallId] : []));
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
629
|
+
/**
|
|
630
|
+
* Host tool calls without a result. A call is settled only by a result in its result window: the
|
|
631
|
+
* consecutive `ToolResult` messages right after its assistant message (the transcript rule of
|
|
632
|
+
* `danglingHostToolCalls`). A provider-reused call id is never settled by an earlier call's result.
|
|
633
|
+
*/
|
|
634
|
+
const pendingHostToolCalls = (messages) => messages.flatMap((message, index) => {
|
|
635
|
+
if (!Predicate.isTagged(message, "Assistant")) return [];
|
|
636
|
+
const completed = toolResultIds(Array$1.takeWhile(messages.slice(index + 1), (later) => Predicate.isTagged(later, "ToolResult")));
|
|
637
|
+
return assistantHostToolCalls(message).filter((call) => !completed.has(call.id));
|
|
638
|
+
});
|
|
581
639
|
const nonEmptyHitlRequests = (requests) => {
|
|
582
640
|
const first = requests[0];
|
|
583
641
|
return first === void 0 ? void 0 : [first, ...requests.slice(1)];
|
|
@@ -652,6 +710,7 @@ const makeAfterLlmStream = (input, llmEventsRef) => Stream.unwrap(Effect.gen(fun
|
|
|
652
710
|
const toolResultMessages = yield* Ref.make([]);
|
|
653
711
|
const prepared = yield* prepareToolBatch({
|
|
654
712
|
tools: input.config.tools,
|
|
713
|
+
resume: false,
|
|
655
714
|
responses: input.config.hitlResponses ?? [],
|
|
656
715
|
calls: completion.toolCalls,
|
|
657
716
|
inputs: input.config.inputs,
|
|
@@ -699,6 +758,7 @@ const makeAfterLlmStream = (input, llmEventsRef) => Stream.unwrap(Effect.gen(fun
|
|
|
699
758
|
reason: completion.stopReason
|
|
700
759
|
})).pipe(Stream.concat(makeTurnStream({
|
|
701
760
|
...input,
|
|
761
|
+
config: withoutConsumedAnswers(input.config),
|
|
702
762
|
currentMessages: [
|
|
703
763
|
...input.currentMessages,
|
|
704
764
|
assistantMessage,
|
|
@@ -753,6 +813,15 @@ const makeModelOnlyLlmStream = (input, llmEvents, result) => {
|
|
|
753
813
|
return Ref.update(input.usage, (usage) => addAgentUsage(usage, event.usage)).pipe(Effect.flatMap(() => appendEvent));
|
|
754
814
|
}), Stream.flatMap((event) => Predicate.isTagged(event, "AgentRetry") ? Stream.make(event) : isLlmEvent(event) ? Stream.fromIterable(toLlmEvent(event)) : Stream.make(event)), Stream.concat(makeModelOnlyAfterLlmStream(input, llmEvents)));
|
|
755
815
|
};
|
|
816
|
+
/**
|
|
817
|
+
* The config of the model turns after a batch settled: answers and the pause are consumed by the
|
|
818
|
+
* batch they settled and never reach a later turn, where a provider may reuse a call id.
|
|
819
|
+
*/
|
|
820
|
+
const withoutConsumedAnswers = (config) => Struct.omit(config, [
|
|
821
|
+
"hitlResponses",
|
|
822
|
+
"awaitingRequests",
|
|
823
|
+
"resume"
|
|
824
|
+
]);
|
|
756
825
|
const makeTurnStream = (input) => Stream.suspend(() => {
|
|
757
826
|
if (input.turn > input.loopConfig.maxTurns) return Stream.fail(new AbortError({ reason: "max_turns" }));
|
|
758
827
|
const llmStream = Stream.unwrap(Effect.gen(function* () {
|
|
@@ -765,15 +834,19 @@ const makeTurnStream = (input) => Stream.suspend(() => {
|
|
|
765
834
|
});
|
|
766
835
|
const makePendingToolResumeStream = (input) => Stream.unwrap(Effect.gen(function* () {
|
|
767
836
|
const pendingCalls = pendingHostToolCalls(input.currentMessages);
|
|
768
|
-
|
|
837
|
+
const detected = (input.config.hitlResponses ?? []).length > 0 || (input.config.awaitingRequests ?? []).length > 0 || input.config.interactionHost !== void 0;
|
|
838
|
+
if (pendingCalls.length === 0 || input.config.resume === void 0 && !detected) return makeTurnStream(input);
|
|
839
|
+
const resume = input.config.resume ?? true;
|
|
769
840
|
const toolResultMessages = yield* Ref.make([]);
|
|
770
841
|
const prepared = yield* prepareToolBatch({
|
|
771
842
|
tools: input.config.tools,
|
|
843
|
+
resume,
|
|
772
844
|
responses: input.config.hitlResponses ?? [],
|
|
773
845
|
calls: pendingCalls,
|
|
774
846
|
inputs: input.config.inputs,
|
|
775
847
|
interactions: input.config.interactions,
|
|
776
848
|
approvalPreviews: input.config.approvalPreviews,
|
|
849
|
+
awaitingRequests: input.config.awaitingRequests,
|
|
777
850
|
interactionReceipts: yield* loadInteractionReceipts(pendingCalls, input.config.interactionHost)
|
|
778
851
|
});
|
|
779
852
|
if (prepared.resultMessages.length > 0) yield* Ref.update(toolResultMessages, (results) => [...results, ...prepared.resultMessages]);
|
|
@@ -808,6 +881,7 @@ const makePendingToolResumeStream = (input) => Stream.unwrap(Effect.gen(function
|
|
|
808
881
|
const orderedResults = orderedToolResultMessages(results);
|
|
809
882
|
return Ref.update(input.createdMessages, (messages) => [...messages, ...orderedResults]).pipe(Effect.as(makeTurnStream({
|
|
810
883
|
...input,
|
|
884
|
+
config: withoutConsumedAnswers(input.config),
|
|
811
885
|
currentMessages: [...input.currentMessages, ...orderedResults]
|
|
812
886
|
})));
|
|
813
887
|
})));
|
|
@@ -852,6 +926,8 @@ const runToolBatch = (config) => Stream.unwrap(Effect.gen(function* () {
|
|
|
852
926
|
inputs: config.inputs,
|
|
853
927
|
interactions: config.interactions,
|
|
854
928
|
approvalPreviews: config.approvalPreviews,
|
|
929
|
+
awaitingRequests: config.awaitingRequests,
|
|
930
|
+
resume: config.resume,
|
|
855
931
|
interactionReceipts: yield* loadInteractionReceipts(config.calls, config.interactionHost)
|
|
856
932
|
});
|
|
857
933
|
const hasPendingRequests = prepared.pendingRequests.length > 0;
|