@alvin0/ai-agent-sdk-core 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/agent/loop/run-turn.d.ts.map +1 -1
- package/dist/agent/loop/run-turn.js +1 -1
- package/dist/agent/loop/run-turn.js.map +1 -1
- package/dist/agent/loop/schedule.d.ts +5 -0
- package/dist/agent/loop/schedule.d.ts.map +1 -1
- package/dist/agent/loop/schedule.js +1 -1
- package/dist/agent/loop/schedule.js.map +1 -1
- package/dist/composition/embedding/retry.js +1 -1
- package/dist/composition/embedding/retry.js.map +1 -1
- package/dist/embedding/errors.d.ts +5 -1
- package/dist/embedding/errors.d.ts.map +1 -1
- package/dist/embedding/errors.js +1 -1
- package/dist/embedding/errors.js.map +1 -1
- package/dist/observability/bus.js +1 -1
- package/dist/primitives/version.d.ts +1 -1
- package/dist/primitives/version.js +1 -1
- package/dist/primitives/version.js.map +1 -1
- package/dist/runtime/registry.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,3 +67,9 @@ For 0.1.7 completion, pending input, stream replacement and Stop behavior, see
|
|
|
67
67
|
the [upgrade guide](https://github.com/alvin0/ai-agent-sdk/blob/main/docs/upgrading-from-0.1.6.md).
|
|
68
68
|
`RuntimeAgentSession.hasUnansweredInput()` and `runPending()` expose work left
|
|
69
69
|
after a terminal round. Use `response.completed` for objective completion.
|
|
70
|
+
|
|
71
|
+
In 0.1.8, the exact-repeat guard can reuse the immediately preceding successful
|
|
72
|
+
tool result and allow one bounded model replan. `EmbeddingError.failure` preserves
|
|
73
|
+
available provider HTTP status, retry delay and request id through normalization.
|
|
74
|
+
See [upgrading from 0.1.7](https://github.com/alvin0/ai-agent-sdk/blob/main/docs/upgrading-from-0.1.7.md)
|
|
75
|
+
for exclusions, cancellation and budget behavior.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-turn.d.ts","names":[],"sources":["../../../src/agent/loop/run-turn.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"run-turn.d.ts","names":[],"sources":["../../../src/agent/loop/run-turn.ts"],"mappings":";;;;iBA8BgB,QAAQ,SAAS,iBAAiB,cAAc"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{detachedFrozen as e}from"../../primitives/snapshot.js";import{waitForSettlement as t}from"../../async/settlement.js";import{createSpanId as n,createTraceId as r}from"../../observation/context.js";import{createUserMessage as i}from"../../message/message.js";import{authoritativeTokenUsage as a,budgetTokenTotal as o,summarizeModelCallUsage as s}from"../accounting/usage.js";import{ContextSectionRuntime as c}from"../context/section.js";import{deliverQueuedInput as l}from"./turn/model-request-boundary.js";import{AwaitedEventQueue as u}from"./queue.js";import{captureProgramGrants as d}from"../tool/nested.js";import{scheduleToolCalls as ee}from"./schedule.js";import{ProgramResultStore as f}from"../tool/program-results.js";import{positiveFinite as p,resolveBounds as m,snapshotRunTurnOptions as h}from"./turn/config.js";import{codedRuntimeError as g,errorCodeOf as _,messageOf as v,now as y}from"./turn/common.js";import{runHook as te,runOptionalHook as b}from"./turn/hooks.js";import{emitAssistantContent as x,maintenanceEmitter as S,textOf as C}from"./turn/content.js";import{repeatKey as ne,repeatedSuffixCycle as re,toolActionPattern as ie}from"./turn/repetition.js";import{accountingUsageStop as w}from"./turn/usage-stop.js";import{modelRound as T}from"./turn/model-round.js";function E(e){return e.toolChoice===`none`?!1:(e.tools?.names().length??0)>0||(e.nativeTools?.length??0)>0}function D(n){return{async*[Symbol.asyncIterator](){let r=h(n),i=new u,a=new AbortController,o=p(r.teardownTimeoutMs??3e4,`teardownTimeoutMs`),s=O(r,r.signal===void 0?a.signal:AbortSignal.any([r.signal,a.signal]),t=>i.push(e(t))).then(()=>i.close(),e=>i.fail(e));try{for(;;){let e=await i.take();if(e.done)break;yield e.value}await s}finally{if(a.abort(Error(`turn event consumer stopped`)),i.close(),!await t(s,o))throw g(`turn producer ignored cancellation for more than ${o}ms`,`TURN_TEARDOWN_TIMEOUT`,a.signal.reason)}}}}async function O(e,t,u){let p=m(e.bounds),h=p.maxTotalTokens===`auto`?1/0:p.maxTotalTokens,g=p.maxSteps===`auto`?1/0:p.maxSteps,D=e.trace?.traceId??r(),O={traceId:D,spanId:n(),parentSpanId:e.trace?.parentSpanId??null},k=Math.max(1,e.history.entries().filter(e=>e.event.kind===`user`&&e.event.message.source.kind===`user`).length),A=y(),j=0,M=0,ae=[...p.toolBudgetRemindAt].filter(e=>e>0&&e<p.maxToolCalls).sort((e,t)=>t-e),oe=0,se=0,N,P=!1,F=0,I=0,L=``,R=[],z,B,V,ce=[],H=S(u,O),le=t=>{let n=i({source:{kind:`app`,producer:`tool-loop-budget-guard`},content:[{type:`text`,text:t}]}),r=N;if(r!==void 0)try{N=e.history.append({kind:`user`,message:n},{op:`replace`,from:r,to:r,targets:[r]}).seq;return}catch{N=void 0}N=e.history.append({kind:`user`,message:n}).seq},U=e.contextSections===void 0||e.contextSections.length===0?void 0:new c({sections:e.contextSections,history:e.history,...e.logger===void 0?{}:{logger:e.logger},guard:(n,r)=>te(n,e,t,r),scope:{agentId:e.trace?.agentId,conversationId:e.trace?.conversationId}}),W=[],G=!1,K=!1,q=E(e),J=e.experimentalPrograms===void 0?void 0:d(e.experimentalPrograms),Y=J===void 0?void 0:new f,ue=e.outputFormat?.type===`json_schema`&&q,X=e.accounting?.startOperation(`turn`,{data:{turn:k,model:e.config.model}}),Z=!1,Q,$=()=>{if(t.aborted)return{kind:`aborted`};let n=w(e.accounting);if(n!==void 0)return n;if(Q!==void 0)return Q;let r=o(s(R));return r!==void 0&&r>=h?{kind:`budget-exhausted`,budget:`tokens`,forcedFinalAnswer:!1}:void 0};try{await u({type:`span-start`,trace:O,at:A,name:`invoke_agent ${e.trace?.agentId??`agent`}`,kind:`invoke_agent`,attributes:{"gen_ai.operation.name":`invoke_agent`,"gen_ai.agent.id":e.trace?.agentId??`agent`,"gen_ai.agent.name":e.trace?.agentName??e.trace?.agentId??`agent`,"gen_ai.request.model":e.config.model,...e.trace?.conversationId===void 0?{}:{"gen_ai.conversation.id":e.trace.conversationId}}}),G=!0,await u({type:`turn-start`,turn:k,trace:O});turnLifecycle:for(;;){for(;z===void 0&&j<g&&!t.aborted&&(z=$(),z===void 0);){let n=o(s(R));if(p.maxTotalTokens!==`auto`&&n!==void 0){let t=[.5,.75,.9].filter(e=>n>=h*e).length;if(t>F){F=t;let r=Math.max(0,h-n);e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`tool-loop-token-guard`},content:[{type:`text`,text:`Token budget: ${r} of ${p.maxTotalTokens} aggregate tokens remain. Each request also spends input tokens on the accumulated context. `+(t>=2?`Stop broad exploration. `:`Prioritize outstanding verification and reporting. `)+`Reserve capacity for the required self-check, honest todo reconciliation and substantive final report. State unavailable evidence and unfinished work; do not claim it is verified. No extra model call is allowed after the hard token limit.`}]})})}}!P&&j>0&&g-j<=2&&(P=!0,e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`tool-loop-step-guard`},content:[{type:`text`,text:`${g-j} work steps remain. Finish essential verification, update any task list honestly, and submit the result if required. Summarize findings, evidence, and unfinished work; do not start new exploration.`}]})})),U!==void 0&&(await U.reconcile(j,W,t),W=[]);let r=j+1,a=await T(e,t,u,H,O,k,r,ue?`process`:e.outputFormat?.type===`json_schema`?`final`:`standard`);if(j++,a.report!==void 0&&R.push(a.report),a.message===void 0?a.finish.kind===`stop`&&(L=``):(e.history.append({kind:`assistant`,message:a.message,...a.finish.kind===`aborted`?{interrupted:!0}:{},...a.usage===void 0?{}:{usage:a.usage}}),await u({type:`assistant-message`,message:a.message,trace:a.trace}),await x(a,u),L=C(a.message.content)),Q=w(e.accounting)??(a.usageRequired?{kind:`error`,failure:{message:`provider usage is required by the configured run policy`,code:`USAGE_REQUIRED`}}:a.usageUnavailable?{kind:`usage-unavailable`,modelCallId:a.report?.modelCallId??`unknown`}:void 0),t.aborted||a.finish.kind===`aborted`){z={kind:`aborted`};break}if(a.finish.kind===`error`&&Q!==void 0){z={kind:`error`,failure:a.finish.failure};break}if(a.usageRequired&&(a.calls.length===0||e.tools===void 0)){z=Q;break}if(a.finish.kind===`error`&&!a.usageRequired){if(z=$(),z!==void 0)break;let n=await b(e.hooks?.onRequestError,[{turn:k,step:r,failure:a.finish.failure,snapshot:e.history.snapshot(),signal:t,...e.logger===void 0?{}:{logger:e.logger},emit:H}],e,t,`onRequestError`),i=w(e.accounting);if(i!==void 0){z=t.aborted?{kind:`aborted`}:i;break}if(n===`retry`&&j<g)continue;z={kind:`error`,failure:a.finish.failure};break}if(a.finish.kind===`max-tokens`&&!a.usageRequired){z={kind:`max-tokens`};break}if(a.calls.length===0&&a.usageUnavailable){z=w(e.accounting)??{kind:`usage-unavailable`,modelCallId:a.report?.modelCallId??`unknown`};break}if(a.calls.length===0&&ue){if(z=$(),z!==void 0)break;e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`structured-output-finalizer`},content:[{type:`text`,text:`The process phase is complete. Return the final answer now in the requested output format. Do not call tools.`}]})});let n=await T(e,t,u,H,O,k,j+1,`final`);j++,n.report!==void 0&&R.push(n.report),n.message!==void 0&&(e.history.append({kind:`assistant`,message:n.message,...n.usage===void 0?{}:{usage:n.usage}}),await u({type:`assistant-message`,message:n.message,trace:n.trace}),await x(n,u),L=C(n.message.content)),z=n.finish.kind===`aborted`?{kind:`aborted`}:n.finish.kind===`error`?{kind:`error`,failure:n.finish.failure}:n.finish.kind===`max-tokens`?{kind:`max-tokens`}:n.usageRequired?{kind:`error`,failure:{message:`provider usage is required by the configured run policy`,code:`USAGE_REQUIRED`}}:n.usageUnavailable?w(e.accounting)??{kind:`usage-unavailable`,modelCallId:n.report?.modelCallId??`unknown`}:{kind:`completed`};break}if(a.droppedDuplicateCalls!==void 0&&e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`tool-loop-duplicate-guard`},content:[{type:`text`,text:`You reused the tool-call id ${a.droppedDuplicateCalls.map(e=>`'${e}'`).join(`, `)}. A result pairs to exactly one call, so the repeat was not run and only the first call of that id has a result. Give every call its own id, and call again if you still need what the dropped one would have done.`}]})}),a.calls.length===0||e.tools===void 0){z={kind:`completed`};break}let c=a.calls.filter(t=>e.tools?.get(t.toolName)?.budgetExempt!==!0).length,l=Math.max(0,p.maxToolCalls-M),d=V,f=a.calls.map(e=>{let t=ne(e),n=d?.key===t?d.count+1:1;return d={key:t,count:n},n}).some(e=>e>=p.repeatToolLimit),m=ie(a.calls),_=re([...ce,m],p.maxToolCycleLength),v=_!==void 0&&_.repetitions>=p.toolCycleLimit,y=s(R),S=o(y),E=S!==void 0&&S>=h,D=f||v||E||a.usageRequired,A=E?`tokens`:v?`tool-call-cycle`:f?`repeated-tool-call`:a.usageRequired?`usage-required`:`tool-calls`,N=p.onExhausted!==`continue`,B=await ee({calls:a.calls,catalog:e.tools,history:e.history,position:{turn:k,step:r,...e.accounting===void 0?{}:{runId:e.accounting.runId},...e.trace?.conversationId===void 0?{}:{conversationId:e.trace.conversationId}},signal:t,parentTrace:O,maxParallel:p.maxParallel,dispatchLimit:D?0:N?l:a.calls.length,declineReason:A,maxResultBytes:p.maxToolResultBytes,maxResultTokens:p.maxToolResultTokens,resultOverflow:p.toolResultOverflow,...e.spillStore===void 0?{}:{spillStore:e.spillStore},maxDurationMs:p.maxToolDurationMs,teardownTimeoutMs:p.toolTeardownTimeoutMs,...e.logger===void 0?{}:{logger:e.logger},...e.interceptors===void 0?{}:{interceptors:e.interceptors},...e.approvals===void 0?{}:{approvals:e.approvals},...e.accounting===void 0?{}:{accounting:e.accounting},emit:u,...e.hooks?.checkpoint===void 0?{}:{checkpoint:n=>te(Promise.resolve(e.hooks?.checkpoint?.(n)),e,t,`checkpoint`)}},{admissionLimit:D?0:N?l:`unbounded`,...J===void 0?{}:{programs:J},...Y===void 0?{}:{programResults:Y}});U!==void 0&&B.results.length===a.calls.length&&W.push(...a.calls.map((e,t)=>({toolName:e.toolName,rawArguments:e.rawArguments,failed:B.results[t]?.isError??!0}))),M+=B.budgeted;let G=Math.max(0,p.maxToolCalls-M),K=ae.filter(e=>G<=e).length;if(K>oe&&G>0&&(oe=K,le(`Tool budget: ${G} of ${p.maxToolCalls} calls remain in this turn. Stop broad exploration, make the necessary edits, and reserve calls for`+(N?` verification. When the budget runs out no further work call will run, so finish with what you can still verify.`:` verification. Past the budget you are expected to be wrapping up, not opening new lines of work.`))),!N&&G===0){let e=Math.floor(M/p.maxToolCalls);e>se&&(se=e,le(`Tool budget spent: ${M} calls made against a budget of ${p.maxToolCalls}. Do not start new substantive work. Finish what is in flight, verify what you can, and answer — stating plainly what is unverified or unfinished.`))}await u({type:`step-end`,turn:k,step:r,trace:a.trace}),ce.push(m);let q=f;for(let t=0;t<a.calls.length;t++){let n=a.calls[t],r=B.results[t];if(n===void 0||r===void 0)continue;I=r.isError?I+1:0;let o=ne(n),s=V?.key===o?V.count+1:1;V={key:o,count:s},s===p.repeatToolWarningAt&&e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`tool-loop-repeat-guard`},content:[{type:`text`,text:`You have called ${n.toolName} with the same arguments ${s} consecutive times. Reassess before repeating it.`}]})}),s>=p.repeatToolLimit&&(q=!0)}if(_?.repetitions===p.toolCycleWarningAt&&e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`tool-loop-cycle-guard`},content:[{type:`text`,text:`Tool-use pattern repeated ${_.repetitions} times across ${_.period} step(s). Reassess the approach and identify concrete new evidence before continuing.`}]})}),B.concluded){z={kind:`concluded-by-tool`,toolName:B.concludedBy??`unknown`};break}if(t.aborted){z={kind:`aborted`};break}if(a.usageRequired){z=Q;break}if(a.usageUnavailable){z=w(e.accounting)??{kind:`usage-unavailable`,modelCallId:a.report?.modelCallId??`unknown`};break}let X,Z=p.maxTotalTokens!==`auto`&&p.finalReportReserveTokens>0&&S!==void 0&&S>=h-p.finalReportReserveTokens;if(E||Z?X=`tokens`:v?X=`tool-call-cycle`:N&&c>l?X=`tool-calls`:I>=p.maxConsecutiveToolErrors?X=`consecutive-tool-errors`:q?X=`repeated-tool-call`:j>=g&&(X=`steps`),X!==void 0){let n=(X!==`tokens`||Z&&!E)&&p.onExhausted!==`stop`&&$()===void 0;if(n){let n=await T(e,t,u,H,O,k,j+1,`forced-final`);if(j++,n.report!==void 0&&R.push(n.report),n.message!==void 0&&(e.history.append({kind:`assistant`,message:n.message,...n.usage===void 0?{}:{usage:n.usage}}),await u({type:`assistant-message`,message:n.message,trace:n.trace}),await x(n,u),L=C(n.message.content)),n.finish.kind===`aborted`){z={kind:`aborted`};break}if(n.finish.kind===`error`){z={kind:`error`,failure:n.finish.failure};break}if(n.finish.kind===`max-tokens`){z={kind:`max-tokens`};break}if(n.usageRequired){z={kind:`error`,failure:{message:`provider usage is required by the configured run policy`,code:`USAGE_REQUIRED`}};break}if(n.usageUnavailable){z=w(e.accounting)??{kind:`usage-unavailable`,modelCallId:n.report?.modelCallId??`unknown`};break}}z={kind:`budget-exhausted`,budget:X,forcedFinalAnswer:n,...X===`tokens`&&Z&&!E?{trigger:`report-reserve`}:{}}}}z===void 0&&(z=t.aborted?{kind:`aborted`}:{kind:`budget-exhausted`,budget:`steps`,forcedFinalAnswer:!1});let n=s(R),r=a(n),c={reason:z,text:L,steps:j,usageReport:n,toolCalls:M,traceId:D,...r===void 0?{}:{usage:r}},d=e.history.entries().length,f=z.kind===`completed`&&j<g&&$()===void 0;if(await b(e.hooks?.onTurnEnd,[{outcome:c,snapshot:e.history.snapshot(),canContinue:f}],e,t.aborted?new AbortController().signal:t,`onTurnEnd`),f&&!t.aborted&&(e.history.entries().length>d||l(e.history))){z=void 0;continue turnLifecycle}B=c;break}B.reason.kind===`aborted`&&e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`turn-interrupted`},content:[{type:`text`,text:`The previous request was interrupted before it finished. Use the next message to determine what to do next.`}]})}),await u({type:`span-end`,trace:O,at:y(),status:B.reason.kind===`error`?`error`:B.reason.kind===`aborted`?`aborted`:`success`,output:{reason:B.reason,text:L},...B.usage===void 0?{}:{usage:B.usage},...B.reason.kind===`error`?{error:{type:`ModelError`,message:B.reason.failure.message,code:B.reason.failure.code}}:{}}),K=!0,X!==void 0&&(e.accounting?.endOperation(X,B.reason.kind===`error`?`error`:B.reason.kind===`aborted`?`aborted`:`success`,{data:{reason:B.reason.kind,steps:j,toolCalls:M}}),Z=!0),await u({type:`turn-end`,outcome:B,trace:O})}catch(n){let r=_(n);throw G&&!K&&await u({type:`span-end`,trace:O,at:y(),status:t.aborted?`aborted`:`error`,error:{type:n instanceof Error?n.name:`RuntimeError`,message:v(n),...r===void 0?{}:{code:r}}}).catch(()=>void 0),X!==void 0&&!Z&&(e.accounting?.endOperation(X,t.aborted?`aborted`:`error`,{error:n}),Z=!0),n}finally{Y?.close()}}export{D as runTurn};
|
|
1
|
+
import{detachedFrozen as e}from"../../primitives/snapshot.js";import{waitForSettlement as t}from"../../async/settlement.js";import{createSpanId as n,createTraceId as r}from"../../observation/context.js";import{createUserMessage as i}from"../../message/message.js";import{authoritativeTokenUsage as a,budgetTokenTotal as o,summarizeModelCallUsage as s}from"../accounting/usage.js";import{ContextSectionRuntime as c}from"../context/section.js";import{deliverQueuedInput as l}from"./turn/model-request-boundary.js";import{AwaitedEventQueue as u}from"./queue.js";import{captureProgramGrants as d}from"../tool/nested.js";import{scheduleToolCalls as ee}from"./schedule.js";import{ProgramResultStore as f}from"../tool/program-results.js";import{positiveFinite as p,resolveBounds as m,snapshotRunTurnOptions as h}from"./turn/config.js";import{codedRuntimeError as g,errorCodeOf as _,messageOf as v,now as y}from"./turn/common.js";import{runHook as te,runOptionalHook as ne}from"./turn/hooks.js";import{emitAssistantContent as b,maintenanceEmitter as x,textOf as S}from"./turn/content.js";import{repeatKey as C,repeatedSuffixCycle as re,toolActionPattern as ie}from"./turn/repetition.js";import{accountingUsageStop as w}from"./turn/usage-stop.js";import{modelRound as T}from"./turn/model-round.js";function E(e){return e.toolChoice===`none`?!1:(e.tools?.names().length??0)>0||(e.nativeTools?.length??0)>0}function D(n){return{async*[Symbol.asyncIterator](){let r=h(n),i=new u,a=new AbortController,o=p(r.teardownTimeoutMs??3e4,`teardownTimeoutMs`),s=O(r,r.signal===void 0?a.signal:AbortSignal.any([r.signal,a.signal]),t=>i.push(e(t))).then(()=>i.close(),e=>i.fail(e));try{for(;;){let e=await i.take();if(e.done)break;yield e.value}await s}finally{if(a.abort(Error(`turn event consumer stopped`)),i.close(),!await t(s,o))throw g(`turn producer ignored cancellation for more than ${o}ms`,`TURN_TEARDOWN_TIMEOUT`,a.signal.reason)}}}}async function O(e,t,u){let p=m(e.bounds),h=p.maxTotalTokens===`auto`?1/0:p.maxTotalTokens,g=p.maxSteps===`auto`?1/0:p.maxSteps,D=e.trace?.traceId??r(),O={traceId:D,spanId:n(),parentSpanId:e.trace?.parentSpanId??null},k=Math.max(1,e.history.entries().filter(e=>e.event.kind===`user`&&e.event.message.source.kind===`user`).length),oe=y(),A=0,j=0,se=[...p.toolBudgetRemindAt].filter(e=>e>0&&e<p.maxToolCalls).sort((e,t)=>t-e),ce=0,le=0,M,ue=!1,de=0,N=0,P=``,F=[],I,L,R,fe=new Map,z=new Set,pe=[],B=x(u,O),me=t=>{let n=i({source:{kind:`app`,producer:`tool-loop-budget-guard`},content:[{type:`text`,text:t}]}),r=M;if(r!==void 0)try{M=e.history.append({kind:`user`,message:n},{op:`replace`,from:r,to:r,targets:[r]}).seq;return}catch{M=void 0}M=e.history.append({kind:`user`,message:n}).seq},V=e.contextSections===void 0||e.contextSections.length===0?void 0:new c({sections:e.contextSections,history:e.history,...e.logger===void 0?{}:{logger:e.logger},guard:(n,r)=>te(n,e,t,r),scope:{agentId:e.trace?.agentId,conversationId:e.trace?.conversationId}}),H=[],U=!1,W=!1,G=E(e),K=e.experimentalPrograms===void 0?void 0:d(e.experimentalPrograms),q=K===void 0?void 0:new f,he=e.outputFormat?.type===`json_schema`&&G,J=e.accounting?.startOperation(`turn`,{data:{turn:k,model:e.config.model}}),Y=!1,X,Z=()=>{if(t.aborted)return{kind:`aborted`};let n=w(e.accounting);if(n!==void 0)return n;if(X!==void 0)return X;let r=o(s(F));return r!==void 0&&r>=h?{kind:`budget-exhausted`,budget:`tokens`,forcedFinalAnswer:!1}:void 0};try{await u({type:`span-start`,trace:O,at:oe,name:`invoke_agent ${e.trace?.agentId??`agent`}`,kind:`invoke_agent`,attributes:{"gen_ai.operation.name":`invoke_agent`,"gen_ai.agent.id":e.trace?.agentId??`agent`,"gen_ai.agent.name":e.trace?.agentName??e.trace?.agentId??`agent`,"gen_ai.request.model":e.config.model,...e.trace?.conversationId===void 0?{}:{"gen_ai.conversation.id":e.trace.conversationId}}}),U=!0,await u({type:`turn-start`,turn:k,trace:O});turnLifecycle:for(;;){for(;I===void 0&&A<g&&!t.aborted&&(I=Z(),I===void 0);){let n=o(s(F));if(p.maxTotalTokens!==`auto`&&n!==void 0){let t=[.5,.75,.9].filter(e=>n>=h*e).length;if(t>de){de=t;let r=Math.max(0,h-n);e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`tool-loop-token-guard`},content:[{type:`text`,text:`Token budget: ${r} of ${p.maxTotalTokens} aggregate tokens remain. Each request also spends input tokens on the accumulated context. `+(t>=2?`Stop broad exploration. `:`Prioritize outstanding verification and reporting. `)+`Reserve capacity for the required self-check, honest todo reconciliation and substantive final report. State unavailable evidence and unfinished work; do not claim it is verified. No extra model call is allowed after the hard token limit.`}]})})}}!ue&&A>0&&g-A<=2&&(ue=!0,e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`tool-loop-step-guard`},content:[{type:`text`,text:`${g-A} work steps remain. Finish essential verification, update any task list honestly, and submit the result if required. Summarize findings, evidence, and unfinished work; do not start new exploration.`}]})})),V!==void 0&&(await V.reconcile(A,H,t),H=[]);let r=A+1,a=await T(e,t,u,B,O,k,r,he?`process`:e.outputFormat?.type===`json_schema`?`final`:`standard`);if(A++,a.report!==void 0&&F.push(a.report),a.message===void 0?a.finish.kind===`stop`&&(P=``):(e.history.append({kind:`assistant`,message:a.message,...a.finish.kind===`aborted`?{interrupted:!0}:{},...a.usage===void 0?{}:{usage:a.usage}}),await u({type:`assistant-message`,message:a.message,trace:a.trace}),await b(a,u),P=S(a.message.content)),X=w(e.accounting)??(a.usageRequired?{kind:`error`,failure:{message:`provider usage is required by the configured run policy`,code:`USAGE_REQUIRED`}}:a.usageUnavailable?{kind:`usage-unavailable`,modelCallId:a.report?.modelCallId??`unknown`}:void 0),t.aborted||a.finish.kind===`aborted`){I={kind:`aborted`};break}if(a.finish.kind===`error`&&X!==void 0){I={kind:`error`,failure:a.finish.failure};break}if(a.usageRequired&&(a.calls.length===0||e.tools===void 0)){I=X;break}if(a.finish.kind===`error`&&!a.usageRequired){if(I=Z(),I!==void 0)break;let n=await ne(e.hooks?.onRequestError,[{turn:k,step:r,failure:a.finish.failure,snapshot:e.history.snapshot(),signal:t,...e.logger===void 0?{}:{logger:e.logger},emit:B}],e,t,`onRequestError`),i=w(e.accounting);if(i!==void 0){I=t.aborted?{kind:`aborted`}:i;break}if(n===`retry`&&A<g)continue;I={kind:`error`,failure:a.finish.failure};break}if(a.finish.kind===`max-tokens`&&!a.usageRequired){I={kind:`max-tokens`};break}if(a.calls.length===0&&a.usageUnavailable){I=w(e.accounting)??{kind:`usage-unavailable`,modelCallId:a.report?.modelCallId??`unknown`};break}if(a.calls.length===0&&he){if(I=Z(),I!==void 0)break;e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`structured-output-finalizer`},content:[{type:`text`,text:`The process phase is complete. Return the final answer now in the requested output format. Do not call tools.`}]})});let n=await T(e,t,u,B,O,k,A+1,`final`);A++,n.report!==void 0&&F.push(n.report),n.message!==void 0&&(e.history.append({kind:`assistant`,message:n.message,...n.usage===void 0?{}:{usage:n.usage}}),await u({type:`assistant-message`,message:n.message,trace:n.trace}),await b(n,u),P=S(n.message.content)),I=n.finish.kind===`aborted`?{kind:`aborted`}:n.finish.kind===`error`?{kind:`error`,failure:n.finish.failure}:n.finish.kind===`max-tokens`?{kind:`max-tokens`}:n.usageRequired?{kind:`error`,failure:{message:`provider usage is required by the configured run policy`,code:`USAGE_REQUIRED`}}:n.usageUnavailable?w(e.accounting)??{kind:`usage-unavailable`,modelCallId:n.report?.modelCallId??`unknown`}:{kind:`completed`};break}if(a.droppedDuplicateCalls!==void 0&&e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`tool-loop-duplicate-guard`},content:[{type:`text`,text:`You reused the tool-call id ${a.droppedDuplicateCalls.map(e=>`'${e}'`).join(`, `)}. A result pairs to exactly one call, so the repeat was not run and only the first call of that id has a result. Give every call its own id, and call again if you still need what the dropped one would have done.`}]})}),a.calls.length===0||e.tools===void 0){I={kind:`completed`};break}let c=Math.max(0,p.maxToolCalls-j),l=R,d=a.calls.map(e=>{let t=C(e),n=l?.key===t?l.count+1:1;return l={key:t,count:n},n}),f=d.some(e=>e>=p.repeatToolLimit),m=ie(a.calls),_=re([...pe,m],p.maxToolCycleLength),v=_!==void 0&&_.repetitions>=p.toolCycleLimit,y=s(F),x=o(y),E=x!==void 0&&x>=h,D=v||E||a.usageRequired,oe=E?`tokens`:v?`tool-call-cycle`:a.usageRequired?`usage-required`:`tool-calls`,M=p.onExhausted!==`continue`,L=new Set,U=new Set(a.calls.filter((e,t)=>(d[t]??0)>=p.repeatToolLimit).map(e=>String(e.callId))),W=a.calls.filter(t=>!U.has(String(t.callId))&&e.tools?.get(t.toolName)?.budgetExempt!==!0).length,G=f&&!v&&!E&&!a.usageRequired?t=>{let n=fe.get(C(t));if(!(!U.has(String(t.callId))||R?.key!==C(t)||R.succeeded!==!0||n===void 0||n.callId!==R.callId||n.rawArguments!==t.rawArguments||n.definition!==e.tools?.get(t.toolName)||n.result.additionalContext!==void 0||n.result.concludesTurn===!0||e.tools?.get(t.toolName)?.budgetExempt===!0))return L.add(String(t.callId)),ae(n)}:void 0,J=await ee({calls:a.calls,catalog:e.tools,history:e.history,position:{turn:k,step:r,...e.accounting===void 0?{}:{runId:e.accounting.runId},...e.trace?.conversationId===void 0?{}:{conversationId:e.trace.conversationId}},signal:t,parentTrace:O,maxParallel:p.maxParallel,dispatchLimit:D?0:M?c:a.calls.length,declineReason:oe,maxResultBytes:p.maxToolResultBytes,maxResultTokens:p.maxToolResultTokens,resultOverflow:p.toolResultOverflow,...e.spillStore===void 0?{}:{spillStore:e.spillStore},maxDurationMs:p.maxToolDurationMs,teardownTimeoutMs:p.toolTeardownTimeoutMs,...e.logger===void 0?{}:{logger:e.logger},...e.interceptors===void 0?{}:{interceptors:e.interceptors},...e.approvals===void 0?{}:{approvals:e.approvals},...e.accounting===void 0?{}:{accounting:e.accounting},emit:u,...G===void 0?{}:{recover:G},...e.hooks?.checkpoint===void 0?{}:{checkpoint:n=>te(Promise.resolve(e.hooks?.checkpoint?.(n)),e,t,`checkpoint`)}},{admissionLimit:D?0:M?c:`unbounded`,...D||!f?{}:{decline:e=>U.has(String(e.callId))?`repeated-tool-call`:void 0},...K===void 0?{}:{programs:K},...q===void 0?{}:{programResults:q}});V!==void 0&&J.results.length===a.calls.length&&H.push(...a.calls.map((e,t)=>({toolName:e.toolName,rawArguments:e.rawArguments,failed:J.results[t]?.isError??!0}))),j+=J.budgeted;let Y=Math.max(0,p.maxToolCalls-j),ge=se.filter(e=>Y<=e).length;if(ge>ce&&Y>0&&(ce=ge,me(`Tool budget: ${Y} of ${p.maxToolCalls} calls remain in this turn. Stop broad exploration, make the necessary edits, and reserve calls for`+(M?` verification. When the budget runs out no further work call will run, so finish with what you can still verify.`:` verification. Past the budget you are expected to be wrapping up, not opening new lines of work.`))),!M&&Y===0){let e=Math.floor(j/p.maxToolCalls);e>le&&(le=e,me(`Tool budget spent: ${j} calls made against a budget of ${p.maxToolCalls}. Do not start new substantive work. Finish what is in flight, verify what you can, and answer — stating plainly what is unverified or unfinished.`))}await u({type:`step-end`,turn:k,step:r,trace:a.trace}),pe.push(m);let _e=f;for(let t=0;t<a.calls.length;t++){let n=a.calls[t],r=J.results[t];if(n===void 0||r===void 0)continue;N=r.isError?N+1:0;let o=C(n),s=R?.key===o?R.count+1:1;R={key:o,count:s,callId:String(n.callId),succeeded:!r.isError&&r.meta?.declined!==!0&&e.tools?.get(n.toolName)?.budgetExempt!==!0},s===p.repeatToolWarningAt&&e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`tool-loop-repeat-guard`},content:[{type:`text`,text:`You have called ${n.toolName} with the same arguments ${s} consecutive times. Reassess before repeating it.`}]})}),s>=p.repeatToolLimit&&(_e=!0),!r.isError&&r.meta?.declined!==!0&&e.tools?.get(n.toolName)?.budgetExempt!==!0&&fe.set(o,{callId:String(n.callId),rawArguments:n.rawArguments,definition:e.tools.get(n.toolName),result:r})}if(_?.repetitions===p.toolCycleWarningAt&&e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`tool-loop-cycle-guard`},content:[{type:`text`,text:`Tool-use pattern repeated ${_.repetitions} times across ${_.period} step(s). Reassess the approach and identify concrete new evidence before continuing.`}]})}),J.concluded){I={kind:`concluded-by-tool`,toolName:J.concludedBy??`unknown`};break}if(t.aborted){I={kind:`aborted`};break}if(a.usageRequired){I=X;break}if(a.usageUnavailable){I=w(e.accounting)??{kind:`usage-unavailable`,modelCallId:a.report?.modelCallId??`unknown`};break}let Q,$=p.maxTotalTokens!==`auto`&&p.finalReportReserveTokens>0&&x!==void 0&&x>=h-p.finalReportReserveTokens,ve=f&&L.size===a.calls.length&&a.calls.every(e=>L.has(String(e.callId))&&!z.has(C(e)))&&J.results.length===a.calls.length&&J.results.every(e=>!e.isError)&&J.dispatched===0&&J.declined===0&&!v&&!E&&!$&&!a.usageRequired&&!a.usageUnavailable&&!t.aborted&&!(M&&W>c)&&N===0&&A<g&&p.onExhausted!==`stop`&&Z()===void 0,ye=f&&[...U].every(e=>L.has(e))&&J.results.length===a.calls.length&&J.dispatched>0&&J.declined===0;if(ve)for(let e of a.calls)z.add(C(e));else E||$?Q=`tokens`:v?Q=`tool-call-cycle`:M&&W>c?Q=`tool-calls`:N>=p.maxConsecutiveToolErrors?Q=`consecutive-tool-errors`:_e&&!ye?Q=`repeated-tool-call`:A>=g&&(Q=`steps`);if(Q===void 0&&ye)for(let e of a.calls)L.has(String(e.callId))&&z.add(C(e));if(Q!==void 0){let n=(Q!==`tokens`||$&&!E)&&p.onExhausted!==`stop`&&Z()===void 0;if(n){let n=await T(e,t,u,B,O,k,A+1,`forced-final`);if(A++,n.report!==void 0&&F.push(n.report),n.message!==void 0&&(e.history.append({kind:`assistant`,message:n.message,...n.usage===void 0?{}:{usage:n.usage}}),await u({type:`assistant-message`,message:n.message,trace:n.trace}),await b(n,u),P=S(n.message.content)),n.finish.kind===`aborted`){I={kind:`aborted`};break}if(n.finish.kind===`error`){I={kind:`error`,failure:n.finish.failure};break}if(n.finish.kind===`max-tokens`){I={kind:`max-tokens`};break}if(n.usageRequired){I={kind:`error`,failure:{message:`provider usage is required by the configured run policy`,code:`USAGE_REQUIRED`}};break}if(n.usageUnavailable){I=w(e.accounting)??{kind:`usage-unavailable`,modelCallId:n.report?.modelCallId??`unknown`};break}}I={kind:`budget-exhausted`,budget:Q,forcedFinalAnswer:n,...Q===`tokens`&&$&&!E?{trigger:`report-reserve`}:{}}}}I===void 0&&(I=t.aborted?{kind:`aborted`}:{kind:`budget-exhausted`,budget:`steps`,forcedFinalAnswer:!1});let n=s(F),r=a(n),c={reason:I,text:P,steps:A,usageReport:n,toolCalls:j,traceId:D,...r===void 0?{}:{usage:r}},d=e.history.entries().length,f=I.kind===`completed`&&A<g&&Z()===void 0;if(await ne(e.hooks?.onTurnEnd,[{outcome:c,snapshot:e.history.snapshot(),canContinue:f}],e,t.aborted?new AbortController().signal:t,`onTurnEnd`),f&&!t.aborted&&(e.history.entries().length>d||l(e.history))){I=void 0;continue turnLifecycle}L=c;break}L.reason.kind===`aborted`&&e.history.append({kind:`user`,message:i({source:{kind:`app`,producer:`turn-interrupted`},content:[{type:`text`,text:`The previous request was interrupted before it finished. Use the next message to determine what to do next.`}]})}),await u({type:`span-end`,trace:O,at:y(),status:L.reason.kind===`error`?`error`:L.reason.kind===`aborted`?`aborted`:`success`,output:{reason:L.reason,text:P},...L.usage===void 0?{}:{usage:L.usage},...L.reason.kind===`error`?{error:{type:`ModelError`,message:L.reason.failure.message,code:L.reason.failure.code}}:{}}),W=!0,J!==void 0&&(e.accounting?.endOperation(J,L.reason.kind===`error`?`error`:L.reason.kind===`aborted`?`aborted`:`success`,{data:{reason:L.reason.kind,steps:A,toolCalls:j}}),Y=!0),await u({type:`turn-end`,outcome:L,trace:O})}catch(n){let r=_(n);throw U&&!W&&await u({type:`span-end`,trace:O,at:y(),status:t.aborted?`aborted`:`error`,error:{type:n instanceof Error?n.name:`RuntimeError`,message:v(n),...r===void 0?{}:{code:r}}}).catch(()=>void 0),J!==void 0&&!Y&&(e.accounting?.endOperation(J,t.aborted?`aborted`:`error`,{error:n}),Y=!0),n}finally{q?.close()}}function ae(t){return e({isError:!1,value:t.result.value,content:[{type:`text`,text:JSON.stringify({status:`duplicate_of`,callId:t.callId,instruction:`This exact call already succeeded in this run. Use the retained result below; do not call it again.`})},...t.result.content],meta:{...t.result.meta,recovered:!0,duplicateOfCallId:t.callId}})}export{D as runTurn};
|
|
2
2
|
//# sourceMappingURL=run-turn.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-turn.js","names":[],"sources":["../../../src/agent/loop/run-turn.ts"],"sourcesContent":["import { createUserMessage } from '../../message/index.ts'\nimport { detachedFrozen } from '../../primitives/index.ts'\nimport type { ModelCallReport } from '../../observation/index.ts'\nimport { waitForSettlement } from '../../async/index.ts'\nimport { authoritativeTokenUsage, budgetTokenTotal, summarizeModelCallUsage } from '../accounting/ledger.ts'\nimport { createSpanId, createTraceId, type TraceRef } from '../trace/trace.ts'\nimport type { AgentEvent, ExhaustedBudget, ToolDeclineReason, TurnHooks, TurnOutcome } from './types.ts'\nimport { AwaitedEventQueue } from './queue.ts'\nimport { scheduleToolCalls } from './schedule.ts'\nimport { captureProgramGrants } from '../tool/nested.ts'\nimport { ProgramResultStore } from '../tool/program-results.ts'\nimport { type RunTurnOptions } from './turn/types.ts'\nimport { resolveBounds, positiveFinite, snapshotRunTurnOptions } from './turn/config.ts'\nimport { codedRuntimeError, messageOf, errorCodeOf, now } from './turn/common.ts'\nimport { deliverQueuedInput } from './turn/model-request-boundary.ts'\nimport { runOptionalHook, runHook } from './turn/hooks.ts'\nimport { maintenanceEmitter, emitAssistantContent, textOf } from './turn/content.ts'\nimport { repeatKey, toolActionPattern, repeatedSuffixCycle } from './turn/repetition.ts'\nimport { modelRound } from './turn/model-round.ts'\nimport { accountingUsageStop } from './turn/usage-stop.ts'\nimport { ContextSectionRuntime } from '../context/section.ts'\nimport type { ContextToolTouch } from '../context/types.ts'\n\nfunction hasCallableTools(options: RunTurnOptions): boolean {\n if (options.toolChoice === 'none') return false\n return (options.tools?.names().length ?? 0) > 0 || (options.nativeTools?.length ?? 0) > 0\n}\n\n/** Run one bounded turn as a backpressured event stream. */\nexport function runTurn(options: RunTurnOptions): AsyncIterable<AgentEvent> {\n return {\n async * [Symbol.asyncIterator]() {\n const invocation = snapshotRunTurnOptions(options)\n const queue = new AwaitedEventQueue<AgentEvent>()\n const consumer = new AbortController()\n const teardownTimeoutMs = positiveFinite(invocation.teardownTimeoutMs ?? 30_000, 'teardownTimeoutMs')\n const signal = invocation.signal === undefined\n ? consumer.signal\n : AbortSignal.any([invocation.signal, consumer.signal])\n const task = driveTurn(invocation, signal, event => queue.push(detachedFrozen(event)))\n .then(() => queue.close(), error => queue.fail(error))\n try {\n while (true) {\n const item = await queue.take()\n if (item.done) break\n yield item.value\n }\n await task\n } finally {\n consumer.abort(new Error('turn event consumer stopped'))\n queue.close()\n if (!await waitForSettlement(task, teardownTimeoutMs)) {\n throw codedRuntimeError(\n `turn producer ignored cancellation for more than ${teardownTimeoutMs}ms`,\n 'TURN_TEARDOWN_TIMEOUT',\n consumer.signal.reason,\n )\n }\n }\n },\n }\n}\n\nasync function driveTurn(\n options: RunTurnOptions,\n signal: AbortSignal,\n emit: (event: AgentEvent) => Promise<void>,\n): Promise<void> {\n const bounds = resolveBounds(options.bounds)\n const maxTotalTokens = bounds.maxTotalTokens === 'auto' ? Infinity : bounds.maxTotalTokens\n // Infinity is local control flow only. Public configuration/events retain\n // the serializable 'auto' value, and all other admission guards still apply.\n const maxSteps = bounds.maxSteps === 'auto' ? Infinity : bounds.maxSteps\n const traceId = options.trace?.traceId ?? createTraceId()\n const root: TraceRef = { traceId, spanId: createSpanId(), parentSpanId: options.trace?.parentSpanId ?? null }\n const turn = Math.max(1, options.history.entries().filter(entry =>\n entry.event.kind === 'user' && entry.event.message.source.kind === 'user').length)\n const startedAt = now()\n let steps = 0\n let toolCalls = 0\n // Descending, so \"how many are below the remainder\" counts the crossings.\n const budgetReminders = [...bounds.toolBudgetRemindAt]\n .filter(threshold => threshold > 0 && threshold < bounds.maxToolCalls)\n .sort((left, right) => right - left)\n let budgetRemindersSent = 0\n /** How many full budgets an unwalled turn has spent and been told about. */\n let overBudgetNoticesSent = 0\n /** Sequence of the budget notice currently on the surface, if any. */\n let budgetNoticeSeq: number | undefined\n let stepReminderSent = false\n let tokenRemindersSent = 0\n let consecutiveErrors = 0\n let text = ''\n const modelCallReports: ModelCallReport[] = []\n let reason: TurnOutcome['reason'] | undefined\n let outcome: TurnOutcome\n let lastRepeat: { key: string; count: number } | undefined\n const actionSteps: string[] = []\n const emitMaintenance = maintenanceEmitter(emit, root)\n /**\n * Put the current budget notice on the surface, replacing the previous one.\n *\n * One live notice, never a pile of them. Appending each new remainder leaves\n * \"16 calls remain\" and \"6 calls remain\" both readable, and a model that\n * plans against the older number plans against a budget it no longer has.\n * Codex keeps exactly one `<rollout_budget>` fragment for the same reason\n * (`context/rollout_budget.rs`), replacing it as the number moves.\n * @param text - What the model should read now.\n */\n const appendBudgetNotice = (text: string): void => {\n const message = createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-budget-guard' },\n content: [{ type: 'text', text }],\n })\n const previous = budgetNoticeSeq\n // Replacing is best-effort. Compaction can shadow the earlier notice\n // between steps, and a replace whose target is no longer on the surface is\n // rejected — which must cost the model a reminder, never the turn.\n if (previous !== undefined) {\n try {\n budgetNoticeSeq = options.history.append(\n { kind: 'user', message },\n { op: 'replace', from: previous, to: previous, targets: [previous] },\n ).seq\n return\n } catch {\n budgetNoticeSeq = undefined\n }\n }\n budgetNoticeSeq = options.history.append({ kind: 'user', message }).seq\n }\n const contextSections = options.contextSections === undefined || options.contextSections.length === 0\n ? undefined\n : new ContextSectionRuntime({\n sections: options.contextSections,\n history: options.history,\n ...options.logger === undefined ? {} : { logger: options.logger },\n guard: (pending, name) => runHook(pending, options, signal, name),\n scope: { agentId: options.trace?.agentId, conversationId: options.trace?.conversationId },\n })\n /** Tool calls committed since the last section reconcile. */\n let contextTouches: ContextToolTouch[] = []\n let rootStarted = false\n let rootEnded = false\n const callableTools = hasCallableTools(options)\n const programs = options.experimentalPrograms === undefined ? undefined : captureProgramGrants(options.experimentalPrograms)\n // Handles programs retain live for this turn only.\n const programResults = programs === undefined ? undefined : new ProgramResultStore()\n const dedicatedFinalOutput = options.outputFormat?.type === 'json_schema' && callableTools\n const turnOperationId = options.accounting?.startOperation('turn', {\n data: { turn, model: options.config.model },\n })\n let turnOperationEnded = false\n let usageStop: TurnOutcome['reason'] | undefined\n // Shared by retries, hook continuations, and both finalizer paths. Finalizers\n // may have a separate step allowance, never a separate token allowance.\n const admissionStop = (): TurnOutcome['reason'] | undefined => {\n if (signal.aborted) return { kind: 'aborted' }\n const mandatoryStop = accountingUsageStop(options.accounting)\n if (mandatoryStop !== undefined) return mandatoryStop\n if (usageStop !== undefined) return usageStop\n const tokens = budgetTokenTotal(summarizeModelCallUsage(modelCallReports))\n return tokens !== undefined && tokens >= maxTotalTokens\n ? { kind: 'budget-exhausted', budget: 'tokens', forcedFinalAnswer: false }\n : undefined\n }\n\n try {\n await emit({\n type: 'span-start', trace: root, at: startedAt,\n name: `invoke_agent ${options.trace?.agentId ?? 'agent'}`, kind: 'invoke_agent',\n attributes: {\n 'gen_ai.operation.name': 'invoke_agent',\n 'gen_ai.agent.id': options.trace?.agentId ?? 'agent',\n 'gen_ai.agent.name': options.trace?.agentName ?? options.trace?.agentId ?? 'agent',\n 'gen_ai.request.model': options.config.model,\n ...options.trace?.conversationId === undefined\n ? {}\n : { 'gen_ai.conversation.id': options.trace.conversationId },\n },\n })\n rootStarted = true\n await emit({ type: 'turn-start', turn, trace: root })\n\n turnLifecycle: while (true) {\n while (reason === undefined && steps < maxSteps && !signal.aborted) {\n reason = admissionStop()\n if (reason !== undefined) break\n // Auto has no step countdown. Warn from the same usage total as admission,\n // before a hard token stop can leave the agent without a reporting call.\n const usedTokens = budgetTokenTotal(summarizeModelCallUsage(modelCallReports))\n if (bounds.maxTotalTokens !== 'auto' && usedTokens !== undefined) {\n const crossed = [0.5, 0.75, 0.9].filter(ratio => usedTokens >= maxTotalTokens * ratio).length\n if (crossed > tokenRemindersSent) {\n tokenRemindersSent = crossed\n const remaining = Math.max(0, maxTotalTokens - usedTokens)\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-token-guard' },\n content: [{ type: 'text', text: `Token budget: ${remaining} of ${bounds.maxTotalTokens} aggregate tokens remain. Each request also spends input tokens on the accumulated context. `\n + (crossed >= 2 ? 'Stop broad exploration. ' : 'Prioritize outstanding verification and reporting. ')\n + 'Reserve capacity for the required self-check, honest todo reconciliation and substantive final report. State unavailable evidence and unfinished work; do not claim it is verified. No extra model call is allowed after the hard token limit.',\n }],\n }) })\n }\n }\n // Leave time to reconcile a plan and submit a result before the final\n // tools-disabled summary. Tool-call reminders do not cover step limits.\n if (!stepReminderSent && steps > 0 && maxSteps - steps <= 2) {\n stepReminderSent = true\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-step-guard' },\n content: [{ type: 'text', text:\n `${maxSteps - steps} work steps remain. Finish essential verification, update any task list honestly, and submit the result if required. Summarize findings, evidence, and unfinished work; do not start new exploration.`,\n }],\n }) })\n }\n // Recomputed before the request that will read it, so a section reacting to\n // the previous step's tool calls is already on the surface.\n if (contextSections !== undefined) {\n await contextSections.reconcile(steps, contextTouches, signal)\n contextTouches = []\n }\n const step = steps + 1\n const round = await modelRound(\n options, signal, emit, emitMaintenance, root, turn, step,\n dedicatedFinalOutput\n ? 'process'\n : options.outputFormat?.type === 'json_schema' ? 'final' : 'standard',\n )\n steps++\n if (round.report !== undefined) modelCallReports.push(round.report)\n if (round.message !== undefined) {\n options.history.append({\n kind: 'assistant', message: round.message,\n ...round.finish.kind === 'aborted' ? { interrupted: true as const } : {},\n ...round.usage === undefined ? {} : { usage: round.usage },\n })\n await emit({ type: 'assistant-message', message: round.message, trace: round.trace })\n await emitAssistantContent(round, emit)\n text = textOf(round.message.content)\n } else if (round.finish.kind === 'stop') {\n // A successful empty round cannot answer new input with earlier text.\n // Keep earlier text only when a later request fails or is interrupted.\n text = ''\n }\n usageStop = accountingUsageStop(options.accounting) ?? (round.usageRequired\n ? { kind: 'error', failure: { message: 'provider usage is required by the configured run policy', code: 'USAGE_REQUIRED' } }\n : round.usageUnavailable\n ? { kind: 'usage-unavailable', modelCallId: round.report?.modelCallId ?? 'unknown' }\n : undefined)\n if (signal.aborted || round.finish.kind === 'aborted') { reason = { kind: 'aborted' }; break }\n // Pair emitted tool calls with declined results even when policy stops the\n // run; never execute those calls or allow a retry hook to override policy.\n if (round.finish.kind === 'error' && usageStop !== undefined) {\n reason = { kind: 'error', failure: round.finish.failure }\n break\n }\n if (round.usageRequired && (round.calls.length === 0 || options.tools === undefined)) { reason = usageStop; break }\n if (round.finish.kind === 'error' && !round.usageRequired) {\n reason = admissionStop()\n if (reason !== undefined) break\n const decision = await runOptionalHook(options.hooks?.onRequestError, [{\n turn, step, failure: round.finish.failure, snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n emit: emitMaintenance,\n }], options, signal, 'onRequestError')\n const maintenanceStop = accountingUsageStop(options.accounting)\n if (maintenanceStop !== undefined) { reason = signal.aborted ? { kind: 'aborted' } : maintenanceStop; break }\n if (decision === 'retry' && steps < maxSteps) continue\n reason = { kind: 'error', failure: round.finish.failure }\n break\n }\n if (round.finish.kind === 'max-tokens' && !round.usageRequired) { reason = { kind: 'max-tokens' }; break }\n if (round.calls.length === 0 && round.usageUnavailable) {\n reason = accountingUsageStop(options.accounting) ?? { kind: 'usage-unavailable', modelCallId: round.report?.modelCallId ?? 'unknown' }\n break\n }\n if (round.calls.length === 0 && dedicatedFinalOutput) {\n reason = admissionStop()\n if (reason !== undefined) break\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'structured-output-finalizer' },\n content: [{\n type: 'text',\n text: 'The process phase is complete. Return the final answer now in the requested output format. Do not call tools.',\n }],\n }) })\n const final = await modelRound(\n options, signal, emit, emitMaintenance, root, turn, steps + 1, 'final',\n )\n steps++\n if (final.report !== undefined) modelCallReports.push(final.report)\n if (final.message !== undefined) {\n options.history.append({\n kind: 'assistant', message: final.message,\n ...final.usage === undefined ? {} : { usage: final.usage },\n })\n await emit({ type: 'assistant-message', message: final.message, trace: final.trace })\n await emitAssistantContent(final, emit)\n text = textOf(final.message.content)\n }\n if (final.finish.kind === 'aborted') reason = { kind: 'aborted' }\n else if (final.finish.kind === 'error') reason = { kind: 'error', failure: final.finish.failure }\n else if (final.finish.kind === 'max-tokens') reason = { kind: 'max-tokens' }\n else if (final.usageRequired) reason = { kind: 'error', failure: {\n message: 'provider usage is required by the configured run policy',\n code: 'USAGE_REQUIRED',\n } }\n else if (final.usageUnavailable) {\n reason = accountingUsageStop(options.accounting) ?? { kind: 'usage-unavailable', modelCallId: final.report?.modelCallId ?? 'unknown' }\n } else reason = { kind: 'completed' }\n break\n }\n // A provider that repeated an id had the repeat dropped. Saying so is what\n // keeps the model from reading one result for two calls as a tool that\n // silently ignored it.\n if (round.droppedDuplicateCalls !== undefined) {\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-duplicate-guard' },\n content: [{\n type: 'text',\n text: `You reused the tool-call id ${round.droppedDuplicateCalls.map(id => `'${id}'`).join(', ')}.`\n + ' A result pairs to exactly one call, so the repeat was not run and only the first'\n + ' call of that id has a result. Give every call its own id, and call again if you'\n + ' still need what the dropped one would have done.',\n }],\n }) })\n }\n if (round.calls.length === 0 || options.tools === undefined) { reason = { kind: 'completed' }; break }\n\n // Calls the budget does not govern: submitting, asking, delegating. They\n // are dispatched even when the budget is spent, so the model always has a\n // legal way to finish. See `ToolDefinition.budgetExempt`.\n const budgetedCalls = round.calls\n .filter(call => options.tools?.get(call.toolName)?.budgetExempt !== true).length\n const remaining = Math.max(0, bounds.maxToolCalls - toolCalls)\n let repeatProjection = lastRepeat\n const projectedRepeats = round.calls.map(call => {\n const key = repeatKey(call)\n // Count consecutive calls, not lifetime visits to a source or test command.\n // Distinct intervening work may change its result. Alternating loops are\n // handled separately by the step-cycle guard below.\n const count = repeatProjection?.key === key ? repeatProjection.count + 1 : 1\n repeatProjection = { key, count }\n return count\n })\n const repeatedLimitBeforeDispatch = projectedRepeats.some(count => count >= bounds.repeatToolLimit)\n const actionPattern = toolActionPattern(round.calls)\n const projectedCycle = repeatedSuffixCycle(\n [...actionSteps, actionPattern],\n bounds.maxToolCycleLength,\n )\n const cycleLimitBeforeDispatch = projectedCycle !== undefined\n && projectedCycle.repetitions >= bounds.toolCycleLimit\n const currentUsage = summarizeModelCallUsage(modelCallReports)\n const budgetTokens = budgetTokenTotal(currentUsage)\n const tokenLimitBeforeDispatch = budgetTokens !== undefined\n && budgetTokens >= maxTotalTokens\n const guardDeclined = repeatedLimitBeforeDispatch || cycleLimitBeforeDispatch || tokenLimitBeforeDispatch || round.usageRequired\n // Name the limit that actually declined the call. Reporting a repeat guard\n // as an empty budget teaches the model the wrong lesson, and it repeats the\n // call on the next turn with the budget it was told it lacked.\n const declineReason: ToolDeclineReason = tokenLimitBeforeDispatch\n ? 'tokens'\n : cycleLimitBeforeDispatch\n ? 'tool-call-cycle'\n : repeatedLimitBeforeDispatch\n ? 'repeated-tool-call'\n : round.usageRequired\n ? 'usage-required'\n : 'tool-calls'\n // `continue` takes the wall down: the budget becomes a notice and the turn\n // is bounded by steps, tokens, and the run-level ledger instead. Guards\n // that mean \"this is not working\" still decline, whatever the setting.\n const budgetIsAWall = bounds.onExhausted !== 'continue'\n const scheduled = await scheduleToolCalls({\n calls: round.calls, catalog: options.tools, history: options.history,\n position: { turn, step,\n ...(options.accounting === undefined ? {} : { runId: options.accounting.runId }),\n ...(options.trace?.conversationId === undefined ? {} : { conversationId: options.trace.conversationId }),\n }, signal, parentTrace: root,\n maxParallel: bounds.maxParallel,\n dispatchLimit: guardDeclined ? 0 : budgetIsAWall ? remaining : round.calls.length,\n declineReason,\n maxResultBytes: bounds.maxToolResultBytes,\n maxResultTokens: bounds.maxToolResultTokens,\n resultOverflow: bounds.toolResultOverflow,\n ...options.spillStore === undefined ? {} : { spillStore: options.spillStore },\n maxDurationMs: bounds.maxToolDurationMs,\n teardownTimeoutMs: bounds.toolTeardownTimeoutMs,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n ...options.interceptors === undefined ? {} : { interceptors: options.interceptors },\n ...options.approvals === undefined ? {} : { approvals: options.approvals },\n ...options.accounting === undefined ? {} : { accounting: options.accounting },\n emit,\n ...options.hooks?.checkpoint === undefined ? {} : {\n checkpoint: (context: Parameters<NonNullable<TurnHooks['checkpoint']>>[0]) => runHook(\n Promise.resolve(options.hooks?.checkpoint?.(context)), options, signal, 'checkpoint',\n ),\n },\n }, {\n admissionLimit: guardDeclined ? 0 : budgetIsAWall ? remaining : 'unbounded',\n ...programs === undefined ? {} : { programs },\n ...programResults === undefined ? {} : { programResults },\n })\n // Results commit in model order, one per requested call, so the pairing\n // holds for declined calls too. A short result list means a sibling failed\n // its contract and the turn is already unwinding; reporting a guessed\n // outcome would be worse than reporting nothing.\n if (contextSections !== undefined && scheduled.results.length === round.calls.length) {\n contextTouches.push(...round.calls.map((call, index) => ({\n toolName: call.toolName,\n rawArguments: call.rawArguments,\n failed: scheduled.results[index]?.isError ?? true,\n })))\n }\n toolCalls += scheduled.budgeted\n const remainingAfterDispatch = Math.max(0, bounds.maxToolCalls - toolCalls)\n // One reminder per threshold crossed, not one per turn.\n //\n // This was a single boolean: the model was told once, at 25% remaining,\n // and then never again however close it came to the end. A model warned at\n // sixteen calls left and still exploring at four has been told nothing\n // since, and runs into the wall with no notice — which is what produces a\n // red \"no remaining tool-call budget\" where an answer should have been.\n //\n // Codex counts thresholds rather than remembering a flag\n // (`rollout_budget.rs`, `reminder_index`): every threshold now below the\n // remaining budget that has not been reported yet gets reported. Crossing\n // several at once collapses into the one that matters, the lowest.\n const crossed = budgetReminders.filter(threshold => remainingAfterDispatch <= threshold).length\n if (crossed > budgetRemindersSent && remainingAfterDispatch > 0) {\n budgetRemindersSent = crossed\n appendBudgetNotice(\n `Tool budget: ${remainingAfterDispatch} of ${bounds.maxToolCalls} calls remain in this turn.`\n + ' Stop broad exploration, make the necessary edits, and reserve calls for'\n + (budgetIsAWall\n ? ' verification. When the budget runs out no further work call will run, so'\n + ' finish with what you can still verify.'\n : ' verification. Past the budget you are expected to be wrapping up, not'\n + ' opening new lines of work.'),\n )\n }\n // Past the budget with the wall down. The turn is not stopped — the model\n // keeps its tools — but it is told, once per further budget spent, that it\n // is now expected to be finishing. This is the shape of Codex's\n // `budget_limit` goal prompt: no new substantive work, wrap up, name what\n // is left.\n if (!budgetIsAWall && remainingAfterDispatch === 0) {\n const windows = Math.floor(toolCalls / bounds.maxToolCalls)\n if (windows > overBudgetNoticesSent) {\n overBudgetNoticesSent = windows\n appendBudgetNotice(\n `Tool budget spent: ${toolCalls} calls made against a budget of ${bounds.maxToolCalls}.`\n + ' Do not start new substantive work. Finish what is in flight, verify what you'\n + ' can, and answer — stating plainly what is unverified or unfinished.',\n )\n }\n }\n await emit({ type: 'step-end', turn, step, trace: round.trace })\n\n actionSteps.push(actionPattern)\n let repeatedLimit = repeatedLimitBeforeDispatch\n for (let index = 0; index < round.calls.length; index++) {\n const call = round.calls[index]\n const result = scheduled.results[index]\n if (call === undefined || result === undefined) continue\n consecutiveErrors = result.isError ? consecutiveErrors + 1 : 0\n const key = repeatKey(call)\n const count = lastRepeat?.key === key ? lastRepeat.count + 1 : 1\n lastRepeat = { key, count }\n if (count === bounds.repeatToolWarningAt) {\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-repeat-guard' },\n content: [{ type: 'text', text: `You have called ${call.toolName} with the same arguments ${count} consecutive times. Reassess before repeating it.` }],\n }) })\n }\n if (count >= bounds.repeatToolLimit) repeatedLimit = true\n }\n if (projectedCycle?.repetitions === bounds.toolCycleWarningAt) {\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-cycle-guard' },\n content: [{\n type: 'text',\n text: `Tool-use pattern repeated ${projectedCycle.repetitions} times across ${projectedCycle.period} step(s). Reassess the approach and identify concrete new evidence before continuing.`,\n }],\n }) })\n }\n if (scheduled.concluded) { reason = { kind: 'concluded-by-tool', toolName: scheduled.concludedBy ?? 'unknown' }; break }\n if (signal.aborted) { reason = { kind: 'aborted' }; break }\n if (round.usageRequired) { reason = usageStop; break }\n if (round.usageUnavailable) {\n reason = accountingUsageStop(options.accounting) ?? { kind: 'usage-unavailable', modelCallId: round.report?.modelCallId ?? 'unknown' }\n break\n }\n let exhausted: ExhaustedBudget | undefined\n const reportReserveReached = bounds.maxTotalTokens !== 'auto'\n && bounds.finalReportReserveTokens > 0 && budgetTokens !== undefined\n && budgetTokens >= maxTotalTokens - bounds.finalReportReserveTokens\n if (tokenLimitBeforeDispatch) exhausted = 'tokens'\n else if (reportReserveReached) exhausted = 'tokens'\n else if (cycleLimitBeforeDispatch) exhausted = 'tool-call-cycle'\n else if (budgetIsAWall && budgetedCalls > remaining) exhausted = 'tool-calls'\n else if (consecutiveErrors >= bounds.maxConsecutiveToolErrors) exhausted = 'consecutive-tool-errors'\n else if (repeatedLimit) exhausted = 'repeated-tool-call'\n else if (steps >= maxSteps) exhausted = 'steps'\n if (exhausted !== undefined) {\n const forced = (exhausted !== 'tokens' || (reportReserveReached && !tokenLimitBeforeDispatch))\n && bounds.onExhausted !== 'stop'\n && admissionStop() === undefined\n if (forced) {\n const final = await modelRound(\n options, signal, emit, emitMaintenance, root, turn, steps + 1, 'forced-final',\n )\n steps++\n if (final.report !== undefined) modelCallReports.push(final.report)\n if (final.message !== undefined) {\n options.history.append({ kind: 'assistant', message: final.message, ...final.usage === undefined ? {} : { usage: final.usage } })\n await emit({ type: 'assistant-message', message: final.message, trace: final.trace })\n await emitAssistantContent(final, emit)\n text = textOf(final.message.content)\n }\n if (final.finish.kind === 'aborted') {\n reason = { kind: 'aborted' }\n break\n }\n if (final.finish.kind === 'error') {\n reason = { kind: 'error', failure: final.finish.failure }\n break\n }\n if (final.finish.kind === 'max-tokens') {\n reason = { kind: 'max-tokens' }\n break\n }\n if (final.usageRequired) {\n reason = { kind: 'error', failure: {\n message: 'provider usage is required by the configured run policy',\n code: 'USAGE_REQUIRED',\n } }\n break\n }\n if (final.usageUnavailable) {\n reason = accountingUsageStop(options.accounting) ?? { kind: 'usage-unavailable', modelCallId: final.report?.modelCallId ?? 'unknown' }\n break\n }\n }\n reason = { kind: 'budget-exhausted', budget: exhausted, forcedFinalAnswer: forced,\n ...(exhausted === 'tokens' && reportReserveReached && !tokenLimitBeforeDispatch\n ? { trigger: 'report-reserve' as const } : {}),\n }\n }\n }\n\n if (reason === undefined) {\n reason = signal.aborted\n ? { kind: 'aborted' }\n : { kind: 'budget-exhausted', budget: 'steps', forcedFinalAnswer: false }\n }\n const usageReport = summarizeModelCallUsage(modelCallReports)\n const usage = authoritativeTokenUsage(usageReport)\n const candidate: TurnOutcome = {\n reason, text, steps, usageReport, toolCalls, traceId,\n ...usage === undefined ? {} : { usage },\n }\n const entriesBeforeHook = options.history.entries().length\n const canContinue = reason.kind === 'completed'\n && steps < maxSteps\n && admissionStop() === undefined\n await runOptionalHook(options.hooks?.onTurnEnd, [{\n outcome: candidate,\n snapshot: options.history.snapshot(),\n canContinue,\n }], options, signal.aborted ? new AbortController().signal : signal, 'onTurnEnd')\n // Hooks object by appending context rather than by returning a veto. Re-run only\n // a normally completed turn; abort/error/budget outcomes remain terminal.\n // Input the person sent while the final answer was being written is owed an\n // answer too: when nothing else continues the turn, take it and continue.\n if (canContinue\n && !signal.aborted\n && (options.history.entries().length > entriesBeforeHook || deliverQueuedInput(options.history))) {\n reason = undefined\n continue turnLifecycle\n }\n outcome = candidate\n break\n }\n if (outcome.reason.kind === 'aborted') {\n // Without this, the next turn sees an unfinished request and a cancelled\n // tool call, and a model resumes the abandoned work instead of answering\n // the new message (observed live). Codex and Claude Code record the same\n // interruption marker.\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'turn-interrupted' },\n content: [{ type: 'text', text: 'The previous request was interrupted before it finished. Use the next message to determine what to do next.' }],\n }) })\n }\n await emit({\n type: 'span-end', trace: root, at: now(),\n status: outcome.reason.kind === 'error' ? 'error' : outcome.reason.kind === 'aborted' ? 'aborted' : 'success',\n output: { reason: outcome.reason, text },\n ...outcome.usage === undefined ? {} : { usage: outcome.usage },\n ...outcome.reason.kind === 'error' ? { error: { type: 'ModelError', message: outcome.reason.failure.message, code: outcome.reason.failure.code } } : {},\n })\n rootEnded = true\n if (turnOperationId !== undefined) {\n options.accounting?.endOperation(\n turnOperationId,\n outcome.reason.kind === 'error' ? 'error' : outcome.reason.kind === 'aborted' ? 'aborted' : 'success',\n { data: { reason: outcome.reason.kind, steps, toolCalls } },\n )\n turnOperationEnded = true\n }\n await emit({ type: 'turn-end', outcome, trace: root })\n } catch (error: unknown) {\n const code = errorCodeOf(error)\n if (rootStarted && !rootEnded) {\n await emit({\n type: 'span-end', trace: root, at: now(), status: signal.aborted ? 'aborted' : 'error',\n error: {\n type: error instanceof Error ? error.name : 'RuntimeError',\n message: messageOf(error),\n ...code === undefined ? {} : { code },\n },\n }).catch(() => undefined)\n }\n if (turnOperationId !== undefined && !turnOperationEnded) {\n options.accounting?.endOperation(\n turnOperationId,\n signal.aborted ? 'aborted' : 'error',\n { error },\n )\n turnOperationEnded = true\n }\n throw error\n } finally {\n programResults?.close()\n }\n}\n\n\nexport type { RunTurnOptions } from './turn/types.ts'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"run-turn.js","names":[],"sources":["../../../src/agent/loop/run-turn.ts"],"sourcesContent":["import { createUserMessage } from '../../message/index.ts'\nimport { detachedFrozen } from '../../primitives/index.ts'\nimport type { ModelCallReport } from '../../observation/index.ts'\nimport { waitForSettlement } from '../../async/index.ts'\nimport { authoritativeTokenUsage, budgetTokenTotal, summarizeModelCallUsage } from '../accounting/ledger.ts'\nimport { createSpanId, createTraceId, type TraceRef } from '../trace/trace.ts'\nimport type { ToolDefinition, ToolExecutionResult } from '../tool/definition.ts'\nimport type { AgentEvent, ExhaustedBudget, ToolDeclineReason, TurnHooks, TurnOutcome } from './types.ts'\nimport { AwaitedEventQueue } from './queue.ts'\nimport { scheduleToolCalls } from './schedule.ts'\nimport { captureProgramGrants } from '../tool/nested.ts'\nimport { ProgramResultStore } from '../tool/program-results.ts'\nimport { type RunTurnOptions } from './turn/types.ts'\nimport { resolveBounds, positiveFinite, snapshotRunTurnOptions } from './turn/config.ts'\nimport { codedRuntimeError, messageOf, errorCodeOf, now } from './turn/common.ts'\nimport { deliverQueuedInput } from './turn/model-request-boundary.ts'\nimport { runOptionalHook, runHook } from './turn/hooks.ts'\nimport { maintenanceEmitter, emitAssistantContent, textOf } from './turn/content.ts'\nimport { repeatKey, toolActionPattern, repeatedSuffixCycle } from './turn/repetition.ts'\nimport { modelRound } from './turn/model-round.ts'\nimport { accountingUsageStop } from './turn/usage-stop.ts'\nimport { ContextSectionRuntime } from '../context/section.ts'\nimport type { ContextToolTouch } from '../context/types.ts'\n\nfunction hasCallableTools(options: RunTurnOptions): boolean {\n if (options.toolChoice === 'none') return false\n return (options.tools?.names().length ?? 0) > 0 || (options.nativeTools?.length ?? 0) > 0\n}\n\n/** Run one bounded turn as a backpressured event stream. */\nexport function runTurn(options: RunTurnOptions): AsyncIterable<AgentEvent> {\n return {\n async * [Symbol.asyncIterator]() {\n const invocation = snapshotRunTurnOptions(options)\n const queue = new AwaitedEventQueue<AgentEvent>()\n const consumer = new AbortController()\n const teardownTimeoutMs = positiveFinite(invocation.teardownTimeoutMs ?? 30_000, 'teardownTimeoutMs')\n const signal = invocation.signal === undefined\n ? consumer.signal\n : AbortSignal.any([invocation.signal, consumer.signal])\n const task = driveTurn(invocation, signal, event => queue.push(detachedFrozen(event)))\n .then(() => queue.close(), error => queue.fail(error))\n try {\n while (true) {\n const item = await queue.take()\n if (item.done) break\n yield item.value\n }\n await task\n } finally {\n consumer.abort(new Error('turn event consumer stopped'))\n queue.close()\n if (!await waitForSettlement(task, teardownTimeoutMs)) {\n throw codedRuntimeError(\n `turn producer ignored cancellation for more than ${teardownTimeoutMs}ms`,\n 'TURN_TEARDOWN_TIMEOUT',\n consumer.signal.reason,\n )\n }\n }\n },\n }\n}\n\nasync function driveTurn(\n options: RunTurnOptions,\n signal: AbortSignal,\n emit: (event: AgentEvent) => Promise<void>,\n): Promise<void> {\n const bounds = resolveBounds(options.bounds)\n const maxTotalTokens = bounds.maxTotalTokens === 'auto' ? Infinity : bounds.maxTotalTokens\n // Infinity is local control flow only. Public configuration/events retain\n // the serializable 'auto' value, and all other admission guards still apply.\n const maxSteps = bounds.maxSteps === 'auto' ? Infinity : bounds.maxSteps\n const traceId = options.trace?.traceId ?? createTraceId()\n const root: TraceRef = { traceId, spanId: createSpanId(), parentSpanId: options.trace?.parentSpanId ?? null }\n const turn = Math.max(1, options.history.entries().filter(entry =>\n entry.event.kind === 'user' && entry.event.message.source.kind === 'user').length)\n const startedAt = now()\n let steps = 0\n let toolCalls = 0\n // Descending, so \"how many are below the remainder\" counts the crossings.\n const budgetReminders = [...bounds.toolBudgetRemindAt]\n .filter(threshold => threshold > 0 && threshold < bounds.maxToolCalls)\n .sort((left, right) => right - left)\n let budgetRemindersSent = 0\n /** How many full budgets an unwalled turn has spent and been told about. */\n let overBudgetNoticesSent = 0\n /** Sequence of the budget notice currently on the surface, if any. */\n let budgetNoticeSeq: number | undefined\n let stepReminderSent = false\n let tokenRemindersSent = 0\n let consecutiveErrors = 0\n let text = ''\n const modelCallReports: ModelCallReport[] = []\n let reason: TurnOutcome['reason'] | undefined\n let outcome: TurnOutcome\n /** The immediately preceding call/result, so recovery cannot cross a state-changing call or failure. */\n let lastRepeat: { key: string; count: number; callId: string; succeeded: boolean } | undefined\n /** Successful finalized results, retained only for this turn's exact repeat guard. */\n const successfulCalls = new Map<string, {\n readonly callId: string\n readonly rawArguments: string\n readonly definition: ToolDefinition | undefined\n readonly result: Extract<ToolExecutionResult, { readonly isError: false }>\n }>()\n /** Each exact recovered key gets one ordinary model replan, never a retry loop. */\n const recoveredReplanKeys = new Set<string>()\n const actionSteps: string[] = []\n const emitMaintenance = maintenanceEmitter(emit, root)\n /**\n * Put the current budget notice on the surface, replacing the previous one.\n *\n * One live notice, never a pile of them. Appending each new remainder leaves\n * \"16 calls remain\" and \"6 calls remain\" both readable, and a model that\n * plans against the older number plans against a budget it no longer has.\n * Codex keeps exactly one `<rollout_budget>` fragment for the same reason\n * (`context/rollout_budget.rs`), replacing it as the number moves.\n * @param text - What the model should read now.\n */\n const appendBudgetNotice = (text: string): void => {\n const message = createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-budget-guard' },\n content: [{ type: 'text', text }],\n })\n const previous = budgetNoticeSeq\n // Replacing is best-effort. Compaction can shadow the earlier notice\n // between steps, and a replace whose target is no longer on the surface is\n // rejected — which must cost the model a reminder, never the turn.\n if (previous !== undefined) {\n try {\n budgetNoticeSeq = options.history.append(\n { kind: 'user', message },\n { op: 'replace', from: previous, to: previous, targets: [previous] },\n ).seq\n return\n } catch {\n budgetNoticeSeq = undefined\n }\n }\n budgetNoticeSeq = options.history.append({ kind: 'user', message }).seq\n }\n const contextSections = options.contextSections === undefined || options.contextSections.length === 0\n ? undefined\n : new ContextSectionRuntime({\n sections: options.contextSections,\n history: options.history,\n ...options.logger === undefined ? {} : { logger: options.logger },\n guard: (pending, name) => runHook(pending, options, signal, name),\n scope: { agentId: options.trace?.agentId, conversationId: options.trace?.conversationId },\n })\n /** Tool calls committed since the last section reconcile. */\n let contextTouches: ContextToolTouch[] = []\n let rootStarted = false\n let rootEnded = false\n const callableTools = hasCallableTools(options)\n const programs = options.experimentalPrograms === undefined ? undefined : captureProgramGrants(options.experimentalPrograms)\n // Handles programs retain live for this turn only.\n const programResults = programs === undefined ? undefined : new ProgramResultStore()\n const dedicatedFinalOutput = options.outputFormat?.type === 'json_schema' && callableTools\n const turnOperationId = options.accounting?.startOperation('turn', {\n data: { turn, model: options.config.model },\n })\n let turnOperationEnded = false\n let usageStop: TurnOutcome['reason'] | undefined\n // Shared by retries, hook continuations, and both finalizer paths. Finalizers\n // may have a separate step allowance, never a separate token allowance.\n const admissionStop = (): TurnOutcome['reason'] | undefined => {\n if (signal.aborted) return { kind: 'aborted' }\n const mandatoryStop = accountingUsageStop(options.accounting)\n if (mandatoryStop !== undefined) return mandatoryStop\n if (usageStop !== undefined) return usageStop\n const tokens = budgetTokenTotal(summarizeModelCallUsage(modelCallReports))\n return tokens !== undefined && tokens >= maxTotalTokens\n ? { kind: 'budget-exhausted', budget: 'tokens', forcedFinalAnswer: false }\n : undefined\n }\n\n try {\n await emit({\n type: 'span-start', trace: root, at: startedAt,\n name: `invoke_agent ${options.trace?.agentId ?? 'agent'}`, kind: 'invoke_agent',\n attributes: {\n 'gen_ai.operation.name': 'invoke_agent',\n 'gen_ai.agent.id': options.trace?.agentId ?? 'agent',\n 'gen_ai.agent.name': options.trace?.agentName ?? options.trace?.agentId ?? 'agent',\n 'gen_ai.request.model': options.config.model,\n ...options.trace?.conversationId === undefined\n ? {}\n : { 'gen_ai.conversation.id': options.trace.conversationId },\n },\n })\n rootStarted = true\n await emit({ type: 'turn-start', turn, trace: root })\n\n turnLifecycle: while (true) {\n while (reason === undefined && steps < maxSteps && !signal.aborted) {\n reason = admissionStop()\n if (reason !== undefined) break\n // Auto has no step countdown. Warn from the same usage total as admission,\n // before a hard token stop can leave the agent without a reporting call.\n const usedTokens = budgetTokenTotal(summarizeModelCallUsage(modelCallReports))\n if (bounds.maxTotalTokens !== 'auto' && usedTokens !== undefined) {\n const crossed = [0.5, 0.75, 0.9].filter(ratio => usedTokens >= maxTotalTokens * ratio).length\n if (crossed > tokenRemindersSent) {\n tokenRemindersSent = crossed\n const remaining = Math.max(0, maxTotalTokens - usedTokens)\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-token-guard' },\n content: [{ type: 'text', text: `Token budget: ${remaining} of ${bounds.maxTotalTokens} aggregate tokens remain. Each request also spends input tokens on the accumulated context. `\n + (crossed >= 2 ? 'Stop broad exploration. ' : 'Prioritize outstanding verification and reporting. ')\n + 'Reserve capacity for the required self-check, honest todo reconciliation and substantive final report. State unavailable evidence and unfinished work; do not claim it is verified. No extra model call is allowed after the hard token limit.',\n }],\n }) })\n }\n }\n // Leave time to reconcile a plan and submit a result before the final\n // tools-disabled summary. Tool-call reminders do not cover step limits.\n if (!stepReminderSent && steps > 0 && maxSteps - steps <= 2) {\n stepReminderSent = true\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-step-guard' },\n content: [{ type: 'text', text:\n `${maxSteps - steps} work steps remain. Finish essential verification, update any task list honestly, and submit the result if required. Summarize findings, evidence, and unfinished work; do not start new exploration.`,\n }],\n }) })\n }\n // Recomputed before the request that will read it, so a section reacting to\n // the previous step's tool calls is already on the surface.\n if (contextSections !== undefined) {\n await contextSections.reconcile(steps, contextTouches, signal)\n contextTouches = []\n }\n const step = steps + 1\n const round = await modelRound(\n options, signal, emit, emitMaintenance, root, turn, step,\n dedicatedFinalOutput\n ? 'process'\n : options.outputFormat?.type === 'json_schema' ? 'final' : 'standard',\n )\n steps++\n if (round.report !== undefined) modelCallReports.push(round.report)\n if (round.message !== undefined) {\n options.history.append({\n kind: 'assistant', message: round.message,\n ...round.finish.kind === 'aborted' ? { interrupted: true as const } : {},\n ...round.usage === undefined ? {} : { usage: round.usage },\n })\n await emit({ type: 'assistant-message', message: round.message, trace: round.trace })\n await emitAssistantContent(round, emit)\n text = textOf(round.message.content)\n } else if (round.finish.kind === 'stop') {\n // A successful empty round cannot answer new input with earlier text.\n // Keep earlier text only when a later request fails or is interrupted.\n text = ''\n }\n usageStop = accountingUsageStop(options.accounting) ?? (round.usageRequired\n ? { kind: 'error', failure: { message: 'provider usage is required by the configured run policy', code: 'USAGE_REQUIRED' } }\n : round.usageUnavailable\n ? { kind: 'usage-unavailable', modelCallId: round.report?.modelCallId ?? 'unknown' }\n : undefined)\n if (signal.aborted || round.finish.kind === 'aborted') { reason = { kind: 'aborted' }; break }\n // Pair emitted tool calls with declined results even when policy stops the\n // run; never execute those calls or allow a retry hook to override policy.\n if (round.finish.kind === 'error' && usageStop !== undefined) {\n reason = { kind: 'error', failure: round.finish.failure }\n break\n }\n if (round.usageRequired && (round.calls.length === 0 || options.tools === undefined)) { reason = usageStop; break }\n if (round.finish.kind === 'error' && !round.usageRequired) {\n reason = admissionStop()\n if (reason !== undefined) break\n const decision = await runOptionalHook(options.hooks?.onRequestError, [{\n turn, step, failure: round.finish.failure, snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n emit: emitMaintenance,\n }], options, signal, 'onRequestError')\n const maintenanceStop = accountingUsageStop(options.accounting)\n if (maintenanceStop !== undefined) { reason = signal.aborted ? { kind: 'aborted' } : maintenanceStop; break }\n if (decision === 'retry' && steps < maxSteps) continue\n reason = { kind: 'error', failure: round.finish.failure }\n break\n }\n if (round.finish.kind === 'max-tokens' && !round.usageRequired) { reason = { kind: 'max-tokens' }; break }\n if (round.calls.length === 0 && round.usageUnavailable) {\n reason = accountingUsageStop(options.accounting) ?? { kind: 'usage-unavailable', modelCallId: round.report?.modelCallId ?? 'unknown' }\n break\n }\n if (round.calls.length === 0 && dedicatedFinalOutput) {\n reason = admissionStop()\n if (reason !== undefined) break\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'structured-output-finalizer' },\n content: [{\n type: 'text',\n text: 'The process phase is complete. Return the final answer now in the requested output format. Do not call tools.',\n }],\n }) })\n const final = await modelRound(\n options, signal, emit, emitMaintenance, root, turn, steps + 1, 'final',\n )\n steps++\n if (final.report !== undefined) modelCallReports.push(final.report)\n if (final.message !== undefined) {\n options.history.append({\n kind: 'assistant', message: final.message,\n ...final.usage === undefined ? {} : { usage: final.usage },\n })\n await emit({ type: 'assistant-message', message: final.message, trace: final.trace })\n await emitAssistantContent(final, emit)\n text = textOf(final.message.content)\n }\n if (final.finish.kind === 'aborted') reason = { kind: 'aborted' }\n else if (final.finish.kind === 'error') reason = { kind: 'error', failure: final.finish.failure }\n else if (final.finish.kind === 'max-tokens') reason = { kind: 'max-tokens' }\n else if (final.usageRequired) reason = { kind: 'error', failure: {\n message: 'provider usage is required by the configured run policy',\n code: 'USAGE_REQUIRED',\n } }\n else if (final.usageUnavailable) {\n reason = accountingUsageStop(options.accounting) ?? { kind: 'usage-unavailable', modelCallId: final.report?.modelCallId ?? 'unknown' }\n } else reason = { kind: 'completed' }\n break\n }\n // A provider that repeated an id had the repeat dropped. Saying so is what\n // keeps the model from reading one result for two calls as a tool that\n // silently ignored it.\n if (round.droppedDuplicateCalls !== undefined) {\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-duplicate-guard' },\n content: [{\n type: 'text',\n text: `You reused the tool-call id ${round.droppedDuplicateCalls.map(id => `'${id}'`).join(', ')}.`\n + ' A result pairs to exactly one call, so the repeat was not run and only the first'\n + ' call of that id has a result. Give every call its own id, and call again if you'\n + ' still need what the dropped one would have done.',\n }],\n }) })\n }\n if (round.calls.length === 0 || options.tools === undefined) { reason = { kind: 'completed' }; break }\n\n // Calls the budget does not govern: submitting, asking, delegating. They\n // are dispatched even when the budget is spent, so the model always has a\n // legal way to finish. See `ToolDefinition.budgetExempt`.\n const remaining = Math.max(0, bounds.maxToolCalls - toolCalls)\n let repeatProjection: { key: string; count: number } | undefined = lastRepeat\n const projectedRepeats = round.calls.map(call => {\n const key = repeatKey(call)\n // Count consecutive calls, not lifetime visits to a source or test command.\n // Distinct intervening work may change its result. Alternating loops are\n // handled separately by the step-cycle guard below.\n const count = repeatProjection?.key === key ? repeatProjection.count + 1 : 1\n repeatProjection = { key, count }\n return count\n })\n const repeatedLimitBeforeDispatch = projectedRepeats.some(count => count >= bounds.repeatToolLimit)\n const actionPattern = toolActionPattern(round.calls)\n const projectedCycle = repeatedSuffixCycle(\n [...actionSteps, actionPattern],\n bounds.maxToolCycleLength,\n )\n const cycleLimitBeforeDispatch = projectedCycle !== undefined\n && projectedCycle.repetitions >= bounds.toolCycleLimit\n const currentUsage = summarizeModelCallUsage(modelCallReports)\n const budgetTokens = budgetTokenTotal(currentUsage)\n const tokenLimitBeforeDispatch = budgetTokens !== undefined\n && budgetTokens >= maxTotalTokens\n const guardDeclined = cycleLimitBeforeDispatch || tokenLimitBeforeDispatch || round.usageRequired\n // Name the limit that actually declined the call. Reporting a repeat guard\n // as an empty budget teaches the model the wrong lesson, and it repeats the\n // call on the next turn with the budget it was told it lacked.\n const declineReason: ToolDeclineReason = tokenLimitBeforeDispatch\n ? 'tokens'\n : cycleLimitBeforeDispatch\n ? 'tool-call-cycle'\n : round.usageRequired\n ? 'usage-required'\n : 'tool-calls'\n // `continue` takes the wall down: the budget becomes a notice and the turn\n // is bounded by steps, tokens, and the run-level ledger instead. Guards\n // that mean \"this is not working\" still decline, whatever the setting.\n const budgetIsAWall = bounds.onExhausted !== 'continue'\n const recoveredCallIds = new Set<string>()\n // A round-wide guard must not make an unrelated old success reusable.\n // Recovery is only legal for a call that individually reached the limit and\n // whose successful result is the immediately preceding call in this streak.\n const individuallyRepeatedCallIds = new Set(round.calls\n .filter((_call, index) => (projectedRepeats[index] ?? 0) >= bounds.repeatToolLimit)\n .map(call => String(call.callId)))\n // Only ordinary dispatches request quota: repeated calls are either recovered\n // for free or individually declined. Fresh siblings keep normal admission.\n const budgetedCalls = round.calls.filter(call =>\n !individuallyRepeatedCallIds.has(String(call.callId))\n && options.tools?.get(call.toolName)?.budgetExempt !== true).length\n const recover = repeatedLimitBeforeDispatch && !cycleLimitBeforeDispatch && !tokenLimitBeforeDispatch\n && !round.usageRequired\n ? (call: typeof round.calls[number]): ToolExecutionResult | undefined => {\n const prior = successfulCalls.get(repeatKey(call))\n if (!individuallyRepeatedCallIds.has(String(call.callId))\n || lastRepeat?.key !== repeatKey(call)\n || lastRepeat.succeeded !== true\n || prior === undefined\n || prior.callId !== lastRepeat.callId\n || prior.rawArguments !== call.rawArguments\n || prior.definition !== options.tools?.get(call.toolName)\n || prior.result.additionalContext !== undefined\n || prior.result.concludesTurn === true\n || options.tools?.get(call.toolName)?.budgetExempt === true) return undefined\n recoveredCallIds.add(String(call.callId))\n return duplicateOfResult(prior)\n }\n : undefined\n const scheduled = await scheduleToolCalls({\n calls: round.calls, catalog: options.tools, history: options.history,\n position: { turn, step,\n ...(options.accounting === undefined ? {} : { runId: options.accounting.runId }),\n ...(options.trace?.conversationId === undefined ? {} : { conversationId: options.trace.conversationId }),\n }, signal, parentTrace: root,\n maxParallel: bounds.maxParallel,\n dispatchLimit: guardDeclined ? 0 : budgetIsAWall ? remaining : round.calls.length,\n declineReason,\n maxResultBytes: bounds.maxToolResultBytes,\n maxResultTokens: bounds.maxToolResultTokens,\n resultOverflow: bounds.toolResultOverflow,\n ...options.spillStore === undefined ? {} : { spillStore: options.spillStore },\n maxDurationMs: bounds.maxToolDurationMs,\n teardownTimeoutMs: bounds.toolTeardownTimeoutMs,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n ...options.interceptors === undefined ? {} : { interceptors: options.interceptors },\n ...options.approvals === undefined ? {} : { approvals: options.approvals },\n ...options.accounting === undefined ? {} : { accounting: options.accounting },\n emit,\n ...recover === undefined ? {} : { recover },\n ...options.hooks?.checkpoint === undefined ? {} : {\n checkpoint: (context: Parameters<NonNullable<TurnHooks['checkpoint']>>[0]) => runHook(\n Promise.resolve(options.hooks?.checkpoint?.(context)), options, signal, 'checkpoint',\n ),\n },\n }, {\n admissionLimit: guardDeclined ? 0 : budgetIsAWall ? remaining : 'unbounded',\n ...guardDeclined || !repeatedLimitBeforeDispatch ? {} : {\n decline: (call: typeof round.calls[number]): ToolDeclineReason | undefined =>\n individuallyRepeatedCallIds.has(String(call.callId)) ? 'repeated-tool-call' : undefined,\n },\n ...programs === undefined ? {} : { programs },\n ...programResults === undefined ? {} : { programResults },\n })\n // Results commit in model order, one per requested call, so the pairing\n // holds for declined calls too. A short result list means a sibling failed\n // its contract and the turn is already unwinding; reporting a guessed\n // outcome would be worse than reporting nothing.\n if (contextSections !== undefined && scheduled.results.length === round.calls.length) {\n contextTouches.push(...round.calls.map((call, index) => ({\n toolName: call.toolName,\n rawArguments: call.rawArguments,\n failed: scheduled.results[index]?.isError ?? true,\n })))\n }\n toolCalls += scheduled.budgeted\n const remainingAfterDispatch = Math.max(0, bounds.maxToolCalls - toolCalls)\n // One reminder per threshold crossed, not one per turn.\n //\n // This was a single boolean: the model was told once, at 25% remaining,\n // and then never again however close it came to the end. A model warned at\n // sixteen calls left and still exploring at four has been told nothing\n // since, and runs into the wall with no notice — which is what produces a\n // red \"no remaining tool-call budget\" where an answer should have been.\n //\n // Codex counts thresholds rather than remembering a flag\n // (`rollout_budget.rs`, `reminder_index`): every threshold now below the\n // remaining budget that has not been reported yet gets reported. Crossing\n // several at once collapses into the one that matters, the lowest.\n const crossed = budgetReminders.filter(threshold => remainingAfterDispatch <= threshold).length\n if (crossed > budgetRemindersSent && remainingAfterDispatch > 0) {\n budgetRemindersSent = crossed\n appendBudgetNotice(\n `Tool budget: ${remainingAfterDispatch} of ${bounds.maxToolCalls} calls remain in this turn.`\n + ' Stop broad exploration, make the necessary edits, and reserve calls for'\n + (budgetIsAWall\n ? ' verification. When the budget runs out no further work call will run, so'\n + ' finish with what you can still verify.'\n : ' verification. Past the budget you are expected to be wrapping up, not'\n + ' opening new lines of work.'),\n )\n }\n // Past the budget with the wall down. The turn is not stopped — the model\n // keeps its tools — but it is told, once per further budget spent, that it\n // is now expected to be finishing. This is the shape of Codex's\n // `budget_limit` goal prompt: no new substantive work, wrap up, name what\n // is left.\n if (!budgetIsAWall && remainingAfterDispatch === 0) {\n const windows = Math.floor(toolCalls / bounds.maxToolCalls)\n if (windows > overBudgetNoticesSent) {\n overBudgetNoticesSent = windows\n appendBudgetNotice(\n `Tool budget spent: ${toolCalls} calls made against a budget of ${bounds.maxToolCalls}.`\n + ' Do not start new substantive work. Finish what is in flight, verify what you'\n + ' can, and answer — stating plainly what is unverified or unfinished.',\n )\n }\n }\n await emit({ type: 'step-end', turn, step, trace: round.trace })\n\n actionSteps.push(actionPattern)\n let repeatedLimit = repeatedLimitBeforeDispatch\n for (let index = 0; index < round.calls.length; index++) {\n const call = round.calls[index]\n const result = scheduled.results[index]\n if (call === undefined || result === undefined) continue\n consecutiveErrors = result.isError ? consecutiveErrors + 1 : 0\n const key = repeatKey(call)\n const count = lastRepeat?.key === key ? lastRepeat.count + 1 : 1\n lastRepeat = {\n key, count, callId: String(call.callId),\n succeeded: !result.isError && result.meta?.declined !== true\n && options.tools?.get(call.toolName)?.budgetExempt !== true,\n }\n if (count === bounds.repeatToolWarningAt) {\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-repeat-guard' },\n content: [{ type: 'text', text: `You have called ${call.toolName} with the same arguments ${count} consecutive times. Reassess before repeating it.` }],\n }) })\n }\n if (count >= bounds.repeatToolLimit) repeatedLimit = true\n if (!result.isError && result.meta?.declined !== true\n && options.tools?.get(call.toolName)?.budgetExempt !== true) {\n successfulCalls.set(key, {\n callId: String(call.callId), rawArguments: call.rawArguments,\n definition: options.tools.get(call.toolName), result,\n })\n }\n }\n if (projectedCycle?.repetitions === bounds.toolCycleWarningAt) {\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'tool-loop-cycle-guard' },\n content: [{\n type: 'text',\n text: `Tool-use pattern repeated ${projectedCycle.repetitions} times across ${projectedCycle.period} step(s). Reassess the approach and identify concrete new evidence before continuing.`,\n }],\n }) })\n }\n if (scheduled.concluded) { reason = { kind: 'concluded-by-tool', toolName: scheduled.concludedBy ?? 'unknown' }; break }\n if (signal.aborted) { reason = { kind: 'aborted' }; break }\n if (round.usageRequired) { reason = usageStop; break }\n if (round.usageUnavailable) {\n reason = accountingUsageStop(options.accounting) ?? { kind: 'usage-unavailable', modelCallId: round.report?.modelCallId ?? 'unknown' }\n break\n }\n let exhausted: ExhaustedBudget | undefined\n const reportReserveReached = bounds.maxTotalTokens !== 'auto'\n && bounds.finalReportReserveTokens > 0 && budgetTokens !== undefined\n && budgetTokens >= maxTotalTokens - bounds.finalReportReserveTokens\n const recoveredOnlyReplan = repeatedLimitBeforeDispatch\n && recoveredCallIds.size === round.calls.length\n && round.calls.every(call => recoveredCallIds.has(String(call.callId))\n && !recoveredReplanKeys.has(repeatKey(call)))\n && scheduled.results.length === round.calls.length\n && scheduled.results.every(result => !result.isError)\n && scheduled.dispatched === 0 && scheduled.declined === 0\n && !cycleLimitBeforeDispatch && !tokenLimitBeforeDispatch && !reportReserveReached\n && !round.usageRequired && !round.usageUnavailable && !signal.aborted\n && !(budgetIsAWall && budgetedCalls > remaining)\n && consecutiveErrors === 0 && steps < maxSteps\n && bounds.onExhausted !== 'stop' && admissionStop() === undefined\n // A repeated call cannot poison new work in the same model batch. Successful\n // recovery alongside a real dispatch is ordinary progress, not an extra pure\n // duplicate replan. The other exhaustion checks below still apply.\n const recoveredWithFreshDispatch = repeatedLimitBeforeDispatch\n && [...individuallyRepeatedCallIds].every(id => recoveredCallIds.has(id))\n && scheduled.results.length === round.calls.length\n && scheduled.dispatched > 0 && scheduled.declined === 0\n if (recoveredOnlyReplan) {\n for (const call of round.calls) recoveredReplanKeys.add(repeatKey(call))\n } else if (tokenLimitBeforeDispatch) exhausted = 'tokens'\n else if (reportReserveReached) exhausted = 'tokens'\n else if (cycleLimitBeforeDispatch) exhausted = 'tool-call-cycle'\n else if (budgetIsAWall && budgetedCalls > remaining) exhausted = 'tool-calls'\n else if (consecutiveErrors >= bounds.maxConsecutiveToolErrors) exhausted = 'consecutive-tool-errors'\n else if (repeatedLimit && !recoveredWithFreshDispatch) exhausted = 'repeated-tool-call'\n else if (steps >= maxSteps) exhausted = 'steps'\n if (exhausted === undefined && recoveredWithFreshDispatch) {\n for (const call of round.calls) {\n if (recoveredCallIds.has(String(call.callId))) recoveredReplanKeys.add(repeatKey(call))\n }\n }\n if (exhausted !== undefined) {\n const forced = (exhausted !== 'tokens' || (reportReserveReached && !tokenLimitBeforeDispatch))\n && bounds.onExhausted !== 'stop'\n && admissionStop() === undefined\n if (forced) {\n const final = await modelRound(\n options, signal, emit, emitMaintenance, root, turn, steps + 1, 'forced-final',\n )\n steps++\n if (final.report !== undefined) modelCallReports.push(final.report)\n if (final.message !== undefined) {\n options.history.append({ kind: 'assistant', message: final.message, ...final.usage === undefined ? {} : { usage: final.usage } })\n await emit({ type: 'assistant-message', message: final.message, trace: final.trace })\n await emitAssistantContent(final, emit)\n text = textOf(final.message.content)\n }\n if (final.finish.kind === 'aborted') {\n reason = { kind: 'aborted' }\n break\n }\n if (final.finish.kind === 'error') {\n reason = { kind: 'error', failure: final.finish.failure }\n break\n }\n if (final.finish.kind === 'max-tokens') {\n reason = { kind: 'max-tokens' }\n break\n }\n if (final.usageRequired) {\n reason = { kind: 'error', failure: {\n message: 'provider usage is required by the configured run policy',\n code: 'USAGE_REQUIRED',\n } }\n break\n }\n if (final.usageUnavailable) {\n reason = accountingUsageStop(options.accounting) ?? { kind: 'usage-unavailable', modelCallId: final.report?.modelCallId ?? 'unknown' }\n break\n }\n }\n reason = { kind: 'budget-exhausted', budget: exhausted, forcedFinalAnswer: forced,\n ...(exhausted === 'tokens' && reportReserveReached && !tokenLimitBeforeDispatch\n ? { trigger: 'report-reserve' as const } : {}),\n }\n }\n }\n\n if (reason === undefined) {\n reason = signal.aborted\n ? { kind: 'aborted' }\n : { kind: 'budget-exhausted', budget: 'steps', forcedFinalAnswer: false }\n }\n const usageReport = summarizeModelCallUsage(modelCallReports)\n const usage = authoritativeTokenUsage(usageReport)\n const candidate: TurnOutcome = {\n reason, text, steps, usageReport, toolCalls, traceId,\n ...usage === undefined ? {} : { usage },\n }\n const entriesBeforeHook = options.history.entries().length\n const canContinue = reason.kind === 'completed'\n && steps < maxSteps\n && admissionStop() === undefined\n await runOptionalHook(options.hooks?.onTurnEnd, [{\n outcome: candidate,\n snapshot: options.history.snapshot(),\n canContinue,\n }], options, signal.aborted ? new AbortController().signal : signal, 'onTurnEnd')\n // Hooks object by appending context rather than by returning a veto. Re-run only\n // a normally completed turn; abort/error/budget outcomes remain terminal.\n // Input the person sent while the final answer was being written is owed an\n // answer too: when nothing else continues the turn, take it and continue.\n if (canContinue\n && !signal.aborted\n && (options.history.entries().length > entriesBeforeHook || deliverQueuedInput(options.history))) {\n reason = undefined\n continue turnLifecycle\n }\n outcome = candidate\n break\n }\n if (outcome.reason.kind === 'aborted') {\n // Without this, the next turn sees an unfinished request and a cancelled\n // tool call, and a model resumes the abandoned work instead of answering\n // the new message (observed live). Codex and Claude Code record the same\n // interruption marker.\n options.history.append({ kind: 'user', message: createUserMessage({\n source: { kind: 'app', producer: 'turn-interrupted' },\n content: [{ type: 'text', text: 'The previous request was interrupted before it finished. Use the next message to determine what to do next.' }],\n }) })\n }\n await emit({\n type: 'span-end', trace: root, at: now(),\n status: outcome.reason.kind === 'error' ? 'error' : outcome.reason.kind === 'aborted' ? 'aborted' : 'success',\n output: { reason: outcome.reason, text },\n ...outcome.usage === undefined ? {} : { usage: outcome.usage },\n ...outcome.reason.kind === 'error' ? { error: { type: 'ModelError', message: outcome.reason.failure.message, code: outcome.reason.failure.code } } : {},\n })\n rootEnded = true\n if (turnOperationId !== undefined) {\n options.accounting?.endOperation(\n turnOperationId,\n outcome.reason.kind === 'error' ? 'error' : outcome.reason.kind === 'aborted' ? 'aborted' : 'success',\n { data: { reason: outcome.reason.kind, steps, toolCalls } },\n )\n turnOperationEnded = true\n }\n await emit({ type: 'turn-end', outcome, trace: root })\n } catch (error: unknown) {\n const code = errorCodeOf(error)\n if (rootStarted && !rootEnded) {\n await emit({\n type: 'span-end', trace: root, at: now(), status: signal.aborted ? 'aborted' : 'error',\n error: {\n type: error instanceof Error ? error.name : 'RuntimeError',\n message: messageOf(error),\n ...code === undefined ? {} : { code },\n },\n }).catch(() => undefined)\n }\n if (turnOperationId !== undefined && !turnOperationEnded) {\n options.accounting?.endOperation(\n turnOperationId,\n signal.aborted ? 'aborted' : 'error',\n { error },\n )\n turnOperationEnded = true\n }\n throw error\n } finally {\n programResults?.close()\n }\n}\n\nfunction duplicateOfResult(prior: {\n readonly callId: string\n readonly result: Extract<ToolExecutionResult, { readonly isError: false }>\n}): ToolExecutionResult {\n return detachedFrozen({\n isError: false,\n // The prior structured value remains the host's canonical result. The\n // header is model-visible so it knows this call was intentionally reused.\n value: prior.result.value,\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n status: 'duplicate_of',\n callId: prior.callId,\n instruction: 'This exact call already succeeded in this run. Use the retained result below; do not call it again.',\n }),\n },\n ...prior.result.content,\n ],\n meta: { ...prior.result.meta, recovered: true, duplicateOfCallId: prior.callId },\n })\n}\n\nexport type { RunTurnOptions } from './turn/types.ts'\n"],"mappings":"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"}
|
|
@@ -44,6 +44,11 @@ interface RunToolCallsOptions {
|
|
|
44
44
|
readonly emit?: (event: AgentEvent) => Promise<void>;
|
|
45
45
|
readonly checkpoint?: TurnHooks['checkpoint'];
|
|
46
46
|
readonly accounting?: RunAccountingPort;
|
|
47
|
+
/**
|
|
48
|
+
* A same-turn result the loop may expose instead of dispatching this exact
|
|
49
|
+
* call again. The scheduler still records a distinct call/result pair.
|
|
50
|
+
*/
|
|
51
|
+
readonly recover?: (call: ToolCallRequest) => ToolExecutionResult | undefined;
|
|
47
52
|
}
|
|
48
53
|
interface ToolCallsOutcome {
|
|
49
54
|
readonly results: readonly ToolExecutionResult[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schedule.d.ts","names":[],"sources":["../../../src/agent/loop/schedule.ts"],"mappings":";;;;;;;;;;;UA8BiB;WACN,gBAAgB;WAChB,SAAS;WACT,SAAS;WACT,UAAU;WACV,QAAQ;WACR,SAAS;WACT,aAAa;WACb;;;;;;;WAOA;;WAEA,gBAAgB;;WAEhB;;WAEA,iBAAiB;;WAEjB,aAAa;;WAEb;;WAEA;;WAEA;WACA,wBAAwB;WACxB,YAAY;WACZ,QAAQ,OAAO,eAAe;WAC9B,aAAa;WACb,aAAa;;
|
|
1
|
+
{"version":3,"file":"schedule.d.ts","names":[],"sources":["../../../src/agent/loop/schedule.ts"],"mappings":";;;;;;;;;;;UA8BiB;WACN,gBAAgB;WAChB,SAAS;WACT,SAAS;WACT,UAAU;WACV,QAAQ;WACR,SAAS;WACT,aAAa;WACb;;;;;;;WAOA;;WAEA,gBAAgB;;WAEhB;;WAEA,iBAAiB;;WAEjB,aAAa;;WAEb;;WAEA;;WAEA;WACA,wBAAwB;WACxB,YAAY;WACZ,QAAQ,OAAO,eAAe;WAC9B,aAAa;WACb,aAAa;;;;;WAKb,WAAW,MAAM,oBAAoB;;UAE/B;WACN,kBAAkB;WAClB;WACA;WACA;;WAEA;;WAEA;;;iBAkCW,aAAa,SAAS,sBAAsB,QAAQ"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{detachedFrozen as e}from"../../primitives/snapshot.js";import{waitForSettlement as t}from"../../async/settlement.js";import{createSpanId as n}from"../../observation/context.js";import{createToolResultMessage as r,createUserMessage as i}from"../../message/message.js";import{estimateTextBlockTokens as a,previewForSpill as o,truncateMiddleToTokens as s}from"../tool/output-budget.js";import{TOOL_ERROR_CODES as c,ToolError as l,toolErrorDisposition as u}from"../tool/errors.js";import{NESTED_TOOL_ERROR_CODES as d,attachNestedToolPort as f}from"../tool/nested.js";import{authorizeToolCall as p,dispatchAuthorizedToolCall as m,finalizeToolCall as h,prepareToolCall as g,toolFailure as _}from"../tool/pipeline.js";import{createToolAdmission as v}from"./admission.js";import{emitEvent as y,errorCode as b,immutableResult as x,messageOf as S,now as C,raceWithSignal as w,teardownFailure as T,withApprovalEvents as E}from"./tool-call-support.js";import{ProgramRun as D,validPrograms as O}from"./program.js";async function k(e){return await A(e)}async function A(e,t={}){let n=H(e.maxParallel??8,`maxParallel`),r=H(e.maxResultBytes??4194304,`maxResultBytes`),i=H(e.maxDurationMs??6e5,`maxDurationMs`),a=H(e.teardownTimeoutMs??3e4,`teardownTimeoutMs`),o=Object.freeze(e.calls.map(B)),s=v(t.admissionLimit??Math.max(0,e.dispatchLimit??o.length)),c={admission:s,maxDurationMs:i,teardownTimeoutMs:a,maxResultBytes:r,programs:O(t.programs),programResults:t.programResults},l=[],u=0,d=0,f,p=0,m;for(;p<o.length;){let
|
|
1
|
+
import{detachedFrozen as e}from"../../primitives/snapshot.js";import{waitForSettlement as t}from"../../async/settlement.js";import{createSpanId as n}from"../../observation/context.js";import{createToolResultMessage as r,createUserMessage as i}from"../../message/message.js";import{estimateTextBlockTokens as a,previewForSpill as o,truncateMiddleToTokens as s}from"../tool/output-budget.js";import{TOOL_ERROR_CODES as c,ToolError as l,toolErrorDisposition as u}from"../tool/errors.js";import{NESTED_TOOL_ERROR_CODES as d,attachNestedToolPort as f}from"../tool/nested.js";import{authorizeToolCall as p,dispatchAuthorizedToolCall as m,finalizeToolCall as h,prepareToolCall as g,toolFailure as _}from"../tool/pipeline.js";import{createToolAdmission as v}from"./admission.js";import{emitEvent as y,errorCode as b,immutableResult as x,messageOf as S,now as C,raceWithSignal as w,teardownFailure as T,withApprovalEvents as E}from"./tool-call-support.js";import{ProgramRun as D,validPrograms as O}from"./program.js";async function k(e){return await A(e)}async function A(e,t={}){let n=H(e.maxParallel??8,`maxParallel`),r=H(e.maxResultBytes??4194304,`maxResultBytes`),i=H(e.maxDurationMs??6e5,`maxDurationMs`),a=H(e.teardownTimeoutMs??3e4,`teardownTimeoutMs`),o=Object.freeze(e.calls.map(B)),s=v(t.admissionLimit??Math.max(0,e.dispatchLimit??o.length)),c={admission:s,maxDurationMs:i,teardownTimeoutMs:a,maxResultBytes:r,programs:O(t.programs),programResults:t.programResults},l=[],u=0,d=0,f,p=0,m;for(;p<o.length;){let i=o[p];if(i===void 0)break;let a=m??j(e,i);if(m=void 0,a.mode===`exclusive`){let n=await M(e,a,c,t);u+=+!!n.dispatched,d+=+(n.declined===!0);let o=await L(e,n,r);l.push(o),f===void 0&&!o.isError&&o.concludesTurn&&(f=i.toolName),p++;continue}let s=[],h=new AbortController,g={...e,signal:AbortSignal.any([e.signal,h.signal])},_=a;try{for(;p<o.length&&s.length<n&&o[p]!==void 0;){let e=_;if(e.mode!==`parallel`)break;let r=await M(g,e,c,t);u+=+!!r.dispatched,d+=+(r.declined===!0),s.push(r),p++;let i=o[p];i!==void 0&&s.length<n&&(_=j(g,i),_.mode!==`parallel`&&(m=_))}}catch(t){throw h.abort(t),await I(e,s,r),t}if(s.length===0)continue;let v,y=!1;for(let t of s)try{let n=await L(e,t,r);l.push(n),f===void 0&&!n.isError&&n.concludesTurn&&(f=t.call.toolName)}catch(e){y||(v=e),y=!0,h.abort(e)}if(y)throw v}return{results:Object.freeze(l),concluded:f!==void 0,...f===void 0?{}:{concludedBy:f},dispatched:u,budgeted:s.budgeted,declined:d}}function j(e,t){return g({catalog:e.catalog,call:t,position:e.position,signal:e.signal,...e.logger===void 0?{}:{logger:e.logger},...e.interceptors===void 0?{}:{interceptors:e.interceptors},...e.approvals===void 0?{}:{approvals:e.approvals}})}async function M(e,t,r,i){let{maxDurationMs:a,teardownTimeoutMs:o}=r,s=AbortSignal.timeout(a),l=AbortSignal.any([e.signal,s]),u={...t,options:{...t.options,signal:l,teardownTimeoutMs:o},context:{...t.context,signal:l}},f=u.options.call,p={traceId:e.parentTrace.traceId,spanId:n(),parentSpanId:e.parentTrace.spanId},m=!e.signal.aborted&&!l.aborted?e.recover?.(f):void 0;if(e.history.append({kind:`tool-call`,callId:f.callId,name:f.toolName,rawArguments:f.rawArguments}),await y(e,{type:`span-start`,trace:p,at:C(),name:`execute_tool ${f.toolName}`,kind:`execute_tool`,attributes:{"gen_ai.operation.name":`execute_tool`,"gen_ai.tool.name":f.toolName,"gen_ai.tool.call.id":f.callId,...m===void 0?{}:{"sdk.tool.recovered":!0}},input:f.rawArguments}),await y(e,{type:`tool-call`,call:f,trace:p}),e.signal.aborted)return{call:f,trace:p,signal:l,deadline:s,teardownTimeoutMs:o,dispatched:!1,pending:Promise.resolve(_(`the call was cancelled before it started`,c.ABORTED_BEFORE_DISPATCH))};if(l.aborted)return{call:f,trace:p,signal:l,deadline:s,teardownTimeoutMs:o,dispatched:!1,pending:Promise.resolve(V(s,a))};if(m!==void 0)return{call:f,trace:p,signal:l,deadline:s,teardownTimeoutMs:o,dispatched:!1,pending:Promise.resolve(m)};let h=e.catalog.get(f.toolName)?.budgetExempt===!0,g=h?void 0:i.decline?.(f);if(g!==void 0)return{call:f,trace:p,signal:l,deadline:s,teardownTimeoutMs:o,dispatched:!1,declined:!0,pending:Promise.resolve(P(g))};let v=r.programs.get(f.toolName);if(v!==void 0&&(h||t.mode!==`exclusive`))return{call:f,trace:p,signal:l,deadline:s,teardownTimeoutMs:o,dispatched:!1,pending:Promise.resolve(_(`this program tool must be exclusive and must not be budget-exempt`,d.CONFIGURATION))};let b=r.admission.reserve(h);if(b===void 0)return{call:f,trace:p,signal:l,deadline:s,teardownTimeoutMs:o,dispatched:!1,declined:!0,pending:Promise.resolve(P(e.declineReason??`tool-calls`))};try{return await N(e,u,f,p,l,s,b,r,v)}finally{b.release()}}async function N(e,n,r,i,a,o,s,u,d){let{maxDurationMs:h,teardownTimeoutMs:g}=u;if(e.signal.aborted)return{call:r,trace:i,signal:a,deadline:o,teardownTimeoutMs:g,dispatched:!1,pending:Promise.resolve(_(`the call was cancelled before it started`,c.ABORTED_BEFORE_DISPATCH))};let v=p(E(e,n,i)),y;try{y=await w(v,a)}catch(e){if(!a.aborted)throw e;if(!await t(v,g))throw T(r.toolName,`authorization`,g,e);return{call:r,trace:i,signal:a,deadline:o,teardownTimeoutMs:g,dispatched:!1,pending:Promise.resolve(V(o,h))}}if(y.kind===`final`)return{call:r,trace:i,signal:a,deadline:o,teardownTimeoutMs:g,dispatched:!1,pending:Promise.resolve(y.result)};try{let n=Promise.resolve(e.checkpoint?.({kind:`before-tool-dispatch`,call:r,snapshot:e.history.snapshot(),signal:a,...e.logger===void 0?{}:{logger:e.logger}}));try{await w(n,a)}catch(e){if(!a.aborted)throw e;if(!await t(n,g))throw T(r.toolName,`checkpoint`,g,e)}}catch(e){if(e instanceof l&&e.code===c.TEARDOWN_TIMEOUT)throw e;return{call:r,trace:i,signal:a,deadline:o,teardownTimeoutMs:g,dispatched:!1,pending:Promise.resolve(_(`history checkpoint failed: ${S(e)}`,c.CHECKPOINT_FAILED))}}if(a.aborted)return{call:r,trace:i,signal:a,deadline:o,teardownTimeoutMs:g,dispatched:!1,pending:Promise.resolve(V(o,h))};let b=d===void 0?void 0:new D(e,u,d,{call:r,trace:i,signal:a});b!==void 0&&f(y.call,b.port,e=>b.bindExecutionSignal(e)),s.confirm();let x=m(y.call);return x.catch(()=>void 0),{call:r,trace:i,signal:a,deadline:o,teardownTimeoutMs:g,authorized:y.call,dispatched:!0,...b===void 0?{}:{program:b},pending:x}}function P(e){return{isError:!1,value:void 0,content:[{type:`text`,text:F(e)}],meta:{declined:!0,reason:e}}}function F(e){let t=` Answer now from what you already have, and say plainly what is unverified or unfinished.`;switch(e){case`tool-calls`:return`This call was not run: the turn has spent its tool-call budget. Further work calls will not run either, so do not retry it.`+t;case`repeated-tool-call`:return`This call was not run: it repeats a call already made with the same arguments, and repeating it cannot produce a new result. Change approach or finish.`+t;case`tool-call-cycle`:return`This call was not run: the same sequence of calls has repeated several times without progress. Break the cycle rather than re-entering it.`+t;case`tokens`:return`This call was not run: the turn has spent its token budget.`+t;case`consecutive-tool-errors`:return`This call was not run: too many calls in a row have failed. Something in the approach is wrong, not the individual call.`+t;case`steps`:return`This call was not run: the turn has no model steps left.`+t;case`usage-required`:return`This call was not run: the run policy requires provider usage reporting that the last model call did not supply.`+t}}async function I(e,t,n){for(let r of t)try{await L(e,r,n)}catch{}}async function L(e,n,a){let o,s,c=!1;try{let r;try{r=await w(n.pending,n.signal)}catch(e){if(!n.signal.aborted)throw e;if(!await t(n.pending,n.teardownTimeoutMs))throw T(n.call.toolName,`execution`,n.teardownTimeoutMs,e);r=V(n.deadline)}if(n.program!==void 0){if(!await n.program.close(n.teardownTimeoutMs))throw T(n.call.toolName,`nested call`,n.teardownTimeoutMs,void 0);let e=n.program.fatal;if(e!==void 0)throw u(e)===`fatal`?e:l.fatal(S(e),b(e),{cause:e})}let i=r;if(n.authorized!==void 0){let e=h(n.authorized,r);try{i=await w(e,n.signal)}catch(r){if(!n.signal.aborted)throw r;if(!await t(e,n.teardownTimeoutMs))throw T(n.call.toolName,`finalization`,n.teardownTimeoutMs,r);i=V(n.deadline)}}o=x(await R(e,n,i),a)}catch(e){n.program?.abort(e),c=u(e)===`fatal`,s=e,o=x(_(S(e),b(e)),a)}let d=r({callId:n.call.callId,content:[...o.content],isError:o.isError});if(e.history.append({kind:`tool-result`,callId:n.call.callId,message:d,result:o}),await y(e,{type:`tool-result`,call:n.call,result:o,trace:n.trace}),await y(e,{type:`span-end`,trace:n.trace,at:C(),status:o.isError?`error`:`success`,output:o.isError?{error:o.error}:o.value,...o.isError?{error:{type:`ToolError`,message:o.error.message,code:o.error.code}}:{}}),o.additionalContext!==void 0&&e.history.append({kind:`user`,message:i({content:[...o.additionalContext],source:{kind:`app`,producer:`tool:${n.call.toolName}`}})}),c)throw s;return o}async function R(e,t,n){let r=e.maxResultTokens,i=e.catalog.get(t.call.toolName)?.maxOutputTokens,c=r===void 0?i:i===void 0?r:Math.min(r,i);if(c===void 0)return n;let l=a(n.content);if(l<=c)return n;let u=n.content.filter(e=>e.type===`text`);if(u.length===0)return n;let d=u.map(e=>e.text).join(`
|
|
2
2
|
`),f=e.resultOverflow??`auto`,p=e.spillStore;if(f!==`truncate`&&p!==void 0)try{let e=await p.save(d,{toolName:t.call.toolName,callId:String(t.call.callId)});return z(n,`${o(d,Math.max(1,Math.floor(c*.75)))}\n\n[Output exceeded this call's budget of ${String(c)} estimated tokens. The full ${String(e.bytes)} bytes are saved. ${e.retrieval}]`,{outputSpilled:{locator:e.locator,bytes:e.bytes,estimatedTokens:l}})}catch{}let m=s(d,c);return z(n,m.text+`\n\n[Output was ${String(m.originalTokens)} estimated tokens, over this call's budget of ${String(c)}. Omitted output does not mean the operation failed. Check an existing receipt or current state; repeat the operation only when the host confirms it is safe.]`,{outputTruncated:{estimatedTokens:m.originalTokens,budget:c}})}function z(e,t,n){let r=!1,i=[];for(let n of e.content){if(n.type!==`text`){i.push(n);continue}r||(r=!0,i.push({type:`text`,text:t}))}return{...e,content:i,meta:{...e.meta,...n}}}function B(t){if(typeof t.callId!=`string`||t.callId.length===0||typeof t.toolName!=`string`||t.toolName.length===0||typeof t.rawArguments!=`string`)throw TypeError(`tool call identity, name, and raw arguments must be strings`);return e(t)}function V(e,t){let n=t===void 0?`configured time limit`:`${t}ms time limit`;return e.aborted?_(`the tool exceeded its ${n}`,c.TIMEOUT):_(`the call was cancelled`,c.ABORTED)}function H(e,t){if(!Number.isInteger(e)||e<1)throw RangeError(`${t} must be a positive integer`);return e}export{k as runToolCalls,A as scheduleToolCalls};
|
|
3
3
|
//# sourceMappingURL=schedule.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schedule.js","names":[],"sources":["../../../src/agent/loop/schedule.ts"],"sourcesContent":["import { createToolResultMessage, createUserMessage } from '../../message/index.ts'\nimport type { ContentBlock } from '../../message/index.ts'\nimport { detachedFrozen } from '../../primitives/index.ts'\nimport type { JsonObject } from '../../primitives/index.ts'\nimport { waitForSettlement } from '../../async/index.ts'\nimport type { History } from '../history/history.ts'\nimport type { ApprovalBroker } from '../tool/approval.ts'\nimport type { ToolCallPosition, ToolExecutionResult } from '../tool/definition.ts'\nimport {\n estimateTextBlockTokens, previewForSpill, truncateMiddleToTokens,\n type SpillStore, type ToolOutputOverflowPolicy,\n} from '../tool/output-budget.ts'\nimport { TOOL_ERROR_CODES, ToolError, toolErrorDisposition } from '../tool/errors.ts'\nimport {\n authorizeToolCall, dispatchAuthorizedToolCall, finalizeToolCall, prepareToolCall, toolFailure,\n type AuthorizedToolCall, type ToolCallRequest, type ToolInterceptor,\n} from '../tool/pipeline.ts'\nimport type { ToolCatalog } from '../tool/registry.ts'\nimport { createSpanId, type TraceRef } from '../trace/trace.ts'\nimport type { AgentEvent, ToolDeclineReason, TurnHooks } from './events.ts'\nimport type { RunAccountingPort } from '../accounting/contracts.ts'\nimport type { SdkLogger } from '../../logging/types.ts'\nimport { createToolAdmission, type AdmissionTicket } from './admission.ts'\nimport { ProgramRun, validPrograms, type StepRuntime } from './program.ts'\nimport {\n emitEvent, errorCode, immutableResult, messageOf, now, raceWithSignal, teardownFailure, withApprovalEvents,\n} from './tool-call-support.ts'\nimport { attachNestedToolPort, NESTED_TOOL_ERROR_CODES, type ProgramGrant } from '../tool/nested.ts'\nimport type { ProgramResultStore } from '../tool/program-results.ts'\n\nexport interface RunToolCallsOptions {\n readonly calls: readonly ToolCallRequest[]\n readonly catalog: ToolCatalog\n readonly history: History\n readonly position: ToolCallPosition\n readonly signal: AbortSignal\n readonly logger?: SdkLogger\n readonly parentTrace: TraceRef\n readonly maxParallel?: number\n /**\n * How many BUDGETED calls may still be dispatched this step.\n *\n * Calls to a `budgetExempt` tool are not counted against it, so a spent\n * budget never blocks the model from submitting, asking, or delegating.\n */\n readonly dispatchLimit?: number\n /** Why calls past `dispatchLimit` are not run; shown to the model verbatim. */\n readonly declineReason?: ToolDeclineReason\n /** Estimated tokens of text one result may put in front of the model. */\n readonly maxResultTokens?: number\n /** What happens to output over that budget. Defaults to `auto`. */\n readonly resultOverflow?: ToolOutputOverflowPolicy\n /** Where spilled output goes; `auto` spills only when this is mounted. */\n readonly spillStore?: SpillStore\n /** Maximum serialized bytes retained for one finalized result. Defaults to 4 MiB. */\n readonly maxResultBytes?: number\n /** End-to-end wall-clock allowance for one call. Defaults to 10 minutes. */\n readonly maxDurationMs?: number\n /** Maximum cancellation settlement wait. Defaults to 30 seconds. */\n readonly teardownTimeoutMs?: number\n readonly interceptors?: readonly ToolInterceptor[]\n readonly approvals?: ApprovalBroker\n readonly emit?: (event: AgentEvent) => Promise<void>\n readonly checkpoint?: TurnHooks['checkpoint']\n readonly accounting?: RunAccountingPort\n}\nexport interface ToolCallsOutcome {\n readonly results: readonly ToolExecutionResult[]\n readonly concluded: boolean\n readonly concludedBy?: string\n readonly dispatched: number\n /** Dispatched calls that spent budget; exempt tools are excluded. */\n readonly budgeted: number\n /** Calls the loop refused to run, for the reason in `declineReason`. */\n readonly declined: number\n}\n\ninterface Slot {\n readonly call: ToolCallRequest\n readonly trace: TraceRef\n readonly authorized?: AuthorizedToolCall\n readonly pending: Promise<ToolExecutionResult>\n readonly dispatched: boolean\n /** The program this call hosts, when the host granted it one. */\n readonly program?: ProgramRun\n /** Set when the loop refused to run the call. */\n readonly declined?: boolean\n readonly signal: AbortSignal\n readonly deadline: AbortSignal\n readonly teardownTimeoutMs: number\n}\n\n/** Scheduler options the loop owns and does not publish. */\nexport interface InternalScheduleOptions {\n /** Overrides `dispatchLimit`; `unbounded` when the budget is a notice rather than a wall. */\n readonly admissionLimit?: number | 'unbounded'\n /**\n * Program tools and what each may call. Research seam for SP-01; the public\n * way to enable programs is not decided.\n */\n readonly programs?: ReadonlyMap<string, ProgramGrant>\n /** Where programs retain values; owned and closed by the caller. */\n readonly programResults?: ProgramResultStore\n}\n\n/** Execute safe siblings concurrently while committing every result in model order. */\nexport async function runToolCalls(options: RunToolCallsOptions): Promise<ToolCallsOutcome> {\n return await scheduleToolCalls(options)\n}\n\n/** {@link runToolCalls} with the loop's internal options. */\nexport async function scheduleToolCalls(\n options: RunToolCallsOptions,\n internal: InternalScheduleOptions = {},\n): Promise<ToolCallsOutcome> {\n const maxParallel = positiveInteger(options.maxParallel ?? 8, 'maxParallel')\n const maxResultBytes = positiveInteger(options.maxResultBytes ?? 4 * 1024 * 1024, 'maxResultBytes')\n const maxDurationMs = positiveInteger(options.maxDurationMs ?? 10 * 60_000, 'maxDurationMs')\n const teardownTimeoutMs = positiveInteger(options.teardownTimeoutMs ?? 30_000, 'teardownTimeoutMs')\n const calls = Object.freeze(options.calls.map(immutableCall))\n const admission = createToolAdmission(internal.admissionLimit ?? Math.max(0, options.dispatchLimit ?? calls.length))\n const step: StepRuntime = {\n admission, maxDurationMs, teardownTimeoutMs, maxResultBytes,\n programs: validPrograms(internal.programs),\n programResults: internal.programResults,\n }\n const results: ToolExecutionResult[] = []\n let dispatched = 0\n let declined = 0\n let concludedBy: string | undefined\n let index = 0\n let carried: ReturnType<typeof prepareToolCall> | undefined\n\n while (index < calls.length) {\n const first = calls[index]\n if (first === undefined) break\n const prepared = carried ?? prepare(options, first)\n carried = undefined\n if (prepared.mode === 'exclusive') {\n const slot = await start(options, prepared, step)\n dispatched += slot.dispatched ? 1 : 0\n declined += slot.declined === true ? 1 : 0\n const result = await commit(options, slot, maxResultBytes)\n results.push(result)\n if (concludedBy === undefined && !result.isError && result.concludesTurn) concludedBy = first.toolName\n index++\n continue\n }\n\n // A rolling parallel segment. Re-prepare each call immediately before start;\n // an updated registry can therefore turn the next call into a barrier.\n const segment: Slot[] = []\n const segmentAbort = new AbortController()\n const segmentOptions: RunToolCallsOptions = {\n ...options,\n signal: AbortSignal.any([options.signal, segmentAbort.signal]),\n }\n let nextPrepared = prepared\n try {\n while (index < calls.length && segment.length < maxParallel) {\n const call = calls[index]\n if (call === undefined) break\n const candidate = nextPrepared\n if (candidate.mode !== 'parallel') break\n const slot = await start(segmentOptions, candidate, step)\n dispatched += slot.dispatched ? 1 : 0\n declined += slot.declined === true ? 1 : 0\n segment.push(slot)\n index++\n const nextCall = calls[index]\n if (nextCall !== undefined && segment.length < maxParallel) {\n nextPrepared = prepare(segmentOptions, nextCall)\n if (nextPrepared.mode !== 'parallel') carried = nextPrepared\n }\n }\n } catch (error: unknown) {\n // Admission of a later sibling failed after earlier bodies started.\n // Cancel and drain everything already owned before propagating that error.\n segmentAbort.abort(error)\n await drainSegment(options, segment, maxResultBytes)\n throw error\n }\n if (segment.length === 0) continue\n // Bodies have already started. Finalization and history publication remain ordered.\n let fatal: unknown\n let hasFatal = false\n for (const slot of segment) {\n try {\n const result = await commit(options, slot, maxResultBytes)\n results.push(result)\n if (concludedBy === undefined && !result.isError && result.concludesTurn) concludedBy = slot.call.toolName\n } catch (error: unknown) {\n if (!hasFatal) fatal = error\n hasFatal = true\n segmentAbort.abort(error)\n }\n }\n // Every parallel body was already dispatched. Commit/drain all siblings so\n // none is orphaned, then propagate the first fatal contract violation.\n if (hasFatal) throw fatal\n }\n return {\n results: Object.freeze(results),\n concluded: concludedBy !== undefined,\n ...concludedBy === undefined ? {} : { concludedBy },\n dispatched,\n budgeted: admission.budgeted,\n declined,\n }\n}\n\nfunction prepare(options: RunToolCallsOptions, call: ToolCallRequest) {\n return prepareToolCall({\n catalog: options.catalog, call, position: options.position, signal: options.signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n ...options.interceptors === undefined ? {} : { interceptors: options.interceptors },\n ...options.approvals === undefined ? {} : { approvals: options.approvals },\n })\n}\n\nasync function start(\n options: RunToolCallsOptions,\n prepared: ReturnType<typeof prepareToolCall>,\n step: StepRuntime,\n): Promise<Slot> {\n const { maxDurationMs, teardownTimeoutMs } = step\n const deadline = AbortSignal.timeout(maxDurationMs)\n const signal = AbortSignal.any([options.signal, deadline])\n const boundedPrepared = {\n ...prepared,\n options: { ...prepared.options, signal, teardownTimeoutMs },\n context: { ...prepared.context, signal },\n }\n const call = boundedPrepared.options.call\n const trace: TraceRef = {\n traceId: options.parentTrace.traceId,\n spanId: createSpanId(),\n parentSpanId: options.parentTrace.spanId,\n }\n options.history.append({ kind: 'tool-call', callId: call.callId, name: call.toolName, rawArguments: call.rawArguments })\n await emitEvent(options, { type: 'span-start', trace, at: now(), name: `execute_tool ${call.toolName}`, kind: 'execute_tool', attributes: {\n 'gen_ai.operation.name': 'execute_tool', 'gen_ai.tool.name': call.toolName, 'gen_ai.tool.call.id': call.callId,\n }, input: call.rawArguments })\n await emitEvent(options, { type: 'tool-call', call, trace })\n // A tool the model may always reach: submitting, asking, delegating. Letting\n // a budget block these is what turns a spent budget into a dead run.\n const exempt = options.catalog.get(call.toolName)?.budgetExempt === true\n const grant = step.programs.get(call.toolName)\n // A program spends the budget it is charged for; it can never be the free\n // call, and a parallel program would race its own children for siblings.\n if (grant !== undefined && (exempt || prepared.mode !== 'exclusive')) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure(\n 'this program tool must be exclusive and must not be budget-exempt',\n NESTED_TOOL_ERROR_CODES.CONFIGURATION,\n )),\n }\n const ticket = step.admission.reserve(exempt)\n if (ticket === undefined) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false, declined: true,\n pending: Promise.resolve(declinedResult(options.declineReason ?? 'tool-calls')),\n }\n // Every path below that does not start the body returns the reservation.\n try {\n return await authorizeAndDispatch(options, boundedPrepared, call, trace, signal, deadline, ticket, step, grant)\n } finally {\n ticket.release()\n }\n}\n\nasync function authorizeAndDispatch(\n options: RunToolCallsOptions,\n boundedPrepared: ReturnType<typeof prepareToolCall>,\n call: ToolCallRequest,\n trace: TraceRef,\n signal: AbortSignal,\n deadline: AbortSignal,\n ticket: AdmissionTicket,\n step: StepRuntime,\n grant: ProgramGrant | undefined,\n): Promise<Slot> {\n const { maxDurationMs, teardownTimeoutMs } = step\n if (options.signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure('the call was cancelled before it started', TOOL_ERROR_CODES.ABORTED_BEFORE_DISPATCH)),\n }\n const authorizationPending = authorizeToolCall(withApprovalEvents(options, boundedPrepared, trace))\n let authorization: Awaited<ReturnType<typeof authorizeToolCall>>\n try {\n authorization = await raceWithSignal(authorizationPending, signal)\n } catch (error: unknown) {\n if (!signal.aborted) throw error\n const settled = await waitForSettlement(authorizationPending, teardownTimeoutMs)\n if (!settled) throw teardownFailure(call.toolName, 'authorization', teardownTimeoutMs, error)\n return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(cancelledResult(deadline, maxDurationMs)),\n }\n }\n if (authorization.kind === 'final') return {\n call, trace, signal, deadline, teardownTimeoutMs,\n dispatched: false, pending: Promise.resolve(authorization.result),\n }\n try {\n const checkpointing = Promise.resolve(options.checkpoint?.({\n kind: 'before-tool-dispatch', call, snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n }))\n try { await raceWithSignal(checkpointing, signal) }\n catch (error: unknown) {\n if (!signal.aborted) throw error\n if (!await waitForSettlement(checkpointing, teardownTimeoutMs)) {\n throw teardownFailure(call.toolName, 'checkpoint', teardownTimeoutMs, error)\n }\n }\n } catch (error: unknown) {\n if (error instanceof ToolError && error.code === TOOL_ERROR_CODES.TEARDOWN_TIMEOUT) throw error\n return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure(`history checkpoint failed: ${messageOf(error)}`, TOOL_ERROR_CODES.CHECKPOINT_FAILED)),\n }\n }\n if (signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(cancelledResult(deadline, maxDurationMs)),\n }\n const program = grant === undefined\n ? undefined\n : new ProgramRun(options, step, grant, { call, trace, signal })\n if (program !== undefined) attachNestedToolPort(authorization.call, program.port, signal => program.bindExecutionSignal(signal))\n ticket.confirm()\n const pending = dispatchAuthorizedToolCall(authorization.call)\n // Observe rejection in the same turn in which dispatch creates the promise.\n // commit() still receives and propagates the original rejection in order.\n void pending.catch(() => undefined)\n return {\n call, trace, signal, deadline, teardownTimeoutMs,\n authorized: authorization.call, dispatched: true,\n ...program === undefined ? {} : { program },\n pending,\n }\n}\n\n/**\n * What the model reads when the loop refuses to run a call.\n *\n * Not a failure. A failure is something that went wrong, and a model answers\n * one by retrying — with a smaller argument, a different path — which spends\n * exactly what is no longer there. This says what happened and what to do\n * instead, in the plainest terms available, and nothing about it reads as\n * broken tooling.\n * @param reason - Which limit refused the call.\n * @returns A successful result carrying the instruction.\n */\nfunction declinedResult(reason: ToolDeclineReason): ToolExecutionResult {\n return {\n isError: false,\n value: undefined,\n content: [{ type: 'text', text: declineText(reason) }],\n // Never shown to the model; lets a UI render this as declined rather than\n // as work that succeeded.\n meta: { declined: true, reason },\n }\n}\n\n/**\n * The instruction for one decline reason.\n *\n * Each limit gets its own wording: a model told \"no remaining tool-call\n * budget\" when it actually tripped the repeat guard learns the wrong lesson\n * and repeats the call in the next turn.\n * @param reason - Which limit refused the call.\n * @returns The sentence the model reads.\n */\nfunction declineText(reason: ToolDeclineReason): string {\n const finish = ' Answer now from what you already have, and say plainly what is'\n + ' unverified or unfinished.'\n switch (reason) {\n case 'tool-calls':\n return 'This call was not run: the turn has spent its tool-call budget.'\n + ' Further work calls will not run either, so do not retry it.' + finish\n case 'repeated-tool-call':\n return 'This call was not run: it repeats a call already made with the same'\n + ' arguments, and repeating it cannot produce a new result. Change'\n + ' approach or finish.' + finish\n case 'tool-call-cycle':\n return 'This call was not run: the same sequence of calls has repeated'\n + ' several times without progress. Break the cycle rather than'\n + ' re-entering it.' + finish\n case 'tokens':\n return 'This call was not run: the turn has spent its token budget.' + finish\n case 'consecutive-tool-errors':\n return 'This call was not run: too many calls in a row have failed.'\n + ' Something in the approach is wrong, not the individual call.' + finish\n case 'steps':\n return 'This call was not run: the turn has no model steps left.' + finish\n case 'usage-required':\n return 'This call was not run: the run policy requires provider usage'\n + ' reporting that the last model call did not supply.' + finish\n }\n}\n\nasync function drainSegment(\n options: RunToolCallsOptions,\n segment: readonly Slot[],\n maxResultBytes: number,\n): Promise<void> {\n for (const slot of segment) {\n try { await commit(options, slot, maxResultBytes) }\n catch { /* The admission failure remains primary after every owned slot settles. */ }\n }\n}\n\nasync function commit(\n options: RunToolCallsOptions,\n slot: Slot,\n maxResultBytes: number,\n): Promise<ToolExecutionResult> {\n let result: ToolExecutionResult\n let fatal: unknown\n let hasFatal = false\n try {\n let pending: ToolExecutionResult\n try {\n pending = await raceWithSignal(slot.pending, slot.signal)\n } catch (error: unknown) {\n if (!slot.signal.aborted) throw error\n const settled = await waitForSettlement(slot.pending, slot.teardownTimeoutMs)\n if (!settled) throw teardownFailure(slot.call.toolName, 'execution', slot.teardownTimeoutMs, error)\n pending = cancelledResult(slot.deadline)\n }\n if (slot.program !== undefined) {\n // The body has returned; no child may start or publish after this point.\n const closed = await slot.program.close(slot.teardownTimeoutMs)\n if (!closed) throw teardownFailure(slot.call.toolName, 'nested call', slot.teardownTimeoutMs, undefined)\n const latched: unknown = slot.program.fatal\n if (latched !== undefined) {\n throw toolErrorDisposition(latched) === 'fatal'\n ? latched\n : ToolError.fatal(messageOf(latched), errorCode(latched), { cause: latched })\n }\n }\n let finalized = pending\n if (slot.authorized !== undefined) {\n const finalizing = finalizeToolCall(slot.authorized, pending)\n try {\n finalized = await raceWithSignal(finalizing, slot.signal)\n } catch (error: unknown) {\n if (!slot.signal.aborted) throw error\n const settled = await waitForSettlement(finalizing, slot.teardownTimeoutMs)\n if (!settled) throw teardownFailure(slot.call.toolName, 'finalization', slot.teardownTimeoutMs, error)\n finalized = cancelledResult(slot.deadline)\n }\n }\n result = immutableResult(await boundOutput(options, slot, finalized), maxResultBytes)\n } catch (error: unknown) {\n slot.program?.abort(error)\n hasFatal = toolErrorDisposition(error) === 'fatal'\n fatal = error\n result = immutableResult(toolFailure(messageOf(error), errorCode(error)), maxResultBytes)\n }\n const message = createToolResultMessage({ callId: slot.call.callId, content: [...result.content], isError: result.isError })\n options.history.append({ kind: 'tool-result', callId: slot.call.callId, message, result })\n await emitEvent(options, { type: 'tool-result', call: slot.call, result, trace: slot.trace })\n await emitEvent(options, {\n type: 'span-end', trace: slot.trace, at: now(), status: result.isError ? 'error' : 'success',\n output: result.isError ? { error: result.error } : result.value,\n ...result.isError ? { error: { type: 'ToolError', message: result.error.message, code: result.error.code } } : {},\n })\n if (result.additionalContext !== undefined) {\n // Additional context is deliberately a separate application message.\n options.history.append({ kind: 'user', message: createUserMessage({\n content: [...result.additionalContext], source: { kind: 'app', producer: `tool:${slot.call.toolName}` },\n }) })\n }\n if (hasFatal) throw fatal\n return result\n}\n\n/**\n * Keep one tool result inside its share of the context window.\n *\n * This runs at the RESULT boundary rather than when the request is assembled,\n * which is the whole point: by the time an oversized result reaches the model\n * the window is already gone, and the turn's only remaining move is to lose\n * everything it has paid for. Both reference harnesses cut here.\n *\n * The budget is the stricter of the turn's and the tool's own declaration, so\n * a tool that knows it returns a lot can ask for room without any tool being\n * able to exceed what the host allows.\n * @param options - The scheduling options carrying budget and store.\n * @param slot - The call this result belongs to.\n * @param result - The finalized result.\n * @returns The result the model will read.\n */\nasync function boundOutput(\n options: RunToolCallsOptions,\n slot: Slot,\n result: ToolExecutionResult,\n): Promise<ToolExecutionResult> {\n const turnBudget = options.maxResultTokens\n const toolBudget = options.catalog.get(slot.call.toolName)?.maxOutputTokens\n const budget = turnBudget === undefined\n ? toolBudget\n : toolBudget === undefined ? turnBudget : Math.min(turnBudget, toolBudget)\n if (budget === undefined) return result\n const tokens = estimateTextBlockTokens(result.content)\n if (tokens <= budget) return result\n\n // Only text is shortened. Cutting an image block produces a corrupt image\n // rather than a smaller one, so those pass through and are counted by the\n // byte cap instead.\n const texts = result.content.filter(block => block.type === 'text')\n if (texts.length === 0) return result\n const full = texts.map(block => block.text).join('\\n')\n const policy = options.resultOverflow ?? 'auto'\n const store = options.spillStore\n\n if (policy !== 'truncate' && store !== undefined) {\n try {\n // Reserve room for the notice inside the budget, so the replacement is\n // never bigger than what it replaced.\n const record = await store.save(full, {\n toolName: slot.call.toolName, callId: String(slot.call.callId),\n })\n const preview = previewForSpill(full, Math.max(1, Math.floor(budget * 0.75)))\n return replaceText(result, `${preview}\\n\\n[Output exceeded this call's budget of `\n + `${String(budget)} estimated tokens. The full ${String(record.bytes)} bytes are saved. `\n + `${record.retrieval}]`, {\n outputSpilled: { locator: record.locator, bytes: record.bytes, estimatedTokens: tokens },\n })\n } catch {\n // Best-effort, deliberately: a store that is full or unreachable must not\n // cost the model a result it can still read most of.\n }\n }\n\n const truncated = truncateMiddleToTokens(full, budget)\n return replaceText(result, truncated.text\n + `\\n\\n[Output was ${String(truncated.originalTokens)} estimated tokens, over this call's `\n + `budget of ${String(budget)}. Omitted output does not mean the operation failed. `\n + 'Check an existing receipt or current state; repeat the operation only when the host confirms it is safe.]', {\n outputTruncated: { estimatedTokens: truncated.originalTokens, budget },\n })\n}\n\n/**\n * Swap a result's text for one shortened block, keeping everything else.\n * @param result - The original result.\n * @param text - The replacement text.\n * @param meta - What a UI should know about the replacement.\n * @returns The rewritten result.\n */\nfunction replaceText(\n result: ToolExecutionResult,\n text: string,\n meta: JsonObject,\n): ToolExecutionResult {\n // The replacement takes the FIRST text block's position and the other text\n // blocks drop out. Appending it after the non-text blocks instead would\n // reorder a result whose image came after its caption.\n let placed = false\n const content: ContentBlock[] = []\n for (const block of result.content) {\n if (block.type !== 'text') { content.push(block); continue }\n if (placed) continue\n placed = true\n content.push({ type: 'text', text })\n }\n // `value` is deliberately untouched: it is what a host logs and replays, and\n // shortening it would make the record disagree with what the tool returned.\n return { ...result, content, meta: { ...result.meta, ...meta } }\n}\n\nfunction immutableCall(call: ToolCallRequest): ToolCallRequest {\n if (typeof call.callId !== 'string' || call.callId.length === 0\n || typeof call.toolName !== 'string' || call.toolName.length === 0\n || typeof call.rawArguments !== 'string') {\n throw new TypeError('tool call identity, name, and raw arguments must be strings')\n }\n return detachedFrozen(call)\n}\n\nfunction cancelledResult(deadline: AbortSignal, maxDurationMs?: number): ToolExecutionResult {\n const limit = maxDurationMs === undefined ? 'configured time limit' : `${maxDurationMs}ms time limit`\n return deadline.aborted\n ? toolFailure(\n `the tool exceeded its ${limit}`,\n TOOL_ERROR_CODES.TIMEOUT,\n )\n : toolFailure('the call was cancelled', TOOL_ERROR_CODES.ABORTED)\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) throw new RangeError(`${name} must be a positive integer`)\n return value\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"schedule.js","names":[],"sources":["../../../src/agent/loop/schedule.ts"],"sourcesContent":["import { createToolResultMessage, createUserMessage } from '../../message/index.ts'\nimport type { ContentBlock } from '../../message/index.ts'\nimport { detachedFrozen } from '../../primitives/index.ts'\nimport type { JsonObject } from '../../primitives/index.ts'\nimport { waitForSettlement } from '../../async/index.ts'\nimport type { History } from '../history/history.ts'\nimport type { ApprovalBroker } from '../tool/approval.ts'\nimport type { ToolCallPosition, ToolExecutionResult } from '../tool/definition.ts'\nimport {\n estimateTextBlockTokens, previewForSpill, truncateMiddleToTokens,\n type SpillStore, type ToolOutputOverflowPolicy,\n} from '../tool/output-budget.ts'\nimport { TOOL_ERROR_CODES, ToolError, toolErrorDisposition } from '../tool/errors.ts'\nimport {\n authorizeToolCall, dispatchAuthorizedToolCall, finalizeToolCall, prepareToolCall, toolFailure,\n type AuthorizedToolCall, type ToolCallRequest, type ToolInterceptor,\n} from '../tool/pipeline.ts'\nimport type { ToolCatalog } from '../tool/registry.ts'\nimport { createSpanId, type TraceRef } from '../trace/trace.ts'\nimport type { AgentEvent, ToolDeclineReason, TurnHooks } from './events.ts'\nimport type { RunAccountingPort } from '../accounting/contracts.ts'\nimport type { SdkLogger } from '../../logging/types.ts'\nimport { createToolAdmission, type AdmissionTicket } from './admission.ts'\nimport { ProgramRun, validPrograms, type StepRuntime } from './program.ts'\nimport {\n emitEvent, errorCode, immutableResult, messageOf, now, raceWithSignal, teardownFailure, withApprovalEvents,\n} from './tool-call-support.ts'\nimport { attachNestedToolPort, NESTED_TOOL_ERROR_CODES, type ProgramGrant } from '../tool/nested.ts'\nimport type { ProgramResultStore } from '../tool/program-results.ts'\n\nexport interface RunToolCallsOptions {\n readonly calls: readonly ToolCallRequest[]\n readonly catalog: ToolCatalog\n readonly history: History\n readonly position: ToolCallPosition\n readonly signal: AbortSignal\n readonly logger?: SdkLogger\n readonly parentTrace: TraceRef\n readonly maxParallel?: number\n /**\n * How many BUDGETED calls may still be dispatched this step.\n *\n * Calls to a `budgetExempt` tool are not counted against it, so a spent\n * budget never blocks the model from submitting, asking, or delegating.\n */\n readonly dispatchLimit?: number\n /** Why calls past `dispatchLimit` are not run; shown to the model verbatim. */\n readonly declineReason?: ToolDeclineReason\n /** Estimated tokens of text one result may put in front of the model. */\n readonly maxResultTokens?: number\n /** What happens to output over that budget. Defaults to `auto`. */\n readonly resultOverflow?: ToolOutputOverflowPolicy\n /** Where spilled output goes; `auto` spills only when this is mounted. */\n readonly spillStore?: SpillStore\n /** Maximum serialized bytes retained for one finalized result. Defaults to 4 MiB. */\n readonly maxResultBytes?: number\n /** End-to-end wall-clock allowance for one call. Defaults to 10 minutes. */\n readonly maxDurationMs?: number\n /** Maximum cancellation settlement wait. Defaults to 30 seconds. */\n readonly teardownTimeoutMs?: number\n readonly interceptors?: readonly ToolInterceptor[]\n readonly approvals?: ApprovalBroker\n readonly emit?: (event: AgentEvent) => Promise<void>\n readonly checkpoint?: TurnHooks['checkpoint']\n readonly accounting?: RunAccountingPort\n /**\n * A same-turn result the loop may expose instead of dispatching this exact\n * call again. The scheduler still records a distinct call/result pair.\n */\n readonly recover?: (call: ToolCallRequest) => ToolExecutionResult | undefined\n}\nexport interface ToolCallsOutcome {\n readonly results: readonly ToolExecutionResult[]\n readonly concluded: boolean\n readonly concludedBy?: string\n readonly dispatched: number\n /** Dispatched calls that spent budget; exempt tools are excluded. */\n readonly budgeted: number\n /** Calls the loop refused to run, for the reason in `declineReason`. */\n readonly declined: number\n}\n\ninterface Slot {\n readonly call: ToolCallRequest\n readonly trace: TraceRef\n readonly authorized?: AuthorizedToolCall\n readonly pending: Promise<ToolExecutionResult>\n readonly dispatched: boolean\n /** The program this call hosts, when the host granted it one. */\n readonly program?: ProgramRun\n /** Set when the loop refused to run the call. */\n readonly declined?: boolean\n readonly signal: AbortSignal\n readonly deadline: AbortSignal\n readonly teardownTimeoutMs: number\n}\n\n/** Scheduler options the loop owns and does not publish. */\nexport interface InternalScheduleOptions {\n /** Overrides `dispatchLimit`; `unbounded` when the budget is a notice rather than a wall. */\n readonly admissionLimit?: number | 'unbounded'\n /** A call-specific guard; refusing one sibling does not reserve another's quota. */\n readonly decline?: (call: ToolCallRequest) => ToolDeclineReason | undefined\n /**\n * Program tools and what each may call. Research seam for SP-01; the public\n * way to enable programs is not decided.\n */\n readonly programs?: ReadonlyMap<string, ProgramGrant>\n /** Where programs retain values; owned and closed by the caller. */\n readonly programResults?: ProgramResultStore\n}\n\n/** Execute safe siblings concurrently while committing every result in model order. */\nexport async function runToolCalls(options: RunToolCallsOptions): Promise<ToolCallsOutcome> {\n return await scheduleToolCalls(options)\n}\n\n/** {@link runToolCalls} with the loop's internal options. */\nexport async function scheduleToolCalls(\n options: RunToolCallsOptions,\n internal: InternalScheduleOptions = {},\n): Promise<ToolCallsOutcome> {\n const maxParallel = positiveInteger(options.maxParallel ?? 8, 'maxParallel')\n const maxResultBytes = positiveInteger(options.maxResultBytes ?? 4 * 1024 * 1024, 'maxResultBytes')\n const maxDurationMs = positiveInteger(options.maxDurationMs ?? 10 * 60_000, 'maxDurationMs')\n const teardownTimeoutMs = positiveInteger(options.teardownTimeoutMs ?? 30_000, 'teardownTimeoutMs')\n const calls = Object.freeze(options.calls.map(immutableCall))\n const admission = createToolAdmission(internal.admissionLimit ?? Math.max(0, options.dispatchLimit ?? calls.length))\n const step: StepRuntime = {\n admission, maxDurationMs, teardownTimeoutMs, maxResultBytes,\n programs: validPrograms(internal.programs),\n programResults: internal.programResults,\n }\n const results: ToolExecutionResult[] = []\n let dispatched = 0\n let declined = 0\n let concludedBy: string | undefined\n let index = 0\n let carried: ReturnType<typeof prepareToolCall> | undefined\n\n while (index < calls.length) {\n const first = calls[index]\n if (first === undefined) break\n const prepared = carried ?? prepare(options, first)\n carried = undefined\n if (prepared.mode === 'exclusive') {\n const slot = await start(options, prepared, step, internal)\n dispatched += slot.dispatched ? 1 : 0\n declined += slot.declined === true ? 1 : 0\n const result = await commit(options, slot, maxResultBytes)\n results.push(result)\n if (concludedBy === undefined && !result.isError && result.concludesTurn) concludedBy = first.toolName\n index++\n continue\n }\n\n // A rolling parallel segment. Re-prepare each call immediately before start;\n // an updated registry can therefore turn the next call into a barrier.\n const segment: Slot[] = []\n const segmentAbort = new AbortController()\n const segmentOptions: RunToolCallsOptions = {\n ...options,\n signal: AbortSignal.any([options.signal, segmentAbort.signal]),\n }\n let nextPrepared = prepared\n try {\n while (index < calls.length && segment.length < maxParallel) {\n const call = calls[index]\n if (call === undefined) break\n const candidate = nextPrepared\n if (candidate.mode !== 'parallel') break\n const slot = await start(segmentOptions, candidate, step, internal)\n dispatched += slot.dispatched ? 1 : 0\n declined += slot.declined === true ? 1 : 0\n segment.push(slot)\n index++\n const nextCall = calls[index]\n if (nextCall !== undefined && segment.length < maxParallel) {\n nextPrepared = prepare(segmentOptions, nextCall)\n if (nextPrepared.mode !== 'parallel') carried = nextPrepared\n }\n }\n } catch (error: unknown) {\n // Admission of a later sibling failed after earlier bodies started.\n // Cancel and drain everything already owned before propagating that error.\n segmentAbort.abort(error)\n await drainSegment(options, segment, maxResultBytes)\n throw error\n }\n if (segment.length === 0) continue\n // Bodies have already started. Finalization and history publication remain ordered.\n let fatal: unknown\n let hasFatal = false\n for (const slot of segment) {\n try {\n const result = await commit(options, slot, maxResultBytes)\n results.push(result)\n if (concludedBy === undefined && !result.isError && result.concludesTurn) concludedBy = slot.call.toolName\n } catch (error: unknown) {\n if (!hasFatal) fatal = error\n hasFatal = true\n segmentAbort.abort(error)\n }\n }\n // Every parallel body was already dispatched. Commit/drain all siblings so\n // none is orphaned, then propagate the first fatal contract violation.\n if (hasFatal) throw fatal\n }\n return {\n results: Object.freeze(results),\n concluded: concludedBy !== undefined,\n ...concludedBy === undefined ? {} : { concludedBy },\n dispatched,\n budgeted: admission.budgeted,\n declined,\n }\n}\n\nfunction prepare(options: RunToolCallsOptions, call: ToolCallRequest) {\n return prepareToolCall({\n catalog: options.catalog, call, position: options.position, signal: options.signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n ...options.interceptors === undefined ? {} : { interceptors: options.interceptors },\n ...options.approvals === undefined ? {} : { approvals: options.approvals },\n })\n}\n\nasync function start(\n options: RunToolCallsOptions,\n prepared: ReturnType<typeof prepareToolCall>,\n step: StepRuntime,\n internal: InternalScheduleOptions,\n): Promise<Slot> {\n const { maxDurationMs, teardownTimeoutMs } = step\n const deadline = AbortSignal.timeout(maxDurationMs)\n const signal = AbortSignal.any([options.signal, deadline])\n const boundedPrepared = {\n ...prepared,\n options: { ...prepared.options, signal, teardownTimeoutMs },\n context: { ...prepared.context, signal },\n }\n const call = boundedPrepared.options.call\n const trace: TraceRef = {\n traceId: options.parentTrace.traceId,\n spanId: createSpanId(),\n parentSpanId: options.parentTrace.spanId,\n }\n const recovered = !options.signal.aborted && !signal.aborted\n ? options.recover?.(call)\n : undefined\n options.history.append({ kind: 'tool-call', callId: call.callId, name: call.toolName, rawArguments: call.rawArguments })\n await emitEvent(options, { type: 'span-start', trace, at: now(), name: `execute_tool ${call.toolName}`, kind: 'execute_tool', attributes: {\n 'gen_ai.operation.name': 'execute_tool', 'gen_ai.tool.name': call.toolName, 'gen_ai.tool.call.id': call.callId,\n ...recovered === undefined ? {} : { 'sdk.tool.recovered': true },\n }, input: call.rawArguments })\n await emitEvent(options, { type: 'tool-call', call, trace })\n // Recovery is not a completion signal. Preserve the existing cancellation\n // outcomes before consulting retained same-turn state.\n if (options.signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure(\n 'the call was cancelled before it started',\n TOOL_ERROR_CODES.ABORTED_BEFORE_DISPATCH,\n )),\n }\n if (signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(cancelledResult(deadline, maxDurationMs)),\n }\n if (recovered !== undefined) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(recovered),\n }\n // A tool the model may always reach: submitting, asking, delegating. Letting\n // a budget block these is what turns a spent budget into a dead run.\n const exempt = options.catalog.get(call.toolName)?.budgetExempt === true\n const decline = exempt ? undefined : internal.decline?.(call)\n if (decline !== undefined) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false, declined: true,\n pending: Promise.resolve(declinedResult(decline)),\n }\n const grant = step.programs.get(call.toolName)\n // A program spends the budget it is charged for; it can never be the free\n // call, and a parallel program would race its own children for siblings.\n if (grant !== undefined && (exempt || prepared.mode !== 'exclusive')) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure(\n 'this program tool must be exclusive and must not be budget-exempt',\n NESTED_TOOL_ERROR_CODES.CONFIGURATION,\n )),\n }\n const ticket = step.admission.reserve(exempt)\n if (ticket === undefined) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false, declined: true,\n pending: Promise.resolve(declinedResult(options.declineReason ?? 'tool-calls')),\n }\n // Every path below that does not start the body returns the reservation.\n try {\n return await authorizeAndDispatch(options, boundedPrepared, call, trace, signal, deadline, ticket, step, grant)\n } finally {\n ticket.release()\n }\n}\n\nasync function authorizeAndDispatch(\n options: RunToolCallsOptions,\n boundedPrepared: ReturnType<typeof prepareToolCall>,\n call: ToolCallRequest,\n trace: TraceRef,\n signal: AbortSignal,\n deadline: AbortSignal,\n ticket: AdmissionTicket,\n step: StepRuntime,\n grant: ProgramGrant | undefined,\n): Promise<Slot> {\n const { maxDurationMs, teardownTimeoutMs } = step\n if (options.signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure('the call was cancelled before it started', TOOL_ERROR_CODES.ABORTED_BEFORE_DISPATCH)),\n }\n const authorizationPending = authorizeToolCall(withApprovalEvents(options, boundedPrepared, trace))\n let authorization: Awaited<ReturnType<typeof authorizeToolCall>>\n try {\n authorization = await raceWithSignal(authorizationPending, signal)\n } catch (error: unknown) {\n if (!signal.aborted) throw error\n const settled = await waitForSettlement(authorizationPending, teardownTimeoutMs)\n if (!settled) throw teardownFailure(call.toolName, 'authorization', teardownTimeoutMs, error)\n return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(cancelledResult(deadline, maxDurationMs)),\n }\n }\n if (authorization.kind === 'final') return {\n call, trace, signal, deadline, teardownTimeoutMs,\n dispatched: false, pending: Promise.resolve(authorization.result),\n }\n try {\n const checkpointing = Promise.resolve(options.checkpoint?.({\n kind: 'before-tool-dispatch', call, snapshot: options.history.snapshot(), signal,\n ...(options.logger === undefined ? {} : { logger: options.logger }),\n }))\n try { await raceWithSignal(checkpointing, signal) }\n catch (error: unknown) {\n if (!signal.aborted) throw error\n if (!await waitForSettlement(checkpointing, teardownTimeoutMs)) {\n throw teardownFailure(call.toolName, 'checkpoint', teardownTimeoutMs, error)\n }\n }\n } catch (error: unknown) {\n if (error instanceof ToolError && error.code === TOOL_ERROR_CODES.TEARDOWN_TIMEOUT) throw error\n return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(toolFailure(`history checkpoint failed: ${messageOf(error)}`, TOOL_ERROR_CODES.CHECKPOINT_FAILED)),\n }\n }\n if (signal.aborted) return {\n call, trace, signal, deadline, teardownTimeoutMs, dispatched: false,\n pending: Promise.resolve(cancelledResult(deadline, maxDurationMs)),\n }\n const program = grant === undefined\n ? undefined\n : new ProgramRun(options, step, grant, { call, trace, signal })\n if (program !== undefined) attachNestedToolPort(authorization.call, program.port, signal => program.bindExecutionSignal(signal))\n ticket.confirm()\n const pending = dispatchAuthorizedToolCall(authorization.call)\n // Observe rejection in the same turn in which dispatch creates the promise.\n // commit() still receives and propagates the original rejection in order.\n void pending.catch(() => undefined)\n return {\n call, trace, signal, deadline, teardownTimeoutMs,\n authorized: authorization.call, dispatched: true,\n ...program === undefined ? {} : { program },\n pending,\n }\n}\n\n/**\n * What the model reads when the loop refuses to run a call.\n *\n * Not a failure. A failure is something that went wrong, and a model answers\n * one by retrying — with a smaller argument, a different path — which spends\n * exactly what is no longer there. This says what happened and what to do\n * instead, in the plainest terms available, and nothing about it reads as\n * broken tooling.\n * @param reason - Which limit refused the call.\n * @returns A successful result carrying the instruction.\n */\nfunction declinedResult(reason: ToolDeclineReason): ToolExecutionResult {\n return {\n isError: false,\n value: undefined,\n content: [{ type: 'text', text: declineText(reason) }],\n // Never shown to the model; lets a UI render this as declined rather than\n // as work that succeeded.\n meta: { declined: true, reason },\n }\n}\n\n/**\n * The instruction for one decline reason.\n *\n * Each limit gets its own wording: a model told \"no remaining tool-call\n * budget\" when it actually tripped the repeat guard learns the wrong lesson\n * and repeats the call in the next turn.\n * @param reason - Which limit refused the call.\n * @returns The sentence the model reads.\n */\nfunction declineText(reason: ToolDeclineReason): string {\n const finish = ' Answer now from what you already have, and say plainly what is'\n + ' unverified or unfinished.'\n switch (reason) {\n case 'tool-calls':\n return 'This call was not run: the turn has spent its tool-call budget.'\n + ' Further work calls will not run either, so do not retry it.' + finish\n case 'repeated-tool-call':\n return 'This call was not run: it repeats a call already made with the same'\n + ' arguments, and repeating it cannot produce a new result. Change'\n + ' approach or finish.' + finish\n case 'tool-call-cycle':\n return 'This call was not run: the same sequence of calls has repeated'\n + ' several times without progress. Break the cycle rather than'\n + ' re-entering it.' + finish\n case 'tokens':\n return 'This call was not run: the turn has spent its token budget.' + finish\n case 'consecutive-tool-errors':\n return 'This call was not run: too many calls in a row have failed.'\n + ' Something in the approach is wrong, not the individual call.' + finish\n case 'steps':\n return 'This call was not run: the turn has no model steps left.' + finish\n case 'usage-required':\n return 'This call was not run: the run policy requires provider usage'\n + ' reporting that the last model call did not supply.' + finish\n }\n}\n\nasync function drainSegment(\n options: RunToolCallsOptions,\n segment: readonly Slot[],\n maxResultBytes: number,\n): Promise<void> {\n for (const slot of segment) {\n try { await commit(options, slot, maxResultBytes) }\n catch { /* The admission failure remains primary after every owned slot settles. */ }\n }\n}\n\nasync function commit(\n options: RunToolCallsOptions,\n slot: Slot,\n maxResultBytes: number,\n): Promise<ToolExecutionResult> {\n let result: ToolExecutionResult\n let fatal: unknown\n let hasFatal = false\n try {\n let pending: ToolExecutionResult\n try {\n pending = await raceWithSignal(slot.pending, slot.signal)\n } catch (error: unknown) {\n if (!slot.signal.aborted) throw error\n const settled = await waitForSettlement(slot.pending, slot.teardownTimeoutMs)\n if (!settled) throw teardownFailure(slot.call.toolName, 'execution', slot.teardownTimeoutMs, error)\n pending = cancelledResult(slot.deadline)\n }\n if (slot.program !== undefined) {\n // The body has returned; no child may start or publish after this point.\n const closed = await slot.program.close(slot.teardownTimeoutMs)\n if (!closed) throw teardownFailure(slot.call.toolName, 'nested call', slot.teardownTimeoutMs, undefined)\n const latched: unknown = slot.program.fatal\n if (latched !== undefined) {\n throw toolErrorDisposition(latched) === 'fatal'\n ? latched\n : ToolError.fatal(messageOf(latched), errorCode(latched), { cause: latched })\n }\n }\n let finalized = pending\n if (slot.authorized !== undefined) {\n const finalizing = finalizeToolCall(slot.authorized, pending)\n try {\n finalized = await raceWithSignal(finalizing, slot.signal)\n } catch (error: unknown) {\n if (!slot.signal.aborted) throw error\n const settled = await waitForSettlement(finalizing, slot.teardownTimeoutMs)\n if (!settled) throw teardownFailure(slot.call.toolName, 'finalization', slot.teardownTimeoutMs, error)\n finalized = cancelledResult(slot.deadline)\n }\n }\n result = immutableResult(await boundOutput(options, slot, finalized), maxResultBytes)\n } catch (error: unknown) {\n slot.program?.abort(error)\n hasFatal = toolErrorDisposition(error) === 'fatal'\n fatal = error\n result = immutableResult(toolFailure(messageOf(error), errorCode(error)), maxResultBytes)\n }\n const message = createToolResultMessage({ callId: slot.call.callId, content: [...result.content], isError: result.isError })\n options.history.append({ kind: 'tool-result', callId: slot.call.callId, message, result })\n await emitEvent(options, { type: 'tool-result', call: slot.call, result, trace: slot.trace })\n await emitEvent(options, {\n type: 'span-end', trace: slot.trace, at: now(), status: result.isError ? 'error' : 'success',\n output: result.isError ? { error: result.error } : result.value,\n ...result.isError ? { error: { type: 'ToolError', message: result.error.message, code: result.error.code } } : {},\n })\n if (result.additionalContext !== undefined) {\n // Additional context is deliberately a separate application message.\n options.history.append({ kind: 'user', message: createUserMessage({\n content: [...result.additionalContext], source: { kind: 'app', producer: `tool:${slot.call.toolName}` },\n }) })\n }\n if (hasFatal) throw fatal\n return result\n}\n\n/**\n * Keep one tool result inside its share of the context window.\n *\n * This runs at the RESULT boundary rather than when the request is assembled,\n * which is the whole point: by the time an oversized result reaches the model\n * the window is already gone, and the turn's only remaining move is to lose\n * everything it has paid for. Both reference harnesses cut here.\n *\n * The budget is the stricter of the turn's and the tool's own declaration, so\n * a tool that knows it returns a lot can ask for room without any tool being\n * able to exceed what the host allows.\n * @param options - The scheduling options carrying budget and store.\n * @param slot - The call this result belongs to.\n * @param result - The finalized result.\n * @returns The result the model will read.\n */\nasync function boundOutput(\n options: RunToolCallsOptions,\n slot: Slot,\n result: ToolExecutionResult,\n): Promise<ToolExecutionResult> {\n const turnBudget = options.maxResultTokens\n const toolBudget = options.catalog.get(slot.call.toolName)?.maxOutputTokens\n const budget = turnBudget === undefined\n ? toolBudget\n : toolBudget === undefined ? turnBudget : Math.min(turnBudget, toolBudget)\n if (budget === undefined) return result\n const tokens = estimateTextBlockTokens(result.content)\n if (tokens <= budget) return result\n\n // Only text is shortened. Cutting an image block produces a corrupt image\n // rather than a smaller one, so those pass through and are counted by the\n // byte cap instead.\n const texts = result.content.filter(block => block.type === 'text')\n if (texts.length === 0) return result\n const full = texts.map(block => block.text).join('\\n')\n const policy = options.resultOverflow ?? 'auto'\n const store = options.spillStore\n\n if (policy !== 'truncate' && store !== undefined) {\n try {\n // Reserve room for the notice inside the budget, so the replacement is\n // never bigger than what it replaced.\n const record = await store.save(full, {\n toolName: slot.call.toolName, callId: String(slot.call.callId),\n })\n const preview = previewForSpill(full, Math.max(1, Math.floor(budget * 0.75)))\n return replaceText(result, `${preview}\\n\\n[Output exceeded this call's budget of `\n + `${String(budget)} estimated tokens. The full ${String(record.bytes)} bytes are saved. `\n + `${record.retrieval}]`, {\n outputSpilled: { locator: record.locator, bytes: record.bytes, estimatedTokens: tokens },\n })\n } catch {\n // Best-effort, deliberately: a store that is full or unreachable must not\n // cost the model a result it can still read most of.\n }\n }\n\n const truncated = truncateMiddleToTokens(full, budget)\n return replaceText(result, truncated.text\n + `\\n\\n[Output was ${String(truncated.originalTokens)} estimated tokens, over this call's `\n + `budget of ${String(budget)}. Omitted output does not mean the operation failed. `\n + 'Check an existing receipt or current state; repeat the operation only when the host confirms it is safe.]', {\n outputTruncated: { estimatedTokens: truncated.originalTokens, budget },\n })\n}\n\n/**\n * Swap a result's text for one shortened block, keeping everything else.\n * @param result - The original result.\n * @param text - The replacement text.\n * @param meta - What a UI should know about the replacement.\n * @returns The rewritten result.\n */\nfunction replaceText(\n result: ToolExecutionResult,\n text: string,\n meta: JsonObject,\n): ToolExecutionResult {\n // The replacement takes the FIRST text block's position and the other text\n // blocks drop out. Appending it after the non-text blocks instead would\n // reorder a result whose image came after its caption.\n let placed = false\n const content: ContentBlock[] = []\n for (const block of result.content) {\n if (block.type !== 'text') { content.push(block); continue }\n if (placed) continue\n placed = true\n content.push({ type: 'text', text })\n }\n // `value` is deliberately untouched: it is what a host logs and replays, and\n // shortening it would make the record disagree with what the tool returned.\n return { ...result, content, meta: { ...result.meta, ...meta } }\n}\n\nfunction immutableCall(call: ToolCallRequest): ToolCallRequest {\n if (typeof call.callId !== 'string' || call.callId.length === 0\n || typeof call.toolName !== 'string' || call.toolName.length === 0\n || typeof call.rawArguments !== 'string') {\n throw new TypeError('tool call identity, name, and raw arguments must be strings')\n }\n return detachedFrozen(call)\n}\n\nfunction cancelledResult(deadline: AbortSignal, maxDurationMs?: number): ToolExecutionResult {\n const limit = maxDurationMs === undefined ? 'configured time limit' : `${maxDurationMs}ms time limit`\n return deadline.aborted\n ? toolFailure(\n `the tool exceeded its ${limit}`,\n TOOL_ERROR_CODES.TIMEOUT,\n )\n : toolFailure('the call was cancelled', TOOL_ERROR_CODES.ABORTED)\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) throw new RangeError(`${name} must be a positive integer`)\n return value\n}\n"],"mappings":"g/BAiHA,eAAsB,EAAa,EAAyD,CAC1F,OAAO,MAAM,EAAkB,CAAO,CACxC,CAGA,eAAsB,EACpB,EACA,EAAoC,CAAC,EACV,CAC3B,IAAM,EAAc,EAAgB,EAAQ,aAAe,EAAG,aAAa,EACrE,EAAiB,EAAgB,EAAQ,gBAAkB,QAAiB,gBAAgB,EAC5F,EAAgB,EAAgB,EAAQ,eAAiB,IAAa,eAAe,EACrF,EAAoB,EAAgB,EAAQ,mBAAqB,IAAQ,mBAAmB,EAC5F,EAAQ,OAAO,OAAO,EAAQ,MAAM,IAAI,CAAa,CAAC,EACtD,EAAY,EAAoB,EAAS,gBAAkB,KAAK,IAAI,EAAG,EAAQ,eAAiB,EAAM,MAAM,CAAC,EAC7G,EAAoB,CACxB,YAAW,gBAAe,oBAAmB,iBAC7C,SAAU,EAAc,EAAS,QAAQ,EACzC,eAAgB,EAAS,cAC3B,EACM,EAAiC,CAAC,EACpC,EAAa,EACb,EAAW,EACX,EACA,EAAQ,EACR,EAEJ,KAAO,EAAQ,EAAM,QAAQ,CAC3B,IAAM,EAAQ,EAAM,GACpB,GAAI,IAAU,IAAA,GAAW,MACzB,IAAM,EAAW,GAAW,EAAQ,EAAS,CAAK,EAElD,GADA,EAAU,IAAA,GACN,EAAS,OAAS,YAAa,CACjC,IAAM,EAAO,MAAM,EAAM,EAAS,EAAU,EAAM,CAAQ,EAC1D,GAAc,KAAK,WACnB,GAAY,IAAK,WAAa,IAC9B,IAAM,EAAS,MAAM,EAAO,EAAS,EAAM,CAAc,EACzD,EAAQ,KAAK,CAAM,EACf,IAAgB,IAAA,IAAa,CAAC,EAAO,SAAW,EAAO,gBAAe,EAAc,EAAM,UAC9F,IACA,QACF,CAIA,IAAM,EAAkB,CAAC,EACnB,EAAe,IAAI,gBACnB,EAAsC,CAC1C,GAAG,EACH,OAAQ,YAAY,IAAI,CAAC,EAAQ,OAAQ,EAAa,MAAM,CAAC,CAC/D,EACI,EAAe,EACnB,GAAI,CACF,KAAO,EAAQ,EAAM,QAAU,EAAQ,OAAS,GACjC,EAAM,KACN,IAAA,IAF8C,CAG3D,IAAM,EAAY,EAClB,GAAI,EAAU,OAAS,WAAY,MACnC,IAAM,EAAO,MAAM,EAAM,EAAgB,EAAW,EAAM,CAAQ,EAClE,GAAc,KAAK,WACnB,GAAY,IAAK,WAAa,IAC9B,EAAQ,KAAK,CAAI,EACjB,IACA,IAAM,EAAW,EAAM,GACnB,IAAa,IAAA,IAAa,EAAQ,OAAS,IAC7C,EAAe,EAAQ,EAAgB,CAAQ,EAC3C,EAAa,OAAS,aAAY,EAAU,GAEpD,CACF,OAAS,EAAgB,CAKvB,MAFA,EAAa,MAAM,CAAK,EACxB,MAAM,EAAa,EAAS,EAAS,CAAc,EAC7C,CACR,CACA,GAAI,EAAQ,SAAW,EAAG,SAE1B,IAAI,EACA,EAAW,GACf,IAAK,IAAM,KAAQ,EACjB,GAAI,CACF,IAAM,EAAS,MAAM,EAAO,EAAS,EAAM,CAAc,EACzD,EAAQ,KAAK,CAAM,EACf,IAAgB,IAAA,IAAa,CAAC,EAAO,SAAW,EAAO,gBAAe,EAAc,EAAK,KAAK,SACpG,OAAS,EAAgB,CAClB,IAAU,EAAQ,GACvB,EAAW,GACX,EAAa,MAAM,CAAK,CAC1B,CAIF,GAAI,EAAU,MAAM,CACtB,CACA,MAAO,CACL,QAAS,OAAO,OAAO,CAAO,EAC9B,UAAW,IAAgB,IAAA,GAC3B,GAAG,IAAgB,IAAA,GAAY,CAAC,EAAI,CAAE,aAAY,EAClD,aACA,SAAU,EAAU,SACpB,UACF,CACF,CAEA,SAAS,EAAQ,EAA8B,EAAuB,CACpE,OAAO,EAAgB,CACrB,QAAS,EAAQ,QAAS,OAAM,SAAU,EAAQ,SAAU,OAAQ,EAAQ,OAC5E,GAAI,EAAQ,SAAW,IAAA,GAAY,CAAC,EAAI,CAAE,OAAQ,EAAQ,MAAO,EACjE,GAAG,EAAQ,eAAiB,IAAA,GAAY,CAAC,EAAI,CAAE,aAAc,EAAQ,YAAa,EAClF,GAAG,EAAQ,YAAc,IAAA,GAAY,CAAC,EAAI,CAAE,UAAW,EAAQ,SAAU,CAC3E,CAAC,CACH,CAEA,eAAe,EACb,EACA,EACA,EACA,EACe,CACf,GAAM,CAAE,gBAAe,qBAAsB,EACvC,EAAW,YAAY,QAAQ,CAAa,EAC5C,EAAS,YAAY,IAAI,CAAC,EAAQ,OAAQ,CAAQ,CAAC,EACnD,EAAkB,CACtB,GAAG,EACH,QAAS,CAAE,GAAG,EAAS,QAAS,SAAQ,mBAAkB,EAC1D,QAAS,CAAE,GAAG,EAAS,QAAS,QAAO,CACzC,EACM,EAAO,EAAgB,QAAQ,KAC/B,EAAkB,CACtB,QAAS,EAAQ,YAAY,QAC7B,OAAQ,EAAa,EACrB,aAAc,EAAQ,YAAY,MACpC,EACM,EAAY,CAAC,EAAQ,OAAO,SAAW,CAAC,EAAO,QACjD,EAAQ,UAAU,CAAI,EACtB,IAAA,GASJ,GARA,EAAQ,QAAQ,OAAO,CAAE,KAAM,YAAa,OAAQ,EAAK,OAAQ,KAAM,EAAK,SAAU,aAAc,EAAK,YAAa,CAAC,EACvH,MAAM,EAAU,EAAS,CAAE,KAAM,aAAc,QAAO,GAAI,EAAI,EAAG,KAAM,gBAAgB,EAAK,WAAY,KAAM,eAAgB,WAAY,CACxI,wBAAyB,eAAgB,mBAAoB,EAAK,SAAU,sBAAuB,EAAK,OACxG,GAAG,IAAc,IAAA,GAAY,CAAC,EAAI,CAAE,qBAAsB,EAAK,CACjE,EAAG,MAAO,EAAK,YAAa,CAAC,EAC7B,MAAM,EAAU,EAAS,CAAE,KAAM,YAAa,OAAM,OAAM,CAAC,EAGvD,EAAQ,OAAO,QAAS,MAAO,CACjC,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EACvB,2CACA,EAAiB,uBACnB,CAAC,CACH,EACA,GAAI,EAAO,QAAS,MAAO,CACzB,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EAAgB,EAAU,CAAa,CAAC,CACnE,EACA,GAAI,IAAc,IAAA,GAAW,MAAO,CAClC,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,CAAS,CACpC,EAGA,IAAM,EAAS,EAAQ,QAAQ,IAAI,EAAK,QAAQ,CAAC,EAAE,eAAiB,GAC9D,EAAU,EAAS,IAAA,GAAY,EAAS,UAAU,CAAI,EAC5D,GAAI,IAAY,IAAA,GAAW,MAAO,CAChC,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAAO,SAAU,GAC/E,QAAS,QAAQ,QAAQ,EAAe,CAAO,CAAC,CAClD,EACA,IAAM,EAAQ,EAAK,SAAS,IAAI,EAAK,QAAQ,EAG7C,GAAI,IAAU,IAAA,KAAc,GAAU,EAAS,OAAS,aAAc,MAAO,CAC3E,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EACvB,oEACA,EAAwB,aAC1B,CAAC,CACH,EACA,IAAM,EAAS,EAAK,UAAU,QAAQ,CAAM,EAC5C,GAAI,IAAW,IAAA,GAAW,MAAO,CAC/B,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAAO,SAAU,GAC/E,QAAS,QAAQ,QAAQ,EAAe,EAAQ,eAAiB,YAAY,CAAC,CAChF,EAEA,GAAI,CACF,OAAO,MAAM,EAAqB,EAAS,EAAiB,EAAM,EAAO,EAAQ,EAAU,EAAQ,EAAM,CAAK,CAChH,QAAU,CACR,EAAO,QAAQ,CACjB,CACF,CAEA,eAAe,EACb,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACe,CACf,GAAM,CAAE,gBAAe,qBAAsB,EAC7C,GAAI,EAAQ,OAAO,QAAS,MAAO,CACjC,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EAAY,2CAA4C,EAAiB,uBAAuB,CAAC,CAC5H,EACA,IAAM,EAAuB,EAAkB,EAAmB,EAAS,EAAiB,CAAK,CAAC,EAC9F,EACJ,GAAI,CACF,EAAgB,MAAM,EAAe,EAAsB,CAAM,CACnE,OAAS,EAAgB,CACvB,GAAI,CAAC,EAAO,QAAS,MAAM,EAE3B,GAAI,CAAC,MADiB,EAAkB,EAAsB,CAAiB,EACjE,MAAM,EAAgB,EAAK,SAAU,gBAAiB,EAAmB,CAAK,EAC5F,MAAO,CACL,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EAAgB,EAAU,CAAa,CAAC,CACnE,CACF,CACA,GAAI,EAAc,OAAS,QAAS,MAAO,CACzC,OAAM,QAAO,SAAQ,WAAU,oBAC/B,WAAY,GAAO,QAAS,QAAQ,QAAQ,EAAc,MAAM,CAClE,EACA,GAAI,CACF,IAAM,EAAgB,QAAQ,QAAQ,EAAQ,aAAa,CACzD,KAAM,uBAAwB,OAAM,SAAU,EAAQ,QAAQ,SAAS,EAAG,SAC1E,GAAI,EAAQ,SAAW,IAAA,GAAY,CAAC,EAAI,CAAE,OAAQ,EAAQ,MAAO,CACnE,CAAC,CAAC,EACF,GAAI,CAAE,MAAM,EAAe,EAAe,CAAM,CAAE,OAC3C,EAAgB,CACrB,GAAI,CAAC,EAAO,QAAS,MAAM,EAC3B,GAAI,CAAC,MAAM,EAAkB,EAAe,CAAiB,EAC3D,MAAM,EAAgB,EAAK,SAAU,aAAc,EAAmB,CAAK,CAE/E,CACF,OAAS,EAAgB,CACvB,GAAI,aAAiB,GAAa,EAAM,OAAS,EAAiB,iBAAkB,MAAM,EAC1F,MAAO,CACL,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EAAY,8BAA8B,EAAU,CAAK,IAAK,EAAiB,iBAAiB,CAAC,CAC5H,CACF,CACA,GAAI,EAAO,QAAS,MAAO,CACzB,OAAM,QAAO,SAAQ,WAAU,oBAAmB,WAAY,GAC9D,QAAS,QAAQ,QAAQ,EAAgB,EAAU,CAAa,CAAC,CACnE,EACA,IAAM,EAAU,IAAU,IAAA,GACtB,IAAA,GACA,IAAI,EAAW,EAAS,EAAM,EAAO,CAAE,OAAM,QAAO,QAAO,CAAC,EAC5D,IAAY,IAAA,IAAW,EAAqB,EAAc,KAAM,EAAQ,KAAM,GAAU,EAAQ,oBAAoB,CAAM,CAAC,EAC/H,EAAO,QAAQ,EACf,IAAM,EAAU,EAA2B,EAAc,IAAI,EAI7D,OADA,EAAa,UAAY,IAAA,EAAS,EAC3B,CACL,OAAM,QAAO,SAAQ,WAAU,oBAC/B,WAAY,EAAc,KAAM,WAAY,GAC5C,GAAG,IAAY,IAAA,GAAY,CAAC,EAAI,CAAE,SAAQ,EAC1C,SACF,CACF,CAaA,SAAS,EAAe,EAAgD,CACtE,MAAO,CACL,QAAS,GACT,MAAO,IAAA,GACP,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,EAAY,CAAM,CAAE,CAAC,EAGrD,KAAM,CAAE,SAAU,GAAM,QAAO,CACjC,CACF,CAWA,SAAS,EAAY,EAAmC,CACtD,IAAM,EAAS,4FAEf,OAAQ,EAAR,CACE,IAAK,aACH,MAAO,8HAC8D,EACvE,IAAK,qBACH,MAAO,0JAEsB,EAC/B,IAAK,kBACH,MAAO,6IAEkB,EAC3B,IAAK,SACH,MAAO,8DAAgE,EACzE,IAAK,0BACH,MAAO,2HAC+D,EACxE,IAAK,QACH,MAAO,2DAA6D,EACtE,IAAK,iBACH,MAAO,mHACqD,CAChE,CACF,CAEA,eAAe,EACb,EACA,EACA,EACe,CACf,IAAK,IAAM,KAAQ,EACjB,GAAI,CAAE,MAAM,EAAO,EAAS,EAAM,CAAc,CAAE,MAC5C,CAA8E,CAExF,CAEA,eAAe,EACb,EACA,EACA,EAC8B,CAC9B,IAAI,EACA,EACA,EAAW,GACf,GAAI,CACF,IAAI,EACJ,GAAI,CACF,EAAU,MAAM,EAAe,EAAK,QAAS,EAAK,MAAM,CAC1D,OAAS,EAAgB,CACvB,GAAI,CAAC,EAAK,OAAO,QAAS,MAAM,EAEhC,GAAI,CAAC,MADiB,EAAkB,EAAK,QAAS,EAAK,iBAAiB,EAC9D,MAAM,EAAgB,EAAK,KAAK,SAAU,YAAa,EAAK,kBAAmB,CAAK,EAClG,EAAU,EAAgB,EAAK,QAAQ,CACzC,CACA,GAAI,EAAK,UAAY,IAAA,GAAW,CAG9B,GAAI,CAAC,MADgB,EAAK,QAAQ,MAAM,EAAK,iBAAiB,EACjD,MAAM,EAAgB,EAAK,KAAK,SAAU,cAAe,EAAK,kBAAmB,IAAA,EAAS,EACvG,IAAM,EAAmB,EAAK,QAAQ,MACtC,GAAI,IAAY,IAAA,GACd,MAAM,EAAqB,CAAO,IAAM,QACpC,EACA,EAAU,MAAM,EAAU,CAAO,EAAG,EAAU,CAAO,EAAG,CAAE,MAAO,CAAQ,CAAC,CAElF,CACA,IAAI,EAAY,EAChB,GAAI,EAAK,aAAe,IAAA,GAAW,CACjC,IAAM,EAAa,EAAiB,EAAK,WAAY,CAAO,EAC5D,GAAI,CACF,EAAY,MAAM,EAAe,EAAY,EAAK,MAAM,CAC1D,OAAS,EAAgB,CACvB,GAAI,CAAC,EAAK,OAAO,QAAS,MAAM,EAEhC,GAAI,CAAC,MADiB,EAAkB,EAAY,EAAK,iBAAiB,EAC5D,MAAM,EAAgB,EAAK,KAAK,SAAU,eAAgB,EAAK,kBAAmB,CAAK,EACrG,EAAY,EAAgB,EAAK,QAAQ,CAC3C,CACF,CACA,EAAS,EAAgB,MAAM,EAAY,EAAS,EAAM,CAAS,EAAG,CAAc,CACtF,OAAS,EAAgB,CACvB,EAAK,SAAS,MAAM,CAAK,EACzB,EAAW,EAAqB,CAAK,IAAM,QAC3C,EAAQ,EACR,EAAS,EAAgB,EAAY,EAAU,CAAK,EAAG,EAAU,CAAK,CAAC,EAAG,CAAc,CAC1F,CACA,IAAM,EAAU,EAAwB,CAAE,OAAQ,EAAK,KAAK,OAAQ,QAAS,CAAC,GAAG,EAAO,OAAO,EAAG,QAAS,EAAO,OAAQ,CAAC,EAc3H,GAbA,EAAQ,QAAQ,OAAO,CAAE,KAAM,cAAe,OAAQ,EAAK,KAAK,OAAQ,UAAS,QAAO,CAAC,EACzF,MAAM,EAAU,EAAS,CAAE,KAAM,cAAe,KAAM,EAAK,KAAM,SAAQ,MAAO,EAAK,KAAM,CAAC,EAC5F,MAAM,EAAU,EAAS,CACvB,KAAM,WAAY,MAAO,EAAK,MAAO,GAAI,EAAI,EAAG,OAAQ,EAAO,QAAU,QAAU,UACnF,OAAQ,EAAO,QAAU,CAAE,MAAO,EAAO,KAAM,EAAI,EAAO,MAC1D,GAAG,EAAO,QAAU,CAAE,MAAO,CAAE,KAAM,YAAa,QAAS,EAAO,MAAM,QAAS,KAAM,EAAO,MAAM,IAAK,CAAE,EAAI,CAAC,CAClH,CAAC,EACG,EAAO,oBAAsB,IAAA,IAE/B,EAAQ,QAAQ,OAAO,CAAE,KAAM,OAAQ,QAAS,EAAkB,CAChE,QAAS,CAAC,GAAG,EAAO,iBAAiB,EAAG,OAAQ,CAAE,KAAM,MAAO,SAAU,QAAQ,EAAK,KAAK,UAAW,CACxG,CAAC,CAAE,CAAC,EAEF,EAAU,MAAM,EACpB,OAAO,CACT,CAkBA,eAAe,EACb,EACA,EACA,EAC8B,CAC9B,IAAM,EAAa,EAAQ,gBACrB,EAAa,EAAQ,QAAQ,IAAI,EAAK,KAAK,QAAQ,CAAC,EAAE,gBACtD,EAAS,IAAe,IAAA,GAC1B,EACA,IAAe,IAAA,GAAY,EAAa,KAAK,IAAI,EAAY,CAAU,EAC3E,GAAI,IAAW,IAAA,GAAW,OAAO,EACjC,IAAM,EAAS,EAAwB,EAAO,OAAO,EACrD,GAAI,GAAU,EAAQ,OAAO,EAK7B,IAAM,EAAQ,EAAO,QAAQ,OAAO,GAAS,EAAM,OAAS,MAAM,EAClE,GAAI,EAAM,SAAW,EAAG,OAAO,EAC/B,IAAM,EAAO,EAAM,IAAI,GAAS,EAAM,IAAI,CAAC,CAAC,KAAK;CAAI,EAC/C,EAAS,EAAQ,gBAAkB,OACnC,EAAQ,EAAQ,WAEtB,GAAI,IAAW,YAAc,IAAU,IAAA,GACrC,GAAI,CAGF,IAAM,EAAS,MAAM,EAAM,KAAK,EAAM,CACpC,SAAU,EAAK,KAAK,SAAU,OAAQ,OAAO,EAAK,KAAK,MAAM,CAC/D,CAAC,EAED,OAAO,EAAY,EAAQ,GADX,EAAgB,EAAM,KAAK,IAAI,EAAG,KAAK,MAAM,EAAS,GAAI,CAAC,CACvC,EAAE,6CAC/B,OAAO,CAAM,EAAE,8BAA8B,OAAO,EAAO,KAAK,EAAE,oBAClE,EAAO,UAAU,GAAI,CAC1B,cAAe,CAAE,QAAS,EAAO,QAAS,MAAO,EAAO,MAAO,gBAAiB,CAAO,CACzF,CAAC,CACH,MAAQ,CAGR,CAGF,IAAM,EAAY,EAAuB,EAAM,CAAM,EACrD,OAAO,EAAY,EAAQ,EAAU,KACjC,mBAAmB,OAAO,EAAU,cAAc,EAAE,gDACvC,OAAO,CAAM,EAAE,gKACiF,CAC/G,gBAAiB,CAAE,gBAAiB,EAAU,eAAgB,QAAO,CACvE,CAAC,CACH,CASA,SAAS,EACP,EACA,EACA,EACqB,CAIrB,IAAI,EAAS,GACP,EAA0B,CAAC,EACjC,IAAK,IAAM,KAAS,EAAO,QAAS,CAClC,GAAI,EAAM,OAAS,OAAQ,CAAE,EAAQ,KAAK,CAAK,EAAG,QAAS,CACvD,IACJ,EAAS,GACT,EAAQ,KAAK,CAAE,KAAM,OAAQ,MAAK,CAAC,EACrC,CAGA,MAAO,CAAE,GAAG,EAAQ,UAAS,KAAM,CAAE,GAAG,EAAO,KAAM,GAAG,CAAK,CAAE,CACjE,CAEA,SAAS,EAAc,EAAwC,CAC7D,GAAI,OAAO,EAAK,QAAW,UAAY,EAAK,OAAO,SAAW,GACzD,OAAO,EAAK,UAAa,UAAY,EAAK,SAAS,SAAW,GAC9D,OAAO,EAAK,cAAiB,SAChC,MAAU,UAAU,6DAA6D,EAEnF,OAAO,EAAe,CAAI,CAC5B,CAEA,SAAS,EAAgB,EAAuB,EAA6C,CAC3F,IAAM,EAAQ,IAAkB,IAAA,GAAY,wBAA0B,GAAG,EAAc,eACvF,OAAO,EAAS,QACZ,EACE,yBAAyB,IACzB,EAAiB,OACnB,EACA,EAAY,yBAA0B,EAAiB,OAAO,CACpE,CAEA,SAAS,EAAgB,EAAe,EAAsB,CAC5D,GAAI,CAAC,OAAO,UAAU,CAAK,GAAK,EAAQ,EAAG,MAAU,WAAW,GAAG,EAAK,4BAA4B,EACpG,OAAO,CACT"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{MODEL_ERROR_CODES as e}from"../../errors/model-error.js";import{normalizeModelFailure as t}from"../../errors/failure.js";import{backoffDelayMs as n,resolveRetryPolicy as r}from"../../contract/retry-policy.js";import{EMBEDDING_ERROR_CODES as i,EmbeddingError as a}from"../../embedding/errors.js";const o=Object.freeze({phase:`pending`});function s(t){return t===e.ABORTED||t===i.ABORTED}function c(e,t,n){return e instanceof a?e:new a(t.message,t.code,{cause:e,provider:n.provider,model:n.model,itemIndexes:n.items.map(e=>e.index)})}function l(e,t){return t?.aborted===!0?Promise.resolve(!1):new Promise(n=>{let r=()=>{clearTimeout(i),n(!1)},i=setTimeout(()=>{t?.removeEventListener(`abort`,r),n(!0)},e);t?.addEventListener(`abort`,r,{once:!0})})}function u(e,t,r,i){let a=t.providerRetryAfterMs;return a!==void 0&&Number.isFinite(a)&&a>0?a<=e.maxDelayMs?a:e.mode===`always`?n(e,r,i):`give-up`:n(e,r,i)}function d(e,t){let n=e?.startProviderAttempt;return e===void 0||n===void 0?e:{...e,startProviderAttempt:async(e,r)=>{let i=await n(e,r);return{attemptId:i.attemptId,attemptNumber:i.attemptNumber,traceparent:i.traceparent,end:e=>(t(e.dispatchState),i.end(e))}}}}function f(e,n={}){let f=n.policy??r(void 0,`embedding.retryPolicy`);if(f.mode===`always`&&n.signal===void 0)throw new a(`an always retry policy requires an AbortSignal`,i.CONFIGURATION_INVALID);let p=n.random??Math.random,m=n.sleep??l,h=new Map,g=e=>n.signal?.aborted===!0||e.signal?.aborted===!0,_=(e,t,n,r,i)=>{let a=Object.freeze({phase:`failed`,error:r,retryable:!1,dispatch:i});return h.set(e,a),Object.freeze({batchIndex:e,itemIndexes:Object.freeze(t.items.map(e=>e.index)),state:a,attempts:n.length,attemptRecords:Object.freeze([...n]),warnings:Object.freeze([])})};return{stateOf(e){return h.get(e)??o},states(){return new Map(h)},async dispatch(r,o,l){let v=l??n.context,y=h.get(r);if(y!==void 0&&y.phase===`succeeded`)throw new a(`embedding batch ${r} already succeeded and must not be dispatched again`,i.CONFIGURATION_INVALID,{provider:o.provider,model:o.model});let b=Object.freeze(o.items.map(e=>e.index)),x=[],S=0;for(;;){let l=x.length+1;if(g(o))return _(r,o,x,new a(`embedding call aborted before dispatch`,i.ABORTED,{provider:o.provider,model:o.model,itemIndexes:[...b]}),x.length===0?`not-sent`:`unknown`);h.set(r,Object.freeze({phase:`in-flight`,attempt:l}));let y,C=d(v,e=>{y=e}),w;try{w=await e.embedBatch(o,C)}catch(e){let d=t(e),C=y??`unknown`;x.push(Object.freeze({attempt:l,outcome:`failure`,dispatch:C,failureCode:d.code}));let w=c(e,d,o);if(g(o)||s(d.code))return _(r,o,x,w,C);let T=f.mode===`always`||f.retryableCodes.includes(d.code)&&S<f.maxRetries,E=T?u(f,d,S+1,p):`give-up`;if(!T||E===`give-up`){let e=Object.freeze({phase:`failed`,error:w,retryable:f.mode===`always`||f.retryableCodes.includes(d.code),dispatch:C});return h.set(r,e),Object.freeze({batchIndex:r,itemIndexes:b,state:e,attempts:x.length,attemptRecords:Object.freeze([...x]),warnings:Object.freeze([])})}S+=1,v?.recordProviderRetry?.({nextAttemptNumber:S+1,delayMs:E,failureCode:d.code});try{n.onRetry?.({provider:o.provider,model:o.model,batchIndex:r,attempt:S,maxRetries:f.mode===`normal`?f.maxRetries:void 0,failureCode:d.code,dispatch:C,delayMs:E})}catch{}if(!await m(E,n.signal??o.signal))return _(r,o,x,new a(`embedding call aborted while waiting to retry`,i.ABORTED,{provider:o.provider,model:o.model,itemIndexes:[...b],cause:e}),C);continue}let T=y??`sent`;x.push(Object.freeze({attempt:l,outcome:`success`,dispatch:T}));let E=Object.freeze({phase:`succeeded`,vectors:w.vectors});return h.set(r,E),Object.freeze({batchIndex:r,itemIndexes:b,state:E,attempts:x.length,attemptRecords:Object.freeze([...x]),...w.usage===void 0?{}:{usage:w.usage},...w.providerRequestId===void 0?{}:{providerRequestId:w.providerRequestId},warnings:Object.freeze([...w.warnings??[]])})}}}}export{f as createEmbeddingRetryLedger};
|
|
1
|
+
import{MODEL_ERROR_CODES as e}from"../../errors/model-error.js";import{normalizeModelFailure as t}from"../../errors/failure.js";import{backoffDelayMs as n,resolveRetryPolicy as r}from"../../contract/retry-policy.js";import{EMBEDDING_ERROR_CODES as i,EmbeddingError as a}from"../../embedding/errors.js";const o=Object.freeze({phase:`pending`});function s(t){return t===e.ABORTED||t===i.ABORTED}function c(e,t,n){return e instanceof a?e:new a(t.message,t.code,{cause:e,...t.status===void 0?{}:{status:t.status},...t.providerRetryAfterMs===void 0?{}:{providerRetryAfterMs:t.providerRetryAfterMs},...t.requestId===void 0?{}:{requestId:t.requestId},provider:n.provider,model:n.model,itemIndexes:n.items.map(e=>e.index)})}function l(e,t){return t?.aborted===!0?Promise.resolve(!1):new Promise(n=>{let r=()=>{clearTimeout(i),n(!1)},i=setTimeout(()=>{t?.removeEventListener(`abort`,r),n(!0)},e);t?.addEventListener(`abort`,r,{once:!0})})}function u(e,t,r,i){let a=t.providerRetryAfterMs;return a!==void 0&&Number.isFinite(a)&&a>0?a<=e.maxDelayMs?a:e.mode===`always`?n(e,r,i):`give-up`:n(e,r,i)}function d(e,t){let n=e?.startProviderAttempt;return e===void 0||n===void 0?e:{...e,startProviderAttempt:async(e,r)=>{let i=await n(e,r);return{attemptId:i.attemptId,attemptNumber:i.attemptNumber,traceparent:i.traceparent,end:e=>(t(e.dispatchState),i.end(e))}}}}function f(e,n={}){let f=n.policy??r(void 0,`embedding.retryPolicy`);if(f.mode===`always`&&n.signal===void 0)throw new a(`an always retry policy requires an AbortSignal`,i.CONFIGURATION_INVALID);let p=n.random??Math.random,m=n.sleep??l,h=new Map,g=e=>n.signal?.aborted===!0||e.signal?.aborted===!0,_=(e,t,n,r,i)=>{let a=Object.freeze({phase:`failed`,error:r,retryable:!1,dispatch:i});return h.set(e,a),Object.freeze({batchIndex:e,itemIndexes:Object.freeze(t.items.map(e=>e.index)),state:a,attempts:n.length,attemptRecords:Object.freeze([...n]),warnings:Object.freeze([])})};return{stateOf(e){return h.get(e)??o},states(){return new Map(h)},async dispatch(r,o,l){let v=l??n.context,y=h.get(r);if(y!==void 0&&y.phase===`succeeded`)throw new a(`embedding batch ${r} already succeeded and must not be dispatched again`,i.CONFIGURATION_INVALID,{provider:o.provider,model:o.model});let b=Object.freeze(o.items.map(e=>e.index)),x=[],S=0;for(;;){let l=x.length+1;if(g(o))return _(r,o,x,new a(`embedding call aborted before dispatch`,i.ABORTED,{provider:o.provider,model:o.model,itemIndexes:[...b]}),x.length===0?`not-sent`:`unknown`);h.set(r,Object.freeze({phase:`in-flight`,attempt:l}));let y,C=d(v,e=>{y=e}),w;try{w=await e.embedBatch(o,C)}catch(e){let d=t(e),C=y??`unknown`;x.push(Object.freeze({attempt:l,outcome:`failure`,dispatch:C,failureCode:d.code}));let w=c(e,d,o);if(g(o)||s(d.code))return _(r,o,x,w,C);let T=f.mode===`always`||f.retryableCodes.includes(d.code)&&S<f.maxRetries,E=T?u(f,d,S+1,p):`give-up`;if(!T||E===`give-up`){let e=Object.freeze({phase:`failed`,error:w,retryable:f.mode===`always`||f.retryableCodes.includes(d.code),dispatch:C});return h.set(r,e),Object.freeze({batchIndex:r,itemIndexes:b,state:e,attempts:x.length,attemptRecords:Object.freeze([...x]),warnings:Object.freeze([])})}S+=1,v?.recordProviderRetry?.({nextAttemptNumber:S+1,delayMs:E,failureCode:d.code});try{n.onRetry?.({provider:o.provider,model:o.model,batchIndex:r,attempt:S,maxRetries:f.mode===`normal`?f.maxRetries:void 0,failureCode:d.code,dispatch:C,delayMs:E})}catch{}if(!await m(E,n.signal??o.signal))return _(r,o,x,new a(`embedding call aborted while waiting to retry`,i.ABORTED,{provider:o.provider,model:o.model,itemIndexes:[...b],cause:e}),C);continue}let T=y??`sent`;x.push(Object.freeze({attempt:l,outcome:`success`,dispatch:T}));let E=Object.freeze({phase:`succeeded`,vectors:w.vectors});return h.set(r,E),Object.freeze({batchIndex:r,itemIndexes:b,state:E,attempts:x.length,attemptRecords:Object.freeze([...x]),...w.usage===void 0?{}:{usage:w.usage},...w.providerRequestId===void 0?{}:{providerRequestId:w.providerRequestId},warnings:Object.freeze([...w.warnings??[]])})}}}}export{f as createEmbeddingRetryLedger};
|
|
2
2
|
//# sourceMappingURL=retry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry.js","names":[],"sources":["../../../src/composition/embedding/retry.ts"],"sourcesContent":["/**\n * The ONE retry layer for embedding.\n *\n * `Embedding_Runtime` owns retry; an `Embedding_Adapter` performs exactly one\n * `Provider_Attempt` per `embedBatch()` call and never retries inside\n * (Requirement 4.3). Keeping retry here is what makes a `Provider_Attempt`\n * countable: the number of attempts a caller is billed for equals the number of\n * times this file called the adapter.\n *\n * Four facts drive every branch below:\n *\n * 1. **Only unsuccessful batches are retried.** A batch that reached\n * `succeeded` is removed from every later retry pass of the same\n * `Logical_Call` — its vectors already exist, and re-sending it would be a\n * second charge for a result we hold (Requirement 4.7). The ledger enforces\n * this structurally: a terminal state is never re-entered.\n * 2. **Dispatch state comes from the transport, it is not re-derived.**\n * `Http_Transport` sets `dispatchState = 'unknown'` immediately before\n * `fetch` and only raises it to `'sent'` once a response exists, so this file\n * reads the value the transport reported through `attempt.end` instead of\n * guessing from the shape of the error. When nothing reported a state, the\n * record stays `'unknown'`: only the transport is in a position to claim\n * `'not-sent'`, and a timeout in particular is never that (Requirement 4.8).\n * 3. **No fallback model.** A failure of the primary model propagates to the\n * caller. Fallback is only ever configured within a group sharing one\n * `compatibilityIdentity`, and that condition is checked when the handle is\n * built, not after a failure has happened (Requirements 6.6, 6.7).\n * 4. **Order is not this file's business.** Vectors are handed back per batch and\n * the caller writes them at `item.index`, so retries and out-of-order\n * settlement cannot disturb output order (Requirement 4.6).\n *\n * Concurrency lives in `limiter.ts`; this file drives one batch at a time and is\n * composed underneath it.\n *\n * @module ai-agent-sdk/core/composition/embedding/retry\n */\n\nimport {\n backoffDelayMs,\n resolveRetryPolicy,\n type ResolvedRetryPolicy,\n} from '../../contract/retry-policy.ts'\nimport { EMBEDDING_ERROR_CODES, EmbeddingError } from '../../embedding/errors.ts'\nimport type { EmbeddingBatchRequest } from '../../embedding/request.ts'\nimport type { EmbeddingBatchResult, EmbeddingVector, EmbeddingWarning } from '../../embedding/result.ts'\nimport { normalizeModelFailure, type ModelFailure } from '../../errors/failure.ts'\nimport { MODEL_ERROR_CODES } from '../../errors/model-error.ts'\nimport type {\n EndProviderAttemptInput,\n ModelInvocationContext,\n ProviderAttemptHandle,\n} from '../../observation/report.ts'\nimport type { AttemptUsageReport, DispatchState } from '../../observation/usage.ts'\n\n/**\n * Lifecycle of one `Physical_Batch` inside a single `Logical_Call`.\n *\n * `succeeded` and `failed` are terminal. That is the whole mechanism behind\n * Requirement 4.7: retry only ever considers a batch that is not yet terminal.\n */\nexport type BatchState =\n | { readonly phase: 'pending' }\n | { readonly phase: 'in-flight'; readonly attempt: number }\n | { readonly phase: 'succeeded'; readonly vectors: readonly EmbeddingVector[] }\n | {\n readonly phase: 'failed'\n readonly error: EmbeddingError\n readonly retryable: boolean\n /** As reported by the transport; `'unknown'` whenever nothing reported. */\n readonly dispatch: DispatchState\n }\n\n/** One retry that is about to be waited out, for logging and metrics. */\nexport interface EmbeddingRetryAttempt {\n readonly provider: string\n readonly model: string\n /** Plan coordinate of the batch being retried. */\n readonly batchIndex: number\n /** 1-based retry number. */\n readonly attempt: number\n /** Retry ceiling, or `undefined` under an `always` policy. */\n readonly maxRetries: number | undefined\n /** Stable code of the failure being recovered from. */\n readonly failureCode: string\n /** Dispatch state the transport reported for the failed attempt. */\n readonly dispatch: DispatchState\n readonly delayMs: number\n}\n\n/** What one `Provider_Attempt` on a batch left behind. */\nexport interface EmbeddingAttemptRecord {\n /** 1-based attempt number within this batch. */\n readonly attempt: number\n readonly outcome: 'success' | 'failure'\n /** From the transport, never re-derived (Requirement 4.8). */\n readonly dispatch: DispatchState\n /** Stable failure code, absent on success. */\n readonly failureCode?: string\n}\n\n/** Everything a `Logical_Call` needs to know about one settled batch. */\nexport interface EmbeddingBatchOutcome {\n readonly batchIndex: number\n /** Input indexes this batch carried, in `Logical_Call` numbering. */\n readonly itemIndexes: readonly number[]\n /** Terminal state: `succeeded` or `failed`. */\n readonly state: BatchState\n /** `Provider_Attempt`s spent, retries included (Requirement 16.6). */\n readonly attempts: number\n /** One record per adapter call, in order. */\n readonly attemptRecords: readonly EmbeddingAttemptRecord[]\n /**\n * Raw provider usage evidence from the attempt that settled the batch.\n *\n * `unknown` on purpose: proving it is a usage shape belongs to\n * `validateEmbeddingUsage`, and absence must never become a zero.\n */\n readonly usage?: unknown\n readonly providerRequestId?: string\n readonly warnings: readonly EmbeddingWarning[]\n}\n\n/** Dispatch surface of a `Prepared_Embedding_Call`; the ledger needs nothing else. */\nexport interface EmbeddingBatchDispatcher {\n embedBatch(\n batch: EmbeddingBatchRequest,\n context?: ModelInvocationContext,\n ): Promise<EmbeddingBatchResult>\n}\n\n/** Knobs for {@link createEmbeddingRetryLedger}. */\nexport interface EmbeddingRetryOptions {\n /** Route policy; omission takes the shared SDK defaults. */\n readonly policy?: ResolvedRetryPolicy\n /** Sample in `[0, 1)` for jitter; injectable so tests can be deterministic. */\n readonly random?: () => number\n /** Caller signal or runtime `close()`; an abort is never a transient fault. */\n readonly signal?: AbortSignal\n /** Invocation context; wrapped so transport-reported dispatch states are observed. */\n readonly context?: ModelInvocationContext\n /** Observe each scheduled retry. Failures in the observer are swallowed. */\n readonly onRetry?: (attempt: EmbeddingRetryAttempt) => void\n /** Sleep hook; resolves `false` when the signal fired first. Injectable for tests. */\n readonly sleep?: (delayMs: number, signal?: AbortSignal) => Promise<boolean>\n}\n\n/**\n * Per-`Logical_Call` retry driver and state ledger.\n *\n * One ledger per `Logical_Call`, shared across every batch of that call, which\n * is what lets it guarantee that a `succeeded` batch is never dispatched again.\n */\nexport interface EmbeddingRetryLedger {\n /**\n * Dispatch one `Physical_Batch` and retry it until it settles.\n *\n * Resolves with a terminal outcome instead of throwing, because only the\n * caller knows whether a failed batch should abandon the whole call or leave\n * the batches that already succeeded intact. There is no fallback model: a\n * failure here is the failure the caller sees.\n *\n * @param context - overrides the ledger's context for this batch only. One\n * ledger spans the whole `Logical_Call` while observation is per-batch, so this\n * is how an attempt gets parented under the batch that provoked it. The\n * override is wrapped for dispatch-state observation exactly like the\n * ledger-wide context, so nothing about Requirement 4.8 changes.\n */\n dispatch(\n batchIndex: number,\n request: EmbeddingBatchRequest,\n context?: ModelInvocationContext,\n ): Promise<EmbeddingBatchOutcome>\n /** Current state of one batch; `pending` for a batch never dispatched. */\n stateOf(batchIndex: number): BatchState\n /** Immutable view of every batch state seen so far. */\n states(): ReadonlyMap<number, BatchState>\n}\n\nconst PENDING: BatchState = Object.freeze({ phase: 'pending' as const })\n\n/** Codes that mean \"the caller stopped this\", never \"the provider wobbled\". */\nfunction isAbortCode(code: string): boolean {\n return code === MODEL_ERROR_CODES.ABORTED || code === EMBEDDING_ERROR_CODES.ABORTED\n}\n\n/**\n * Turn any thrown value into an {@link EmbeddingError} without losing its code.\n *\n * An `EmbeddingError` from the adapter passes through untouched: it already\n * carries `itemIndexes` and `limit`, and re-wrapping would drop exactly the\n * facts a caller needs to re-chunk the offending inputs. Anything else keeps its\n * normalized stable code — a rate limit stays `MODEL_RATE_LIMIT` so the retry\n * allow-list still recognises it.\n */\nfunction asEmbeddingError(\n value: unknown,\n failure: ModelFailure,\n request: EmbeddingBatchRequest,\n): EmbeddingError {\n if (value instanceof EmbeddingError) return value\n return new EmbeddingError(failure.message, failure.code, {\n cause: value,\n provider: request.provider,\n model: request.model,\n itemIndexes: request.items.map(item => item.index),\n })\n}\n\n/** Sleep, resolving early and reporting `false` if the signal aborts first. */\nfunction cancellableDelay(delayMs: number, signal?: AbortSignal): Promise<boolean> {\n if (signal?.aborted === true) return Promise.resolve(false)\n return new Promise((resolve) => {\n const onAbort = (): void => {\n clearTimeout(timer)\n resolve(false)\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve(true)\n }, delayMs)\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\n/** Honour a sane provider-requested `retry-after`, else local bounded backoff. */\nfunction delayFor(\n policy: ResolvedRetryPolicy,\n failure: ModelFailure,\n attempt: number,\n random: () => number,\n): number | 'give-up' {\n const requested = failure.providerRetryAfterMs\n if (requested !== undefined && Number.isFinite(requested) && requested > 0) {\n if (requested <= policy.maxDelayMs) return requested\n // The provider asked for longer than this policy will wait. Under a bounded\n // policy that is a refusal: sleeping less than asked just earns another rate\n // limit. An `always` policy has nowhere to give up to.\n return policy.mode === 'always' ? backoffDelayMs(policy, attempt, random) : 'give-up'\n }\n return backoffDelayMs(policy, attempt, random)\n}\n\n/**\n * Wrap an invocation context so the dispatch state the TRANSPORT reports is\n * captured, rather than inferred from the error afterwards.\n *\n * The wrapper is transparent: it forwards `startProviderAttempt` untouched and\n * only tees the `dispatchState` passed to `attempt.end`. When a context has no\n * attempt accounting there is nothing to observe, and the record stays\n * `'unknown'` — that is the honest answer, and it is the answer Requirement 4.8\n * demands for a timeout.\n */\nfunction observeDispatchState(\n context: ModelInvocationContext | undefined,\n sink: (state: DispatchState) => void,\n): ModelInvocationContext | undefined {\n const start = context?.startProviderAttempt\n if (context === undefined || start === undefined) return context\n return {\n ...context,\n startProviderAttempt: async (input, signal): Promise<ProviderAttemptHandle> => {\n const handle = await start(input, signal)\n return {\n attemptId: handle.attemptId,\n attemptNumber: handle.attemptNumber,\n traceparent: handle.traceparent,\n end: (end: EndProviderAttemptInput): AttemptUsageReport => {\n sink(end.dispatchState)\n return handle.end(end)\n },\n }\n },\n }\n}\n\n/**\n * Create the retry ledger for one `Logical_Call`.\n *\n * An `always` policy without a signal is rejected here rather than at the first\n * failure: unbounded retry is bounded only by cancellation, so a call that\n * cannot be cancelled would never terminate.\n */\nexport function createEmbeddingRetryLedger(\n dispatcher: EmbeddingBatchDispatcher,\n options: EmbeddingRetryOptions = {},\n): EmbeddingRetryLedger {\n const policy = options.policy ?? resolveRetryPolicy(undefined, 'embedding.retryPolicy')\n if (policy.mode === 'always' && options.signal === undefined) {\n throw new EmbeddingError(\n 'an always retry policy requires an AbortSignal',\n EMBEDDING_ERROR_CODES.CONFIGURATION_INVALID,\n )\n }\n const random = options.random ?? Math.random\n const sleep = options.sleep ?? cancellableDelay\n const states = new Map<number, BatchState>()\n\n /**\n * Whether either signal has fired.\n *\n * A function rather than an inline check so the answer is re-read after every\n * `await`: a signal that aborts while a request is in flight is precisely the\n * case that matters.\n */\n const cancelled = (request: EmbeddingBatchRequest): boolean =>\n options.signal?.aborted === true || request.signal?.aborted === true\n\n const abortedOutcome = (\n batchIndex: number,\n request: EmbeddingBatchRequest,\n records: readonly EmbeddingAttemptRecord[],\n error: EmbeddingError,\n dispatch: DispatchState,\n ): EmbeddingBatchOutcome => {\n const state: BatchState = Object.freeze({\n phase: 'failed' as const,\n error,\n // An abort is the caller's decision, so it is terminal by definition.\n retryable: false,\n dispatch,\n })\n states.set(batchIndex, state)\n return Object.freeze({\n batchIndex,\n itemIndexes: Object.freeze(request.items.map(item => item.index)),\n state,\n attempts: records.length,\n attemptRecords: Object.freeze([...records]),\n warnings: Object.freeze([]),\n })\n }\n\n return {\n stateOf(batchIndex: number): BatchState {\n return states.get(batchIndex) ?? PENDING\n },\n\n states(): ReadonlyMap<number, BatchState> {\n return new Map(states)\n },\n\n async dispatch(\n batchIndex: number,\n request: EmbeddingBatchRequest,\n batchContext?: ModelInvocationContext,\n ): Promise<EmbeddingBatchOutcome> {\n const invocationContext = batchContext ?? options.context\n const existing = states.get(batchIndex)\n if (existing !== undefined && existing.phase === 'succeeded') {\n // Requirement 4.7 as an invariant rather than a convention: a batch whose\n // vectors are already held is never handed to the provider again.\n throw new EmbeddingError(\n `embedding batch ${batchIndex} already succeeded and must not be dispatched again`,\n EMBEDDING_ERROR_CODES.CONFIGURATION_INVALID,\n { provider: request.provider, model: request.model },\n )\n }\n\n const itemIndexes = Object.freeze(request.items.map(item => item.index))\n const records: EmbeddingAttemptRecord[] = []\n let retries = 0\n\n for (;;) {\n const attemptNumber = records.length + 1\n // Report the abort before spending an attempt on a call already cancelled.\n if (cancelled(request)) {\n return abortedOutcome(\n batchIndex,\n request,\n records,\n new EmbeddingError(\n 'embedding call aborted before dispatch',\n EMBEDDING_ERROR_CODES.ABORTED,\n { provider: request.provider, model: request.model, itemIndexes: [...itemIndexes] },\n ),\n // No adapter call was ever made on this pass, which the runtime knows\n // first-hand. Once an attempt HAS been made, the runtime stops\n // claiming anything and leaves the state `unknown`.\n records.length === 0 ? 'not-sent' : 'unknown',\n )\n }\n\n states.set(batchIndex, Object.freeze({ phase: 'in-flight' as const, attempt: attemptNumber }))\n\n // Reported by the transport through `attempt.end`; unreported stays unknown.\n let reportedDispatch: DispatchState | undefined\n const context = observeDispatchState(invocationContext, (state) => {\n reportedDispatch = state\n })\n\n let result: EmbeddingBatchResult\n try {\n result = await dispatcher.embedBatch(request, context)\n } catch (error: unknown) {\n const failure = normalizeModelFailure(error)\n const dispatch = reportedDispatch ?? 'unknown'\n records.push(Object.freeze({\n attempt: attemptNumber,\n outcome: 'failure' as const,\n dispatch,\n failureCode: failure.code,\n }))\n\n const embeddingError = asEmbeddingError(error, failure, request)\n if (cancelled(request) || isAbortCode(failure.code)) {\n return abortedOutcome(batchIndex, request, records, embeddingError, dispatch)\n }\n\n const eligible = policy.mode === 'always'\n || (policy.retryableCodes.includes(failure.code) && retries < policy.maxRetries)\n const delayMs = eligible ? delayFor(policy, failure, retries + 1, random) : 'give-up'\n if (!eligible || delayMs === 'give-up') {\n const state: BatchState = Object.freeze({\n phase: 'failed' as const,\n error: embeddingError,\n // `retryable` describes the failure, not the budget: a rate limit\n // that ran out of attempts is still a retryable KIND of failure,\n // and a caller deciding whether to re-run later needs that apart\n // from \"this call gave up\".\n retryable: policy.mode === 'always' || policy.retryableCodes.includes(failure.code),\n dispatch,\n })\n states.set(batchIndex, state)\n return Object.freeze({\n batchIndex,\n itemIndexes,\n state,\n attempts: records.length,\n attemptRecords: Object.freeze([...records]),\n warnings: Object.freeze([]),\n })\n }\n\n retries += 1\n invocationContext?.recordProviderRetry?.({\n nextAttemptNumber: retries + 1,\n delayMs,\n failureCode: failure.code,\n })\n try {\n options.onRetry?.({\n provider: request.provider,\n model: request.model,\n batchIndex,\n attempt: retries,\n maxRetries: policy.mode === 'normal' ? policy.maxRetries : undefined,\n failureCode: failure.code,\n dispatch,\n delayMs,\n })\n } catch {\n // Metrics and logging observers do not own request availability.\n }\n\n if (!await sleep(delayMs, options.signal ?? request.signal)) {\n return abortedOutcome(\n batchIndex,\n request,\n records,\n new EmbeddingError(\n 'embedding call aborted while waiting to retry',\n EMBEDDING_ERROR_CODES.ABORTED,\n { provider: request.provider, model: request.model, itemIndexes: [...itemIndexes], cause: error },\n ),\n dispatch,\n )\n }\n continue\n }\n\n // A response exists, so the request reached the provider. The transport's\n // own report still wins when it made one.\n const dispatch = reportedDispatch ?? 'sent'\n records.push(Object.freeze({\n attempt: attemptNumber,\n outcome: 'success' as const,\n dispatch,\n }))\n const state: BatchState = Object.freeze({\n phase: 'succeeded' as const,\n vectors: result.vectors,\n })\n states.set(batchIndex, state)\n return Object.freeze({\n batchIndex,\n itemIndexes,\n state,\n attempts: records.length,\n attemptRecords: Object.freeze([...records]),\n // Absent usage is left absent: the aggregator downgrades coverage\n // rather than counting an unreported batch as zero tokens.\n ...(result.usage === undefined ? {} : { usage: result.usage }),\n ...(result.providerRequestId === undefined\n ? {}\n : { providerRequestId: result.providerRequestId }),\n warnings: Object.freeze([...(result.warnings ?? [])]),\n })\n }\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"retry.js","names":[],"sources":["../../../src/composition/embedding/retry.ts"],"sourcesContent":["/**\n * The ONE retry layer for embedding.\n *\n * `Embedding_Runtime` owns retry; an `Embedding_Adapter` performs exactly one\n * `Provider_Attempt` per `embedBatch()` call and never retries inside\n * (Requirement 4.3). Keeping retry here is what makes a `Provider_Attempt`\n * countable: the number of attempts a caller is billed for equals the number of\n * times this file called the adapter.\n *\n * Four facts drive every branch below:\n *\n * 1. **Only unsuccessful batches are retried.** A batch that reached\n * `succeeded` is removed from every later retry pass of the same\n * `Logical_Call` — its vectors already exist, and re-sending it would be a\n * second charge for a result we hold (Requirement 4.7). The ledger enforces\n * this structurally: a terminal state is never re-entered.\n * 2. **Dispatch state comes from the transport, it is not re-derived.**\n * `Http_Transport` sets `dispatchState = 'unknown'` immediately before\n * `fetch` and only raises it to `'sent'` once a response exists, so this file\n * reads the value the transport reported through `attempt.end` instead of\n * guessing from the shape of the error. When nothing reported a state, the\n * record stays `'unknown'`: only the transport is in a position to claim\n * `'not-sent'`, and a timeout in particular is never that (Requirement 4.8).\n * 3. **No fallback model.** A failure of the primary model propagates to the\n * caller. Fallback is only ever configured within a group sharing one\n * `compatibilityIdentity`, and that condition is checked when the handle is\n * built, not after a failure has happened (Requirements 6.6, 6.7).\n * 4. **Order is not this file's business.** Vectors are handed back per batch and\n * the caller writes them at `item.index`, so retries and out-of-order\n * settlement cannot disturb output order (Requirement 4.6).\n *\n * Concurrency lives in `limiter.ts`; this file drives one batch at a time and is\n * composed underneath it.\n *\n * @module ai-agent-sdk/core/composition/embedding/retry\n */\n\nimport {\n backoffDelayMs,\n resolveRetryPolicy,\n type ResolvedRetryPolicy,\n} from '../../contract/retry-policy.ts'\nimport { EMBEDDING_ERROR_CODES, EmbeddingError } from '../../embedding/errors.ts'\nimport type { EmbeddingBatchRequest } from '../../embedding/request.ts'\nimport type { EmbeddingBatchResult, EmbeddingVector, EmbeddingWarning } from '../../embedding/result.ts'\nimport { normalizeModelFailure, type ModelFailure } from '../../errors/failure.ts'\nimport { MODEL_ERROR_CODES } from '../../errors/model-error.ts'\nimport type {\n EndProviderAttemptInput,\n ModelInvocationContext,\n ProviderAttemptHandle,\n} from '../../observation/report.ts'\nimport type { AttemptUsageReport, DispatchState } from '../../observation/usage.ts'\n\n/**\n * Lifecycle of one `Physical_Batch` inside a single `Logical_Call`.\n *\n * `succeeded` and `failed` are terminal. That is the whole mechanism behind\n * Requirement 4.7: retry only ever considers a batch that is not yet terminal.\n */\nexport type BatchState =\n | { readonly phase: 'pending' }\n | { readonly phase: 'in-flight'; readonly attempt: number }\n | { readonly phase: 'succeeded'; readonly vectors: readonly EmbeddingVector[] }\n | {\n readonly phase: 'failed'\n readonly error: EmbeddingError\n readonly retryable: boolean\n /** As reported by the transport; `'unknown'` whenever nothing reported. */\n readonly dispatch: DispatchState\n }\n\n/** One retry that is about to be waited out, for logging and metrics. */\nexport interface EmbeddingRetryAttempt {\n readonly provider: string\n readonly model: string\n /** Plan coordinate of the batch being retried. */\n readonly batchIndex: number\n /** 1-based retry number. */\n readonly attempt: number\n /** Retry ceiling, or `undefined` under an `always` policy. */\n readonly maxRetries: number | undefined\n /** Stable code of the failure being recovered from. */\n readonly failureCode: string\n /** Dispatch state the transport reported for the failed attempt. */\n readonly dispatch: DispatchState\n readonly delayMs: number\n}\n\n/** What one `Provider_Attempt` on a batch left behind. */\nexport interface EmbeddingAttemptRecord {\n /** 1-based attempt number within this batch. */\n readonly attempt: number\n readonly outcome: 'success' | 'failure'\n /** From the transport, never re-derived (Requirement 4.8). */\n readonly dispatch: DispatchState\n /** Stable failure code, absent on success. */\n readonly failureCode?: string\n}\n\n/** Everything a `Logical_Call` needs to know about one settled batch. */\nexport interface EmbeddingBatchOutcome {\n readonly batchIndex: number\n /** Input indexes this batch carried, in `Logical_Call` numbering. */\n readonly itemIndexes: readonly number[]\n /** Terminal state: `succeeded` or `failed`. */\n readonly state: BatchState\n /** `Provider_Attempt`s spent, retries included (Requirement 16.6). */\n readonly attempts: number\n /** One record per adapter call, in order. */\n readonly attemptRecords: readonly EmbeddingAttemptRecord[]\n /**\n * Raw provider usage evidence from the attempt that settled the batch.\n *\n * `unknown` on purpose: proving it is a usage shape belongs to\n * `validateEmbeddingUsage`, and absence must never become a zero.\n */\n readonly usage?: unknown\n readonly providerRequestId?: string\n readonly warnings: readonly EmbeddingWarning[]\n}\n\n/** Dispatch surface of a `Prepared_Embedding_Call`; the ledger needs nothing else. */\nexport interface EmbeddingBatchDispatcher {\n embedBatch(\n batch: EmbeddingBatchRequest,\n context?: ModelInvocationContext,\n ): Promise<EmbeddingBatchResult>\n}\n\n/** Knobs for {@link createEmbeddingRetryLedger}. */\nexport interface EmbeddingRetryOptions {\n /** Route policy; omission takes the shared SDK defaults. */\n readonly policy?: ResolvedRetryPolicy\n /** Sample in `[0, 1)` for jitter; injectable so tests can be deterministic. */\n readonly random?: () => number\n /** Caller signal or runtime `close()`; an abort is never a transient fault. */\n readonly signal?: AbortSignal\n /** Invocation context; wrapped so transport-reported dispatch states are observed. */\n readonly context?: ModelInvocationContext\n /** Observe each scheduled retry. Failures in the observer are swallowed. */\n readonly onRetry?: (attempt: EmbeddingRetryAttempt) => void\n /** Sleep hook; resolves `false` when the signal fired first. Injectable for tests. */\n readonly sleep?: (delayMs: number, signal?: AbortSignal) => Promise<boolean>\n}\n\n/**\n * Per-`Logical_Call` retry driver and state ledger.\n *\n * One ledger per `Logical_Call`, shared across every batch of that call, which\n * is what lets it guarantee that a `succeeded` batch is never dispatched again.\n */\nexport interface EmbeddingRetryLedger {\n /**\n * Dispatch one `Physical_Batch` and retry it until it settles.\n *\n * Resolves with a terminal outcome instead of throwing, because only the\n * caller knows whether a failed batch should abandon the whole call or leave\n * the batches that already succeeded intact. There is no fallback model: a\n * failure here is the failure the caller sees.\n *\n * @param context - overrides the ledger's context for this batch only. One\n * ledger spans the whole `Logical_Call` while observation is per-batch, so this\n * is how an attempt gets parented under the batch that provoked it. The\n * override is wrapped for dispatch-state observation exactly like the\n * ledger-wide context, so nothing about Requirement 4.8 changes.\n */\n dispatch(\n batchIndex: number,\n request: EmbeddingBatchRequest,\n context?: ModelInvocationContext,\n ): Promise<EmbeddingBatchOutcome>\n /** Current state of one batch; `pending` for a batch never dispatched. */\n stateOf(batchIndex: number): BatchState\n /** Immutable view of every batch state seen so far. */\n states(): ReadonlyMap<number, BatchState>\n}\n\nconst PENDING: BatchState = Object.freeze({ phase: 'pending' as const })\n\n/** Codes that mean \"the caller stopped this\", never \"the provider wobbled\". */\nfunction isAbortCode(code: string): boolean {\n return code === MODEL_ERROR_CODES.ABORTED || code === EMBEDDING_ERROR_CODES.ABORTED\n}\n\n/**\n * Turn any thrown value into an {@link EmbeddingError} without losing its code.\n *\n * An `EmbeddingError` from the adapter passes through untouched: it already\n * carries `itemIndexes` and `limit`, and re-wrapping would drop exactly the\n * facts a caller needs to re-chunk the offending inputs. Anything else keeps its\n * normalized stable code — a rate limit stays `MODEL_RATE_LIMIT` so the retry\n * allow-list still recognises it.\n */\nfunction asEmbeddingError(\n value: unknown,\n failure: ModelFailure,\n request: EmbeddingBatchRequest,\n): EmbeddingError {\n if (value instanceof EmbeddingError) return value\n return new EmbeddingError(failure.message, failure.code, {\n cause: value,\n ...failure.status === undefined ? {} : { status: failure.status },\n ...failure.providerRetryAfterMs === undefined ? {} : { providerRetryAfterMs: failure.providerRetryAfterMs },\n ...failure.requestId === undefined ? {} : { requestId: failure.requestId },\n provider: request.provider,\n model: request.model,\n itemIndexes: request.items.map(item => item.index),\n })\n}\n\n/** Sleep, resolving early and reporting `false` if the signal aborts first. */\nfunction cancellableDelay(delayMs: number, signal?: AbortSignal): Promise<boolean> {\n if (signal?.aborted === true) return Promise.resolve(false)\n return new Promise((resolve) => {\n const onAbort = (): void => {\n clearTimeout(timer)\n resolve(false)\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve(true)\n }, delayMs)\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\n/** Honour a sane provider-requested `retry-after`, else local bounded backoff. */\nfunction delayFor(\n policy: ResolvedRetryPolicy,\n failure: ModelFailure,\n attempt: number,\n random: () => number,\n): number | 'give-up' {\n const requested = failure.providerRetryAfterMs\n if (requested !== undefined && Number.isFinite(requested) && requested > 0) {\n if (requested <= policy.maxDelayMs) return requested\n // The provider asked for longer than this policy will wait. Under a bounded\n // policy that is a refusal: sleeping less than asked just earns another rate\n // limit. An `always` policy has nowhere to give up to.\n return policy.mode === 'always' ? backoffDelayMs(policy, attempt, random) : 'give-up'\n }\n return backoffDelayMs(policy, attempt, random)\n}\n\n/**\n * Wrap an invocation context so the dispatch state the TRANSPORT reports is\n * captured, rather than inferred from the error afterwards.\n *\n * The wrapper is transparent: it forwards `startProviderAttempt` untouched and\n * only tees the `dispatchState` passed to `attempt.end`. When a context has no\n * attempt accounting there is nothing to observe, and the record stays\n * `'unknown'` — that is the honest answer, and it is the answer Requirement 4.8\n * demands for a timeout.\n */\nfunction observeDispatchState(\n context: ModelInvocationContext | undefined,\n sink: (state: DispatchState) => void,\n): ModelInvocationContext | undefined {\n const start = context?.startProviderAttempt\n if (context === undefined || start === undefined) return context\n return {\n ...context,\n startProviderAttempt: async (input, signal): Promise<ProviderAttemptHandle> => {\n const handle = await start(input, signal)\n return {\n attemptId: handle.attemptId,\n attemptNumber: handle.attemptNumber,\n traceparent: handle.traceparent,\n end: (end: EndProviderAttemptInput): AttemptUsageReport => {\n sink(end.dispatchState)\n return handle.end(end)\n },\n }\n },\n }\n}\n\n/**\n * Create the retry ledger for one `Logical_Call`.\n *\n * An `always` policy without a signal is rejected here rather than at the first\n * failure: unbounded retry is bounded only by cancellation, so a call that\n * cannot be cancelled would never terminate.\n */\nexport function createEmbeddingRetryLedger(\n dispatcher: EmbeddingBatchDispatcher,\n options: EmbeddingRetryOptions = {},\n): EmbeddingRetryLedger {\n const policy = options.policy ?? resolveRetryPolicy(undefined, 'embedding.retryPolicy')\n if (policy.mode === 'always' && options.signal === undefined) {\n throw new EmbeddingError(\n 'an always retry policy requires an AbortSignal',\n EMBEDDING_ERROR_CODES.CONFIGURATION_INVALID,\n )\n }\n const random = options.random ?? Math.random\n const sleep = options.sleep ?? cancellableDelay\n const states = new Map<number, BatchState>()\n\n /**\n * Whether either signal has fired.\n *\n * A function rather than an inline check so the answer is re-read after every\n * `await`: a signal that aborts while a request is in flight is precisely the\n * case that matters.\n */\n const cancelled = (request: EmbeddingBatchRequest): boolean =>\n options.signal?.aborted === true || request.signal?.aborted === true\n\n const abortedOutcome = (\n batchIndex: number,\n request: EmbeddingBatchRequest,\n records: readonly EmbeddingAttemptRecord[],\n error: EmbeddingError,\n dispatch: DispatchState,\n ): EmbeddingBatchOutcome => {\n const state: BatchState = Object.freeze({\n phase: 'failed' as const,\n error,\n // An abort is the caller's decision, so it is terminal by definition.\n retryable: false,\n dispatch,\n })\n states.set(batchIndex, state)\n return Object.freeze({\n batchIndex,\n itemIndexes: Object.freeze(request.items.map(item => item.index)),\n state,\n attempts: records.length,\n attemptRecords: Object.freeze([...records]),\n warnings: Object.freeze([]),\n })\n }\n\n return {\n stateOf(batchIndex: number): BatchState {\n return states.get(batchIndex) ?? PENDING\n },\n\n states(): ReadonlyMap<number, BatchState> {\n return new Map(states)\n },\n\n async dispatch(\n batchIndex: number,\n request: EmbeddingBatchRequest,\n batchContext?: ModelInvocationContext,\n ): Promise<EmbeddingBatchOutcome> {\n const invocationContext = batchContext ?? options.context\n const existing = states.get(batchIndex)\n if (existing !== undefined && existing.phase === 'succeeded') {\n // Requirement 4.7 as an invariant rather than a convention: a batch whose\n // vectors are already held is never handed to the provider again.\n throw new EmbeddingError(\n `embedding batch ${batchIndex} already succeeded and must not be dispatched again`,\n EMBEDDING_ERROR_CODES.CONFIGURATION_INVALID,\n { provider: request.provider, model: request.model },\n )\n }\n\n const itemIndexes = Object.freeze(request.items.map(item => item.index))\n const records: EmbeddingAttemptRecord[] = []\n let retries = 0\n\n for (;;) {\n const attemptNumber = records.length + 1\n // Report the abort before spending an attempt on a call already cancelled.\n if (cancelled(request)) {\n return abortedOutcome(\n batchIndex,\n request,\n records,\n new EmbeddingError(\n 'embedding call aborted before dispatch',\n EMBEDDING_ERROR_CODES.ABORTED,\n { provider: request.provider, model: request.model, itemIndexes: [...itemIndexes] },\n ),\n // No adapter call was ever made on this pass, which the runtime knows\n // first-hand. Once an attempt HAS been made, the runtime stops\n // claiming anything and leaves the state `unknown`.\n records.length === 0 ? 'not-sent' : 'unknown',\n )\n }\n\n states.set(batchIndex, Object.freeze({ phase: 'in-flight' as const, attempt: attemptNumber }))\n\n // Reported by the transport through `attempt.end`; unreported stays unknown.\n let reportedDispatch: DispatchState | undefined\n const context = observeDispatchState(invocationContext, (state) => {\n reportedDispatch = state\n })\n\n let result: EmbeddingBatchResult\n try {\n result = await dispatcher.embedBatch(request, context)\n } catch (error: unknown) {\n const failure = normalizeModelFailure(error)\n const dispatch = reportedDispatch ?? 'unknown'\n records.push(Object.freeze({\n attempt: attemptNumber,\n outcome: 'failure' as const,\n dispatch,\n failureCode: failure.code,\n }))\n\n const embeddingError = asEmbeddingError(error, failure, request)\n if (cancelled(request) || isAbortCode(failure.code)) {\n return abortedOutcome(batchIndex, request, records, embeddingError, dispatch)\n }\n\n const eligible = policy.mode === 'always'\n || (policy.retryableCodes.includes(failure.code) && retries < policy.maxRetries)\n const delayMs = eligible ? delayFor(policy, failure, retries + 1, random) : 'give-up'\n if (!eligible || delayMs === 'give-up') {\n const state: BatchState = Object.freeze({\n phase: 'failed' as const,\n error: embeddingError,\n // `retryable` describes the failure, not the budget: a rate limit\n // that ran out of attempts is still a retryable KIND of failure,\n // and a caller deciding whether to re-run later needs that apart\n // from \"this call gave up\".\n retryable: policy.mode === 'always' || policy.retryableCodes.includes(failure.code),\n dispatch,\n })\n states.set(batchIndex, state)\n return Object.freeze({\n batchIndex,\n itemIndexes,\n state,\n attempts: records.length,\n attemptRecords: Object.freeze([...records]),\n warnings: Object.freeze([]),\n })\n }\n\n retries += 1\n invocationContext?.recordProviderRetry?.({\n nextAttemptNumber: retries + 1,\n delayMs,\n failureCode: failure.code,\n })\n try {\n options.onRetry?.({\n provider: request.provider,\n model: request.model,\n batchIndex,\n attempt: retries,\n maxRetries: policy.mode === 'normal' ? policy.maxRetries : undefined,\n failureCode: failure.code,\n dispatch,\n delayMs,\n })\n } catch {\n // Metrics and logging observers do not own request availability.\n }\n\n if (!await sleep(delayMs, options.signal ?? request.signal)) {\n return abortedOutcome(\n batchIndex,\n request,\n records,\n new EmbeddingError(\n 'embedding call aborted while waiting to retry',\n EMBEDDING_ERROR_CODES.ABORTED,\n { provider: request.provider, model: request.model, itemIndexes: [...itemIndexes], cause: error },\n ),\n dispatch,\n )\n }\n continue\n }\n\n // A response exists, so the request reached the provider. The transport's\n // own report still wins when it made one.\n const dispatch = reportedDispatch ?? 'sent'\n records.push(Object.freeze({\n attempt: attemptNumber,\n outcome: 'success' as const,\n dispatch,\n }))\n const state: BatchState = Object.freeze({\n phase: 'succeeded' as const,\n vectors: result.vectors,\n })\n states.set(batchIndex, state)\n return Object.freeze({\n batchIndex,\n itemIndexes,\n state,\n attempts: records.length,\n attemptRecords: Object.freeze([...records]),\n // Absent usage is left absent: the aggregator downgrades coverage\n // rather than counting an unreported batch as zero tokens.\n ...(result.usage === undefined ? {} : { usage: result.usage }),\n ...(result.providerRequestId === undefined\n ? {}\n : { providerRequestId: result.providerRequestId }),\n warnings: Object.freeze([...(result.warnings ?? [])]),\n })\n }\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { ModelFailure } from "../errors/failure.js";
|
|
1
2
|
import { AgentSdkError } from "../errors/agent-sdk-error.js";
|
|
3
|
+
import { ModelErrorOptions } from "../errors/model-error.js";
|
|
2
4
|
import { EmbeddingSpaceId } from "./profile.js";
|
|
3
5
|
//#region src/embedding/errors.d.ts
|
|
4
6
|
/**
|
|
@@ -44,7 +46,7 @@ declare const EMBEDDING_ERROR_CODES: Readonly<{
|
|
|
44
46
|
/** Any value of {@link EMBEDDING_ERROR_CODES}; a foreign adapter may still use its own `string`. */
|
|
45
47
|
type EmbeddingErrorCode = typeof EMBEDDING_ERROR_CODES[keyof typeof EMBEDDING_ERROR_CODES];
|
|
46
48
|
/** Structured embedding facts and cause accepted by {@link EmbeddingError}. */
|
|
47
|
-
interface EmbeddingErrorOptions extends
|
|
49
|
+
interface EmbeddingErrorOptions extends ModelErrorOptions {
|
|
48
50
|
/**
|
|
49
51
|
* Zero-based indexes of the request items this failure belongs to.
|
|
50
52
|
*
|
|
@@ -76,6 +78,8 @@ interface EmbeddingErrorOptions extends ErrorOptions {
|
|
|
76
78
|
* out of errors and traces alike.
|
|
77
79
|
*/
|
|
78
80
|
declare class EmbeddingError extends AgentSdkError {
|
|
81
|
+
/** Validated provider facts survive the embedding wrapper and normalization. */
|
|
82
|
+
readonly failure: ModelFailure;
|
|
79
83
|
/** Request item indexes this failure belongs to, when input-specific. */
|
|
80
84
|
readonly itemIndexes?: readonly number[];
|
|
81
85
|
/** The applied limit, when the failure is a limit violation. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/embedding/errors.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/embedding/errors.ts"],"mappings":";;;;;;;;;;;;;cA6Ba,uBAAqB;;WAEf;;WAEA;;WAEO;;WAEP;;WAEG;;WAEC;;WAEG;;WAED;;WAED;;WAEA;;WAEM;;WAER;;WAEX;;WAEc;;WAEd;;;KAIC,4BAA4B,mCAAmC;;UAG1D,8BAA8B;;;;;;;EAO7C;;EAEA;;EAEA;;EAEA;;EAEA,QAAQ;;;;;;;;;;;;;;;;cAiBG,uBAAuB;;WAEzB,SAAS;;WAET;;WAEA;;WAEA;;WAEA;;WAEA,QAAQ;;;;;;EAOjB,YAAY,iBAAiB,cAAc,UAAU"}
|
package/dist/embedding/errors.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{AgentSdkError as e}from"../errors/agent-sdk-error.js";const
|
|
1
|
+
import{AgentSdkError as e}from"../errors/agent-sdk-error.js";import{ModelError as t}from"../errors/model-error.js";const n=Object.freeze({ADAPTER_MISSING:`EMBEDDING_ADAPTER_MISSING`,REQUEST_INVALID:`EMBEDDING_REQUEST_INVALID`,DIMENSIONS_UNSUPPORTED:`EMBEDDING_DIMENSIONS_UNSUPPORTED`,INPUT_TOO_LARGE:`EMBEDDING_INPUT_TOO_LARGE`,SPACE_INCOMPATIBLE:`EMBEDDING_SPACE_INCOMPATIBLE`,PURPOSE_UNSUPPORTED:`EMBEDDING_PURPOSE_UNSUPPORTED`,TRUNCATION_UNSUPPORTED:`EMBEDDING_TRUNCATION_UNSUPPORTED`,VECTOR_COUNT_MISMATCH:`EMBEDDING_VECTOR_COUNT_MISMATCH`,VECTOR_INDEX_INVALID:`EMBEDDING_VECTOR_INDEX_INVALID`,VECTOR_VALUE_INVALID:`EMBEDDING_VECTOR_VALUE_INVALID`,VECTOR_DIMENSIONS_MISMATCH:`EMBEDDING_VECTOR_DIMENSIONS_MISMATCH`,RESPONSE_MALFORMED:`EMBEDDING_RESPONSE_MALFORMED`,ABORTED:`EMBEDDING_ABORTED`,CONFIGURATION_INVALID:`EMBEDDING_CONFIGURATION_INVALID`,UNKNOWN:`EMBEDDING_UNKNOWN`});var r=class extends e{failure;itemIndexes;limit;provider;model;space;constructor(e,n,r){if(typeof e!=`string`||e.length===0)throw Error(`EmbeddingError message must be a non-empty string`);if(typeof n!=`string`||n.length===0)throw Error(`EmbeddingError code must be a non-empty string`);if(r?.itemIndexes!==void 0&&(!Array.isArray(r.itemIndexes)||r.itemIndexes.some(e=>!Number.isInteger(e)||e<0)))throw Error(`EmbeddingError itemIndexes must be an array of non-negative integers`);if(r?.limit!==void 0&&(!Number.isFinite(r.limit)||r.limit<=0))throw Error(`EmbeddingError limit must be a positive finite number`);if(r?.provider!==void 0&&(typeof r.provider!=`string`||r.provider.length===0))throw Error(`EmbeddingError provider must be a non-empty string`);if(r?.model!==void 0&&(typeof r.model!=`string`||r.model.length===0))throw Error(`EmbeddingError model must be a non-empty string`);if(r?.space!==void 0&&(typeof r.space!=`string`||r.space.length===0))throw Error(`EmbeddingError space must be a non-empty string`);let i=new t(e,n,r).failure;super(e,n,r),this.name=`EmbeddingError`,this.failure=i,r?.itemIndexes!==void 0&&(this.itemIndexes=Object.freeze([...r.itemIndexes])),r?.limit!==void 0&&(this.limit=r.limit),r?.provider!==void 0&&(this.provider=r.provider),r?.model!==void 0&&(this.model=r.model),r?.space!==void 0&&(this.space=r.space)}};export{n as EMBEDDING_ERROR_CODES,r as EmbeddingError};
|
|
2
2
|
//# sourceMappingURL=errors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../../src/embedding/errors.ts"],"sourcesContent":["/**\n * The stable embedding failure taxonomy, and the error the embedding layers throw.\n *\n * Codes here are owned by two layers only: `Embedding_Runtime` before dispatch\n * (dimensions, limits, space, configuration) and `Embedding_Adapter` when it\n * validates a response (vector count, index, values, width). Transport-level\n * faults keep using `MODEL_ERROR_CODES`, so a caller can tell \"the provider is\n * rate limiting us\" apart from \"the provider returned a vector of the wrong\n * width\" without parsing any message.\n *\n * Both provider adapters emit the SAME codes for mapping, dimensions and vector\n * faults. That is what lets one contract-test suite run against both.\n *\n * @module ai-agent-sdk/core/embedding/errors\n */\n\nimport { AgentSdkError } from '../errors/agent-sdk-error.ts'\nimport type { EmbeddingSpaceId } from './profile.ts'\n\n/**\n * Codes produced by the embedding contract and runtime.\n *\n * A frozen object rather than a TS enum, matching `MODEL_ERROR_CODES`, so a\n * third-party embedding adapter can emit its own code without this union having\n * to know about it. Values are prefixed `EMBEDDING_` because they travel beside\n * generation codes in the same failure envelopes.\n */\nexport const EMBEDDING_ERROR_CODES = Object.freeze({\n /** No `Embedding_Adapter` is registered for the requested route/model. */\n ADAPTER_MISSING: 'EMBEDDING_ADAPTER_MISSING',\n /** Malformed at the SDK boundary: missing purpose, empty values, empty item. */\n REQUEST_INVALID: 'EMBEDDING_REQUEST_INVALID',\n /** `dimensions` is not among the widths the route declares support for. */\n DIMENSIONS_UNSUPPORTED: 'EMBEDDING_DIMENSIONS_UNSUPPORTED',\n /** Input exceeds the declared max input tokens; carries `itemIndexes` and `limit`. */\n INPUT_TOO_LARGE: 'EMBEDDING_INPUT_TOO_LARGE',\n /** The expected `Space_Id` is incompatible with the `Prepared_Embedding_Call`. */\n SPACE_INCOMPATIBLE: 'EMBEDDING_SPACE_INCOMPATIBLE',\n /** The route has no way to express this purpose and the caller demands the distinction. */\n PURPOSE_UNSUPPORTED: 'EMBEDDING_PURPOSE_UNSUPPORTED',\n /** The caller asked for truncation but the provider has no equivalent parameter. */\n TRUNCATION_UNSUPPORTED: 'EMBEDDING_TRUNCATION_UNSUPPORTED',\n /** The response carried a different number of vectors than inputs sent. */\n VECTOR_COUNT_MISMATCH: 'EMBEDDING_VECTOR_COUNT_MISMATCH',\n /** A vector index is duplicated, missing, or out of range. */\n VECTOR_INDEX_INVALID: 'EMBEDDING_VECTOR_INDEX_INVALID',\n /** A vector contains `NaN` or `Infinity`. */\n VECTOR_VALUE_INVALID: 'EMBEDDING_VECTOR_VALUE_INVALID',\n /** A vector's width differs from the requested `dimensions`. */\n VECTOR_DIMENSIONS_MISMATCH: 'EMBEDDING_VECTOR_DIMENSIONS_MISMATCH',\n /** The response does not satisfy the embedding contract structurally. */\n RESPONSE_MALFORMED: 'EMBEDDING_RESPONSE_MALFORMED',\n /** The caller's signal or a runtime close aborted the call. */\n ABORTED: 'EMBEDDING_ABORTED',\n /** Invalid handle configuration: cache enabled without scope, fallback outside the group. */\n CONFIGURATION_INVALID: 'EMBEDDING_CONFIGURATION_INVALID',\n /** Nothing in this taxonomy classified the embedding failure. */\n UNKNOWN: 'EMBEDDING_UNKNOWN',\n} as const)\n\n/** Any value of {@link EMBEDDING_ERROR_CODES}; a foreign adapter may still use its own `string`. */\nexport type EmbeddingErrorCode = typeof EMBEDDING_ERROR_CODES[keyof typeof EMBEDDING_ERROR_CODES]\n\n/** Structured embedding facts and cause accepted by {@link EmbeddingError}. */\nexport interface EmbeddingErrorOptions extends
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../../src/embedding/errors.ts"],"sourcesContent":["/**\n * The stable embedding failure taxonomy, and the error the embedding layers throw.\n *\n * Codes here are owned by two layers only: `Embedding_Runtime` before dispatch\n * (dimensions, limits, space, configuration) and `Embedding_Adapter` when it\n * validates a response (vector count, index, values, width). Transport-level\n * faults keep using `MODEL_ERROR_CODES`, so a caller can tell \"the provider is\n * rate limiting us\" apart from \"the provider returned a vector of the wrong\n * width\" without parsing any message.\n *\n * Both provider adapters emit the SAME codes for mapping, dimensions and vector\n * faults. That is what lets one contract-test suite run against both.\n *\n * @module ai-agent-sdk/core/embedding/errors\n */\n\nimport { AgentSdkError } from '../errors/agent-sdk-error.ts'\nimport { ModelError, type ModelErrorOptions } from '../errors/model-error.ts'\nimport type { ModelFailure } from '../errors/failure.ts'\nimport type { EmbeddingSpaceId } from './profile.ts'\n\n/**\n * Codes produced by the embedding contract and runtime.\n *\n * A frozen object rather than a TS enum, matching `MODEL_ERROR_CODES`, so a\n * third-party embedding adapter can emit its own code without this union having\n * to know about it. Values are prefixed `EMBEDDING_` because they travel beside\n * generation codes in the same failure envelopes.\n */\nexport const EMBEDDING_ERROR_CODES = Object.freeze({\n /** No `Embedding_Adapter` is registered for the requested route/model. */\n ADAPTER_MISSING: 'EMBEDDING_ADAPTER_MISSING',\n /** Malformed at the SDK boundary: missing purpose, empty values, empty item. */\n REQUEST_INVALID: 'EMBEDDING_REQUEST_INVALID',\n /** `dimensions` is not among the widths the route declares support for. */\n DIMENSIONS_UNSUPPORTED: 'EMBEDDING_DIMENSIONS_UNSUPPORTED',\n /** Input exceeds the declared max input tokens; carries `itemIndexes` and `limit`. */\n INPUT_TOO_LARGE: 'EMBEDDING_INPUT_TOO_LARGE',\n /** The expected `Space_Id` is incompatible with the `Prepared_Embedding_Call`. */\n SPACE_INCOMPATIBLE: 'EMBEDDING_SPACE_INCOMPATIBLE',\n /** The route has no way to express this purpose and the caller demands the distinction. */\n PURPOSE_UNSUPPORTED: 'EMBEDDING_PURPOSE_UNSUPPORTED',\n /** The caller asked for truncation but the provider has no equivalent parameter. */\n TRUNCATION_UNSUPPORTED: 'EMBEDDING_TRUNCATION_UNSUPPORTED',\n /** The response carried a different number of vectors than inputs sent. */\n VECTOR_COUNT_MISMATCH: 'EMBEDDING_VECTOR_COUNT_MISMATCH',\n /** A vector index is duplicated, missing, or out of range. */\n VECTOR_INDEX_INVALID: 'EMBEDDING_VECTOR_INDEX_INVALID',\n /** A vector contains `NaN` or `Infinity`. */\n VECTOR_VALUE_INVALID: 'EMBEDDING_VECTOR_VALUE_INVALID',\n /** A vector's width differs from the requested `dimensions`. */\n VECTOR_DIMENSIONS_MISMATCH: 'EMBEDDING_VECTOR_DIMENSIONS_MISMATCH',\n /** The response does not satisfy the embedding contract structurally. */\n RESPONSE_MALFORMED: 'EMBEDDING_RESPONSE_MALFORMED',\n /** The caller's signal or a runtime close aborted the call. */\n ABORTED: 'EMBEDDING_ABORTED',\n /** Invalid handle configuration: cache enabled without scope, fallback outside the group. */\n CONFIGURATION_INVALID: 'EMBEDDING_CONFIGURATION_INVALID',\n /** Nothing in this taxonomy classified the embedding failure. */\n UNKNOWN: 'EMBEDDING_UNKNOWN',\n} as const)\n\n/** Any value of {@link EMBEDDING_ERROR_CODES}; a foreign adapter may still use its own `string`. */\nexport type EmbeddingErrorCode = typeof EMBEDDING_ERROR_CODES[keyof typeof EMBEDDING_ERROR_CODES]\n\n/** Structured embedding facts and cause accepted by {@link EmbeddingError}. */\nexport interface EmbeddingErrorOptions extends ModelErrorOptions {\n /**\n * Zero-based indexes of the request items this failure belongs to.\n *\n * Present only when the fault is attributable to specific inputs, which is\n * what lets a caller drop or re-chunk those items instead of the whole call.\n */\n itemIndexes?: readonly number[]\n /** The limit that was applied, when the failure is a limit violation. */\n limit?: number\n /** Provider id of the route that produced the failure. */\n provider?: string\n /** Model id of the route that produced the failure. */\n model?: string\n /** The embedding space involved, when the failure concerns space compatibility. */\n space?: EmbeddingSpaceId\n}\n\n/**\n * Typed error for embedding failures, carrying a stable `code` plus the facts a\n * caller needs to act without re-deriving them.\n *\n * The constructor validates rather than trusts: `itemIndexes` must be\n * non-negative integers and `limit` a positive finite number. A negative index\n * or a `limit` of `-1` means a caller upstream miscomputed a bound, and letting\n * it through would surface a nonsense limit in a message that reads as\n * authoritative. Indexes are copied and frozen so the array cannot be mutated\n * after the error is thrown.\n *\n * No field carries raw input text or vector values: redaction rules keep those\n * out of errors and traces alike.\n */\nexport class EmbeddingError extends AgentSdkError {\n /** Validated provider facts survive the embedding wrapper and normalization. */\n readonly failure: ModelFailure\n /** Request item indexes this failure belongs to, when input-specific. */\n readonly itemIndexes?: readonly number[]\n /** The applied limit, when the failure is a limit violation. */\n readonly limit?: number\n /** Provider id of the failing route. */\n readonly provider?: string\n /** Model id of the failing route. */\n readonly model?: string\n /** The embedding space involved in the failure. */\n readonly space?: EmbeddingSpaceId\n\n /**\n * @param message - non-empty human-readable failure summary, free of credentials and raw input.\n * @param code - non-empty stable machine code, normally from {@link EMBEDDING_ERROR_CODES}.\n * @param options - optional cause and validated embedding facts.\n */\n constructor(message: string, code: string, options?: EmbeddingErrorOptions) {\n if (typeof message !== 'string' || message.length === 0) {\n throw new Error('EmbeddingError message must be a non-empty string')\n }\n if (typeof code !== 'string' || code.length === 0) {\n throw new Error('EmbeddingError code must be a non-empty string')\n }\n if (options?.itemIndexes !== undefined\n && (!Array.isArray(options.itemIndexes)\n || options.itemIndexes.some(index => !Number.isInteger(index) || index < 0))) {\n throw new Error('EmbeddingError itemIndexes must be an array of non-negative integers')\n }\n if (options?.limit !== undefined\n && (!Number.isFinite(options.limit) || options.limit <= 0)) {\n throw new Error('EmbeddingError limit must be a positive finite number')\n }\n if (options?.provider !== undefined\n && (typeof options.provider !== 'string' || options.provider.length === 0)) {\n throw new Error('EmbeddingError provider must be a non-empty string')\n }\n if (options?.model !== undefined\n && (typeof options.model !== 'string' || options.model.length === 0)) {\n throw new Error('EmbeddingError model must be a non-empty string')\n }\n if (options?.space !== undefined\n && (typeof options.space !== 'string' || options.space.length === 0)) {\n throw new Error('EmbeddingError space must be a non-empty string')\n }\n const failure = new ModelError(message, code, options).failure\n super(message, code, options)\n this.name = 'EmbeddingError'\n this.failure = failure\n if (options?.itemIndexes !== undefined) {\n this.itemIndexes = Object.freeze([...options.itemIndexes])\n }\n if (options?.limit !== undefined) this.limit = options.limit\n if (options?.provider !== undefined) this.provider = options.provider\n if (options?.model !== undefined) this.model = options.model\n if (options?.space !== undefined) this.space = options.space\n }\n}\n"],"mappings":"mHA6BA,MAAa,EAAwB,OAAO,OAAO,CAEjD,gBAAiB,4BAEjB,gBAAiB,4BAEjB,uBAAwB,mCAExB,gBAAiB,4BAEjB,mBAAoB,+BAEpB,oBAAqB,gCAErB,uBAAwB,mCAExB,sBAAuB,kCAEvB,qBAAsB,iCAEtB,qBAAsB,iCAEtB,2BAA4B,uCAE5B,mBAAoB,+BAEpB,QAAS,oBAET,sBAAuB,kCAEvB,QAAS,mBACX,CAAU,EAsCV,IAAa,EAAb,cAAoC,CAAc,CAEhD,QAEA,YAEA,MAEA,SAEA,MAEA,MAOA,YAAY,EAAiB,EAAc,EAAiC,CAC1E,GAAI,OAAO,GAAY,UAAY,EAAQ,SAAW,EACpD,MAAU,MAAM,mDAAmD,EAErE,GAAI,OAAO,GAAS,UAAY,EAAK,SAAW,EAC9C,MAAU,MAAM,gDAAgD,EAElE,GAAI,GAAS,cAAgB,IAAA,KACvB,CAAC,MAAM,QAAQ,EAAQ,WAAW,GACjC,EAAQ,YAAY,KAAK,GAAS,CAAC,OAAO,UAAU,CAAK,GAAK,EAAQ,CAAC,GAC5E,MAAU,MAAM,sEAAsE,EAExF,GAAI,GAAS,QAAU,IAAA,KACjB,CAAC,OAAO,SAAS,EAAQ,KAAK,GAAK,EAAQ,OAAS,GACxD,MAAU,MAAM,uDAAuD,EAEzE,GAAI,GAAS,WAAa,IAAA,KACpB,OAAO,EAAQ,UAAa,UAAY,EAAQ,SAAS,SAAW,GACxE,MAAU,MAAM,oDAAoD,EAEtE,GAAI,GAAS,QAAU,IAAA,KACjB,OAAO,EAAQ,OAAU,UAAY,EAAQ,MAAM,SAAW,GAClE,MAAU,MAAM,iDAAiD,EAEnE,GAAI,GAAS,QAAU,IAAA,KACjB,OAAO,EAAQ,OAAU,UAAY,EAAQ,MAAM,SAAW,GAClE,MAAU,MAAM,iDAAiD,EAEnE,IAAM,EAAU,IAAI,EAAW,EAAS,EAAM,CAAO,CAAC,CAAC,QACvD,MAAM,EAAS,EAAM,CAAO,EAC5B,KAAK,KAAO,iBACZ,KAAK,QAAU,EACX,GAAS,cAAgB,IAAA,KAC3B,KAAK,YAAc,OAAO,OAAO,CAAC,GAAG,EAAQ,WAAW,CAAC,GAEvD,GAAS,QAAU,IAAA,KAAW,KAAK,MAAQ,EAAQ,OACnD,GAAS,WAAa,IAAA,KAAW,KAAK,SAAW,EAAQ,UACzD,GAAS,QAAU,IAAA,KAAW,KAAK,MAAQ,EAAQ,OACnD,GAAS,QAAU,IAAA,KAAW,KAAK,MAAQ,EAAQ,MACzD,CACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{deepFreeze as e}from"../primitives/freeze.js";import"../primitives/version.js";import{createObservationRunScope as t,createOperationId as n}from"../observation/context.js";import{safeErrorRecord as r}from"../observation/event.js";import{createCoreSpan as i,snapshotObservationSpan as a}from"../observation/port.js";import{sanitizeObservationEvent as o,serializedEventBytes as s}from"../observation/privacy.js";import{createBusLogger as c}from"./logger.js";const l={none:0,"local-durable":1,"remote-acknowledged":2},u=new Set([`ABORT_ERR`,`ECONNREFUSED`,`ECONNRESET`,`EHOSTUNREACH`,`ENETUNREACH`,`ETIMEDOUT`,`OBSERVABILITY_EXPORT_FAILED`,`OBSERVABILITY_FLUSH_TIMEOUT`,`OBSERVABILITY_PROCESSOR_FAILED`,`OTEL_PROVIDER_UNCONFIGURED`]),d=/^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,63})$/,f=new Set([`none`,`metadata`,`redacted`,`full`]),p=new Set([`trace`,`debug`,`info`,`warn`,`error`,`fatal`]);function m(e,t){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`${t} must be a positive safe integer`);return e}function h(t,n){let i=r(t);return e({type:[`AbortError`,`Error`,`RangeError`,`TypeError`].includes(i.type)?i.type:`Error`,message:n,...i.code!==void 0&&u.has(i.code)?{code:i.code}:{},...i.retryable===void 0?{}:{retryable:i.retryable},...i.status===void 0?{}:{status:i.status}})}function g(e,t,n=`none`,r){return Object.freeze({eventId:e,status:t,durable:t===`accepted`&&n!==`none`,boundary:n,...r===void 0?{}:{reason:r}})}function _(e){try{let t=Reflect.get(e,`eventId`);return typeof t==`string`&&t.length>0?t:n()}catch{return n()}}function v(e){let t=`none`;for(let n of e)n.requirement===`required`&&l[n.boundary]>l[t]&&(t=n.boundary);return t}function y(e,t){let n=new Set;for(let r of t){let t=r.exporter?.id;if(typeof t!=`string`||!d.test(t))throw TypeError(`observation exporter id must be a safe 1-64 character identifier`);if(n.has(t))throw TypeError(`duplicate observation exporter id: ${t}`);if(n.add(t),r.requirement!==`required`&&r.requirement!==`best-effort`)throw TypeError(`observation exporter ${t} has an invalid requirement`);if(!(r.boundary in l))throw TypeError(`observation exporter ${t} has an invalid boundary`);let i=r.exporter.supportedBoundaries??[`none`];if(typeof r.exporter.export!=`function`)throw TypeError(`observation exporter ${t} has no export function`);if(!i.includes(r.boundary))throw TypeError(`observation exporter ${t} does not support ${r.boundary}`);if(e===`operational`&&r.requirement===`required`)throw TypeError(`operational observation exporters must be best-effort`)}if(e!==`operational`&&!t.some(e=>e.requirement===`required`&&e.boundary!==`none`))throw TypeError(`${e} observability requires a durable required exporter`);return Object.freeze(t.map(e=>Object.freeze({...e})))}function b(e,t){let n=new AbortController,r=setTimeout(()=>n.abort(new DOMException(`observation deadline exceeded`,`TimeoutError`)),e),i=()=>n.abort(t?.reason??new DOMException(`observation aborted`,`AbortError`));return t?.aborted===!0?i():t?.addEventListener(`abort`,i,{once:!0}),{signal:n.signal,clear(){clearTimeout(r),t?.removeEventListener(`abort`,i)}}}function x(e,t){return t.aborted?Promise.reject(t.reason??Error(`observation aborted`)):new Promise((n,r)=>{let i=()=>{a(),r(t.reason??Error(`observation aborted`))},a=()=>t.removeEventListener(`abort`,i);t.addEventListener(`abort`,i,{once:!0}),e.then(e=>{a(),n(e)},e=>{a(),r(e)})})}function S(e){try{let t=e.reason;return typeof t==`object`&&!!t&&Reflect.get(t,`name`)===`TimeoutError`}catch{return!1}}function C(e){return Object.freeze({complete:e.complete,exportedEvents:e.exportedEvents,pendingEvents:e.pendingEvents,rejectedCritical:e.rejectedCritical,timedOut:e.timedOut})}function w(e,t){return e.eventId===t.eventId&&e.sequence===t.sequence&&e.name===t.name&&e.phase===t.phase&&e.occurredAt===t.occurredAt&&e.monotonicMs===t.monotonicMs&&e.priority===t.priority&&JSON.stringify(e.resource)===JSON.stringify(t.resource)&&JSON.stringify(e.correlation)===JSON.stringify(t.correlation)}var T=class{mode;resource;registrations;processors;options;queue=[];queueBytes=0;closing=!1;closed=!1;shutdownPromise;flushChain=Promise.resolve();protectedFailures=new Set;healthScope=t();healthRunId=n();healthCorrelation=i({name:`sdk.integration.request`,runId:this.healthRunId,startedAt:new Date().toISOString(),monotonicMs:this.healthScope.monotonicMs()}).correlation;counters={accepted:0,exported:0,droppedVerbose:0,droppedNormal:0,criticalRejected:0,processorFailures:0,exporterFailures:0,flushTimeouts:0,requiredFailure:!1};constructor(t){if(this.mode=t.mode??`operational`,![`operational`,`reliable`,`audit`].includes(this.mode))throw TypeError(`invalid observation mode`);this.resource=e({sdkName:`ai-agent-sdk`,sdkVersion:t.resource?.sdkVersion??`0.1.7`,...t.resource?.serviceName===void 0?{}:{serviceName:t.resource.serviceName},...t.resource?.serviceVersion===void 0?{}:{serviceVersion:t.resource.serviceVersion},runtime:t.resource?.runtime??`unknown`}),this.registrations=y(this.mode,t.exporters??[]);let n=new Set;for(let e of t.processors??[]){if(typeof e.id!=`string`||!d.test(e.id))throw TypeError(`processor id must be a safe 1-64 character identifier`);if(n.has(e.id))throw TypeError(`duplicate observation processor id: ${e.id}`);if(typeof e.transform!=`function`)throw TypeError(`observation processor ${e.id} is invalid`);n.add(e.id)}if(this.processors=Object.freeze([...t.processors??[]]),!f.has(t.content??`none`))throw TypeError(`invalid observation content policy`);if(!p.has(t.minimumLogLevel??`info`))throw TypeError(`invalid minimum log level`);let r=new Set;for(let e of t.redactors??[]){if(typeof e.id!=`string`||!d.test(e.id)||typeof e.redact!=`function`)throw TypeError(`invalid observation content redactor`);if(r.has(e.id))throw TypeError(`duplicate observation redactor id: ${e.id}`);r.add(e.id)}this.options={content:t.content??`none`,includeErrorStacks:t.includeErrorStacks??!1,minimumLogLevel:t.minimumLogLevel??`info`,maxQueueEvents:m(t.maxQueueEvents??1e4,`maxQueueEvents`),maxQueueBytes:m(t.maxQueueBytes??16777216,`maxQueueBytes`),maxBatchEvents:m(t.maxBatchEvents??256,`maxBatchEvents`),maxBatchBytes:m(t.maxBatchBytes??524288,`maxBatchBytes`),flushTimeoutMs:m(t.flushTimeoutMs??1e4,`flushTimeoutMs`),shutdownTimeoutMs:m(t.shutdownTimeoutMs??3e4,`shutdownTimeoutMs`),redactors:Object.freeze([...t.redactors??[]]),...t.onHealthChange===void 0?{}:{onHealthChange:t.onHealthChange},...t.openSpan===void 0?{}:{openSpan:t.openSpan}}}openSpan(e){let t=this.options.openSpan;if(t===void 0)return i(e);try{let n=a(t(e));if(n!==void 0)return n;this.counters.processorFailures++,this.recordFailure(`span:invalid`,TypeError(`invalid span`),`observation span backend returned an invalid span`,!1)}catch(e){this.counters.processorFailures++,this.recordFailure(`span:throw`,e,`observation span backend failed`,!1)}return i(e)}capture(e){let t=_(e);if(this.closing||this.closed)return g(t,`rejected`,`none`,`closed`);let n;try{n=this.prepareEvent(e,!1)}catch(e){return this.counters.processorFailures++,this.counters.lastFailure=h(e,`observation processor failed`),this.notifyHealth(),this.recordFailure(`processor:capture`,e,`observation processor failed`,!0),g(t,`rejected`,`none`,`processor-failed`)}let r=this.enqueue(n,!1);return r.status===`accepted`?this.stageExporters(n,r):r}async checkpoint(e,t){let n=this.capture(e);if(n.status!==`accepted`||this.mode===`operational`)return n;let r=this.queue.find(e=>e.event.eventId===n.eventId)?.event;if(r===void 0)return g(n.eventId,`rejected`,`none`,`exporter-unavailable`);let i=new Set(this.queue.filter(e=>e.event.priority===`critical`&&e.event.correlation.runId===r.correlation.runId&&e.event.sequence<=r.sequence).map(e=>e.event.eventId));return(await this.scheduleFlush(i,this.options.flushTimeoutMs,t)).requiredComplete?g(n.eventId,`accepted`,v(this.registrations)):g(n.eventId,`rejected`,`none`,`exporter-unavailable`)}logger(e){return c({minimumLevel:this.options.minimumLogLevel,resource:this.resource,emit:e=>{this.capture(e)}},e)}health(){let t=this.closed?`closed`:this.counters.requiredFailure||this.counters.criticalRejected>0||this.counters.flushTimeouts>0?`failed`:this.counters.processorFailures>0||this.counters.exporterFailures>0||this.counters.droppedVerbose>0||this.counters.droppedNormal>0?`degraded`:`healthy`;return e({state:t,queuedEvents:this.queue.length,queuedBytes:this.queueBytes,accepted:this.counters.accepted,exported:this.counters.exported,droppedVerbose:this.counters.droppedVerbose,droppedNormal:this.counters.droppedNormal,criticalRejected:this.counters.criticalRejected,processorFailures:this.counters.processorFailures,exporterFailures:this.counters.exporterFailures,flushTimeouts:this.counters.flushTimeouts,...this.counters.lastExportAt===void 0?{}:{lastExportAt:this.counters.lastExportAt},...this.counters.lastFailure===void 0?{}:{lastFailure:this.counters.lastFailure}})}flush(e){if(this.closed||this.closing)throw TypeError(`observability is closed`);return this.scheduleFlush(new Set(this.queue.map(e=>e.event.eventId)),this.options.flushTimeoutMs,e).then(C)}shutdown(e){if(this.shutdownPromise!==void 0)return this.shutdownPromise;this.closing=!0;let t=new Set(this.queue.map(e=>e.event.eventId));return this.shutdownPromise=(async()=>{let n=b(this.options.shutdownTimeoutMs,e),r;try{r=C(await this.scheduleFlush(t,this.options.shutdownTimeoutMs,n.signal));for(let e of[...this.registrations].reverse()){let t=e.exporter.shutdown;if(t!==void 0)try{await x(Promise.resolve(t.call(e.exporter,n.signal)),n.signal)}catch(t){this.recordExporterFailure(e,t,!1),r=Object.freeze({...r,complete:!1})}}}finally{n.clear(),this.closed=!0,this.notifyHealth()}return r})(),this.shutdownPromise}prepareEvent(e,t){let n={content:this.options.content,redactors:this.options.redactors??[],includeErrorStacks:this.options.includeErrorStacks},r=o(e,n);if(t)return r;for(let e of this.processors){let t=e.transform(r);if(t===void 0)throw TypeError(`observation processor ${e.id} returned undefined`);let i=o(t,n);if(!w(r,i))throw TypeError(`observation processor ${e.id} changed the immutable event envelope`);r=i}return r}enqueue(e,t){let n=s(e);return n>this.options.maxBatchBytes||n>this.options.maxQueueBytes||!this.makeCapacity(n,t)?(!t&&e.priority===`critical`&&this.counters.criticalRejected++,t||(this.counters.lastFailure=h(RangeError(`capacity`),`observation queue capacity exhausted`),this.notifyHealth()),g(e.eventId,`rejected`,`none`,`capacity`)):(this.queue.push({event:e,bytes:n,protected:t,pending:new Set(this.registrations.map(e=>e.exporter.id))}),this.queueBytes+=n,this.counters.accepted++,this.notifyHealth(),g(e.eventId,`accepted`))}stageExporters(e,t){let n=this.queue.find(t=>t.event.eventId===e.eventId);if(n===void 0)return g(e.eventId,`rejected`,`none`,`exporter-unavailable`);let r=!1;for(let t of this.registrations){let i=t.exporter.stage;if(i!==void 0)try{let n=i.call(t.exporter,e);n!==void 0&&Promise.resolve(n).catch(()=>void 0)}catch(i){if(this.recordExporterFailure(t,i,!1),n.pending.delete(t.exporter.id),r=!0,t.requirement===`required`)return this.removeEntry(n,!1),e.priority===`critical`&&this.counters.criticalRejected++,this.notifyHealth(),g(e.eventId,`rejected`,`none`,`exporter-unavailable`)}}return r&&n.pending.size===0&&this.removeEntry(n,!1),t}makeCapacity(e,t){for(;this.queue.length+1>this.options.maxQueueEvents||this.queueBytes+e>this.options.maxQueueBytes;){let e=this.queue.findIndex(e=>e.event.priority===`verbose`);if(e===-1&&(e=this.queue.findIndex(e=>e.event.priority===`normal`)),e===-1)return!1;let[n]=this.queue.splice(e,1);if(n===void 0)return!1;this.queueBytes-=n.bytes,t||(n.event.priority===`verbose`?this.counters.droppedVerbose++:this.counters.droppedNormal++)}return!0}scheduleFlush(e,t,n){let r,i,a=new Promise((e,t)=>{r=e,i=t}),o=async()=>{try{r(await this.flushInternal(e,t,n))}catch(e){i(e)}};return this.flushChain=this.flushChain.then(o,o),a}async flushInternal(t,r,i){let a=this.counters.exported,o=b(r,i),s=!1,c=!1;try{if(this.registrations.length===0)for(let e of[...this.queue])t.has(e.event.eventId)&&this.removeEntry(e,!1);for(let r of this.registrations)for(;;){let i=this.queue.filter(e=>t.has(e.event.eventId)&&e.pending.has(r.exporter.id));if(i.length===0)break;let a=[],l=0;for(let e of i){if(a.length>=this.options.maxBatchEvents||l+e.bytes>this.options.maxBatchBytes)break;a.push(e),l+=e.bytes}if(a.length===0)break;let u=e({schemaVersion:1,batchId:n(),createdAt:new Date().toISOString(),events:a.map(e=>e.event)}),d;try{if(d=await x(Promise.resolve(r.exporter.export(u,o.signal)),o.signal),d.batchId!==u.batchId||d.accepted!==!0)throw TypeError(`observation exporter ${r.exporter.id} returned an invalid acknowledgment`)}catch(e){if(s=!0,c||=S(o.signal),this.recordExporterFailure(r,e,!0),r.requirement===`best-effort`)for(let e of a)e.pending.delete(r.exporter.id),e.pending.size===0&&this.removeEntry(e,!1);break}this.counters.lastExportAt=new Date().toISOString();for(let e of a)e.pending.delete(r.exporter.id),e.pending.size===0&&this.removeEntry(e,!0);this.notifyHealth()}}finally{S(o.signal)&&(c=!0,this.counters.flushTimeouts++,this.counters.lastFailure=h(o.signal.reason,`observation flush timed out`),this.notifyHealth()),o.clear()}let l=this.queue.filter(e=>t.has(e.event.eventId)),u=this.registrations.filter(e=>e.requirement===`required`).map(e=>e.exporter.id),d=l.every(e=>u.every(t=>!e.pending.has(t)));return Object.freeze({complete:!s&&!c&&l.length===0,requiredComplete:d,exportedEvents:this.counters.exported-a,pendingEvents:l.length,rejectedCritical:this.counters.criticalRejected,timedOut:c})}removeEntry(e,t){let n=this.queue.indexOf(e);n!==-1&&(this.queue.splice(n,1),this.queueBytes-=e.bytes,t&&this.counters.exported++)}recordExporterFailure(e,t,n){this.counters.exporterFailures++,e.requirement===`required`&&(this.counters.requiredFailure=!0),this.counters.lastFailure=h(t,`observation exporter failed`),this.notifyHealth(),n&&this.recordFailure(`exporter:${e.exporter.id}`,t,`observation exporter failed`,!0)}recordFailure(e,t,r,i){if(this.counters.lastFailure=h(t,r),this.notifyHealth(),!(!i||this.protectedFailures.has(e)||this.closing||this.closed)){this.protectedFailures.add(e);try{let t=this.prepareEvent({schemaVersion:1,eventId:n(),sequence:this.healthScope.nextSequence(),name:`sdk.observer.failure`,phase:`point`,occurredAt:new Date().toISOString(),monotonicMs:this.healthScope.monotonicMs(),priority:`critical`,resource:this.resource,correlation:this.healthCorrelation,data:{observerId:e,failureKind:`contained`,count:1,error:{...this.counters.lastFailure}}},!0);this.enqueue(t,!0)}catch{}}}notifyHealth(){let e=this.options.onHealthChange;if(e!==void 0)try{e(this.health())}catch{}}};function E(e={}){return new T(e)}export{E as createObservability};
|
|
1
|
+
import{deepFreeze as e}from"../primitives/freeze.js";import"../primitives/version.js";import{createObservationRunScope as t,createOperationId as n}from"../observation/context.js";import{safeErrorRecord as r}from"../observation/event.js";import{createCoreSpan as i,snapshotObservationSpan as a}from"../observation/port.js";import{sanitizeObservationEvent as o,serializedEventBytes as s}from"../observation/privacy.js";import{createBusLogger as c}from"./logger.js";const l={none:0,"local-durable":1,"remote-acknowledged":2},u=new Set([`ABORT_ERR`,`ECONNREFUSED`,`ECONNRESET`,`EHOSTUNREACH`,`ENETUNREACH`,`ETIMEDOUT`,`OBSERVABILITY_EXPORT_FAILED`,`OBSERVABILITY_FLUSH_TIMEOUT`,`OBSERVABILITY_PROCESSOR_FAILED`,`OTEL_PROVIDER_UNCONFIGURED`]),d=/^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,63})$/,f=new Set([`none`,`metadata`,`redacted`,`full`]),p=new Set([`trace`,`debug`,`info`,`warn`,`error`,`fatal`]);function m(e,t){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`${t} must be a positive safe integer`);return e}function h(t,n){let i=r(t);return e({type:[`AbortError`,`Error`,`RangeError`,`TypeError`].includes(i.type)?i.type:`Error`,message:n,...i.code!==void 0&&u.has(i.code)?{code:i.code}:{},...i.retryable===void 0?{}:{retryable:i.retryable},...i.status===void 0?{}:{status:i.status}})}function g(e,t,n=`none`,r){return Object.freeze({eventId:e,status:t,durable:t===`accepted`&&n!==`none`,boundary:n,...r===void 0?{}:{reason:r}})}function _(e){try{let t=Reflect.get(e,`eventId`);return typeof t==`string`&&t.length>0?t:n()}catch{return n()}}function v(e){let t=`none`;for(let n of e)n.requirement===`required`&&l[n.boundary]>l[t]&&(t=n.boundary);return t}function y(e,t){let n=new Set;for(let r of t){let t=r.exporter?.id;if(typeof t!=`string`||!d.test(t))throw TypeError(`observation exporter id must be a safe 1-64 character identifier`);if(n.has(t))throw TypeError(`duplicate observation exporter id: ${t}`);if(n.add(t),r.requirement!==`required`&&r.requirement!==`best-effort`)throw TypeError(`observation exporter ${t} has an invalid requirement`);if(!(r.boundary in l))throw TypeError(`observation exporter ${t} has an invalid boundary`);let i=r.exporter.supportedBoundaries??[`none`];if(typeof r.exporter.export!=`function`)throw TypeError(`observation exporter ${t} has no export function`);if(!i.includes(r.boundary))throw TypeError(`observation exporter ${t} does not support ${r.boundary}`);if(e===`operational`&&r.requirement===`required`)throw TypeError(`operational observation exporters must be best-effort`)}if(e!==`operational`&&!t.some(e=>e.requirement===`required`&&e.boundary!==`none`))throw TypeError(`${e} observability requires a durable required exporter`);return Object.freeze(t.map(e=>Object.freeze({...e})))}function b(e,t){let n=new AbortController,r=setTimeout(()=>n.abort(new DOMException(`observation deadline exceeded`,`TimeoutError`)),e),i=()=>n.abort(t?.reason??new DOMException(`observation aborted`,`AbortError`));return t?.aborted===!0?i():t?.addEventListener(`abort`,i,{once:!0}),{signal:n.signal,clear(){clearTimeout(r),t?.removeEventListener(`abort`,i)}}}function x(e,t){return t.aborted?Promise.reject(t.reason??Error(`observation aborted`)):new Promise((n,r)=>{let i=()=>{a(),r(t.reason??Error(`observation aborted`))},a=()=>t.removeEventListener(`abort`,i);t.addEventListener(`abort`,i,{once:!0}),e.then(e=>{a(),n(e)},e=>{a(),r(e)})})}function S(e){try{let t=e.reason;return typeof t==`object`&&!!t&&Reflect.get(t,`name`)===`TimeoutError`}catch{return!1}}function C(e){return Object.freeze({complete:e.complete,exportedEvents:e.exportedEvents,pendingEvents:e.pendingEvents,rejectedCritical:e.rejectedCritical,timedOut:e.timedOut})}function w(e,t){return e.eventId===t.eventId&&e.sequence===t.sequence&&e.name===t.name&&e.phase===t.phase&&e.occurredAt===t.occurredAt&&e.monotonicMs===t.monotonicMs&&e.priority===t.priority&&JSON.stringify(e.resource)===JSON.stringify(t.resource)&&JSON.stringify(e.correlation)===JSON.stringify(t.correlation)}var T=class{mode;resource;registrations;processors;options;queue=[];queueBytes=0;closing=!1;closed=!1;shutdownPromise;flushChain=Promise.resolve();protectedFailures=new Set;healthScope=t();healthRunId=n();healthCorrelation=i({name:`sdk.integration.request`,runId:this.healthRunId,startedAt:new Date().toISOString(),monotonicMs:this.healthScope.monotonicMs()}).correlation;counters={accepted:0,exported:0,droppedVerbose:0,droppedNormal:0,criticalRejected:0,processorFailures:0,exporterFailures:0,flushTimeouts:0,requiredFailure:!1};constructor(t){if(this.mode=t.mode??`operational`,![`operational`,`reliable`,`audit`].includes(this.mode))throw TypeError(`invalid observation mode`);this.resource=e({sdkName:`ai-agent-sdk`,sdkVersion:t.resource?.sdkVersion??`0.1.8`,...t.resource?.serviceName===void 0?{}:{serviceName:t.resource.serviceName},...t.resource?.serviceVersion===void 0?{}:{serviceVersion:t.resource.serviceVersion},runtime:t.resource?.runtime??`unknown`}),this.registrations=y(this.mode,t.exporters??[]);let n=new Set;for(let e of t.processors??[]){if(typeof e.id!=`string`||!d.test(e.id))throw TypeError(`processor id must be a safe 1-64 character identifier`);if(n.has(e.id))throw TypeError(`duplicate observation processor id: ${e.id}`);if(typeof e.transform!=`function`)throw TypeError(`observation processor ${e.id} is invalid`);n.add(e.id)}if(this.processors=Object.freeze([...t.processors??[]]),!f.has(t.content??`none`))throw TypeError(`invalid observation content policy`);if(!p.has(t.minimumLogLevel??`info`))throw TypeError(`invalid minimum log level`);let r=new Set;for(let e of t.redactors??[]){if(typeof e.id!=`string`||!d.test(e.id)||typeof e.redact!=`function`)throw TypeError(`invalid observation content redactor`);if(r.has(e.id))throw TypeError(`duplicate observation redactor id: ${e.id}`);r.add(e.id)}this.options={content:t.content??`none`,includeErrorStacks:t.includeErrorStacks??!1,minimumLogLevel:t.minimumLogLevel??`info`,maxQueueEvents:m(t.maxQueueEvents??1e4,`maxQueueEvents`),maxQueueBytes:m(t.maxQueueBytes??16777216,`maxQueueBytes`),maxBatchEvents:m(t.maxBatchEvents??256,`maxBatchEvents`),maxBatchBytes:m(t.maxBatchBytes??524288,`maxBatchBytes`),flushTimeoutMs:m(t.flushTimeoutMs??1e4,`flushTimeoutMs`),shutdownTimeoutMs:m(t.shutdownTimeoutMs??3e4,`shutdownTimeoutMs`),redactors:Object.freeze([...t.redactors??[]]),...t.onHealthChange===void 0?{}:{onHealthChange:t.onHealthChange},...t.openSpan===void 0?{}:{openSpan:t.openSpan}}}openSpan(e){let t=this.options.openSpan;if(t===void 0)return i(e);try{let n=a(t(e));if(n!==void 0)return n;this.counters.processorFailures++,this.recordFailure(`span:invalid`,TypeError(`invalid span`),`observation span backend returned an invalid span`,!1)}catch(e){this.counters.processorFailures++,this.recordFailure(`span:throw`,e,`observation span backend failed`,!1)}return i(e)}capture(e){let t=_(e);if(this.closing||this.closed)return g(t,`rejected`,`none`,`closed`);let n;try{n=this.prepareEvent(e,!1)}catch(e){return this.counters.processorFailures++,this.counters.lastFailure=h(e,`observation processor failed`),this.notifyHealth(),this.recordFailure(`processor:capture`,e,`observation processor failed`,!0),g(t,`rejected`,`none`,`processor-failed`)}let r=this.enqueue(n,!1);return r.status===`accepted`?this.stageExporters(n,r):r}async checkpoint(e,t){let n=this.capture(e);if(n.status!==`accepted`||this.mode===`operational`)return n;let r=this.queue.find(e=>e.event.eventId===n.eventId)?.event;if(r===void 0)return g(n.eventId,`rejected`,`none`,`exporter-unavailable`);let i=new Set(this.queue.filter(e=>e.event.priority===`critical`&&e.event.correlation.runId===r.correlation.runId&&e.event.sequence<=r.sequence).map(e=>e.event.eventId));return(await this.scheduleFlush(i,this.options.flushTimeoutMs,t)).requiredComplete?g(n.eventId,`accepted`,v(this.registrations)):g(n.eventId,`rejected`,`none`,`exporter-unavailable`)}logger(e){return c({minimumLevel:this.options.minimumLogLevel,resource:this.resource,emit:e=>{this.capture(e)}},e)}health(){let t=this.closed?`closed`:this.counters.requiredFailure||this.counters.criticalRejected>0||this.counters.flushTimeouts>0?`failed`:this.counters.processorFailures>0||this.counters.exporterFailures>0||this.counters.droppedVerbose>0||this.counters.droppedNormal>0?`degraded`:`healthy`;return e({state:t,queuedEvents:this.queue.length,queuedBytes:this.queueBytes,accepted:this.counters.accepted,exported:this.counters.exported,droppedVerbose:this.counters.droppedVerbose,droppedNormal:this.counters.droppedNormal,criticalRejected:this.counters.criticalRejected,processorFailures:this.counters.processorFailures,exporterFailures:this.counters.exporterFailures,flushTimeouts:this.counters.flushTimeouts,...this.counters.lastExportAt===void 0?{}:{lastExportAt:this.counters.lastExportAt},...this.counters.lastFailure===void 0?{}:{lastFailure:this.counters.lastFailure}})}flush(e){if(this.closed||this.closing)throw TypeError(`observability is closed`);return this.scheduleFlush(new Set(this.queue.map(e=>e.event.eventId)),this.options.flushTimeoutMs,e).then(C)}shutdown(e){if(this.shutdownPromise!==void 0)return this.shutdownPromise;this.closing=!0;let t=new Set(this.queue.map(e=>e.event.eventId));return this.shutdownPromise=(async()=>{let n=b(this.options.shutdownTimeoutMs,e),r;try{r=C(await this.scheduleFlush(t,this.options.shutdownTimeoutMs,n.signal));for(let e of[...this.registrations].reverse()){let t=e.exporter.shutdown;if(t!==void 0)try{await x(Promise.resolve(t.call(e.exporter,n.signal)),n.signal)}catch(t){this.recordExporterFailure(e,t,!1),r=Object.freeze({...r,complete:!1})}}}finally{n.clear(),this.closed=!0,this.notifyHealth()}return r})(),this.shutdownPromise}prepareEvent(e,t){let n={content:this.options.content,redactors:this.options.redactors??[],includeErrorStacks:this.options.includeErrorStacks},r=o(e,n);if(t)return r;for(let e of this.processors){let t=e.transform(r);if(t===void 0)throw TypeError(`observation processor ${e.id} returned undefined`);let i=o(t,n);if(!w(r,i))throw TypeError(`observation processor ${e.id} changed the immutable event envelope`);r=i}return r}enqueue(e,t){let n=s(e);return n>this.options.maxBatchBytes||n>this.options.maxQueueBytes||!this.makeCapacity(n,t)?(!t&&e.priority===`critical`&&this.counters.criticalRejected++,t||(this.counters.lastFailure=h(RangeError(`capacity`),`observation queue capacity exhausted`),this.notifyHealth()),g(e.eventId,`rejected`,`none`,`capacity`)):(this.queue.push({event:e,bytes:n,protected:t,pending:new Set(this.registrations.map(e=>e.exporter.id))}),this.queueBytes+=n,this.counters.accepted++,this.notifyHealth(),g(e.eventId,`accepted`))}stageExporters(e,t){let n=this.queue.find(t=>t.event.eventId===e.eventId);if(n===void 0)return g(e.eventId,`rejected`,`none`,`exporter-unavailable`);let r=!1;for(let t of this.registrations){let i=t.exporter.stage;if(i!==void 0)try{let n=i.call(t.exporter,e);n!==void 0&&Promise.resolve(n).catch(()=>void 0)}catch(i){if(this.recordExporterFailure(t,i,!1),n.pending.delete(t.exporter.id),r=!0,t.requirement===`required`)return this.removeEntry(n,!1),e.priority===`critical`&&this.counters.criticalRejected++,this.notifyHealth(),g(e.eventId,`rejected`,`none`,`exporter-unavailable`)}}return r&&n.pending.size===0&&this.removeEntry(n,!1),t}makeCapacity(e,t){for(;this.queue.length+1>this.options.maxQueueEvents||this.queueBytes+e>this.options.maxQueueBytes;){let e=this.queue.findIndex(e=>e.event.priority===`verbose`);if(e===-1&&(e=this.queue.findIndex(e=>e.event.priority===`normal`)),e===-1)return!1;let[n]=this.queue.splice(e,1);if(n===void 0)return!1;this.queueBytes-=n.bytes,t||(n.event.priority===`verbose`?this.counters.droppedVerbose++:this.counters.droppedNormal++)}return!0}scheduleFlush(e,t,n){let r,i,a=new Promise((e,t)=>{r=e,i=t}),o=async()=>{try{r(await this.flushInternal(e,t,n))}catch(e){i(e)}};return this.flushChain=this.flushChain.then(o,o),a}async flushInternal(t,r,i){let a=this.counters.exported,o=b(r,i),s=!1,c=!1;try{if(this.registrations.length===0)for(let e of[...this.queue])t.has(e.event.eventId)&&this.removeEntry(e,!1);for(let r of this.registrations)for(;;){let i=this.queue.filter(e=>t.has(e.event.eventId)&&e.pending.has(r.exporter.id));if(i.length===0)break;let a=[],l=0;for(let e of i){if(a.length>=this.options.maxBatchEvents||l+e.bytes>this.options.maxBatchBytes)break;a.push(e),l+=e.bytes}if(a.length===0)break;let u=e({schemaVersion:1,batchId:n(),createdAt:new Date().toISOString(),events:a.map(e=>e.event)}),d;try{if(d=await x(Promise.resolve(r.exporter.export(u,o.signal)),o.signal),d.batchId!==u.batchId||d.accepted!==!0)throw TypeError(`observation exporter ${r.exporter.id} returned an invalid acknowledgment`)}catch(e){if(s=!0,c||=S(o.signal),this.recordExporterFailure(r,e,!0),r.requirement===`best-effort`)for(let e of a)e.pending.delete(r.exporter.id),e.pending.size===0&&this.removeEntry(e,!1);break}this.counters.lastExportAt=new Date().toISOString();for(let e of a)e.pending.delete(r.exporter.id),e.pending.size===0&&this.removeEntry(e,!0);this.notifyHealth()}}finally{S(o.signal)&&(c=!0,this.counters.flushTimeouts++,this.counters.lastFailure=h(o.signal.reason,`observation flush timed out`),this.notifyHealth()),o.clear()}let l=this.queue.filter(e=>t.has(e.event.eventId)),u=this.registrations.filter(e=>e.requirement===`required`).map(e=>e.exporter.id),d=l.every(e=>u.every(t=>!e.pending.has(t)));return Object.freeze({complete:!s&&!c&&l.length===0,requiredComplete:d,exportedEvents:this.counters.exported-a,pendingEvents:l.length,rejectedCritical:this.counters.criticalRejected,timedOut:c})}removeEntry(e,t){let n=this.queue.indexOf(e);n!==-1&&(this.queue.splice(n,1),this.queueBytes-=e.bytes,t&&this.counters.exported++)}recordExporterFailure(e,t,n){this.counters.exporterFailures++,e.requirement===`required`&&(this.counters.requiredFailure=!0),this.counters.lastFailure=h(t,`observation exporter failed`),this.notifyHealth(),n&&this.recordFailure(`exporter:${e.exporter.id}`,t,`observation exporter failed`,!0)}recordFailure(e,t,r,i){if(this.counters.lastFailure=h(t,r),this.notifyHealth(),!(!i||this.protectedFailures.has(e)||this.closing||this.closed)){this.protectedFailures.add(e);try{let t=this.prepareEvent({schemaVersion:1,eventId:n(),sequence:this.healthScope.nextSequence(),name:`sdk.observer.failure`,phase:`point`,occurredAt:new Date().toISOString(),monotonicMs:this.healthScope.monotonicMs(),priority:`critical`,resource:this.resource,correlation:this.healthCorrelation,data:{observerId:e,failureKind:`contained`,count:1,error:{...this.counters.lastFailure}}},!0);this.enqueue(t,!0)}catch{}}}notifyHealth(){let e=this.options.onHealthChange;if(e!==void 0)try{e(this.health())}catch{}}};function E(e={}){return new T(e)}export{E as createObservability};
|
|
2
2
|
//# sourceMappingURL=bus.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/primitives/version.d.ts
|
|
2
2
|
/** Package version injected as a Web-standard literal; release tooling updates this file. */
|
|
3
|
-
declare const SDK_VERSION = "0.1.
|
|
3
|
+
declare const SDK_VERSION = "0.1.8";
|
|
4
4
|
//#endregion
|
|
5
5
|
export { SDK_VERSION };
|
|
6
6
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=`0.1.
|
|
1
|
+
const e=`0.1.8`;export{e as SDK_VERSION};
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","names":[],"sources":["../../src/primitives/version.ts"],"sourcesContent":["/** Package version injected as a Web-standard literal; release tooling updates this file. */\nexport const SDK_VERSION = '0.1.
|
|
1
|
+
{"version":3,"file":"version.js","names":[],"sources":["../../src/primitives/version.ts"],"sourcesContent":["/** Package version injected as a Web-standard literal; release tooling updates this file. */\nexport const SDK_VERSION = '0.1.8'\n"],"mappings":"AACA,MAAa,EAAc"}
|
package/dist/runtime/registry.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{deepFreeze as e}from"../primitives/freeze.js";import"../primitives/version.js";import{PLUGIN_ERROR_CODES as t,PluginError as n}from"../plugin/provider-plugin.js";import{ModelError as r,REGISTRY_ERROR_CODES as i}from"../errors/model-error.js";import{callConfigEquals as a}from"../contract/call-config.js";import{resolveRetryPolicy as o}from"../contract/retry-policy.js";import{createModelCallHandle as s}from"./model-call-handle.js";import{normalizeResolvedModelInfo as c,resolveCallWithModelInfo as l,validateCatalogModels as u,validateModelCatalogSnapshot as d}from"./model-metadata.js";import{streamAdapter as f}from"./model-stream.js";function p(e){if((typeof e!=`object`||!e)&&typeof e!=`function`)return!1;let t;try{t=Reflect.get(e,`then`)}catch{return!0}if(typeof t!=`function`)return!1;try{Promise.resolve(e).catch(()=>{})}catch{}return!0}function m(e){try{return p(e())?TypeError(`plugin cleanup must be synchronous`):void 0}catch(e){return e}}var h=class{adapters=new Map;middleware=[];listeners=new Set;plugins=new Map;maxCatalogModels;maxCatalogBytes;observation;observationResource;defaults;constructor(t={}){this.maxCatalogModels=g(t.maxCatalogModels??2048,`maxCatalogModels`),this.maxCatalogBytes=g(t.maxCatalogBytes??4194304,`maxCatalogBytes`),this.observation=t.observation,this.defaults=_(t.defaults??{}),this.observationResource=e(t.observationResource??{sdkName:`ai-agent-sdk`,sdkVersion:`0.1.
|
|
1
|
+
import{deepFreeze as e}from"../primitives/freeze.js";import"../primitives/version.js";import{PLUGIN_ERROR_CODES as t,PluginError as n}from"../plugin/provider-plugin.js";import{ModelError as r,REGISTRY_ERROR_CODES as i}from"../errors/model-error.js";import{callConfigEquals as a}from"../contract/call-config.js";import{resolveRetryPolicy as o}from"../contract/retry-policy.js";import{createModelCallHandle as s}from"./model-call-handle.js";import{normalizeResolvedModelInfo as c,resolveCallWithModelInfo as l,validateCatalogModels as u,validateModelCatalogSnapshot as d}from"./model-metadata.js";import{streamAdapter as f}from"./model-stream.js";function p(e){if((typeof e!=`object`||!e)&&typeof e!=`function`)return!1;let t;try{t=Reflect.get(e,`then`)}catch{return!0}if(typeof t!=`function`)return!1;try{Promise.resolve(e).catch(()=>{})}catch{}return!0}function m(e){try{return p(e())?TypeError(`plugin cleanup must be synchronous`):void 0}catch(e){return e}}var h=class{adapters=new Map;middleware=[];listeners=new Set;plugins=new Map;maxCatalogModels;maxCatalogBytes;observation;observationResource;defaults;constructor(t={}){this.maxCatalogModels=g(t.maxCatalogModels??2048,`maxCatalogModels`),this.maxCatalogBytes=g(t.maxCatalogBytes??4194304,`maxCatalogBytes`),this.observation=t.observation,this.defaults=_(t.defaults??{}),this.observationResource=e(t.observationResource??{sdkName:`ai-agent-sdk`,sdkVersion:`0.1.8`,runtime:`unknown`})}install(e){let a=typeof e?.id==`string`?e.id:``,o=(e,r,i=[])=>{let o=[...r===void 0?[]:[r],...i],s=o.length<=1?o[0]:AggregateError(o,`plugin ${a} install cleanup failed`);throw new n(e,t.INSTALL_FAILED,a,s===void 0?void 0:{cause:s})};(a.trim().length===0||a!==a.trim())&&o(`plugin id must be a non-empty trimmed string`),this.plugins.has(a)&&o(`plugin "${a}" is already installed`),(typeof e.displayName!=`string`||e.displayName.trim().length===0)&&o(`plugin "${a}" displayName must be non-empty`),typeof e.setup!=`function`&&o(`plugin "${a}" setup must be a function`);let s=e.family??a;(typeof s!=`string`||s.trim().length===0||s!==s.trim())&&o(`plugin "${a}" family must be a non-empty trimmed string`);let c=Object.freeze({pluginId:a,family:s}),l=[],u=[],d=!0,f={registerAdapter:(e,t)=>{if(d||o(`plugin "${a}" staging registrar is closed`),e.length===0)throw new r(`an adapter must register at least one provider route`,i.INVALID_ADAPTER);let n={active:!0,routes:[...e],adapter:t},s=new Set(l.filter(e=>e.active).flatMap(e=>e.routes));for(let t of e)if(s.has(t))throw new r(`an adapter for provider route "${t}" is already staged`,i.DUPLICATE_ADAPTER);this.prepareRoutes(e,t,new Set,c),l.push(n);let u=(()=>{d&&(n.active=!1)});return u.replace=e=>{if(!d||!n.active)throw new r(`a disposed staged registration cannot replace routes`,i.REGISTRATION_DISPOSED);let a=new Set(l.filter(e=>e.active&&e!==n).flatMap(e=>e.routes));for(let t of e)if(a.has(t))throw new r(`an adapter for provider route "${t}" is already staged`,i.DUPLICATE_ADAPTER);this.prepareRoutes(e,t,new Set,c),n.routes=[...e]},u},use:e=>{if(d||o(`plugin "${a}" staging registrar is closed`),typeof e!=`function`)throw TypeError(`plugin middleware must be a function`);let t={active:!0,middleware:e};return u.push(t),()=>{d&&(t.active=!1)}}},h;try{let t=e.setup(f);p(t)&&o(`plugin "${a}" setup must be synchronous`),t!==void 0&&typeof t!=`function`&&o(`plugin "${a}" setup must be synchronous and return void or cleanup`),h=typeof t==`function`?t:void 0}catch(e){if(e instanceof n&&e.code===t.INSTALL_FAILED)throw e;o(`plugin "${a}" setup failed`,e)}finally{d=!1}let g=[],_=new Set;try{for(let e of l)if(e.active){for(let t of e.routes){if(_.has(t))throw new r(`an adapter for provider route "${t}" is already staged`,i.DUPLICATE_ADAPTER);_.add(t)}g.push(...this.prepareRoutes(e.routes,e.adapter,new Set,c))}}catch(e){let t=[];if(h){let e=m(h);e!==void 0&&t.push(e)}o(`plugin "${a}" commit validation failed`,e,t)}let v=u.filter(e=>e.active).map(e=>e.middleware);for(let e of g)this.adapters.set(e.provider.id,e);this.middleware.push(...v);let y={routes:_,registrations:g,middleware:v,...h===void 0?{}:{cleanup:h}};this.plugins.set(a,y),this.emitAdaptersUpdated();let b=!1,x=(()=>{if(b)return;b=!0,this.plugins.delete(a);for(let e of y.registrations)this.adapters.get(e.provider.id)===e&&this.adapters.delete(e.provider.id);for(let e=y.middleware.length-1;e>=0;e--){let t=y.middleware[e],n=t===void 0?-1:this.middleware.lastIndexOf(t);n>=0&&this.middleware.splice(n,1)}this.emitAdaptersUpdated();let e=[];if(y.cleanup){let t=m(y.cleanup);t!==void 0&&e.push(t)}if(e.length>0)throw new n(`plugin "${a}" cleanup failed`,t.CLEANUP_FAILED,a,{cause:AggregateError(e,`plugin "${a}" cleanup failed`)})});return Object.defineProperty(x,"pluginId",{value:a,enumerable:!0}),x}registerAdapter(e,t){if(e.length===0)throw new r(`an adapter must register at least one provider route`,i.INVALID_ADAPTER);let n=new Set,a=!1;this.commitRoutes(n,this.prepareRoutes(e,t,n));let o=(()=>{if(!a){a=!0;for(let e of n)this.adapters.delete(e);n.clear(),this.emitAdaptersUpdated()}});return o.replace=e=>{if(a)throw new r(`a disposed adapter registration cannot replace its routes`,i.REGISTRATION_DISPOSED);this.commitRoutes(n,this.prepareRoutes(e,t,n))},o}prepareRoutes(e,t,n,a){let s=new Set,c=[];for(let l of e){if(typeof l!=`string`||l.length===0)throw new r(`adapter provider route names must be non-empty strings`,i.INVALID_ADAPTER);if(s.has(l)||this.adapters.has(l)&&!n.has(l))throw new r(`an adapter for provider route "${l}" is already registered`,i.DUPLICATE_ADAPTER);let e=t.providerInfo(l);if(typeof e.id!=`string`||e.id!==l||typeof e.name!=`string`||e.name.length===0)throw new r(`adapter metadata for route "${l}" must preserve its id and carry a non-empty name`,i.INVALID_ADAPTER);s.add(l),c.push({adapter:t,provider:{id:e.id,name:e.name},...a===void 0?{}:a,retryPolicy:t.providerRetryPolicy(l)??o(void 0,`provider "${l}" retryPolicy`)})}return c}commitRoutes(e,t){for(let t of e)this.adapters.delete(t);e.clear();for(let n of t)this.adapters.set(n.provider.id,n),e.add(n.provider.id);this.emitAdaptersUpdated()}use(e){return this.middleware.push(e),()=>{let t=this.middleware.indexOf(e);t>=0&&this.middleware.splice(t,1)}}onAdaptersUpdated(e){return this.listeners.add(e),()=>void this.listeners.delete(e)}emitAdaptersUpdated(){for(let e of[...this.listeners])try{e()}catch{}}listProviders(){return[...this.adapters.values()].map(({provider:e})=>({...e}))}async listModels(e,t){let n=await this.registration(e).adapter.listModels(e,t);return u(e,n,this.maxCatalogModels,this.maxCatalogBytes)}async modelCatalog(e,t={}){let n=await this.registration(e).adapter.modelCatalog(e,t);return d(e,n,this.maxCatalogModels,this.maxCatalogBytes)}async resolveModelInfo(e,t,n){let r=this.registration(e),i=await r.adapter.resolveModel(e,t,n);return c(r.provider.id,t,i,this.maxCatalogBytes,this.defaults)}retryPolicy(e){return this.registration(e).retryPolicy}async prepareCall(t,n,o){let s=this.registration(t.provider),u=await s.adapter.prepareCall(t.provider,t.model,n,o),d=c(s.provider.id,t.model,u.model,this.maxCatalogBytes,this.defaults),f=l(t,d,this.defaults),p=e(structuredClone(f.config)),m=f.context===void 0?void 0:e(structuredClone(f.context)),h=e({...t.maxTokens===void 0&&p.maxTokens!==void 0?{maxTokens:!0}:{}}),g=!1,_=e(structuredClone(d));return Object.freeze({config:p,model:_,retryPolicy:s.retryPolicy,adapterDefaults:h,...m===void 0?{}:{context:m},...f.inputModalities===void 0?{}:{inputModalities:Object.freeze([...f.inputModalities])},stream:(e,t=o)=>{if(g)throw new r(`a prepared call can only be dispatched once`,i.INVALID_PREPARED_CALL);if(!a(e,p))throw new r(`prepared call config changed before adapter dispatch`,i.INVALID_PREPARED_CALL);return g=!0,this.dispatch(e,t,{registration:s,config:p,modelInfo:d,dispatch:(e,t)=>u.stream(e,t)})}})}stream(e,t){return this.dispatch(e,t)}dispatch(e,t,n){let r=`not-sent`,i=n?.registration??this.adapters.get(e.provider);return s({options:e,...i?.family===void 0?{}:{providerFamily:i.family},...i?.pluginId===void 0?{}:{providerPluginId:i.pluginId},...i===void 0?{}:{isRetryable:e=>i.retryPolicy.mode===`always`||i.retryPolicy.retryableCodes.includes(e)},...t===void 0?{}:{context:t},...this.observation===void 0?{}:{defaultObservation:this.observation},resource:this.observationResource,routePresent:n!==void 0||this.adapters.has(e.provider),dispatchState:()=>r,stream:t=>this.dispatchRaw(e,t,()=>{r=`unknown`},n)})}dispatchRaw(e,t,n,r){let i=[...this.middleware],a=()=>{let a=()=>f({options:e,context:t,onDispatch:n,maxCatalogBytes:this.maxCatalogBytes,defaults:this.defaults,registration:e=>this.registration(e),registeredAdapter:e=>this.adapters.get(e)?.adapter,...r===void 0?{}:{prepared:r}});for(let n=i.length-1;n>=0;n--){let r=i[n];if(r===void 0)continue;let o=a;a=()=>r(e,o,t)}return a()};return{async*[Symbol.asyncIterator](){yield*a()}}}registration(e){let t=this.adapters.get(e);if(t===void 0)throw new r(`no adapter registered for provider route "${e}"`,i.NO_ADAPTER);return t}};function g(e,t){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`ModelRegistry ${t} must be a positive safe integer`);return e}function _(t){if(t.contextWindow!==void 0&&g(t.contextWindow,`defaults.contextWindow`),t.maxTokens!==void 0&&g(t.maxTokens,`defaults.maxTokens`),t.inputModalities!==void 0&&(t.inputModalities.length===0||new Set(t.inputModalities).size!==t.inputModalities.length))throw RangeError(`ModelRegistry defaults.inputModalities must be non-empty and unique`);return e({...t.contextWindow===void 0?{}:{contextWindow:t.contextWindow},...t.maxTokens===void 0?{}:{maxTokens:t.maxTokens},...t.inputModalities===void 0?{}:{inputModalities:[...t.inputModalities]}})}export{h as ModelRegistry};
|
|
2
2
|
//# sourceMappingURL=registry.js.map
|
package/package.json
CHANGED