@roll-agent/runtime 0.2.0 → 0.3.0

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.
@@ -1,5 +1,5 @@
1
1
  import { type ModelMessage } from "ai";
2
- import type { LanguageModelV3, SharedV3ProviderOptions } from "@ai-sdk/provider";
2
+ import type { LanguageModelV4, SharedV4ProviderOptions } from "@ai-sdk/provider";
3
3
  import type { ContextCompactionReason, ContextCompactionStrategy, SessionEvent, SessionTokenUsage } from "../types/events.ts";
4
4
  import type { ToolPolicy } from "../types/policy.ts";
5
5
  import { type AgentToolSource } from "../tool-bridge/build-tools.ts";
@@ -12,7 +12,7 @@ export interface SessionCompactionSettings {
12
12
  }
13
13
  export interface AgentSessionOptions {
14
14
  readonly id: string;
15
- readonly model: LanguageModelV3;
15
+ readonly model: LanguageModelV4;
16
16
  readonly sources: readonly AgentToolSource[];
17
17
  readonly maxSteps: number;
18
18
  readonly policy?: ToolPolicy;
@@ -22,7 +22,7 @@ export interface AgentSessionOptions {
22
22
  readonly contextWindow?: number;
23
23
  readonly compaction?: SessionCompactionSettings;
24
24
  readonly turnTimeoutMs?: number;
25
- readonly providerOptions?: SharedV3ProviderOptions;
25
+ readonly providerOptions?: SharedV4ProviderOptions;
26
26
  readonly debugEvents?: boolean;
27
27
  }
28
28
  export declare class AgentSession {
@@ -55,7 +55,7 @@ export declare class AgentSession {
55
55
  getMessages(): readonly ModelMessage[];
56
56
  getContextWindow(): number | undefined;
57
57
  getSessionUsage(): SessionTokenUsage;
58
- setProviderOptions(providerOptions: SharedV3ProviderOptions | undefined): void;
58
+ setProviderOptions(providerOptions: SharedV4ProviderOptions | undefined): void;
59
59
  private shouldAutoCompact;
60
60
  private runCompactionTurn;
61
61
  private runCompaction;
@@ -1 +1 @@
1
- import{randomUUID as e}from"node:crypto";import{stepCountIs as t,streamText as s}from"ai";import{buildAgentToolset as o}from"../tool-bridge/build-tools.js";import{readIsError as n}from"../tool-bridge/normalize-result.js";import{ApprovalGate as i}from"../approval/approval-gate.js";import{compactMessages as a}from"./compactor.js";import{AsyncEventQueue as r}from"./event-queue.js";const l="你是 Roll chat 的运行时助手。你可以使用模型的 thinking/reasoning 能力完成内部推理,但必须把给用户看的最终回复写入普通 text 输出通道;不要只在 reasoning 中写最终答案。 工具调用完成后,也要在普通 text 输出通道给出简洁结论。最终回复不要重复。 不要复述用户输入;如果需要调用工具,直接调用工具,不要先输出用户原文或无意义前置文本。";function u(e){return e instanceof Error?e.message:String(e)}function p(e){return"object"==typeof e&&null!==e&&"output"in e}function h(e){return p(e)?u(e.output):u(e)}function c(e){const t=h(e);return t.startsWith("已取消执行")||t.startsWith("策略拒绝执行")?t:void 0}function g(e){return/context[_ -]?length|context window|maximum context|token limit|too many tokens|prompt is too long|input is too long/i.test(u(e))}function m(e){const t=e.inputTokenDetails?.cacheReadTokens,s=e.outputTokenDetails?.reasoningTokens;return{...void 0!==e.inputTokens?{inputTokens:e.inputTokens}:{},...void 0!==e.outputTokens?{outputTokens:e.outputTokens}:{},...void 0!==e.totalTokens?{totalTokens:e.totalTokens}:{},...void 0!==t?{cachedInputTokens:t}:{},...void 0!==s?{reasoningTokens:s}:{}}}function d(e,t){return void 0===e&&void 0===t?void 0:(e??0)+(t??0)}function v(e,t){const s=d(e.cachedInputTokens,t.cachedInputTokens),o=d(e.reasoningTokens,t.reasoningTokens);return{inputTokens:(e.inputTokens??0)+(t.inputTokens??0),outputTokens:(e.outputTokens??0)+(t.outputTokens??0),totalTokens:(e.totalTokens??0)+(t.totalTokens??0),...void 0!==s?{cachedInputTokens:s}:{},...void 0!==o?{reasoningTokens:o}:{}}}function T(e,t){return void 0===t?e:void 0===e?t:Math.max(e,t)}function b(e,t){const s=e.trim(),o=t.trim();return s.length>0&&o.length>0&&o.startsWith(s)}function k(e){return e.flatMap(e=>{if("assistant"!==e.role||!Array.isArray(e.content))return[e];const t=e.content.filter(e=>"reasoning"!==e.type);return 0===t.length?[]:t.length===e.content.length?[e]:[{...e,content:t}]})}export class AgentSession{id;model;maxSteps;messages;onPersist;onReplace;contextWindow;compaction;turnTimeoutMs;providerOptions;debugEvents;gate=new i;tools;registry;emit;activeTurn;sessionUsage={inputTokens:0,outputTokens:0,totalTokens:0};lastInputTokens;needsCompaction=!1;constructor(e){this.id=e.id,this.model=e.model,this.maxSteps=e.maxSteps,this.messages=e.initialMessages?k(e.initialMessages):[],this.onPersist=e.onPersist,this.onReplace=e.onReplace,this.contextWindow=e.contextWindow,this.compaction=e.compaction,this.turnTimeoutMs=e.turnTimeoutMs,this.providerOptions=e.providerOptions,this.debugEvents=e.debugEvents??!1;const t=o(e.sources,{...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e)});this.tools=t.tools,this.registry=t.registry}async*send(e){if(this.activeTurn)throw new Error("session already has an active turn");const t=new r;this.emit=e=>t.push(e);const s={abortController:new AbortController,aborted:!1};this.activeTurn=s,this.runTurn(t,s,e).catch(e=>{t.push({type:"error",stage:"execute",message:u(e)}),t.close()});try{for await(const e of t)yield e}finally{this.activeTurn===s&&this.abort(),void 0!==this.emit&&(this.emit=void 0)}}async*compact(e="manual"){if(this.activeTurn)throw new Error("session already has an active turn");const t=new r;this.emit=e=>t.push(e);const s={abortController:new AbortController,aborted:!1};this.activeTurn=s,this.runCompactionTurn(t,s,e).catch(e=>{t.push({type:"error",stage:"plan",message:u(e)}),t.close()});try{for await(const e of t)yield e}finally{this.activeTurn===s&&this.abort(),void 0!==this.emit&&(this.emit=void 0)}}async runTurn(o,i,a){const r=Date.now();let p;try{if(this.debug(o,"turn","start",r,{messages:this.messages.length,tools:Object.keys(this.tools).length,maxSteps:this.maxSteps,...void 0!==this.contextWindow?{contextWindow:this.contextWindow}:{},...void 0!==this.lastInputTokens?{lastInputTokens:this.lastInputTokens}:{}}),this.shouldAutoCompact()){this.debug(o,"compaction","auto requested before turn",r,{messages:this.messages.length,...void 0!==this.lastInputTokens?{lastInputTokens:this.lastInputTokens}:{}});try{await this.runCompaction(o,"auto",i)}catch(e){return void o.push({type:"error",stage:"plan",message:u(e)})}}if(i.aborted||i.abortController.signal.aborted)return void o.push({type:"error",stage:"execute",message:"aborted"});p=this.messages.length,this.messages.push({role:"user",content:a}),o.push({type:"message-start",messageId:e()}),this.debug(o,"model","calling streamText",r,{messages:this.messages.length,tools:Object.keys(this.tools).length,...void 0!==this.turnTimeoutMs?{timeoutMs:this.turnTimeoutMs}:{}});const d=s({model:this.model,system:l,messages:this.messages,tools:this.tools,stopWhen:t(this.maxSteps),abortSignal:i.abortController.signal,...this.providerOptions?{providerOptions:this.providerOptions}:{},...void 0!==this.turnTimeoutMs?{timeout:{totalMs:this.turnTimeoutMs}}:{}});this.debug(o,"model","streamText returned",r);let y,f,x,w,C,I="",N="",M=!1,R=0,A=!1;const S=this.scheduleDebug(o,"model","waiting for first stream event",r,{messages:this.messages.length});try{for await(const e of d.fullStream){switch(A||(A=!0,this.clearDebugTimer(S),this.debug(o,"model","first stream event",r,{part:e.type})),e.type){case"text-delta":if(!M){const t=N+e.text;if(b(t,a)){N=t;break}if(N.length>0){const t=N+e.text;N="",I+=t,o.push({type:"text-delta",delta:t});break}}I+=e.text,o.push({type:"text-delta",delta:e.text});break;case"tool-call":{M=!0,N="";const t=this.registry.resolve(e.toolName);o.push({type:"tool-call",toolCallId:e.toolCallId,agentName:t?.agentName??e.toolName,toolName:t?.toolName??e.toolName,input:e.input});break}case"tool-result":{const t=this.registry.resolve(e.toolName),s=n(e.output);o.push({type:"tool-result",toolCallId:e.toolCallId,agentName:t?.agentName??e.toolName,toolName:t?.toolName??e.toolName,output:e.output,isError:s}),i.aborted||i.abortController.signal.aborted||!s||(C=c(e.output));break}case"tool-error":{const t=this.registry.resolve(e.toolName),s=h(e.error);o.push({type:"tool-result",toolCallId:e.toolCallId,agentName:t?.agentName??e.toolName,toolName:t?.toolName??e.toolName,output:s,isError:!0}),i.aborted||i.abortController.signal.aborted||(C=c(e.error));break}case"finish-step":{const t=m(e.usage);f=T(f,t.inputTokens),R+=1,x=e.finishReason,o.push({type:"step-finish",finishReason:e.finishReason,usage:t});break}case"finish":!M&&N.length>0&&(I+=N,o.push({type:"text-delta",delta:N}),N=""),y=m(e.totalUsage);break;case"error":g(e.error)&&(this.needsCompaction=!0),w=u(e.error),o.push({type:"error",stage:"execute",message:w});break;case"abort":i.aborted=!0}if(void 0!==C)break}}finally{this.clearDebugTimer(S)}if(this.debug(o,"model","fullStream finished",r,{textChars:I.length}),void 0!==w)return this.messages.splice(p),void(this.needsCompaction&&await this.recoverFromContextError(o,i));if(i.aborted||i.abortController.signal.aborted)return this.messages.splice(p),void o.push({type:"error",stage:"execute",message:"aborted"});if(void 0!==C){const e=C,t=0===I.length?e:`\n${e}`;o.push({type:"text-delta",delta:t}),this.messages.push({role:"assistant",content:e}),this.debug(o,"persist","persisting messages",r,{appendedMessages:this.messages.length-p}),this.onPersist?.(this.messages.slice(p)),this.debug(o,"persist","messages persisted",r,{totalMessages:this.messages.length}),y&&(this.sessionUsage=v(this.sessionUsage,y));const s=f??y?.inputTokens;return void 0!==s&&(this.lastInputTokens=s),void o.push({type:"message-finish",text:e,...y?{totalUsage:y}:{},sessionUsage:{...this.sessionUsage},...void 0!==f?{contextInputTokens:f}:{}})}let E;this.debug(o,"model","awaiting response messages",r);const U=this.scheduleDebug(o,"model","still awaiting response messages",r);try{E=(await d.response).messages}catch(e){return this.clearDebugTimer(U),g(e)&&(this.needsCompaction=!0),this.messages.splice(p),o.push({type:"error",stage:"execute",message:u(e)}),void(this.needsCompaction&&await this.recoverFromContextError(o,i))}finally{this.clearDebugTimer(U)}this.debug(o,"model","response messages ready",r,{responseMessages:E.length});const W=k(E);this.messages.push(...W),this.debug(o,"persist","persisting messages",r,{appendedMessages:this.messages.length-p}),this.onPersist?.(this.messages.slice(p)),this.debug(o,"persist","messages persisted",r,{totalMessages:this.messages.length}),y&&(this.sessionUsage=v(this.sessionUsage,y));const D=f??y?.inputTokens;void 0!==D&&(this.lastInputTokens=D);const O=R>=this.maxSteps&&"tool-calls"===x;o.push({type:"message-finish",text:I,...y?{totalUsage:y}:{},sessionUsage:{...this.sessionUsage},...void 0!==f?{contextInputTokens:f}:{},...O?{stoppedAtStepLimit:!0}:{}})}catch(e){void 0!==p&&this.messages.splice(p),g(e)&&(this.needsCompaction=!0),this.debug(o,"turn","error",r,{message:u(e)}),o.push({type:"error",stage:"execute",message:u(e)}),this.needsCompaction&&await this.recoverFromContextError(o,i)}finally{this.activeTurn===i&&(this.activeTurn=void 0),o.close()}}requestApproval(t){const s=e(),o=this.gate.request(s);return void 0===this.emit?(this.gate.resolve(s,{approved:!1,reason:"approval event could not be delivered"}),o):(this.emit({type:"confirmation-required",approvalId:s,agentName:t.agentName,toolName:t.toolName,input:t.input,...t.reason?{reason:t.reason}:{}}),o)}approve(e){return this.gate.resolve(e,{approved:!0})}reject(e,t){return this.gate.resolve(e,{approved:!1,...t?{reason:t}:{}})}getMessages(){return this.messages}getContextWindow(){return this.contextWindow}getSessionUsage(){return{...this.sessionUsage}}setProviderOptions(e){this.providerOptions=e}shouldAutoCompact(){const e=this.compaction;return void 0!==e&&e.enabled&&(this.needsCompaction||void 0!==this.contextWindow&&void 0!==this.lastInputTokens&&this.lastInputTokens/this.contextWindow>=e.threshold)}async runCompactionTurn(e,t,s){try{await this.runCompaction(e,s,t)}catch(t){e.push({type:"error",stage:"plan",message:u(t)})}finally{this.activeTurn===t&&(this.activeTurn=void 0),e.close()}}async runCompaction(e,t,s){const o=Date.now(),n=this.compaction,i=n?.strategy??"summarize";if(this.debug(e,"compaction","start",o,{reason:t,messages:this.messages.length}),e.push({type:"compaction-start",reason:t}),this.isTurnAborted(s))return void e.push({type:"error",stage:"plan",message:"aborted"});if(!n)return void e.push({type:"context-compacted",reason:t,strategy:i,removed:0,kept:this.messages.length});const r=this.lastInputTokens;let l=n.strategy;const p=s?.abortController.signal;let h;try{h=await a({messages:this.messages,strategy:l,keepRecentTurns:n.keepRecentTurns,keepRecentTokens:n.keepRecentTokens,model:this.model,...p?{abortSignal:p}:{}})}catch(t){if(this.isTurnAborted(s))return void e.push({type:"error",stage:"plan",message:"aborted"});if("summarize"!==l)throw t;this.debug(e,"compaction","summarize failed, fallback to truncate",o,{message:u(t)}),l="truncate",h=await a({messages:this.messages,strategy:l,keepRecentTurns:n.keepRecentTurns,keepRecentTokens:n.keepRecentTokens,model:this.model,...p?{abortSignal:p}:{}})}if(this.isTurnAborted(s))return void e.push({type:"error",stage:"plan",message:"aborted"});const c=h.removed>0||h.truncatedTools>0;c&&(this.onReplace?.(h.messages),this.messages.splice(0,this.messages.length,...h.messages),this.lastInputTokens=void 0,this.needsCompaction=!1),e.push({type:"context-compacted",reason:t,strategy:l,removed:h.removed,kept:h.kept,...h.truncatedTools>0?{truncatedTools:h.truncatedTools}:{},...void 0!==r?{beforeInputTokens:r}:{}}),this.debug(e,"compaction","finish",o,{reason:t,strategy:l,removed:h.removed,kept:h.kept,truncatedTools:h.truncatedTools,progressed:c})}async recoverFromContextError(e,t){if(this.compaction?.enabled)try{await this.runCompaction(e,"auto",t)}catch(t){e.push({type:"error",stage:"plan",message:u(t)})}}isTurnAborted(e){return!0===e?.aborted||!0===e?.abortController.signal.aborted}abort(){this.activeTurn&&(this.activeTurn.aborted=!0),this.gate.abortAll(),this.activeTurn?.abortController.abort()}debug(e,t,s,o,n){this.debugEvents&&e.push({type:"debug",stage:t,message:s,...void 0!==o?{elapsedMs:Date.now()-o}:{},...void 0!==n?{data:n}:{}})}scheduleDebug(e,t,s,o,n){if(this.debugEvents)return setTimeout(()=>{this.debug(e,t,s,o,n)},5e3)}clearDebugTimer(e){void 0!==e&&clearTimeout(e)}}
1
+ import{randomUUID as e}from"node:crypto";import{stepCountIs as t,streamText as s}from"ai";import{buildAgentToolset as o}from"../tool-bridge/build-tools.js";import{readIsError as n}from"../tool-bridge/normalize-result.js";import{ApprovalGate as i}from"../approval/approval-gate.js";import{compactMessages as r}from"./compactor.js";import{AsyncEventQueue as a}from"./event-queue.js";const u="你是 Roll chat 的运行时助手。你可以使用模型的 thinking/reasoning 能力完成内部推理,但必须把给用户看的最终回复写入普通 text 输出通道;不要只在 reasoning 中写最终答案。 工具调用完成后,也要在普通 text 输出通道给出简洁结论。最终回复不要重复。 不要复述用户输入;如果需要调用工具,直接调用工具,不要先输出用户原文或无意义前置文本。";function l(e){return e instanceof Error?e.message:String(e)}function p(e){return"object"==typeof e&&null!==e&&"output"in e}function h(e){return p(e)?l(e.output):l(e)}function c(e){const t=h(e);return t.startsWith("已取消执行")||t.startsWith("策略拒绝执行")?t:void 0}function d(e){return/context[_ -]?length|context window|maximum context|token limit|too many tokens|prompt is too long|input is too long/i.test(l(e))}function m(e){const t=e.inputTokenDetails?.cacheReadTokens,s=e.inputTokenDetails?.cacheWriteTokens,o=e.outputTokenDetails?.reasoningTokens;return{...void 0!==e.inputTokens?{inputTokens:e.inputTokens}:{},...void 0!==e.outputTokens?{outputTokens:e.outputTokens}:{},...void 0!==e.totalTokens?{totalTokens:e.totalTokens}:{},...void 0!==t?{cachedInputTokens:t}:{},...void 0!==s?{cacheWriteTokens:s}:{},...void 0!==o?{reasoningTokens:o}:{}}}function g(e,t){return void 0===e&&void 0===t?void 0:(e??0)+(t??0)}function v(e,t){const s=g(e.cachedInputTokens,t.cachedInputTokens),o=g(e.cacheWriteTokens,t.cacheWriteTokens),n=g(e.reasoningTokens,t.reasoningTokens);return{inputTokens:(e.inputTokens??0)+(t.inputTokens??0),outputTokens:(e.outputTokens??0)+(t.outputTokens??0),totalTokens:(e.totalTokens??0)+(t.totalTokens??0),...void 0!==s?{cachedInputTokens:s}:{},...void 0!==o?{cacheWriteTokens:o}:{},...void 0!==n?{reasoningTokens:n}:{}}}function T(e,t){return void 0===t?e:void 0===e?t:Math.max(e,t)}function b(e,t){const s=e.trim(),o=t.trim();return s.length>0&&o.length>0&&o.startsWith(s)}function k(e){return e.flatMap(e=>{if("assistant"!==e.role||!Array.isArray(e.content))return[e];const t=e.content.filter(e=>"reasoning"!==e.type);return 0===t.length?[]:t.length===e.content.length?[e]:[{...e,content:t}]})}export class AgentSession{id;model;maxSteps;messages;onPersist;onReplace;contextWindow;compaction;turnTimeoutMs;providerOptions;debugEvents;gate=new i;tools;registry;emit;activeTurn;sessionUsage={inputTokens:0,outputTokens:0,totalTokens:0};lastInputTokens;needsCompaction=!1;constructor(e){this.id=e.id,this.model=e.model,this.maxSteps=e.maxSteps,this.messages=e.initialMessages?k(e.initialMessages):[],this.onPersist=e.onPersist,this.onReplace=e.onReplace,this.contextWindow=e.contextWindow,this.compaction=e.compaction,this.turnTimeoutMs=e.turnTimeoutMs,this.providerOptions=e.providerOptions,this.debugEvents=e.debugEvents??!1;const t=o(e.sources,{...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e)});this.tools=t.tools,this.registry=t.registry}async*send(e){if(this.activeTurn)throw new Error("session already has an active turn");const t=new a;this.emit=e=>t.push(e);const s={abortController:new AbortController,aborted:!1};this.activeTurn=s,this.runTurn(t,s,e).catch(e=>{t.push({type:"error",stage:"execute",message:l(e)}),t.close()});try{for await(const e of t)yield e}finally{this.activeTurn===s&&this.abort(),void 0!==this.emit&&(this.emit=void 0)}}async*compact(e="manual"){if(this.activeTurn)throw new Error("session already has an active turn");const t=new a;this.emit=e=>t.push(e);const s={abortController:new AbortController,aborted:!1};this.activeTurn=s,this.runCompactionTurn(t,s,e).catch(e=>{t.push({type:"error",stage:"plan",message:l(e)}),t.close()});try{for await(const e of t)yield e}finally{this.activeTurn===s&&this.abort(),void 0!==this.emit&&(this.emit=void 0)}}async runTurn(o,i,r){const a=Date.now();let p;try{if(this.debug(o,"turn","start",a,{messages:this.messages.length,tools:Object.keys(this.tools).length,maxSteps:this.maxSteps,...void 0!==this.contextWindow?{contextWindow:this.contextWindow}:{},...void 0!==this.lastInputTokens?{lastInputTokens:this.lastInputTokens}:{}}),this.shouldAutoCompact()){this.debug(o,"compaction","auto requested before turn",a,{messages:this.messages.length,...void 0!==this.lastInputTokens?{lastInputTokens:this.lastInputTokens}:{}});try{await this.runCompaction(o,"auto",i)}catch(e){return void o.push({type:"error",stage:"plan",message:l(e)})}}if(i.aborted||i.abortController.signal.aborted)return void o.push({type:"error",stage:"execute",message:"aborted"});p=this.messages.length,this.messages.push({role:"user",content:r}),o.push({type:"message-start",messageId:e()}),this.debug(o,"model","calling streamText",a,{messages:this.messages.length,tools:Object.keys(this.tools).length,...void 0!==this.turnTimeoutMs?{timeoutMs:this.turnTimeoutMs}:{}});const g=s({model:this.model,system:u,messages:this.messages,tools:this.tools,stopWhen:t(this.maxSteps),abortSignal:i.abortController.signal,...this.providerOptions?{providerOptions:this.providerOptions}:{},...void 0!==this.turnTimeoutMs?{timeout:{totalMs:this.turnTimeoutMs}}:{}});this.debug(o,"model","streamText returned",a);let y,f,x,w,C,I,N="",M="",S=!1,W=0,R=!1;const A=this.scheduleDebug(o,"model","waiting for first stream event",a,{messages:this.messages.length});try{for await(const e of g.fullStream){switch(R||(R=!0,this.clearDebugTimer(A),this.debug(o,"model","first stream event",a,{part:e.type})),e.type){case"text-delta":if(!S){const t=M+e.text;if(b(t,r)){M=t;break}if(M.length>0){const t=M+e.text;M="",N+=t,o.push({type:"text-delta",delta:t});break}}N+=e.text,o.push({type:"text-delta",delta:e.text});break;case"tool-call":{S=!0,M="";const t=this.registry.resolve(e.toolName);o.push({type:"tool-call",toolCallId:e.toolCallId,agentName:t?.agentName??e.toolName,toolName:t?.toolName??e.toolName,input:e.input});break}case"tool-result":{const t=this.registry.resolve(e.toolName),s=n(e.output);o.push({type:"tool-result",toolCallId:e.toolCallId,agentName:t?.agentName??e.toolName,toolName:t?.toolName??e.toolName,output:e.output,isError:s}),i.aborted||i.abortController.signal.aborted||!s||(I=c(e.output));break}case"tool-error":{const t=this.registry.resolve(e.toolName),s=h(e.error);o.push({type:"tool-result",toolCallId:e.toolCallId,agentName:t?.agentName??e.toolName,toolName:t?.toolName??e.toolName,output:s,isError:!0}),i.aborted||i.abortController.signal.aborted||(I=c(e.error));break}case"finish-step":{const t=m(e.usage);f=T(f,t.inputTokens),W+=1,w=e.finishReason;const s=e.performance.outputTokensPerSecond??e.performance.effectiveOutputTokensPerSecond;void 0!==s&&Number.isFinite(s)&&(x=s),o.push({type:"step-finish",finishReason:e.finishReason,usage:t});break}case"finish":!S&&M.length>0&&(N+=M,o.push({type:"text-delta",delta:M}),M=""),y=m(e.totalUsage);break;case"error":d(e.error)&&(this.needsCompaction=!0),C=l(e.error),o.push({type:"error",stage:"execute",message:C});break;case"abort":i.aborted=!0}if(void 0!==I)break}}finally{this.clearDebugTimer(A)}if(this.debug(o,"model","fullStream finished",a,{textChars:N.length}),void 0!==C)return this.messages.splice(p),void(this.needsCompaction&&await this.recoverFromContextError(o,i));if(i.aborted||i.abortController.signal.aborted)return this.messages.splice(p),void o.push({type:"error",stage:"execute",message:"aborted"});if(void 0!==I){const e=I,t=0===N.length?e:`\n${e}`;o.push({type:"text-delta",delta:t}),this.messages.push({role:"assistant",content:e}),this.debug(o,"persist","persisting messages",a,{appendedMessages:this.messages.length-p}),this.onPersist?.(this.messages.slice(p)),this.debug(o,"persist","messages persisted",a,{totalMessages:this.messages.length}),y&&(this.sessionUsage=v(this.sessionUsage,y));const s=f??y?.inputTokens;return void 0!==s&&(this.lastInputTokens=s),void o.push({type:"message-finish",text:e,...y?{totalUsage:y}:{},sessionUsage:{...this.sessionUsage},...void 0!==f?{contextInputTokens:f}:{},...void 0!==x?{outputTokensPerSecond:x}:{}})}let D;this.debug(o,"model","awaiting response messages",a);const E=this.scheduleDebug(o,"model","still awaiting response messages",a);try{D=(await g.response).messages}catch(e){return this.clearDebugTimer(E),d(e)&&(this.needsCompaction=!0),this.messages.splice(p),o.push({type:"error",stage:"execute",message:l(e)}),void(this.needsCompaction&&await this.recoverFromContextError(o,i))}finally{this.clearDebugTimer(E)}this.debug(o,"model","response messages ready",a,{responseMessages:D.length});const U=k(D);this.messages.push(...U),this.debug(o,"persist","persisting messages",a,{appendedMessages:this.messages.length-p}),this.onPersist?.(this.messages.slice(p)),this.debug(o,"persist","messages persisted",a,{totalMessages:this.messages.length}),y&&(this.sessionUsage=v(this.sessionUsage,y));const O=f??y?.inputTokens;void 0!==O&&(this.lastInputTokens=O);const P=W>=this.maxSteps&&"tool-calls"===w;o.push({type:"message-finish",text:N,...y?{totalUsage:y}:{},sessionUsage:{...this.sessionUsage},...void 0!==f?{contextInputTokens:f}:{},...void 0!==x?{outputTokensPerSecond:x}:{},...P?{stoppedAtStepLimit:!0}:{}})}catch(e){void 0!==p&&this.messages.splice(p),d(e)&&(this.needsCompaction=!0),this.debug(o,"turn","error",a,{message:l(e)}),o.push({type:"error",stage:"execute",message:l(e)}),this.needsCompaction&&await this.recoverFromContextError(o,i)}finally{this.activeTurn===i&&(this.activeTurn=void 0),o.close()}}requestApproval(t){const s=e(),o=this.gate.request(s);return void 0===this.emit?(this.gate.resolve(s,{approved:!1,reason:"approval event could not be delivered"}),o):(this.emit({type:"confirmation-required",approvalId:s,agentName:t.agentName,toolName:t.toolName,input:t.input,...t.reason?{reason:t.reason}:{}}),o)}approve(e){return this.gate.resolve(e,{approved:!0})}reject(e,t){return this.gate.resolve(e,{approved:!1,...t?{reason:t}:{}})}getMessages(){return this.messages}getContextWindow(){return this.contextWindow}getSessionUsage(){return{...this.sessionUsage}}setProviderOptions(e){this.providerOptions=e}shouldAutoCompact(){const e=this.compaction;return void 0!==e&&e.enabled&&(this.needsCompaction||void 0!==this.contextWindow&&void 0!==this.lastInputTokens&&this.lastInputTokens/this.contextWindow>=e.threshold)}async runCompactionTurn(e,t,s){try{await this.runCompaction(e,s,t)}catch(t){e.push({type:"error",stage:"plan",message:l(t)})}finally{this.activeTurn===t&&(this.activeTurn=void 0),e.close()}}async runCompaction(e,t,s){const o=Date.now(),n=this.compaction,i=n?.strategy??"summarize";if(this.debug(e,"compaction","start",o,{reason:t,messages:this.messages.length}),e.push({type:"compaction-start",reason:t}),this.isTurnAborted(s))return void e.push({type:"error",stage:"plan",message:"aborted"});if(!n)return void e.push({type:"context-compacted",reason:t,strategy:i,removed:0,kept:this.messages.length});const a=this.lastInputTokens;let u=n.strategy;const p=s?.abortController.signal;let h;try{h=await r({messages:this.messages,strategy:u,keepRecentTurns:n.keepRecentTurns,keepRecentTokens:n.keepRecentTokens,model:this.model,...p?{abortSignal:p}:{}})}catch(t){if(this.isTurnAborted(s))return void e.push({type:"error",stage:"plan",message:"aborted"});if("summarize"!==u)throw t;this.debug(e,"compaction","summarize failed, fallback to truncate",o,{message:l(t)}),u="truncate",h=await r({messages:this.messages,strategy:u,keepRecentTurns:n.keepRecentTurns,keepRecentTokens:n.keepRecentTokens,model:this.model,...p?{abortSignal:p}:{}})}if(this.isTurnAborted(s))return void e.push({type:"error",stage:"plan",message:"aborted"});const c=h.removed>0||h.truncatedTools>0;c&&(this.onReplace?.(h.messages),this.messages.splice(0,this.messages.length,...h.messages),this.lastInputTokens=void 0,this.needsCompaction=!1),e.push({type:"context-compacted",reason:t,strategy:u,removed:h.removed,kept:h.kept,...h.truncatedTools>0?{truncatedTools:h.truncatedTools}:{},...void 0!==a?{beforeInputTokens:a}:{}}),this.debug(e,"compaction","finish",o,{reason:t,strategy:u,removed:h.removed,kept:h.kept,truncatedTools:h.truncatedTools,progressed:c})}async recoverFromContextError(e,t){if(this.compaction?.enabled)try{await this.runCompaction(e,"auto",t)}catch(t){e.push({type:"error",stage:"plan",message:l(t)})}}isTurnAborted(e){return!0===e?.aborted||!0===e?.abortController.signal.aborted}abort(){this.activeTurn&&(this.activeTurn.aborted=!0),this.gate.abortAll(),this.activeTurn?.abortController.abort()}debug(e,t,s,o,n){this.debugEvents&&e.push({type:"debug",stage:t,message:s,...void 0!==o?{elapsedMs:Date.now()-o}:{},...void 0!==n?{data:n}:{}})}scheduleDebug(e,t,s,o,n){if(this.debugEvents)return setTimeout(()=>{this.debug(e,t,s,o,n)},5e3)}clearDebugTimer(e){void 0!==e&&clearTimeout(e)}}
@@ -1,12 +1,12 @@
1
1
  import { type ModelMessage } from "ai";
2
- import type { LanguageModelV3 } from "@ai-sdk/provider";
2
+ import type { LanguageModelV4 } from "@ai-sdk/provider";
3
3
  import type { ContextCompactionStrategy } from "../types/events.ts";
4
4
  export interface CompactionInput {
5
5
  readonly messages: readonly ModelMessage[];
6
6
  readonly strategy: ContextCompactionStrategy;
7
7
  readonly keepRecentTurns: number;
8
8
  readonly keepRecentTokens: number;
9
- readonly model: LanguageModelV3;
9
+ readonly model: LanguageModelV4;
10
10
  readonly abortSignal?: AbortSignal;
11
11
  }
12
12
  export interface CompactionResult {
@@ -1 +1 @@
1
- const w=[{match:"claude-fable-5",window:1e6},{match:"claude-mythos-5",window:1e6},{match:"claude-opus-4-8",window:1e6},{match:"claude-sonnet-4-6",window:1e6},{match:"claude-haiku-4-5",window:2e5},{match:"claude",window:2e5},{match:"gpt-5.5",window:105e4},{match:"gpt-5.4-mini",window:4e5},{match:"gpt-5.4-nano",window:4e5},{match:"gpt-5.4",window:105e4},{match:"gpt-5-mini",window:4e5},{match:"gpt-5-nano",window:4e5},{match:"gpt-5",window:4e5},{match:"gpt-4.1",window:128e3},{match:"gpt-4o",window:128e3},{match:"gpt-4-turbo",window:128e3},{match:"o3",window:2e5},{match:"o1-mini",window:128e3},{match:"o1",window:2e5},{match:"deepseek-v4",window:1e6},{match:"deepseek-chat",window:1e6},{match:"deepseek-reasoner",window:1e6},{match:"deepseek",window:128e3},{match:"qwen-long",window:1e7},{match:"qwen3-coder",window:1e6},{match:"qwen3.7-plus",window:1e6},{match:"qwen3.7-max",window:1e6},{match:"qwen3.6-plus",window:1e6},{match:"qwen3.5-plus",window:1e6},{match:"qwen3.5-flash",window:1e6},{match:"qwen-plus",window:1e6},{match:"qwen-flash",window:1e6},{match:"qwen3-max",window:262144},{match:"qwen3-vl-plus",window:262144},{match:"qwen-vl-plus",window:131072},{match:"qwen",window:131072},{match:"gemini",window:1e6},{match:"kimi",window:128e3},{match:"glm",window:128e3}];export function lookupContextWindow(e){const n=e.toLowerCase();return w.find(w=>n.includes(w.match))?.window}export function resolveContextWindow(w,e){return void 0!==e?e:lookupContextWindow(w)}
1
+ const w=[{match:"claude-fable-5",window:1e6},{match:"claude-mythos-5",window:1e6},{match:"claude-opus-4-8",window:1e6},{match:"claude-opus-4-7",window:1e6},{match:"claude-opus-4-6",window:1e6},{match:"claude-sonnet-5",window:1e6},{match:"claude-sonnet-4-6",window:1e6},{match:"claude-haiku-4-5",window:2e5},{match:"claude",window:2e5},{match:"gpt-5.5",window:105e4},{match:"gpt-5.4-mini",window:4e5},{match:"gpt-5.4-nano",window:4e5},{match:"gpt-5.4",window:105e4},{match:"gpt-5-mini",window:4e5},{match:"gpt-5-nano",window:4e5},{match:"gpt-5",window:4e5},{match:"gpt-4.1",window:128e3},{match:"gpt-4o",window:128e3},{match:"gpt-4-turbo",window:128e3},{match:"o3",window:2e5},{match:"o1-mini",window:128e3},{match:"o1",window:2e5},{match:"deepseek-v4",window:1e6},{match:"deepseek-chat",window:1e6},{match:"deepseek-reasoner",window:1e6},{match:"deepseek",window:128e3},{match:"qwen-long",window:1e7},{match:"qwen3-coder",window:1e6},{match:"qwen3.7-plus",window:1e6},{match:"qwen3.7-max",window:1e6},{match:"qwen3.6-plus",window:1e6},{match:"qwen3.5-plus",window:1e6},{match:"qwen3.5-flash",window:1e6},{match:"qwen-plus",window:1e6},{match:"qwen-flash",window:1e6},{match:"qwen3-max",window:262144},{match:"qwen3-vl-plus",window:262144},{match:"qwen-vl-plus",window:131072},{match:"qwen",window:131072},{match:"gemini",window:1e6},{match:"kimi",window:128e3},{match:"glm",window:128e3}];export function lookupContextWindow(e){const n=e.toLowerCase();return w.find(w=>n.includes(w.match))?.window}export function resolveContextWindow(w,e){return void 0!==e?e:lookupContextWindow(w)}
@@ -1,4 +1,4 @@
1
- import type { LanguageModelV3, SharedV3ProviderOptions } from "@ai-sdk/provider";
1
+ import type { LanguageModelV4, SharedV4ProviderOptions } from "@ai-sdk/provider";
2
2
  import { McpClientManager } from "@roll-agent/core/mcp/client-manager";
3
3
  import type { RollConfig } from "@roll-agent/core/config/schema";
4
4
  import type { RegisteredAgent } from "@roll-agent/core/types/agent";
@@ -10,13 +10,13 @@ export type EnsureAgentReady = (agent: RegisteredAgent, env: Readonly<Record<str
10
10
  export interface ConversationEngineOptions {
11
11
  readonly config: RollConfig;
12
12
  readonly agents?: readonly RegisteredAgent[];
13
- readonly model?: LanguageModelV3;
13
+ readonly model?: LanguageModelV4;
14
14
  readonly sources?: readonly AgentToolSource[];
15
15
  readonly clientManager?: McpClientManager;
16
16
  readonly store?: ThreadStore;
17
17
  readonly policy?: ToolPolicy;
18
18
  readonly maxSteps?: number;
19
- readonly providerOptions?: SharedV3ProviderOptions;
19
+ readonly providerOptions?: SharedV4ProviderOptions;
20
20
  readonly ensureAgentReady?: EnsureAgentReady;
21
21
  readonly debugEvents?: boolean;
22
22
  readonly onAgentBootstrapIssue?: (issue: AgentBootstrapIssue) => void;
@@ -70,23 +70,23 @@ export declare const approveParamsSchema: z.ZodObject<{
70
70
  sessionId: z.ZodString;
71
71
  approvalId: z.ZodString;
72
72
  }, "strip", z.ZodTypeAny, {
73
- approvalId: string;
74
73
  sessionId: string;
75
- }, {
76
74
  approvalId: string;
75
+ }, {
77
76
  sessionId: string;
77
+ approvalId: string;
78
78
  }>;
79
79
  export declare const rejectParamsSchema: z.ZodObject<{
80
80
  sessionId: z.ZodString;
81
81
  approvalId: z.ZodString;
82
82
  reason: z.ZodOptional<z.ZodString>;
83
83
  }, "strip", z.ZodTypeAny, {
84
- approvalId: string;
85
84
  sessionId: string;
85
+ approvalId: string;
86
86
  reason?: string | undefined;
87
87
  }, {
88
- approvalId: string;
89
88
  sessionId: string;
89
+ approvalId: string;
90
90
  reason?: string | undefined;
91
91
  }>;
92
92
  export declare const abortParamsSchema: z.ZodObject<{
@@ -3,6 +3,7 @@ export interface SessionTokenUsage {
3
3
  readonly outputTokens?: number;
4
4
  readonly totalTokens?: number;
5
5
  readonly cachedInputTokens?: number;
6
+ readonly cacheWriteTokens?: number;
6
7
  readonly reasoningTokens?: number;
7
8
  }
8
9
  export type SessionEventStage = "bootstrap" | "plan" | "execute";
@@ -50,6 +51,7 @@ export type SessionEvent = {
50
51
  readonly totalUsage?: SessionTokenUsage;
51
52
  readonly sessionUsage?: SessionTokenUsage;
52
53
  readonly contextInputTokens?: number;
54
+ readonly outputTokensPerSecond?: number;
53
55
  readonly stoppedAtStepLimit?: boolean;
54
56
  } | {
55
57
  readonly type: "compaction-start";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roll-agent/runtime",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,14 +26,14 @@
26
26
  "node": ">=22.6.0"
27
27
  },
28
28
  "dependencies": {
29
- "@ai-sdk/provider": "^3.0.8",
29
+ "@ai-sdk/provider": "^4.0.1",
30
30
  "@modelcontextprotocol/sdk": "^1.12.0",
31
- "ai": "^6.0.154",
31
+ "ai": "^7.0.9",
32
32
  "zod": "^3.25.76"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "^22.0.0",
36
- "@roll-agent/core": "0.12.0"
36
+ "@roll-agent/core": "0.13.0"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/obfuscate.mjs",