@roll-agent/runtime 0.14.1 → 0.15.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.
- package/dist/engine/agent-session.d.ts +2 -1
- package/dist/engine/agent-session.js +1 -1
- package/dist/engine/compaction-checkpoint.d.ts +16 -16
- package/dist/engine/explicit-skill-context.js +1 -1
- package/dist/engine/relocate-tool-images.d.ts +2 -0
- package/dist/engine/relocate-tool-images.js +1 -0
- package/dist/engine/session-attachments.d.ts +16 -0
- package/dist/engine/session-attachments.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/server/protocol.d.ts +2 -2
- package/dist/server/runtime-protocol-adapter.js +1 -1
- package/dist/service/attachment-store.d.ts +88 -0
- package/dist/service/attachment-store.js +1 -0
- package/dist/service/runtime-service.d.ts +18 -7
- package/dist/service/runtime-service.js +1 -1
- package/dist/tool-bridge/normalize-result.js +1 -1
- package/package.json +3 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SessionSendInput } from "./session-attachments.ts";
|
|
1
2
|
import { type ModelMessage } from "ai";
|
|
2
3
|
import type { LanguageModelV4, LanguageModelV4CallOptions, SharedV4ProviderOptions } from "@ai-sdk/provider";
|
|
3
4
|
import type { UserInputResult } from "@roll-agent/protocol";
|
|
@@ -168,7 +169,7 @@ export declare class AgentSession {
|
|
|
168
169
|
private trackPendingToolCall;
|
|
169
170
|
private buildSkillTools;
|
|
170
171
|
applyAgentRefresh(refresh: SessionAgentRefresh): void;
|
|
171
|
-
send(input: string): AsyncIterable<SessionEvent>;
|
|
172
|
+
send(input: string | SessionSendInput): AsyncIterable<SessionEvent>;
|
|
172
173
|
compact(reason?: ContextCompactionReason): AsyncIterable<SessionEvent>;
|
|
173
174
|
private runTurn;
|
|
174
175
|
private persistMessages;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash as e,randomUUID as t}from"node:crypto";import{waitForPromiseSettlement as s}from"../bounded-wait.js";import{stepCountIs as o,streamText as i,InvalidToolInputError as n}from"ai";import{buildAgentToolset as a}from"../tool-bridge/build-tools.js";import{buildAgentInstallToolset as r}from"../tool-bridge/agent-install-tool.js";import{buildSkillToolset as c}from"../tool-bridge/skill-tool.js";import{buildBashToolset as l}from"../tool-bridge/bash-tool.js";import{EXEC_COMMAND_NAME as u,EXEC_LIST_NAME as p,EXEC_POLL_NAME as h,buildSessionExecToolset as d}from"../tool-bridge/session-exec-tool.js";import{SessionManager as m}from"../bash/session/session-manager.js";import{isTerminalSessionState as g}from"../bash/session/types.js";import{withCleanEnv as v}from"../bash/clean-env.js";import{RUNTIME_CANCELLATION_ABORT_REASON as f,SESSION_CANCELLATION_REASONS as y,TURN_TIMEOUT_ABORT_REASON as C,USER_CANCELLATION_ABORT_REASON as b,createTurnCancellationMessage as k,isTurnTimeoutAbortReason as T,stripTurnCancellationMetadata as S}from"../types/cancellation.js";import{ToolRegistry as x}from"../tool-bridge/naming.js";import{TOOL_CANCELLATION_EXECUTION_STATES as I,TOOL_OUTCOME_KINDS as E,createToolResult as M,failedToolResult as w,isNormalizedToolResult as R,normalizeToolResult as q,readDisplayOutput as A,readToolOutcome as P}from"../tool-bridge/normalize-result.js";import{createToolExecutionRecord as N,prepareToolExecutionRecordForPersistence as O,redactSecretText as $,toRedactedToolExecutionRecordSummary as j}from"../tool-bridge/tool-execution-record.js";import{TOOL_RESOURCE_ACCESS_MODES as D,ToolExecutionCoordinator as W}from"../tool-bridge/tool-execution-coordinator.js";import{ApprovalGate as L}from"../approval/approval-gate.js";import{COMPACTION_DRAFT_FALLBACK_REASONS as F,compactMessages as U,CompactionDraftFallbackError as V,isCompactionSummaryAcknowledgement as _,readCompactionSummaryPayload as z}from"./compactor.js";import{AsyncEventQueue as G}from"./event-queue.js";import{applyExplicitSkillContext as J,attachExplicitSkillCheckpoint as B,prepareExplicitSkillContext as H,readExplicitSkillCheckpoint as Y,stripExplicitSkillCheckpoints as K}from"./explicit-skill-context.js";import{createCancelledTurnRecoveryMessage as Q,materializeCancelledTurnRecoveryMessages as X,stripCancelledTurnRecoveryMessages as Z}from"./cancelled-turn-recovery.js";import{CAPABILITY_HOST_MODES as ee,CAPABILITY_TOOL_ROLES as te,buildEffectiveCapabilityTurnContext as se,buildEffectiveCapabilityManifest as oe,findCapabilityToolId as ie}from"./capability-manifest.js";import{buildCapabilityTurnReminder as ne,buildChatSystemPromptFromManifest as ae}from"./system-prompt.js";import{buildCompactionCheckpointReminder as re,buildCompactionToolState as ce,COMPACTION_CHECKPOINT_VERSION as le,createCompactionCheckpoint as ue,createCompactionSummary as pe,createCheckpointSemanticReminderProjection as he,findLatestRealUserGoal as de,resolveActiveCompactionConstraints as me,TRANSCRIPT_MESSAGE_PROVENANCES as ge}from"./compaction-checkpoint.js";import{buildCompactionSemanticModelContext as ve,buildCompactionSemanticEvidenceRegistry as fe,mergeCompactionSemanticState as ye,replaceCompactionSemanticConstraints as Ce,replaceCompactionSemanticGoal as be,renderCompactionSemanticSummary as ke,seedCompactionSemanticConstraints as Te,seedLegacyCompactionSnapshotUncertainties as Se,validateCompactionModelDraft as xe}from"./compaction-semantic-state.js";import{ACTIVE_TOOL_PROTOCOL_REPAIR_STATUS as Ie,repairActiveToolProtocol as Ee}from"./tool-protocol-repair.js";import{buildTranscriptToolset as Me}from"../tool-bridge/transcript-tool.js";import{UserInputInteractionManager as we}from"../interaction/user-input-interaction-manager.js";import{buildUserInputTool as Re}from"../tool-bridge/user-input-tool.js";const qe=6e3,Ae=3e5,Pe=1,Ne=1024,Oe="AI_InvalidToolInputError:",$e={startsNewGoalScope:!1,goal:null,constraints:[],decisions:[],completedWork:[],pendingWork:[],resources:[],runningSessions:[],uncertainties:[],resolutions:[],evidenceReviews:[]};function je(t){const s=[...t];if(s.length<=Ne)return t;const o=`…#${e("sha256").update(t).digest("hex").slice(0,16)}`;return`${s.slice(0,Ne-[...o].length).join("")}${o}`}function De(){return{abortController:new AbortController,execSessionIds:new Set,pendingToolCalls:new Map,completedStepResponses:new Map,toolExecutions:[],hadPotentialSideEffects:!1,aborted:!1,cancellationEventEmitted:!1,cancellationPersistenceAttempted:!1,cancellationPersisted:!1}}function We(e,t){e.completedStepResponses.set(`${t.callId}:${String(t.stepNumber)}`,[...t.response.messages])}function Le(e){return[...e.completedStepResponses.values()].flatMap(e=>[...e])}function Fe(e){return e instanceof Error?e.message:String(e)}function Ue(e){const t=Y(e.message),[s]=K([e.message]),o=s??e.message,i=void 0!==t&&"user"===o.role?t.snapshot.userPrompt:"string"==typeof o.content?o.content:JSON.stringify(o.content);return`message ${String(e.sequence)} ${o.role}: ${i}`}function Ve(e){return JSON.stringify(e)??String(e)}function _e(e){const t=O(e);return{id:e.id,agentName:e.agentName,toolName:e.toolName,inputSummary:Ve(t.input.value),resultSummary:Ve(t.model),outcome:{kind:e.outcome.kind,...e.outcome.kind===E.cancelled&&void 0!==e.outcome.executionState?{executionState:e.outcome.executionState}:{}}}}function ze(e){return"user"===e.role&&"string"==typeof e.content&&void 0!==z(e.content)||"assistant"===e.role&&_(e.content)}function Ge(e,t){const s=t.flatMap((e,t)=>{if("user"===e.role&&"string"==typeof e.content){const t=z(e.content);if(void 0!==t)return 0===t.length?[]:[$(t)]}return"assistant"===e.role&&_(e.content)?[]:[$(Ue({sequence:t,provenance:ge[1],createdAt:new Date(0).toISOString(),message:e}))]});return{checkpointId:e,messageFingerprints:t.map(e=>JSON.stringify(e)),fragments:s,removableRawMessageCount:t.filter(e=>!ze(e)).length}}function Je(e,t,s){return!(1!==t?.version||t.id!==e.checkpointId||s.length<e.messageFingerprints.length)&&e.messageFingerprints.every((e,t)=>JSON.stringify(s[t])===e)}function Be(e){return void 0===e?{messagesThroughSequence:-1,toolExecutionsThroughSequence:-1}:e.version===le?e.semanticEvidence:{messagesThroughSequence:e.transcript.messages.throughSequence,toolExecutionsThroughSequence:e.transcript.toolExecutions.throughSequence}}function He(e,t){return t.aborted?Promise.reject(t.reason):new Promise((s,o)=>{let i=!1;const n=e=>{i||(i=!0,t.removeEventListener("abort",a),e())},a=()=>n(()=>o(t.reason));t.addEventListener("abort",a,{once:!0}),Promise.resolve(e).then(e=>n(()=>s(e)),e=>n(()=>o(e)))})}function Ye(e){return Fe(A(e))}function Ke(e){return n.isInstance(e)||"string"==typeof e&&e.startsWith(Oe)?E.invalidInput:E.toolFailed}function Qe(){return({steps:e})=>e.at(-1)?.toolResults.some(e=>P(e.output).kind===E.userRejected)??!1}function Xe(e){return/context[_ -]?length|context window|maximum context|token limit|too many tokens|prompt is too long|input is too long/i.test(Fe(e))}function Ze(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 et(e,t){return void 0===e&&void 0===t?void 0:(e??0)+(t??0)}function tt(e,t){const s=et(e.cachedInputTokens,t.cachedInputTokens),o=et(e.cacheWriteTokens,t.cacheWriteTokens),i=et(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!==i?{reasoningTokens:i}:{}}}function st(e,t){return void 0===t?e:void 0===e?t:Math.max(e,t)}function ot(e,t){const s=e.trim(),o=t.trim();return s.length>0&&o.length>0&&o.startsWith(s)}function it(e,t){return void 0!==e?e:T(t)?y.timeout:y.runtime}function nt(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}]})}function at(e,t,s){for(const o of Object.keys(t))e[o]=s}function rt(e,t,s){const o={[u]:te.sessionCommand,[h]:te.sessionPoll,[p]:te.sessionList};for(const i of Object.keys(t)){const t=s.resolve(i),n=t?o[t.toolName]:void 0;n&&(e[i]=n)}}function ct(e,t){const s=[...e];for(let e=s.length-1;e>=0;e-=1){const o=s[e];if("user"===o?.role){s[e]={...o,content:"string"==typeof o.content?`${t}\n\n${o.content}`:[{type:"text",text:t},...o.content]};break}}return s}export class AgentSession{id;model;maxSteps;messages;onPersist;onReplace;onToolExecution;listPersistedToolExecutions;getPersistedToolExecution;listPersistedTranscriptMessages;commitPersistedCompaction;inMemoryToolExecutions=[];inMemoryTranscript=[];legacyV1ActiveSnapshot;compactionResources=new Map;contextWindow;compaction;turnTimeoutMs;onClose;providerOptions;structuredOutputProviderOptions;structuredOutputReasoning;onProviderOptionsChange;debugEvents;policy;systemPrompt;explicitSystemPrompt;capabilityContext;resolveDynamicCapabilityContext;capabilityManifest;lastCapabilityTurnContext;toolRoles;skillSummaries;skillLibrary;skillToolBuilt=!1;toolSourceAgentNames;gate=new L;userInputInteractions=new we;toolCoordinator=new W;tools;registry;userInputToolId;userInputTool;userInputAvailable=!1;sessionManager;sessionManagerInstanceId=t();compactionCheckpoint;emit;activeTurn;closePromise;closed=!1;sessionUsage={inputTokens:0,outputTokens:0,totalTokens:0};lastInputTokens;needsCompaction=!1;constructor(e){this.id=e.id,this.model=e.model,this.maxSteps=e.maxSteps;const t=e.initialMessages?nt(e.initialMessages):[];this.messages=[...Ee(t).messages],this.legacyV1ActiveSnapshot=1===e.initialCheckpoint?.version?Ge(e.initialCheckpoint.id,this.messages):void 0,this.onPersist=e.onPersist,this.onReplace=e.onReplace,this.onToolExecution=e.onToolExecution,this.listPersistedToolExecutions=e.listToolExecutions,this.getPersistedToolExecution=e.getToolExecution,this.listPersistedTranscriptMessages=e.listTranscriptMessages,this.commitPersistedCompaction=e.commitCompaction,this.compactionCheckpoint=e.initialCheckpoint;let s=Be(e.initialCheckpoint).messagesThroughSequence+1;for(const e of this.messages)ze(e)||(this.inMemoryTranscript.push({sequence:s,provenance:ge[0],createdAt:new Date(0).toISOString(),message:e}),s+=1);for(const t of e.initialCheckpoint?.resources??[])this.retainCompactionResource(t);this.contextWindow=e.contextWindow,this.compaction=e.compaction,this.turnTimeoutMs=e.turnTimeoutMs,this.onClose=e.onClose,this.providerOptions=e.providerOptions,this.structuredOutputProviderOptions=e.structuredOutputProviderOptions,this.structuredOutputReasoning=e.structuredOutputReasoning,this.onProviderOptionsChange=e.onProviderOptionsChange,this.debugEvents=e.debugEvents??!1,this.policy=e.policy,this.explicitSystemPrompt=e.systemPrompt,this.resolveDynamicCapabilityContext=e.resolveDynamicCapabilityContext,this.skillLibrary=e.skillLibrary,this.skillSummaries=e.skillLibrary?.list()??[],this.toolSourceAgentNames=new Set(e.sources.map(e=>e.agentName));const o=new x,i={},n=Re({sessionId:this.id,isAvailable:()=>this.userInputAvailable,request:(e,t)=>this.requestUserInput(e,t)},o,this.toolCoordinator);this.userInputToolId=n.id,this.userInputTool=n.tools,at(i,n.tools,te.userInput);const c=e.readCheckpointTranscript?Me(e.readCheckpointTranscript,o,this.toolCoordinator,`thread:${this.id}:transcript`):{};at(i,c,te.transcriptRead);const u=e.skillLibrary?this.buildSkillTools(o):{};at(i,u,te.skill);const p={...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e),emitEvent:e=>this.emit?.(e),coordinator:this.toolCoordinator},h=e.bashClassifier?{classifier:e.bashClassifier}:{},g=e.bash?l(e.bash,o,p,h):{};at(i,g,te.shell),e.bashSession&&(this.sessionManager=new m({maxSessions:e.bashSession.maxSessions,profile:e.bashSession.profile,env:v(e.bashSession.env??process.env),bufferCapacity:e.bashSession.bufferCapacity}));const f=e.bashSession&&this.sessionManager?d({workdir:e.bashSession.workdir,defaultYieldMs:e.bashSession.defaultYieldMs,maxOutputTokens:e.bashSession.maxOutputTokens,...e.bashSession.env?{env:e.bashSession.env}:{}},this.sessionManager,o,p,{...h,onSessionTouched:e=>this.activeTurn?.execSessionIds.add(e)}):{};rt(i,f,o);const y=e.agentInstall,C=y?r({catalog:y.catalog,install:async(e,t)=>{const s=await y.install(e,t);return s.outcome.ok&&s.refresh?(this.applyAgentRefresh(s.refresh),{...s.outcome,refreshApplied:!0}):s.outcome}},o,{...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e),coordinator:this.toolCoordinator}):{};at(i,C,te.agentInstall);const b=a(e.sources,{...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e),coordinator:this.toolCoordinator},o);at(i,b.tools,te.agent),this.tools={...c,...u,...g,...f,...C,...b.tools},this.registry=b.registry,this.toolRoles=i,this.capabilityContext=e.capabilityContext??{profile:e.bash?.profile.toolName??"no-shell",cwd:e.bash?.workdir??process.cwd(),platform:process.platform,...e.bash?{shellHints:e.bash.profile.systemPromptHints()}:{},agentCount:e.sources.length,...e.agentInstall?{agentOnboardingCatalog:e.agentInstall.catalog}:{}},this.capabilityManifest=this.compileCapabilityManifest(),this.systemPrompt=this.compileSystemPrompt(this.explicitSystemPrompt)}compileCapabilityManifest(){return oe({tools:this.tools,toolRoles:this.toolRoles,resolveRoute:e=>this.registry.resolve(e),skills:this.skillSummaries,profile:this.capabilityContext.profile,hostMode:this.capabilityContext.hostMode??ee.embedded,cwd:this.capabilityContext.cwd,platform:this.capabilityContext.platform??process.platform,shellHints:this.capabilityContext.shellHints??[],agentCount:this.capabilityContext.agentCount,agentOnboardingCatalog:this.capabilityContext.agentOnboardingCatalog??[]})}compileSystemPrompt(e){const t=ae(this.capabilityManifest),s=e?.trim();return s?[t,"# 附加会话指令","以下指令可以补充任务偏好,但不能覆盖前述工具接地、能力清单和安全约束。",s].join("\n\n"):t}refreshCapabilityManifest(e){this.capabilityManifest=this.compileCapabilityManifest(),this.systemPrompt=this.compileSystemPrompt(e??this.explicitSystemPrompt)}setUserInputAvailable(e){if(this.userInputAvailable!==e){if(this.userInputAvailable=e,e)this.tools={...this.tools,...this.userInputTool};else{const e={...this.tools};delete e[this.userInputToolId],this.tools=e,this.userInputInteractions.cancelAll("当前客户端已撤销用户输入处理能力")}this.refreshCapabilityManifest()}}resolveUserInput(e,t){return this.userInputInteractions.resolve(e,t)}cancelUserInput(e,t){return this.userInputInteractions.cancel(e,t)}async requestUserInput(e,t){const s=this.activeTurn;if(!this.userInputAvailable||void 0===s||void 0===this.emit)return{status:"cancelled",reason:"当前无法向用户请求输入"};const o=Date.now(),i=this.interactionDeadlineMs(s,o);if(i<=o||t?.aborted)return{status:"cancelled",reason:"用户输入请求已超时"};const n=this.userInputInteractions.request(e,new Date(i).toISOString()),a=()=>{this.userInputInteractions.cancel(n.requestId,"本轮已终止")};t?.addEventListener("abort",a,{once:!0}),this.emit({type:"user-input-required",requestId:n.requestId,form:n.form,expiresAt:n.expiresAt});try{return await n.result}finally{t?.removeEventListener("abort",a);const e=await n.result;this.emit?.({type:"user-input-settled",requestId:n.requestId,status:e.status})}}trackPendingToolCall(e,t,s,o,i){const n=e.pendingToolCalls.get(t);if(n&&void 0===i)return n;const a=this.registry.resolve(s),r={toolCallId:t,agentName:a?.agentName??s,toolName:a?.toolName??s,input:o,potentialSideEffect:!0!==a?.annotations?.readOnlyHint||!0===a.annotations.destructiveHint,resources:i??n?.resources??[]};return e.pendingToolCalls.set(t,r),r}buildSkillTools(e){return this.skillToolBuilt=!0,c(()=>{if(!this.skillLibrary)throw new Error("skill library 不可用");return this.skillLibrary},e,this.toolCoordinator)}applyAgentRefresh(e){if(!this.toolSourceAgentNames.has(e.source.agentName)){const t=a([e.source],{...this.policy?{policy:this.policy}:{},requestApproval:e=>this.requestApproval(e),coordinator:this.toolCoordinator},this.registry);at(this.toolRoles,t.tools,te.agent),this.tools={...this.tools,...t.tools},this.toolSourceAgentNames.add(e.source.agentName)}if(e.skillLibrary&&(this.skillLibrary=e.skillLibrary,this.skillSummaries=e.skillLibrary.list(),!this.skillToolBuilt)){const e=this.buildSkillTools(this.registry);at(this.toolRoles,e,te.skill),this.tools={...this.tools,...e}}e.capabilityContext&&(this.capabilityContext=e.capabilityContext),this.refreshCapabilityManifest(e.systemPrompt)}async*send(e){if(this.closed)throw new Error("session is closed");if(this.activeTurn)throw new Error("session already has an active turn");const t=new G;this.emit=e=>t.push(e);const s=De();this.activeTurn=s,this.runTurn(t,s,e).catch(e=>{t.push({type:"error",stage:"execute",message:Fe(e)}),t.close()});try{for await(const e of t)yield e}finally{this.activeTurn===s&&this.abandonTurn(s),void 0!==this.emit&&(this.emit=void 0)}}async*compact(e="manual"){if(this.closed)throw new Error("session is closed");if(this.activeTurn)throw new Error("session already has an active turn");const t=new G;this.emit=e=>t.push(e);const s=De();this.activeTurn=s,this.runCompactionTurn(t,s,e).catch(e=>{t.push({type:"error",stage:"plan",message:Fe(e)}),t.close()});try{for await(const e of t)yield e}finally{this.activeTurn===s&&this.abandonTurn(s),void 0!==this.emit&&(this.emit=void 0)}}async runTurn(e,s,n){const a=Date.now();let r,c;try{if(this.debug(e,"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(e,"compaction","auto requested before turn",a,{messages:this.messages.length,...void 0!==this.lastInputTokens?{lastInputTokens:this.lastInputTokens}:{}});try{await this.runCompaction(e,"auto",s)}catch(t){return void e.push({type:"error",stage:"plan",message:Fe(t)})}}if(s.aborted||s.abortController.signal.aborted)return r=this.messages.length,this.messages.push({role:"user",content:n}),void this.persistCancelledTurn(e,s,r,[],a);e.push({type:"message-start",messageId:t()}),void 0!==this.turnTimeoutMs&&(s.expiresAt=new Date(Date.now()+this.turnTimeoutMs).toISOString(),c=setTimeout(()=>{s.abortController.signal.aborted||(this.captureCancellationActivity(s),s.aborted=!0,s.cancellationReason=y.timeout,this.gate.abortAll("本轮运行超时"),this.userInputInteractions.cancelAll("本轮运行超时"),s.abortController.abort(C))},this.turnTimeoutMs));let l=0;const u=H({rawInput:n,skillSummaries:this.skillSummaries,skillLibrary:this.skillLibrary}),p={role:"user",content:n},h=u.skillNames.length>0?B(p,u):p;let d={};try{d=await He(this.resolveDynamicCapabilityContext?.(s.abortController.signal)??{},s.abortController.signal)??{}}catch(t){if(this.isTurnAborted(s))return r=this.messages.length,this.messages.push(h),s.cancellationReason=it(s.cancellationReason,s.abortController.signal.reason??t),void this.persistCancelledTurn(e,s,r,[],a);this.debug(e,"turn","dynamic capability context unavailable",a,{message:Fe(t)})}const m=se(this.capabilityManifest,{explicitSkillNames:u.skillNames,...d.ruleIds?{ruleIds:d.ruleIds}:{},...d.vcs?{vcs:d.vcs}:{},sessions:(this.sessionManager?.list()??[]).map(e=>({sessionId:e.sessionId,state:e.state}))});this.lastCapabilityTurnContext=m;const g=ne(m);for(;;){r=this.messages.length,this.messages.push(h);const t=ie(this.capabilityManifest,te.transcriptRead),n=this.compactionCheckpoint?re(this.compactionCheckpoint,this.sessionManagerInstanceId,t):void 0,c=K(S(X(this.messages))),p=ct(u.skillNames.length>0?J(c,u):c,n?`${g}\n\n${n}`:g);this.debug(e,"model","calling streamText",a,{messages:this.messages.length,tools:Object.keys(this.tools).length,contextRecoveryAttempts:l,...void 0!==this.turnTimeoutMs?{timeoutMs:this.turnTimeoutMs}:{}});let d=[];const m=()=>i({model:this.model,system:this.systemPrompt,messages:p,tools:this.tools,stopWhen:[o(this.maxSteps),Qe()],toolApproval:async({toolCall:e})=>{this.trackPendingToolCall(s,e.toolCallId,e.toolName,e.input),await this.toolCoordinator.prepare(e.toolCallId,e.toolName,e.input)},onLanguageModelCallStart:({callId:e})=>this.toolCoordinator.startBatch(e),onLanguageModelCallEnd:({callId:e,content:t})=>this.toolCoordinator.sealBatch(e,t.flatMap(e=>"tool-call"!==e.type||!0===e.providerExecuted||"invalid"in e&&!0===e.invalid?[]:[{toolCallId:e.toolCallId,toolId:e.toolName}])),abortSignal:s.abortController.signal,...this.providerOptions?{providerOptions:this.providerOptions}:{},onError:()=>{},onStepEnd:e=>{We(s,e),d=Le(s)},onAbort:({steps:e})=>{for(const t of e)We(s,t);d=Le(s)}});let v;try{v=m()}catch(t){if(this.isTurnAborted(s)||T(t))return s.aborted=!0,s.cancellationReason=it(s.cancellationReason,t),void this.persistCancelledTurn(e,s,r,[],a);if(!Xe(t))throw t;this.needsCompaction=!0,this.messages.splice(r);const o=l<1,i=!!o&&await this.recoverFromContextError(e,s);if(this.isTurnAborted(s))return r=this.messages.length,this.messages.push(h),void this.persistCancelledTurn(e,s,r,[],a);if(i&&o){l+=1;continue}return this.persistContextFailure(e,h,!1,!1,a),void e.push({type:"error",stage:"execute",message:Fe(t)})}this.debug(e,"model","streamText returned",a);let f,C,b,k,x,I,M="",A="",P=!1,O=0,$=!1,j=!1;const D=this.scheduleDebug(e,"model","waiting for first stream event",a,{messages:this.messages.length});try{for await(const t of v.fullStream){if(this.closed){s.aborted=!0,s.cancellationReason??=y.runtime;break}switch(j||(j=!0,this.clearDebugTimer(D),this.debug(e,"model","first stream event",a,{part:t.type})),t.type){case"reasoning-start":e.push({type:"reasoning-start",reasoningId:t.id});break;case"reasoning-delta":e.push({type:"reasoning-delta",reasoningId:t.id,delta:t.text});break;case"reasoning-end":e.push({type:"reasoning-end",reasoningId:t.id});break;case"text-delta":if(!P){const s=A+t.text;if(ot(s,u.userPrompt)){A=s;break}if(A.length>0){const s=A+t.text;A="",M+=s,e.push({type:"text-delta",delta:s});break}}M+=t.text,e.push({type:"text-delta",delta:t.text});break;case"tool-call":{P=!0,A="";const o=this.trackPendingToolCall(s,t.toolCallId,t.toolName,t.input,this.toolCoordinator.describeResources(t.toolName,t.input));e.push({type:"tool-call",toolCallId:t.toolCallId,agentName:o.agentName,toolName:o.toolName,input:t.input});break}case"tool-result":{const o=this.registry.resolve(t.toolName),i=s.pendingToolCalls.get(t.toolCallId),n=R(t.output)?t.output:q(t.output),a=n.outcome,r=n.display,c=N({toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,input:i?.input,result:n});this.persistToolExecution(c,s),!0===i?.potentialSideEffect&&a.kind!==E.userRejected&&a.kind!==E.policyDenied&&a.kind!==E.invalidInput&&(s.hadPotentialSideEffects=!0),a.kind===E.success&&void 0!==i&&this.rememberCompactionResources(c,i.resources),s.pendingToolCalls.delete(t.toolCallId),e.push({type:"tool-result",toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,executionId:c.id,outcome:a,display:r,output:r,isError:a.kind!==E.success}),s.aborted||s.abortController.signal.aborted||a.kind!==E.userRejected||(I=Ye(t.output));break}case"tool-error":{const o=this.registry.resolve(t.toolName),i=Ye(t.error),n=w(Ke(t.error),i,{raw:t.error}),a=N({toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,input:s.pendingToolCalls.get(t.toolCallId)?.input,result:n});this.persistToolExecution(a,s),!0===s.pendingToolCalls.get(t.toolCallId)?.potentialSideEffect&&n.outcome.kind!==E.invalidInput&&(s.hadPotentialSideEffects=!0),s.pendingToolCalls.delete(t.toolCallId),e.push({type:"tool-result",toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,executionId:a.id,outcome:n.outcome,display:n.display,output:i,isError:!0});break}case"finish-step":{const s=Ze(t.usage);C=st(C,s.inputTokens),O+=1,k=t.finishReason;const o=t.performance.outputTokensPerSecond??t.performance.effectiveOutputTokensPerSecond;void 0!==o&&Number.isFinite(o)&&(b=o),e.push({type:"step-finish",finishReason:t.finishReason,usage:s});break}case"finish":!P&&A.length>0&&(M+=A,e.push({type:"text-delta",delta:A}),A=""),f=Ze(t.totalUsage);break;case"error":if(this.isTurnAborted(s)||T(t.error)){s.aborted=!0,s.cancellationReason=it(s.cancellationReason,t.error);break}$=Xe(t.error),$&&(this.needsCompaction=!0),x=Fe(t.error);break;case"abort":s.aborted=!0,s.cancellationReason=it(s.cancellationReason,t.reason)}}}catch(e){this.isTurnAborted(s)||T(e)?(s.aborted=!0,s.cancellationReason=it(s.cancellationReason,e)):($=Xe(e),$&&(this.needsCompaction=!0),x=Fe(e))}finally{this.clearDebugTimer(D)}if(this.debug(e,"model","fullStream finished",a,{textChars:M.length}),void 0!==x){if(this.messages.splice(r),$){const t=0===M.length&&!P,o=t&&l<1,i=!(!o&&t)&&await this.recoverFromContextError(e,s);if(this.isTurnAborted(s))return r=this.messages.length,this.messages.push(h),void this.persistCancelledTurn(e,s,r,[],a);if(t&&i&&o){l+=1;continue}const n=t?"":";本次尝试已有内容或操作开始执行,为避免重复执行,未自动重试";return this.persistContextFailure(e,h,M.length>0,P,a),void e.push({type:"error",stage:"execute",message:`${x}${n}`})}return void e.push({type:"error",stage:"execute",message:x})}if(s.aborted||s.abortController.signal.aborted)return s.cancellationReason=it(s.cancellationReason,s.abortController.signal.reason),void this.persistCancelledTurn(e,s,r,d,a);let W;this.debug(e,"model","awaiting response messages",a);const L=this.scheduleDebug(e,"model","still awaiting response messages",a);try{W=(await v.steps).flatMap(e=>e.response.messages)}catch(t){if(this.clearDebugTimer(L),this.isTurnAborted(s)||T(t))return s.aborted=!0,s.cancellationReason=it(s.cancellationReason,t),void this.persistCancelledTurn(e,s,r,d,a);const o=Xe(t);if(o&&(this.needsCompaction=!0),this.messages.splice(r),o){const o=0===M.length&&!P,i=o&&l<1,n=!(!i&&o)&&await this.recoverFromContextError(e,s);if(this.isTurnAborted(s))return r=this.messages.length,this.messages.push(h),void this.persistCancelledTurn(e,s,r,[],a);if(o&&n&&i){l+=1;continue}const c=o?"":";本次尝试已有内容或操作开始执行,为避免重复执行,未自动重试";return this.persistContextFailure(e,h,M.length>0,P,a),void e.push({type:"error",stage:"execute",message:`${Fe(t)}${c}`})}return void e.push({type:"error",stage:"execute",message:Fe(t)})}finally{this.clearDebugTimer(L)}if(this.debug(e,"model","response messages ready",a,{responseMessages:W.length}),this.isTurnAborted(s))return s.cancellationReason=it(s.cancellationReason,s.abortController.signal.reason),void this.persistCancelledTurn(e,s,r,d,a);const F=nt(W);if(this.messages.push(...F),void 0!==I){const t=0===M.length?I:`\n${I}`;M+=t,e.push({type:"text-delta",delta:t}),this.messages.push({role:"assistant",content:I})}this.debug(e,"persist","persisting messages",a,{appendedMessages:this.messages.length-r}),this.persistMessages(this.messages.slice(r)),this.debug(e,"persist","messages persisted",a,{totalMessages:this.messages.length}),f&&(this.sessionUsage=tt(this.sessionUsage,f));const U=C??f?.inputTokens;void 0!==U&&(this.lastInputTokens=U);const V=O>=this.maxSteps&&"tool-calls"===k;return void e.push({type:"message-finish",text:M,...f?{totalUsage:f}:{},sessionUsage:{...this.sessionUsage},...void 0!==C?{contextInputTokens:C}:{},...void 0!==b?{outputTokensPerSecond:b}:{},...V?{stoppedAtStepLimit:!0}:{}})}}catch(t){if(this.isTurnAborted(s)||T(t))return s.aborted=!0,s.cancellationReason=it(s.cancellationReason,s.abortController.signal.reason??t),void(void 0!==r?this.persistCancelledTurn(e,s,r,[],a):this.emitCancellation(e,s));void 0!==r&&this.messages.splice(r),this.debug(e,"turn","error",a,{message:Fe(t)}),e.push({type:"error",stage:"execute",message:Fe(t)})}finally{void 0!==c&&clearTimeout(c),this.toolCoordinator.finishTurn(),this.activeTurn===s&&(this.activeTurn=void 0),e.close()}}persistMessages(e){if(this.onPersist?.(e),void 0!==this.listPersistedTranscriptMessages)return;let t=(this.inMemoryTranscript.at(-1)?.sequence??-1)+1;const s=(new Date).toISOString();for(const o of e)ze(o)||(this.inMemoryTranscript.push({sequence:t,provenance:ge[0],createdAt:s,message:o}),t+=1)}persistContextFailure(e,t,s,o,i){const n=["本轮因上下文窗口溢出而中断,未自动重放。"];s&&n.push("本轮已产生部分文本,持久历史仅保留此中断标记。"),o&&n.push("本轮已有操作开始执行,部分结果可能已经生效且不会自动撤销,请先检查实际结果。");const a=this.messages.length;this.messages.push(t),this.messages.push({role:"assistant",content:n.join("")}),this.debug(e,"persist","persisting context-overflow marker",i,{hadToolActivity:o,producedText:s});try{this.persistMessages(this.messages.slice(a))}catch(t){this.messages.splice(a),e.push({type:"error",stage:"execute",message:`上下文溢出记录持久化失败: ${Fe(t)}`})}}persistToolExecution(e,t){this.onToolExecution?.(e),this.inMemoryToolExecutions.push(e),t.toolExecutions.push(e)}rememberCompactionResources(e,t){for(const s of t){const t={key:je(s.key),mode:s.mode,evidenceToolCallId:e.toolCallId,evidenceExecutionId:e.id};this.retainCompactionResource(t)}}retainCompactionResource(e){const t=this.compactionResources.get(e.key),s=void 0===t||e.mode===D.write||t.mode!==D.write?e:t;this.compactionResources.delete(e.key),this.compactionResources.set(e.key,s)}interactionDeadlineMs(e,t){const s=void 0===e.expiresAt?Number.POSITIVE_INFINITY:Date.parse(e.expiresAt);return Math.min(t+Ae,Number.isFinite(s)?s:Number.POSITIVE_INFINITY)}requestApproval(e){const s=t(),o=this.gate.request(s);if(void 0===this.emit)return this.gate.resolve(s,{approved:!1,reason:"approval event could not be delivered"}),o;const i=void 0===this.activeTurn?void 0:new Date(this.interactionDeadlineMs(this.activeTurn,Date.now())).toISOString();return this.emit({type:"confirmation-required",approvalId:s,agentName:e.agentName,toolName:e.toolName,input:e.input,...void 0!==i?{expiresAt:i}:{},...e.reason?{reason:e.reason}:{},...void 0!==e.explanation?{explanation:e.explanation}:{}}),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 K(Z(this.messages))}getToolExecutions(e={},t=!1){const s=this.listPersistedToolExecutions?this.listPersistedToolExecutions(e):this.inMemoryToolExecutions.map((e,t)=>({...e,sequence:t})).filter(t=>t.sequence>(e.afterSequence??-1)&&(void 0===e.toolCallId||t.toolCallId===e.toolCallId)).slice(0,e.limit??100);return t?s:s.map(e=>({...j(e),sequence:e.sequence}))}getToolExecution(e,t=!1){const s=this.getPersistedToolExecution?.(e),o=s??(()=>{const t=this.inMemoryToolExecutions.findIndex(t=>t.id===e),s=this.inMemoryToolExecutions[t];return s?{...s,sequence:t}:void 0})();return!o||t?o:{...j(o),sequence:o.sequence}}getCapabilityManifest(){return structuredClone(this.capabilityManifest)}getCapabilityTurnContext(){return this.lastCapabilityTurnContext?structuredClone(this.lastCapabilityTurnContext):void 0}getContextWindow(){return this.contextWindow}getSessionUsage(){return{...this.sessionUsage}}getSkillSummaries(){return this.skillSummaries}setProviderOptions(e){this.providerOptions=e,this.onProviderOptionsChange?.(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)}listCompactionTranscriptEvidence(){const e=this.listPersistedTranscriptMessages;if(void 0===e)return[...this.inMemoryTranscript];const t=[];let s=-1;for(;;){const o=e({afterSequence:s,limit:500});t.push(...o);const i=o.at(-1)?.sequence;if(o.length<500||void 0===i||i<=s)break;s=i}return t}listCompactionToolEvidence(){const e=this.listPersistedToolExecutions;if(void 0===e)return this.inMemoryToolExecutions.map((e,t)=>({...e,sequence:t}));const t=[];let s=-1;for(;;){const o=e({afterSequence:s,limit:500});t.push(...o);const i=o.at(-1)?.sequence;if(o.length<500||void 0===i||i<=s)break;s=i}return t}latestExplicitSkillNames(e,t){if(void 0===t)return this.compactionCheckpoint?.context.explicitSkillNames??[];const s=e.find(e=>e.sequence===t);return void 0===s?[]:Y(s.message)?.snapshot.skillNames??[]}currentCompactionRunningWork(){const e=(this.compactionCheckpoint?.runningWork??[]).filter(e=>e.managerInstanceId!==this.sessionManagerInstanceId).map(e=>({...e,recoverability:"live"===e.recoverability?"stale":e.recoverability})),t=(new Date).toISOString();return[...e,...(this.sessionManager?.list()??[]).map(e=>({managerInstanceId:this.sessionManagerInstanceId,sessionId:e.sessionId,state:e.state,recoverability:g(e.state)?"unavailable":"live",commandPreview:e.commandPreview,workdir:e.workdir,observedAt:t,wallTimeMs:e.wallTimeMs,...void 0!==e.exitCode?{exitCode:e.exitCode}:{},...void 0!==e.terminationCause?{terminationCause:e.terminationCause}:{},...void 0!==e.cleanupError?{cleanupError:e.cleanupError}:{}}))].slice(-128)}captureCompactionEvidence(){const e=this.listCompactionTranscriptEvidence(),t=this.listCompactionToolEvidence(),s=[...this.compactionResources.values()].slice(-256),o=[...this.currentCompactionRunningWork()],i="legacy_snapshot"===this.compactionCheckpoint?.transcript.completeness||e.some(e=>"legacy_snapshot"===e.provenance)?"legacy_snapshot":"complete",n=this.compactionCheckpoint?.goal,a=de(e),r=a??n,c=void 0!==a&&(void 0===n||a.sourceSequence>n.sourceSequence)?a.sourceSequence:void 0,l=this.compactionCheckpoint?.version===le?this.compactionCheckpoint.semanticState:void 0,u=Te(l,this.compactionCheckpoint?.constraints??[]),p=1===this.compactionCheckpoint?.version,h=void 0!==this.legacyV1ActiveSnapshot&&Je(this.legacyV1ActiveSnapshot,this.compactionCheckpoint,this.messages),d=p?h&&void 0!==this.legacyV1ActiveSnapshot?Se(u,{checkpointId:this.legacyV1ActiveSnapshot.checkpointId,fragments:this.legacyV1ActiveSnapshot.fragments}):{state:u,requiredItemIds:[],transcriptFragments:[],complete:!1}:{state:u,requiredItemIds:[],transcriptFragments:[],complete:!0},m=d.state,g=Be(this.compactionCheckpoint),v=g.messagesThroughSequence,f=g.toolExecutionsThroughSequence,y=e.filter(e=>e.sequence>v),C=new Set(y.filter(e=>e.provenance===ge[1]&&ze(e.message)).map(e=>e.sequence)),b=void 0===r?void 0:e.find(e=>e.sequence===r.sourceSequence),k=fe({messages:[...y.filter(e=>!C.has(e.sequence)),...void 0===b||y.includes(b)?[]:[b]].map(e=>({sequence:e.sequence,role:e.message.role,summary:Ue(e)})),toolExecutions:t.filter(e=>e.sequence>f).map(_e),resources:s.slice().reverse().map(e=>({key:e.key,mode:e.mode})),runningSessions:[]}),T=ve(k,m,48e3);return{transcript:e,toolExecutions:t,resources:s,runningWork:o,completeness:i,goal:r,newGoalSourceSequence:c,previousSemanticState:m,registry:k,modelContext:T.prompt,presentedEvidenceIds:T.includedEvidenceIds,expectedActiveMessages:[...this.messages],expectedLatestCheckpointId:this.compactionCheckpoint?.id,previousSemanticEvidenceWatermarks:g,autoCoveredMessageSequences:C,legacySnapshotMigrationComplete:d.complete,legacySnapshotRequiredItemIds:d.requiredItemIds,legacySnapshotTranscriptFragments:d.transcriptFragments,legacySnapshotRemovableRawMessageCount:d.complete&&h?this.legacyV1ActiveSnapshot?.removableRawMessageCount??0:0,evidenceWatermarks:{transcriptMessagesThroughSequence:Math.max(this.compactionCheckpoint?.transcript.messages.throughSequence??-1,e.at(-1)?.sequence??-1),toolExecutionsThroughSequence:Math.max(this.compactionCheckpoint?.transcript.toolExecutions.throughSequence??-1,t.at(-1)?.sequence??-1)}}}buildCompactionDraft(t,s,o,i){const n=xe({draft:s??$e,evidenceRegistry:t.registry,presentedEvidenceIds:t.presentedEvidenceIds,...void 0!==t.previousSemanticState?{previousState:t.previousSemanticState}:{},...void 0!==t.goal?{harnessGoal:{verbatimRequest:t.goal.verbatimRequest,sourceSequence:t.goal.sourceSequence}}:{}});let a=ye(t.previousSemanticState,n,{startsNewGoalScope:n.startsNewGoalScope});if(void 0===s){const e=me(t.transcript,this.compactionCheckpoint?.constraints??[]),s=JSON.stringify(e)!==JSON.stringify(this.compactionCheckpoint?.constraints??[]);a=Ce(a,e);const o=void 0===this.compactionCheckpoint?.goal?t.goal:void 0===t.goal||void 0===t.newGoalSourceSequence||s?this.compactionCheckpoint.goal:t.goal;a=be(a,o)}const r=a.constraints.flatMap(e=>{const t=e.provenance.find(e=>"message"===e.kind)?.messageSequence;return null==t?[]:[{quote:e.text,sourceSequence:t}]}),c=a.goal?.provenance.find(e=>"message"===e.kind)?.messageSequence,l=(null==c?void 0:de(t.transcript.filter(e=>e.sequence===c)))??this.compactionCheckpoint?.goal??t.goal,u=void 0!==l&&l.sourceSequence!==this.compactionCheckpoint?.goal?.sourceSequence?l.sourceSequence:void 0,p=o?ke(a):void 0,h=void 0!==p?pe(p):void 0!==i?{status:"fallback",reason:i}:{status:"skipped"},d=new Set(he(a).items.map(e=>e.itemId)),m=0===t.legacySnapshotRequiredItemIds.length||t.legacySnapshotRequiredItemIds.every(e=>d.has(e)),g=ce(t.transcript.map(e=>e.message),t.toolExecutions,32,t.completeness);return{draft:{...l?{goal:l}:{},constraints:[...r],resources:[...t.resources],toolState:g,runningWork:[...t.runningWork],context:{cwd:this.capabilityManifest.dynamicContext.cwd,stableRuleIds:[...this.capabilityManifest.stableContext.rules],systemPromptSha256:e("sha256").update(this.systemPrompt).digest("hex"),skills:this.capabilityManifest.skills.map(e=>({name:e.name,source:e.source})),explicitSkillNames:[...this.latestExplicitSkillNames(t.transcript,u)]},summary:h},semanticState:a,semanticSummaryText:p,semanticRejectionCount:n.rejections.length,legacySnapshotReminderCoverageComplete:m,coveredEvidenceIds:n.coveredEvidenceIds,expectedActiveMessages:t.expectedActiveMessages,expectedLatestCheckpointId:t.expectedLatestCheckpointId,evidenceWatermarks:t.evidenceWatermarks,legacySnapshotTranscriptFragments:t.legacySnapshotTranscriptFragments}}async runCompactionTurn(e,t,s){try{await this.runCompaction(e,s,t,!0)}catch(t){e.push({type:"error",stage:"plan",message:Fe(t)})}finally{this.activeTurn===t&&(this.activeTurn=void 0),e.close()}}advanceCompactionSemanticEvidenceWatermarks(e,t){const s=e.previousSemanticEvidenceWatermarks;if(0===t.length)return s;const o=new Set(t.filter(t=>e.presentedEvidenceIds.includes(t))),i=new Map(e.registry.flatMap(e=>"message"===e.provenance.kind&&null!==e.provenance.messageSequence?[[e.provenance.messageSequence,e.evidenceId]]:[]));let n=s.messagesThroughSequence;for(let t=s.messagesThroughSequence+1;;t+=1){if(e.autoCoveredMessageSequences.has(t)){n=t;continue}const s=i.get(t);if(void 0===s||!o.has(s))break;n=t}const a=new Map(e.registry.flatMap(e=>"tool_execution"===e.provenance.kind&&null!==e.provenance.toolExecutionId?[[e.provenance.toolExecutionId,e.evidenceId]]:[])),r=new Map(e.toolExecutions.map(e=>[e.sequence,e]));let c=s.toolExecutionsThroughSequence;for(let e=s.toolExecutionsThroughSequence+1;;e+=1){const t=r.get(e);if(void 0===t)break;const s=a.get(t.id);if(void 0===s||!o.has(s))break;c=e}return{messagesThroughSequence:n,toolExecutionsThroughSequence:c}}countContiguousPresentedMessageEvidence(e){if(!e.legacySnapshotMigrationComplete)return 0;const t=new Set(e.presentedEvidenceIds),s=new Map(e.registry.flatMap(e=>"message"===e.provenance.kind&&null!==e.provenance.messageSequence?[[e.provenance.messageSequence,e.evidenceId]]:[]));let o=e.legacySnapshotRemovableRawMessageCount;for(let i=e.previousSemanticEvidenceWatermarks.messagesThroughSequence+1;;i+=1){if(e.autoCoveredMessageSequences.has(i))continue;const n=s.get(i);if(void 0===n||!t.has(n))break;o+=1}return o}async runCompaction(e,t,s,o=!1){const i=Date.now(),n=this.compaction,a=n?.strategy??"summarize";if(this.debug(e,"compaction","start",i,{reason:t,messages:this.messages.length,timeoutMs:n?.timeoutMs??12e4,maxOutputTokens:n?.maxOutputTokens??8192}),e.push({type:"compaction-start",reason:t}),this.isTurnAborted(s))return o&&void 0!==s&&this.emitCancellation(e,s),!1;if(!n)return e.push({type:"context-compacted",reason:t,strategy:a,removed:0,kept:this.messages.length}),!1;const r=this.lastInputTokens;let c,l=n.strategy;const u=s?.abortController.signal,p=Date.now(),h=this.captureCompactionEvidence();if(this.debug(e,"compaction","evidence ready",p,{transcriptMessages:h.transcript.length,toolExecutions:h.toolExecutions.length}),1===this.compactionCheckpoint?.version&&void 0===this.commitPersistedCompaction)return this.debug(e,"compaction","legacy V1 snapshot requires durable transcript",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;if(!h.legacySnapshotMigrationComplete)return this.debug(e,"compaction","legacy V1 snapshot migration incomplete",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;const d=this.countContiguousPresentedMessageEvidence(h);let m;try{const t=Date.now();try{m=await U({messages:this.messages,strategy:l,keepRecentTurns:n.keepRecentTurns,keepRecentTokens:n.keepRecentTokens,model:this.model,semanticEvidencePrompt:h.modelContext,maxRemovedTranscriptMessages:d,...void 0!==n.timeoutMs?{timeoutMs:n.timeoutMs}:{},...void 0!==n.maxOutputTokens?{maxOutputTokens:n.maxOutputTokens}:{},...this.structuredOutputReasoning?{structuredOutputReasoning:this.structuredOutputReasoning}:{},...this.structuredOutputProviderOptions?{structuredOutputProviderOptions:this.structuredOutputProviderOptions}:{},...u?{abortSignal:u}:{}})}finally{this.debug(e,"compaction","draft generation finished",t,{strategy:l})}if("summarize"===l&&m.removed>0&&void 0===m.semanticDraft)throw new V(F.missingObject,"structured compaction draft is missing")}catch(t){if(this.isTurnAborted(s))return o&&void 0!==s&&this.emitCancellation(e,s),!1;if("summarize"!==l||!V.isInstance(t))throw t;this.debug(e,"compaction","summarize failed, fallback to truncate",i,{message:Fe(t)}),c="structured checkpoint generation failed",l="truncate";const a=Date.now();m=await U({messages:this.messages,strategy:l,keepRecentTurns:n.keepRecentTurns,keepRecentTokens:n.keepRecentTokens,model:this.model,maxRemovedTranscriptMessages:d,...u?{abortSignal:u}:{}}),this.debug(e,"compaction","truncate fallback finished",a)}if(this.isTurnAborted(s))return o&&void 0!==s&&this.emitCancellation(e,s),!1;const g=m.removed>0||m.truncatedTools>0,v=Date.now(),f=g?this.buildCompactionDraft(h,m.semanticDraft,"summarize"===l&&m.removed>0,c):void 0;if(this.debug(e,"compaction","checkpoint validation finished",v,{semanticRejections:f?.semanticRejectionCount??0}),void 0!==f&&f.semanticRejectionCount>0&&this.debug(e,"compaction","rejected ungrounded semantic claims",i,{count:f.semanticRejectionCount}),void 0!==f&&!f.legacySnapshotReminderCoverageComplete)return this.debug(e,"compaction","legacy V1 snapshot exceeds reminder projection",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;const y=Ee(m.messages),C=[...y.messages];if(y.status===Ie.repaired&&this.debug(e,"compaction","repaired malformed active Tool protocol",i,{removedToolCallIds:y.removedToolCallIds.join(","),removedToolResultIds:y.removedToolResultIds.join(",")}),1===this.compactionCheckpoint?.version&&C.some(ze))return this.debug(e,"compaction","legacy V1 snapshot remains in active history",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;const b=e=>Buffer.byteLength(JSON.stringify(e),"utf8"),k=g&&b(C)<b(this.messages),T=k?C:[...this.messages],S=k?this.advanceCompactionSemanticEvidenceWatermarks(h,f?.coveredEvidenceIds??[]):h.previousSemanticEvidenceWatermarks;let x;if(k){const t=Date.now();if(void 0!==this.commitPersistedCompaction){if(void 0===f)throw new Error("compaction checkpoint snapshot is missing");x=this.commitPersistedCompaction({messages:T,expectedActiveMessages:f.expectedActiveMessages,expectedLatestCheckpointId:f.expectedLatestCheckpointId,draft:f.draft,semanticState:f.semanticState,semanticEvidenceWatermarks:S,evidenceWatermarks:f.evidenceWatermarks,...f.legacySnapshotTranscriptFragments.length>0?{legacySnapshotTranscriptFragments:f.legacySnapshotTranscriptFragments}:{}})}else{if(void 0===f)throw new Error("compaction checkpoint snapshot is missing");const e=this.compactionCheckpoint;x=ue({draft:f.draft,semanticState:f.semanticState,semanticEvidence:S,generation:(e?.generation??0)+1,transcript:{messages:{fromSequenceExclusive:e?.transcript.messages.throughSequence??-1,throughSequence:f.evidenceWatermarks.transcriptMessagesThroughSequence},toolExecutions:{fromSequenceExclusive:e?.transcript.toolExecutions.throughSequence??-1,throughSequence:f.evidenceWatermarks.toolExecutionsThroughSequence},completeness:h.completeness},...void 0!==e?{previousCheckpointId:e.id}:{}}),this.onReplace?.(T)}this.debug(e,"compaction","checkpoint commit finished",t,{durable:void 0!==this.commitPersistedCompaction}),this.messages.splice(0,this.messages.length,...T),void 0!==x&&(this.compactionCheckpoint=x),this.lastInputTokens=void 0,this.needsCompaction=!1}return e.push({type:"context-compacted",reason:t,strategy:l,removed:k?m.removed:0,kept:T.length,...k&&m.truncatedTools>0?{truncatedTools:m.truncatedTools}:{},...void 0!==r?{beforeInputTokens:r}:{},...void 0!==x?{checkpointId:x.id,checkpointGeneration:x.generation,checkpointSummaryStatus:x.summary.status}:{}}),this.debug(e,"compaction","finish",i,{reason:t,strategy:l,removed:k?m.removed:0,kept:T.length,truncatedTools:k?m.truncatedTools:0,progressed:k}),k}async recoverFromContextError(e,t){if(!this.compaction?.enabled)return!1;try{return await this.runCompaction(e,"auto",t)}catch(t){return e.push({type:"error",stage:"plan",message:Fe(t)}),!1}}isTurnAborted(e){return!0===e?.aborted||!0===e?.abortController.signal.aborted}execSessionIds(e){return[...e.execSessionIds].sort((e,t)=>e-t)}activeExecSessionIds(e){return this.execSessionIds(e).filter(e=>{const t=this.sessionManager?.get(e);return void 0!==t&&!g(t.state)})}interruptExecSessions(e){const t=this.execSessionIds(e);0!==t.length&&this.sessionManager?.interrupt(t).catch(e=>{process.stderr.write(`roll chat: 中断后台会话失败: ${Fe(e)}\n`)})}abandonTurn(e){this.closed||this.activeTurn!==e||e.abortController.signal.aborted||(this.captureCancellationActivity(e),e.aborted=!0,e.cancellationReason??=y.runtime,this.gate.abortAll("本轮事件流已停止"),this.userInputInteractions.cancelAll("本轮事件流已停止"),this.interruptExecSessions(e),e.abortController.abort(f))}currentCancellationActivity(e){const t=this.activeExecSessionIds(e),s=[...e.pendingToolCalls.values()].filter(e=>this.toolCoordinator.hasExecutionStarted(e.toolCallId));return{execSessionIds:t,hadCompletedProgress:e.toolExecutions.length>0||e.completedStepResponses.size>0,hadInFlightWork:s.length>0||t.length>0,hadPotentialSideEffects:e.hadPotentialSideEffects||s.some(e=>e.potentialSideEffect)}}captureCancellationActivity(e){e.cancellationActivity??=this.currentCancellationActivity(e)}cancellationActivity(e){return e.cancellationActivity??this.currentCancellationActivity(e)}cancellationDisplayMessage(e){const t=e.cancellationReason??y.runtime,s=this.cancellationActivity(e);if(e.cancellationPersistenceAttempted&&!e.cancellationPersisted){const e=s.hadPotentialSideEffects?"部分操作可能已经执行,请先检查结果后再继续。":s.hadInFlightWork?"刚才的任务结果尚未确认,请先检查后再继续。":"请重新输入需要继续的内容。";return t===y.timeout?`本轮等待时间过长,已自动停止,但刚才的对话和进度未能保存。${e}`:t===y.runtime?`本轮因运行异常而中断,刚才的对话和进度未能保存。${e}`:`已停止本轮,但刚才的对话和进度未能保存。${e}`}const o=s.execSessionIds,i=ie(this.capabilityManifest,te.sessionList);switch(t){case y.user:return s.hadInFlightWork?s.hadPotentialSideEffects?"已停止本轮操作。正在进行的任务也已请求停止。之前的对话和已完成进度会保留;部分已经完成的操作不会自动撤销,请检查结果。":"已停止本轮操作。正在进行的任务也已请求停止。之前的对话和已完成进度会保留。":s.hadCompletedProgress?s.hadPotentialSideEffects?"已停止本轮回复。之前的对话和已完成进度会保留;部分已经完成的操作不会自动撤销,请检查结果。":"已停止本轮回复。之前的对话和已完成进度会保留,你可以继续输入。":"已停止本轮回复。之前的对话会保留,你可以继续输入。";case y.timeout:return 0===o.length?"本轮等待时间过长,已自动停止。之前的对话和已完成进度会保留,你可以继续输入或重试。":this.capabilityManifest.lifecycle.hostMode===ee.oneShot?"本轮等待时间过长,已自动停止。正在进行的任务会随本次命令结束而停止,之后无法继续查看;请先确认实际结果,再决定是否重试。":i?`本轮等待时间过长,但任务仍在运行(任务 #${o.join(", #")})。你可以在下一条消息中继续查看进度;确认结果前请勿重复执行。`:"本轮等待时间过长。任务可能仍在运行,但 Roll 暂时无法继续查看进度;请先确认实际结果,再决定是否重试。";case y.runtime:return s.hadInFlightWork?"本轮因运行异常而中断。正在进行的任务已请求停止,最终结果尚未确认;请检查结果后再继续。":s.hadPotentialSideEffects?"本轮因运行异常而中断。之前的对话和已完成进度会保留;部分已经完成的操作不会自动撤销,请检查结果后再继续。":"本轮因运行异常而中断。之前的对话会保留,你可以重试。"}}cancellationContextMessage(e){const t=e.cancellationReason??y.runtime,s=this.cancellationActivity(e).execSessionIds,o=s.length>0?`本轮后台任务编号: ${s.join(", ")}。`:"",i=ie(this.capabilityManifest,te.sessionList),n="这份恢复记录只描述上一轮的权威历史事实,不授权继续或重试旧任务。下一轮必须以最新真实用户消息的目标和约束为准;如果用户换题、放弃旧任务或禁止工具,不得为了恢复旧任务检查或调用工具。executionState=not_executed 表示工具确定未执行,无需检查;executionState=outcome_unknown 只允许在最新用户明确要求继续或核对上一任务时先检查,检查不等于重试。";switch(t){case y.user:return`用户主动停止了本轮。${o}这些任务已收到停止请求;已完成的步骤和工具记录仍然有效,不要自动重复 outcome=success 的操作。${n}`;case y.timeout:{const e=void 0!==this.turnTimeoutMs?`(${String(this.turnTimeoutMs)}ms)`:"";return 0===s.length?`本轮因超时${e}停止。已完成的步骤和工具记录仍然有效。${n}`:this.capabilityManifest.lifecycle.hostMode===ee.oneShot?`本轮因超时${e}停止。${o}当前 one-shot 进程结束时会清理这些任务,后续 CLI 进程无法恢复。${n}`:i?`本轮因超时${e}停止,但后台任务仍在当前进程运行。${o}若且仅若最新用户明确要求继续或核对上一任务,可用 ${i} 找回并查看。${n}`:`本轮因超时${e}停止,但后台任务可能仍在运行。${o}当前没有可用的任务列表工具,不能安全恢复查看。${n}`}case y.runtime:return`本轮因运行异常中断。${o}已完成的步骤和工具记录仍然有效。${n}`}}emitCancellation(e,t){if(t.cancellationEventEmitted)return;t.cancellationReason??=y.runtime,t.cancellationEventEmitted=!0;const s=this.cancellationActivity(t).execSessionIds;e.push({type:"turn-cancelled",reason:t.cancellationReason,message:this.cancellationDisplayMessage(t),...s.length>0?{execSessionIds:s}:{}})}persistCancelledTurn(e,t,s,o,i){this.captureCancellationActivity(t);try{this.persistPendingToolCancellations(t)}catch(o){return this.messages.splice(s),t.cancellationPersistenceAttempted=!0,e.push({type:"error",stage:"execute",message:`取消工具账本持久化失败: ${Fe(o)}`}),void this.emitCancellation(e,t)}if(this.closed)return this.messages.splice(s),t.cancellationPersistenceAttempted=!0,void this.emitCancellation(e,t);if(!t.cancellationPersistenceAttempted){t.cancellationPersistenceAttempted=!0,this.messages.splice(s+1);const n=Le(t),a=Ee(nt(n.length>0?n:o)).messages;t.cancellationPersisted=!0,this.messages.push(...a),this.messages.push(Q({context:this.cancellationContextMessage(t),completedMessages:a,toolExecutions:t.toolExecutions})),this.messages.push(k(this.cancellationDisplayMessage(t),t.cancellationReason??y.runtime)),this.debug(e,"persist","persisting cancelled turn",i,{appendedMessages:this.messages.length-s});try{this.persistMessages(this.messages.slice(s)),t.cancellationPersisted=!0}catch(o){t.cancellationPersisted=!1,this.messages.splice(s),e.push({type:"error",stage:"execute",message:`取消状态持久化失败: ${Fe(o)}`})}}this.emitCancellation(e,t)}persistPendingToolCancellations(e){const t=e.cancellationReason??y.runtime;for(const s of e.pendingToolCalls.values()){const o=this.toolCoordinator.hasExecutionStarted(s.toolCallId)?I.outcomeUnknown:I.notExecuted,i=o===I.notExecuted?`工具调用在开始执行前因本轮 ${t} 中断而取消;确定未执行`:`工具调用已开始,因本轮 ${t} 中断而取消;最终结果尚未确认`,n=M({kind:E.cancelled,reason:t,executionState:o},i,{raw:{cancellationReason:t,executionState:o,abortReason:Fe(e.abortController.signal.reason)}});this.persistToolExecution(N({toolCallId:s.toolCallId,agentName:s.agentName,toolName:s.toolName,input:s.input,result:n}),e),e.pendingToolCalls.delete(s.toolCallId)}}cancel(){const e=this.activeTurn;return!!e&&(this.captureCancellationActivity(e),e.aborted=!0,e.cancellationReason=y.user,this.gate.abortAll("用户取消本轮"),this.userInputInteractions.cancelAll("用户取消本轮"),this.interruptExecSessions(e),e.abortController.abort(b),!0)}close(){if(void 0!==this.closePromise)return this.closePromise;this.closed=!0;const e=this.activeTurn;return e&&(this.captureCancellationActivity(e),e.aborted=!0,e.cancellationReason??=y.runtime,this.gate.abortAll(),this.userInputInteractions.cancelAll("Runtime 正在关闭"),e.abortController.abort(f)),this.closePromise=(async()=>{const e=(this.sessionManager?.close()??Promise.resolve([])).then(e=>{for(const t of e)void 0!==t.cleanupError&&process.stderr.write(`roll chat: 后台会话 ${String(t.sessionId)} 清理失败: ${t.cleanupError}\n`)},e=>{process.stderr.write(`roll chat: 会话 ${this.id} 后台清理失败: ${Fe(e)}\n`)});await s(e,qe)||process.stderr.write(`roll chat: 会话 ${this.id} 在 ${String(qe)}ms 内未完成全部关闭步骤\n`);try{this.onClose?.()}catch(e){process.stderr.write(`roll chat: 会话 ${this.id} 关闭回调失败: ${Fe(e)}\n`)}})(),this.closePromise}abort(){this.close().catch(e=>{process.stderr.write(`roll chat: 会话 ${this.id} 关闭失败: ${Fe(e)}\n`)})}debug(e,t,s,o,i){this.debugEvents&&e.push({type:"debug",stage:t,message:s,...void 0!==o?{elapsedMs:Date.now()-o}:{},...void 0!==i?{data:i}:{}})}scheduleDebug(e,t,s,o,i){if(this.debugEvents)return setTimeout(()=>{this.debug(e,t,s,o,i)},5e3)}clearDebugTimer(e){void 0!==e&&clearTimeout(e)}}
|
|
1
|
+
import{createHash as e,randomUUID as t}from"node:crypto";import{waitForPromiseSettlement as s}from"../bounded-wait.js";import{relocateToolImagesToUserMessages as o}from"./relocate-tool-images.js";import{buildUserMessageContent as i,normalizeSessionSendInput as n,redactBinaryPartsForEvidence as a}from"./session-attachments.js";import{stepCountIs as r,streamText as c,InvalidToolInputError as l}from"ai";import{buildAgentToolset as u}from"../tool-bridge/build-tools.js";import{buildAgentInstallToolset as p}from"../tool-bridge/agent-install-tool.js";import{buildSkillToolset as h}from"../tool-bridge/skill-tool.js";import{buildBashToolset as d}from"../tool-bridge/bash-tool.js";import{EXEC_COMMAND_NAME as m,EXEC_LIST_NAME as g,EXEC_POLL_NAME as v,buildSessionExecToolset as f}from"../tool-bridge/session-exec-tool.js";import{SessionManager as y}from"../bash/session/session-manager.js";import{isTerminalSessionState as C}from"../bash/session/types.js";import{withCleanEnv as b}from"../bash/clean-env.js";import{RUNTIME_CANCELLATION_ABORT_REASON as k,SESSION_CANCELLATION_REASONS as T,TURN_TIMEOUT_ABORT_REASON as S,USER_CANCELLATION_ABORT_REASON as x,createTurnCancellationMessage as I,isTurnTimeoutAbortReason as E,stripTurnCancellationMetadata as M}from"../types/cancellation.js";import{ToolRegistry as w}from"../tool-bridge/naming.js";import{TOOL_CANCELLATION_EXECUTION_STATES as R,TOOL_OUTCOME_KINDS as q,createToolResult as A,failedToolResult as P,isNormalizedToolResult as N,normalizeToolResult as O,readDisplayOutput as $,readToolOutcome as j}from"../tool-bridge/normalize-result.js";import{createToolExecutionRecord as D,prepareToolExecutionRecordForPersistence as W,redactSecretText as L,toRedactedToolExecutionRecordSummary as F}from"../tool-bridge/tool-execution-record.js";import{TOOL_RESOURCE_ACCESS_MODES as U,ToolExecutionCoordinator as V}from"../tool-bridge/tool-execution-coordinator.js";import{ApprovalGate as _}from"../approval/approval-gate.js";import{COMPACTION_DRAFT_FALLBACK_REASONS as z,compactMessages as G,CompactionDraftFallbackError as J,isCompactionSummaryAcknowledgement as B,readCompactionSummaryPayload as H}from"./compactor.js";import{AsyncEventQueue as Y}from"./event-queue.js";import{applyExplicitSkillContext as K,attachExplicitSkillCheckpoint as Q,prepareExplicitSkillContext as X,readExplicitSkillCheckpoint as Z,stripExplicitSkillCheckpoints as ee}from"./explicit-skill-context.js";import{createCancelledTurnRecoveryMessage as te,materializeCancelledTurnRecoveryMessages as se,stripCancelledTurnRecoveryMessages as oe}from"./cancelled-turn-recovery.js";import{CAPABILITY_HOST_MODES as ie,CAPABILITY_TOOL_ROLES as ne,buildEffectiveCapabilityTurnContext as ae,buildEffectiveCapabilityManifest as re,findCapabilityToolId as ce}from"./capability-manifest.js";import{buildCapabilityTurnReminder as le,buildChatSystemPromptFromManifest as ue}from"./system-prompt.js";import{buildCompactionCheckpointReminder as pe,buildCompactionToolState as he,COMPACTION_CHECKPOINT_VERSION as de,createCompactionCheckpoint as me,createCompactionSummary as ge,createCheckpointSemanticReminderProjection as ve,findLatestRealUserGoal as fe,resolveActiveCompactionConstraints as ye,TRANSCRIPT_MESSAGE_PROVENANCES as Ce}from"./compaction-checkpoint.js";import{buildCompactionSemanticModelContext as be,buildCompactionSemanticEvidenceRegistry as ke,mergeCompactionSemanticState as Te,replaceCompactionSemanticConstraints as Se,replaceCompactionSemanticGoal as xe,renderCompactionSemanticSummary as Ie,seedCompactionSemanticConstraints as Ee,seedLegacyCompactionSnapshotUncertainties as Me,validateCompactionModelDraft as we}from"./compaction-semantic-state.js";import{ACTIVE_TOOL_PROTOCOL_REPAIR_STATUS as Re,repairActiveToolProtocol as qe}from"./tool-protocol-repair.js";import{buildTranscriptToolset as Ae}from"../tool-bridge/transcript-tool.js";import{UserInputInteractionManager as Pe}from"../interaction/user-input-interaction-manager.js";import{buildUserInputTool as Ne}from"../tool-bridge/user-input-tool.js";const Oe=6e3,$e=3e5,je=1,De=1024,We="AI_InvalidToolInputError:",Le={startsNewGoalScope:!1,goal:null,constraints:[],decisions:[],completedWork:[],pendingWork:[],resources:[],runningSessions:[],uncertainties:[],resolutions:[],evidenceReviews:[]};function Fe(t){const s=[...t];if(s.length<=De)return t;const o=`…#${e("sha256").update(t).digest("hex").slice(0,16)}`;return`${s.slice(0,De-[...o].length).join("")}${o}`}function Ue(){return{abortController:new AbortController,execSessionIds:new Set,pendingToolCalls:new Map,completedStepResponses:new Map,toolExecutions:[],hadPotentialSideEffects:!1,aborted:!1,cancellationEventEmitted:!1,cancellationPersistenceAttempted:!1,cancellationPersisted:!1}}function Ve(e,t){e.completedStepResponses.set(`${t.callId}:${String(t.stepNumber)}`,[...t.response.messages])}function _e(e){return[...e.completedStepResponses.values()].flatMap(e=>[...e])}function ze(e){return e instanceof Error?e.message:String(e)}function Ge(e){const t=Z(e.message),[s]=ee([e.message]),o=s??e.message,i=void 0!==t&&"user"===o.role?t.snapshot.userPrompt:"string"==typeof o.content?o.content:JSON.stringify(a(o.content));return`message ${String(e.sequence)} ${o.role}: ${i}`}function Je(e){return JSON.stringify(e)??String(e)}function Be(e){const t=W(e);return{id:e.id,agentName:e.agentName,toolName:e.toolName,inputSummary:Je(t.input.value),resultSummary:Je(t.model),outcome:{kind:e.outcome.kind,...e.outcome.kind===q.cancelled&&void 0!==e.outcome.executionState?{executionState:e.outcome.executionState}:{}}}}function He(e){return"user"===e.role&&"string"==typeof e.content&&void 0!==H(e.content)||"assistant"===e.role&&B(e.content)}function Ye(e,t){const s=t.flatMap((e,t)=>{if("user"===e.role&&"string"==typeof e.content){const t=H(e.content);if(void 0!==t)return 0===t.length?[]:[L(t)]}return"assistant"===e.role&&B(e.content)?[]:[L(Ge({sequence:t,provenance:Ce[1],createdAt:new Date(0).toISOString(),message:e}))]});return{checkpointId:e,messageFingerprints:t.map(e=>JSON.stringify(e)),fragments:s,removableRawMessageCount:t.filter(e=>!He(e)).length}}function Ke(e,t,s){return!(1!==t?.version||t.id!==e.checkpointId||s.length<e.messageFingerprints.length)&&e.messageFingerprints.every((e,t)=>JSON.stringify(s[t])===e)}function Qe(e){return void 0===e?{messagesThroughSequence:-1,toolExecutionsThroughSequence:-1}:e.version===de?e.semanticEvidence:{messagesThroughSequence:e.transcript.messages.throughSequence,toolExecutionsThroughSequence:e.transcript.toolExecutions.throughSequence}}function Xe(e,t){return t.aborted?Promise.reject(t.reason):new Promise((s,o)=>{let i=!1;const n=e=>{i||(i=!0,t.removeEventListener("abort",a),e())},a=()=>n(()=>o(t.reason));t.addEventListener("abort",a,{once:!0}),Promise.resolve(e).then(e=>n(()=>s(e)),e=>n(()=>o(e)))})}function Ze(e){return ze($(e))}function et(e){return l.isInstance(e)||"string"==typeof e&&e.startsWith(We)?q.invalidInput:q.toolFailed}function tt(){return({steps:e})=>e.at(-1)?.toolResults.some(e=>j(e.output).kind===q.userRejected)??!1}function st(e){return/context[_ -]?length|context window|maximum context|token limit|too many tokens|prompt is too long|input is too long/i.test(ze(e))}function ot(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 it(e,t){return void 0===e&&void 0===t?void 0:(e??0)+(t??0)}function nt(e,t){const s=it(e.cachedInputTokens,t.cachedInputTokens),o=it(e.cacheWriteTokens,t.cacheWriteTokens),i=it(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!==i?{reasoningTokens:i}:{}}}function at(e,t){return void 0===t?e:void 0===e?t:Math.max(e,t)}function rt(e,t){const s=e.trim(),o=t.trim();return s.length>0&&o.length>0&&o.startsWith(s)}function ct(e,t){return void 0!==e?e:E(t)?T.timeout:T.runtime}function lt(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}]})}function ut(e,t,s){for(const o of Object.keys(t))e[o]=s}function pt(e,t,s){const o={[m]:ne.sessionCommand,[v]:ne.sessionPoll,[g]:ne.sessionList};for(const i of Object.keys(t)){const t=s.resolve(i),n=t?o[t.toolName]:void 0;n&&(e[i]=n)}}function ht(e,t){const s=[...e];for(let e=s.length-1;e>=0;e-=1){const o=s[e];if("user"===o?.role){s[e]={...o,content:"string"==typeof o.content?`${t}\n\n${o.content}`:[{type:"text",text:t},...o.content]};break}}return s}export class AgentSession{id;model;maxSteps;messages;onPersist;onReplace;onToolExecution;listPersistedToolExecutions;getPersistedToolExecution;listPersistedTranscriptMessages;commitPersistedCompaction;inMemoryToolExecutions=[];inMemoryTranscript=[];legacyV1ActiveSnapshot;compactionResources=new Map;contextWindow;compaction;turnTimeoutMs;onClose;providerOptions;structuredOutputProviderOptions;structuredOutputReasoning;onProviderOptionsChange;debugEvents;policy;systemPrompt;explicitSystemPrompt;capabilityContext;resolveDynamicCapabilityContext;capabilityManifest;lastCapabilityTurnContext;toolRoles;skillSummaries;skillLibrary;skillToolBuilt=!1;toolSourceAgentNames;gate=new _;userInputInteractions=new Pe;toolCoordinator=new V;tools;registry;userInputToolId;userInputTool;userInputAvailable=!1;sessionManager;sessionManagerInstanceId=t();compactionCheckpoint;emit;activeTurn;closePromise;closed=!1;sessionUsage={inputTokens:0,outputTokens:0,totalTokens:0};lastInputTokens;needsCompaction=!1;constructor(e){this.id=e.id,this.model=e.model,this.maxSteps=e.maxSteps;const t=e.initialMessages?lt(e.initialMessages):[];this.messages=[...qe(t).messages],this.legacyV1ActiveSnapshot=1===e.initialCheckpoint?.version?Ye(e.initialCheckpoint.id,this.messages):void 0,this.onPersist=e.onPersist,this.onReplace=e.onReplace,this.onToolExecution=e.onToolExecution,this.listPersistedToolExecutions=e.listToolExecutions,this.getPersistedToolExecution=e.getToolExecution,this.listPersistedTranscriptMessages=e.listTranscriptMessages,this.commitPersistedCompaction=e.commitCompaction,this.compactionCheckpoint=e.initialCheckpoint;let s=Qe(e.initialCheckpoint).messagesThroughSequence+1;for(const e of this.messages)He(e)||(this.inMemoryTranscript.push({sequence:s,provenance:Ce[0],createdAt:new Date(0).toISOString(),message:e}),s+=1);for(const t of e.initialCheckpoint?.resources??[])this.retainCompactionResource(t);this.contextWindow=e.contextWindow,this.compaction=e.compaction,this.turnTimeoutMs=e.turnTimeoutMs,this.onClose=e.onClose,this.providerOptions=e.providerOptions,this.structuredOutputProviderOptions=e.structuredOutputProviderOptions,this.structuredOutputReasoning=e.structuredOutputReasoning,this.onProviderOptionsChange=e.onProviderOptionsChange,this.debugEvents=e.debugEvents??!1,this.policy=e.policy,this.explicitSystemPrompt=e.systemPrompt,this.resolveDynamicCapabilityContext=e.resolveDynamicCapabilityContext,this.skillLibrary=e.skillLibrary,this.skillSummaries=e.skillLibrary?.list()??[],this.toolSourceAgentNames=new Set(e.sources.map(e=>e.agentName));const o=new w,i={},n=Ne({sessionId:this.id,isAvailable:()=>this.userInputAvailable,request:(e,t)=>this.requestUserInput(e,t)},o,this.toolCoordinator);this.userInputToolId=n.id,this.userInputTool=n.tools,ut(i,n.tools,ne.userInput);const a=e.readCheckpointTranscript?Ae(e.readCheckpointTranscript,o,this.toolCoordinator,`thread:${this.id}:transcript`):{};ut(i,a,ne.transcriptRead);const r=e.skillLibrary?this.buildSkillTools(o):{};ut(i,r,ne.skill);const c={...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e),emitEvent:e=>this.emit?.(e),coordinator:this.toolCoordinator},l=e.bashClassifier?{classifier:e.bashClassifier}:{},h=e.bash?d(e.bash,o,c,l):{};ut(i,h,ne.shell),e.bashSession&&(this.sessionManager=new y({maxSessions:e.bashSession.maxSessions,profile:e.bashSession.profile,env:b(e.bashSession.env??process.env),bufferCapacity:e.bashSession.bufferCapacity}));const m=e.bashSession&&this.sessionManager?f({workdir:e.bashSession.workdir,defaultYieldMs:e.bashSession.defaultYieldMs,maxOutputTokens:e.bashSession.maxOutputTokens,...e.bashSession.env?{env:e.bashSession.env}:{}},this.sessionManager,o,c,{...l,onSessionTouched:e=>this.activeTurn?.execSessionIds.add(e)}):{};pt(i,m,o);const g=e.agentInstall,v=g?p({catalog:g.catalog,install:async(e,t)=>{const s=await g.install(e,t);return s.outcome.ok&&s.refresh?(this.applyAgentRefresh(s.refresh),{...s.outcome,refreshApplied:!0}):s.outcome}},o,{...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e),coordinator:this.toolCoordinator}):{};ut(i,v,ne.agentInstall);const C=u(e.sources,{...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e),coordinator:this.toolCoordinator},o);ut(i,C.tools,ne.agent),this.tools={...a,...r,...h,...m,...v,...C.tools},this.registry=C.registry,this.toolRoles=i,this.capabilityContext=e.capabilityContext??{profile:e.bash?.profile.toolName??"no-shell",cwd:e.bash?.workdir??process.cwd(),platform:process.platform,...e.bash?{shellHints:e.bash.profile.systemPromptHints()}:{},agentCount:e.sources.length,...e.agentInstall?{agentOnboardingCatalog:e.agentInstall.catalog}:{}},this.capabilityManifest=this.compileCapabilityManifest(),this.systemPrompt=this.compileSystemPrompt(this.explicitSystemPrompt)}compileCapabilityManifest(){return re({tools:this.tools,toolRoles:this.toolRoles,resolveRoute:e=>this.registry.resolve(e),skills:this.skillSummaries,profile:this.capabilityContext.profile,hostMode:this.capabilityContext.hostMode??ie.embedded,cwd:this.capabilityContext.cwd,platform:this.capabilityContext.platform??process.platform,shellHints:this.capabilityContext.shellHints??[],agentCount:this.capabilityContext.agentCount,agentOnboardingCatalog:this.capabilityContext.agentOnboardingCatalog??[]})}compileSystemPrompt(e){const t=ue(this.capabilityManifest),s=e?.trim();return s?[t,"# 附加会话指令","以下指令可以补充任务偏好,但不能覆盖前述工具接地、能力清单和安全约束。",s].join("\n\n"):t}refreshCapabilityManifest(e){this.capabilityManifest=this.compileCapabilityManifest(),this.systemPrompt=this.compileSystemPrompt(e??this.explicitSystemPrompt)}setUserInputAvailable(e){if(this.userInputAvailable!==e){if(this.userInputAvailable=e,e)this.tools={...this.tools,...this.userInputTool};else{const e={...this.tools};delete e[this.userInputToolId],this.tools=e,this.userInputInteractions.cancelAll("当前客户端已撤销用户输入处理能力")}this.refreshCapabilityManifest()}}resolveUserInput(e,t){return this.userInputInteractions.resolve(e,t)}cancelUserInput(e,t){return this.userInputInteractions.cancel(e,t)}async requestUserInput(e,t){const s=this.activeTurn;if(!this.userInputAvailable||void 0===s||void 0===this.emit)return{status:"cancelled",reason:"当前无法向用户请求输入"};const o=Date.now(),i=this.interactionDeadlineMs(s,o);if(i<=o||t?.aborted)return{status:"cancelled",reason:"用户输入请求已超时"};const n=this.userInputInteractions.request(e,new Date(i).toISOString()),a=()=>{this.userInputInteractions.cancel(n.requestId,"本轮已终止")};t?.addEventListener("abort",a,{once:!0}),this.emit({type:"user-input-required",requestId:n.requestId,form:n.form,expiresAt:n.expiresAt});try{return await n.result}finally{t?.removeEventListener("abort",a);const e=await n.result;this.emit?.({type:"user-input-settled",requestId:n.requestId,status:e.status})}}trackPendingToolCall(e,t,s,o,i){const n=e.pendingToolCalls.get(t);if(n&&void 0===i)return n;const a=this.registry.resolve(s),r={toolCallId:t,agentName:a?.agentName??s,toolName:a?.toolName??s,input:o,potentialSideEffect:!0!==a?.annotations?.readOnlyHint||!0===a.annotations.destructiveHint,resources:i??n?.resources??[]};return e.pendingToolCalls.set(t,r),r}buildSkillTools(e){return this.skillToolBuilt=!0,h(()=>{if(!this.skillLibrary)throw new Error("skill library 不可用");return this.skillLibrary},e,this.toolCoordinator)}applyAgentRefresh(e){if(!this.toolSourceAgentNames.has(e.source.agentName)){const t=u([e.source],{...this.policy?{policy:this.policy}:{},requestApproval:e=>this.requestApproval(e),coordinator:this.toolCoordinator},this.registry);ut(this.toolRoles,t.tools,ne.agent),this.tools={...this.tools,...t.tools},this.toolSourceAgentNames.add(e.source.agentName)}if(e.skillLibrary&&(this.skillLibrary=e.skillLibrary,this.skillSummaries=e.skillLibrary.list(),!this.skillToolBuilt)){const e=this.buildSkillTools(this.registry);ut(this.toolRoles,e,ne.skill),this.tools={...this.tools,...e}}e.capabilityContext&&(this.capabilityContext=e.capabilityContext),this.refreshCapabilityManifest(e.systemPrompt)}async*send(e){if(this.closed)throw new Error("session is closed");if(this.activeTurn)throw new Error("session already has an active turn");const t=n(e),s=new Y;this.emit=e=>s.push(e);const o=Ue();this.activeTurn=o,this.runTurn(s,o,t).catch(e=>{s.push({type:"error",stage:"execute",message:ze(e)}),s.close()});try{for await(const e of s)yield e}finally{this.activeTurn===o&&this.abandonTurn(o),void 0!==this.emit&&(this.emit=void 0)}}async*compact(e="manual"){if(this.closed)throw new Error("session is closed");if(this.activeTurn)throw new Error("session already has an active turn");const t=new Y;this.emit=e=>t.push(e);const s=Ue();this.activeTurn=s,this.runCompactionTurn(t,s,e).catch(e=>{t.push({type:"error",stage:"plan",message:ze(e)}),t.close()});try{for await(const e of t)yield e}finally{this.activeTurn===s&&this.abandonTurn(s),void 0!==this.emit&&(this.emit=void 0)}}async runTurn(e,s,n){const a=Date.now();let l,u;const p=i(n.text,n.attachments);try{if(this.debug(e,"turn","start",a,{messages:this.messages.length,tools:Object.keys(this.tools).length,maxSteps:this.maxSteps,...n.attachments.length>0?{attachments:n.attachments.length}:{},...void 0!==this.contextWindow?{contextWindow:this.contextWindow}:{},...void 0!==this.lastInputTokens?{lastInputTokens:this.lastInputTokens}:{}}),this.shouldAutoCompact()){this.debug(e,"compaction","auto requested before turn",a,{messages:this.messages.length,...void 0!==this.lastInputTokens?{lastInputTokens:this.lastInputTokens}:{}});try{await this.runCompaction(e,"auto",s)}catch(t){return void e.push({type:"error",stage:"plan",message:ze(t)})}}if(s.aborted||s.abortController.signal.aborted)return l=this.messages.length,this.messages.push({role:"user",content:p}),void this.persistCancelledTurn(e,s,l,[],a);e.push({type:"message-start",messageId:t()}),void 0!==this.turnTimeoutMs&&(s.expiresAt=new Date(Date.now()+this.turnTimeoutMs).toISOString(),u=setTimeout(()=>{s.abortController.signal.aborted||(this.captureCancellationActivity(s),s.aborted=!0,s.cancellationReason=T.timeout,this.gate.abortAll("本轮运行超时"),this.userInputInteractions.cancelAll("本轮运行超时"),s.abortController.abort(S))},this.turnTimeoutMs));let i=0;const h=X({rawInput:n.text,skillSummaries:this.skillSummaries,skillLibrary:this.skillLibrary}),d={role:"user",content:p},m=h.skillNames.length>0?Q(d,h):d;let g={};try{g=await Xe(this.resolveDynamicCapabilityContext?.(s.abortController.signal)??{},s.abortController.signal)??{}}catch(t){if(this.isTurnAborted(s))return l=this.messages.length,this.messages.push(m),s.cancellationReason=ct(s.cancellationReason,s.abortController.signal.reason??t),void this.persistCancelledTurn(e,s,l,[],a);this.debug(e,"turn","dynamic capability context unavailable",a,{message:ze(t)})}const v=ae(this.capabilityManifest,{explicitSkillNames:h.skillNames,...g.ruleIds?{ruleIds:g.ruleIds}:{},...g.vcs?{vcs:g.vcs}:{},sessions:(this.sessionManager?.list()??[]).map(e=>({sessionId:e.sessionId,state:e.state}))});this.lastCapabilityTurnContext=v;const f=le(v);for(;;){l=this.messages.length,this.messages.push(m);const t=ce(this.capabilityManifest,ne.transcriptRead),n=this.compactionCheckpoint?pe(this.compactionCheckpoint,this.sessionManagerInstanceId,t):void 0,u=ee(M(se(this.messages))),p=ht(h.skillNames.length>0?K(u,h):u,n?`${f}\n\n${n}`:f);this.debug(e,"model","calling streamText",a,{messages:this.messages.length,tools:Object.keys(this.tools).length,contextRecoveryAttempts:i,...void 0!==this.turnTimeoutMs?{timeoutMs:this.turnTimeoutMs}:{}});let d=[];const g=()=>c({model:this.model,system:this.systemPrompt,messages:p,tools:this.tools,prepareStep:({messages:e})=>({messages:o(e)}),stopWhen:[r(this.maxSteps),tt()],toolApproval:async({toolCall:e})=>{this.trackPendingToolCall(s,e.toolCallId,e.toolName,e.input),await this.toolCoordinator.prepare(e.toolCallId,e.toolName,e.input)},onLanguageModelCallStart:({callId:e})=>this.toolCoordinator.startBatch(e),onLanguageModelCallEnd:({callId:e,content:t})=>this.toolCoordinator.sealBatch(e,t.flatMap(e=>"tool-call"!==e.type||!0===e.providerExecuted||"invalid"in e&&!0===e.invalid?[]:[{toolCallId:e.toolCallId,toolId:e.toolName}])),abortSignal:s.abortController.signal,...this.providerOptions?{providerOptions:this.providerOptions}:{},onError:()=>{},onStepEnd:e=>{Ve(s,e),d=_e(s)},onAbort:({steps:e})=>{for(const t of e)Ve(s,t);d=_e(s)}});let v;try{v=g()}catch(t){if(this.isTurnAborted(s)||E(t))return s.aborted=!0,s.cancellationReason=ct(s.cancellationReason,t),void this.persistCancelledTurn(e,s,l,[],a);if(!st(t))throw t;this.needsCompaction=!0,this.messages.splice(l);const o=i<1,n=!!o&&await this.recoverFromContextError(e,s);if(this.isTurnAborted(s))return l=this.messages.length,this.messages.push(m),void this.persistCancelledTurn(e,s,l,[],a);if(n&&o){i+=1;continue}return this.persistContextFailure(e,m,!1,!1,a),void e.push({type:"error",stage:"execute",message:ze(t)})}this.debug(e,"model","streamText returned",a);let y,C,b,k,S,x,I="",w="",R=!1,A=0,$=!1,j=!1;const W=this.scheduleDebug(e,"model","waiting for first stream event",a,{messages:this.messages.length});try{for await(const t of v.fullStream){if(this.closed){s.aborted=!0,s.cancellationReason??=T.runtime;break}switch(j||(j=!0,this.clearDebugTimer(W),this.debug(e,"model","first stream event",a,{part:t.type})),t.type){case"reasoning-start":e.push({type:"reasoning-start",reasoningId:t.id});break;case"reasoning-delta":e.push({type:"reasoning-delta",reasoningId:t.id,delta:t.text});break;case"reasoning-end":e.push({type:"reasoning-end",reasoningId:t.id});break;case"text-delta":if(!R){const s=w+t.text;if(rt(s,h.userPrompt)){w=s;break}if(w.length>0){const s=w+t.text;w="",I+=s,e.push({type:"text-delta",delta:s});break}}I+=t.text,e.push({type:"text-delta",delta:t.text});break;case"tool-call":{R=!0,w="";const o=this.trackPendingToolCall(s,t.toolCallId,t.toolName,t.input,this.toolCoordinator.describeResources(t.toolName,t.input));e.push({type:"tool-call",toolCallId:t.toolCallId,agentName:o.agentName,toolName:o.toolName,input:t.input});break}case"tool-result":{const o=this.registry.resolve(t.toolName),i=s.pendingToolCalls.get(t.toolCallId),n=N(t.output)?t.output:O(t.output),a=n.outcome,r=n.display,c=D({toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,input:i?.input,result:n});this.persistToolExecution(c,s),!0===i?.potentialSideEffect&&a.kind!==q.userRejected&&a.kind!==q.policyDenied&&a.kind!==q.invalidInput&&(s.hadPotentialSideEffects=!0),a.kind===q.success&&void 0!==i&&this.rememberCompactionResources(c,i.resources),s.pendingToolCalls.delete(t.toolCallId),e.push({type:"tool-result",toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,executionId:c.id,outcome:a,display:r,output:r,isError:a.kind!==q.success}),s.aborted||s.abortController.signal.aborted||a.kind!==q.userRejected||(x=Ze(t.output));break}case"tool-error":{const o=this.registry.resolve(t.toolName),i=Ze(t.error),n=P(et(t.error),i,{raw:t.error}),a=D({toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,input:s.pendingToolCalls.get(t.toolCallId)?.input,result:n});this.persistToolExecution(a,s),!0===s.pendingToolCalls.get(t.toolCallId)?.potentialSideEffect&&n.outcome.kind!==q.invalidInput&&(s.hadPotentialSideEffects=!0),s.pendingToolCalls.delete(t.toolCallId),e.push({type:"tool-result",toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,executionId:a.id,outcome:n.outcome,display:n.display,output:i,isError:!0});break}case"finish-step":{const s=ot(t.usage);C=at(C,s.inputTokens),A+=1,k=t.finishReason;const o=t.performance.outputTokensPerSecond??t.performance.effectiveOutputTokensPerSecond;void 0!==o&&Number.isFinite(o)&&(b=o),e.push({type:"step-finish",finishReason:t.finishReason,usage:s});break}case"finish":!R&&w.length>0&&(I+=w,e.push({type:"text-delta",delta:w}),w=""),y=ot(t.totalUsage);break;case"error":if(this.isTurnAborted(s)||E(t.error)){s.aborted=!0,s.cancellationReason=ct(s.cancellationReason,t.error);break}$=st(t.error),$&&(this.needsCompaction=!0),S=ze(t.error);break;case"abort":s.aborted=!0,s.cancellationReason=ct(s.cancellationReason,t.reason)}}}catch(e){this.isTurnAborted(s)||E(e)?(s.aborted=!0,s.cancellationReason=ct(s.cancellationReason,e)):($=st(e),$&&(this.needsCompaction=!0),S=ze(e))}finally{this.clearDebugTimer(W)}if(this.debug(e,"model","fullStream finished",a,{textChars:I.length}),void 0!==S){if(this.messages.splice(l),$){const t=0===I.length&&!R,o=t&&i<1,n=!(!o&&t)&&await this.recoverFromContextError(e,s);if(this.isTurnAborted(s))return l=this.messages.length,this.messages.push(m),void this.persistCancelledTurn(e,s,l,[],a);if(t&&n&&o){i+=1;continue}const r=t?"":";本次尝试已有内容或操作开始执行,为避免重复执行,未自动重试";return this.persistContextFailure(e,m,I.length>0,R,a),void e.push({type:"error",stage:"execute",message:`${S}${r}`})}return void e.push({type:"error",stage:"execute",message:S})}if(s.aborted||s.abortController.signal.aborted)return s.cancellationReason=ct(s.cancellationReason,s.abortController.signal.reason),void this.persistCancelledTurn(e,s,l,d,a);let L;this.debug(e,"model","awaiting response messages",a);const F=this.scheduleDebug(e,"model","still awaiting response messages",a);try{L=(await v.steps).flatMap(e=>e.response.messages)}catch(t){if(this.clearDebugTimer(F),this.isTurnAborted(s)||E(t))return s.aborted=!0,s.cancellationReason=ct(s.cancellationReason,t),void this.persistCancelledTurn(e,s,l,d,a);const o=st(t);if(o&&(this.needsCompaction=!0),this.messages.splice(l),o){const o=0===I.length&&!R,n=o&&i<1,r=!(!n&&o)&&await this.recoverFromContextError(e,s);if(this.isTurnAborted(s))return l=this.messages.length,this.messages.push(m),void this.persistCancelledTurn(e,s,l,[],a);if(o&&r&&n){i+=1;continue}const c=o?"":";本次尝试已有内容或操作开始执行,为避免重复执行,未自动重试";return this.persistContextFailure(e,m,I.length>0,R,a),void e.push({type:"error",stage:"execute",message:`${ze(t)}${c}`})}return void e.push({type:"error",stage:"execute",message:ze(t)})}finally{this.clearDebugTimer(F)}if(this.debug(e,"model","response messages ready",a,{responseMessages:L.length}),this.isTurnAborted(s))return s.cancellationReason=ct(s.cancellationReason,s.abortController.signal.reason),void this.persistCancelledTurn(e,s,l,d,a);const U=lt(L);if(this.messages.push(...U),void 0!==x){const t=0===I.length?x:`\n${x}`;I+=t,e.push({type:"text-delta",delta:t}),this.messages.push({role:"assistant",content:x})}this.debug(e,"persist","persisting messages",a,{appendedMessages:this.messages.length-l}),this.persistMessages(this.messages.slice(l)),this.debug(e,"persist","messages persisted",a,{totalMessages:this.messages.length}),y&&(this.sessionUsage=nt(this.sessionUsage,y));const V=C??y?.inputTokens;void 0!==V&&(this.lastInputTokens=V);const _=A>=this.maxSteps&&"tool-calls"===k;return void e.push({type:"message-finish",text:I,...y?{totalUsage:y}:{},sessionUsage:{...this.sessionUsage},...void 0!==C?{contextInputTokens:C}:{},...void 0!==b?{outputTokensPerSecond:b}:{},..._?{stoppedAtStepLimit:!0}:{}})}}catch(t){if(this.isTurnAborted(s)||E(t))return s.aborted=!0,s.cancellationReason=ct(s.cancellationReason,s.abortController.signal.reason??t),void(void 0!==l?this.persistCancelledTurn(e,s,l,[],a):this.emitCancellation(e,s));void 0!==l&&this.messages.splice(l),this.debug(e,"turn","error",a,{message:ze(t)}),e.push({type:"error",stage:"execute",message:ze(t)})}finally{void 0!==u&&clearTimeout(u),this.toolCoordinator.finishTurn(),this.activeTurn===s&&(this.activeTurn=void 0),e.close()}}persistMessages(e){if(this.onPersist?.(e),void 0!==this.listPersistedTranscriptMessages)return;let t=(this.inMemoryTranscript.at(-1)?.sequence??-1)+1;const s=(new Date).toISOString();for(const o of e)He(o)||(this.inMemoryTranscript.push({sequence:t,provenance:Ce[0],createdAt:s,message:o}),t+=1)}persistContextFailure(e,t,s,o,i){const n=["本轮因上下文窗口溢出而中断,未自动重放。"];s&&n.push("本轮已产生部分文本,持久历史仅保留此中断标记。"),o&&n.push("本轮已有操作开始执行,部分结果可能已经生效且不会自动撤销,请先检查实际结果。");const a=this.messages.length;this.messages.push(t),this.messages.push({role:"assistant",content:n.join("")}),this.debug(e,"persist","persisting context-overflow marker",i,{hadToolActivity:o,producedText:s});try{this.persistMessages(this.messages.slice(a))}catch(t){this.messages.splice(a),e.push({type:"error",stage:"execute",message:`上下文溢出记录持久化失败: ${ze(t)}`})}}persistToolExecution(e,t){this.onToolExecution?.(e),this.inMemoryToolExecutions.push(e),t.toolExecutions.push(e)}rememberCompactionResources(e,t){for(const s of t){const t={key:Fe(s.key),mode:s.mode,evidenceToolCallId:e.toolCallId,evidenceExecutionId:e.id};this.retainCompactionResource(t)}}retainCompactionResource(e){const t=this.compactionResources.get(e.key),s=void 0===t||e.mode===U.write||t.mode!==U.write?e:t;this.compactionResources.delete(e.key),this.compactionResources.set(e.key,s)}interactionDeadlineMs(e,t){const s=void 0===e.expiresAt?Number.POSITIVE_INFINITY:Date.parse(e.expiresAt);return Math.min(t+$e,Number.isFinite(s)?s:Number.POSITIVE_INFINITY)}requestApproval(e){const s=t(),o=this.gate.request(s);if(void 0===this.emit)return this.gate.resolve(s,{approved:!1,reason:"approval event could not be delivered"}),o;const i=void 0===this.activeTurn?void 0:new Date(this.interactionDeadlineMs(this.activeTurn,Date.now())).toISOString();return this.emit({type:"confirmation-required",approvalId:s,agentName:e.agentName,toolName:e.toolName,input:e.input,...void 0!==i?{expiresAt:i}:{},...e.reason?{reason:e.reason}:{},...void 0!==e.explanation?{explanation:e.explanation}:{}}),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 ee(oe(this.messages))}getToolExecutions(e={},t=!1){const s=this.listPersistedToolExecutions?this.listPersistedToolExecutions(e):this.inMemoryToolExecutions.map((e,t)=>({...e,sequence:t})).filter(t=>t.sequence>(e.afterSequence??-1)&&(void 0===e.toolCallId||t.toolCallId===e.toolCallId)).slice(0,e.limit??100);return t?s:s.map(e=>({...F(e),sequence:e.sequence}))}getToolExecution(e,t=!1){const s=this.getPersistedToolExecution?.(e),o=s??(()=>{const t=this.inMemoryToolExecutions.findIndex(t=>t.id===e),s=this.inMemoryToolExecutions[t];return s?{...s,sequence:t}:void 0})();return!o||t?o:{...F(o),sequence:o.sequence}}getCapabilityManifest(){return structuredClone(this.capabilityManifest)}getCapabilityTurnContext(){return this.lastCapabilityTurnContext?structuredClone(this.lastCapabilityTurnContext):void 0}getContextWindow(){return this.contextWindow}getSessionUsage(){return{...this.sessionUsage}}getSkillSummaries(){return this.skillSummaries}setProviderOptions(e){this.providerOptions=e,this.onProviderOptionsChange?.(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)}listCompactionTranscriptEvidence(){const e=this.listPersistedTranscriptMessages;if(void 0===e)return[...this.inMemoryTranscript];const t=[];let s=-1;for(;;){const o=e({afterSequence:s,limit:500});t.push(...o);const i=o.at(-1)?.sequence;if(o.length<500||void 0===i||i<=s)break;s=i}return t}listCompactionToolEvidence(){const e=this.listPersistedToolExecutions;if(void 0===e)return this.inMemoryToolExecutions.map((e,t)=>({...e,sequence:t}));const t=[];let s=-1;for(;;){const o=e({afterSequence:s,limit:500});t.push(...o);const i=o.at(-1)?.sequence;if(o.length<500||void 0===i||i<=s)break;s=i}return t}latestExplicitSkillNames(e,t){if(void 0===t)return this.compactionCheckpoint?.context.explicitSkillNames??[];const s=e.find(e=>e.sequence===t);return void 0===s?[]:Z(s.message)?.snapshot.skillNames??[]}currentCompactionRunningWork(){const e=(this.compactionCheckpoint?.runningWork??[]).filter(e=>e.managerInstanceId!==this.sessionManagerInstanceId).map(e=>({...e,recoverability:"live"===e.recoverability?"stale":e.recoverability})),t=(new Date).toISOString();return[...e,...(this.sessionManager?.list()??[]).map(e=>({managerInstanceId:this.sessionManagerInstanceId,sessionId:e.sessionId,state:e.state,recoverability:C(e.state)?"unavailable":"live",commandPreview:e.commandPreview,workdir:e.workdir,observedAt:t,wallTimeMs:e.wallTimeMs,...void 0!==e.exitCode?{exitCode:e.exitCode}:{},...void 0!==e.terminationCause?{terminationCause:e.terminationCause}:{},...void 0!==e.cleanupError?{cleanupError:e.cleanupError}:{}}))].slice(-128)}captureCompactionEvidence(){const e=this.listCompactionTranscriptEvidence(),t=this.listCompactionToolEvidence(),s=[...this.compactionResources.values()].slice(-256),o=[...this.currentCompactionRunningWork()],i="legacy_snapshot"===this.compactionCheckpoint?.transcript.completeness||e.some(e=>"legacy_snapshot"===e.provenance)?"legacy_snapshot":"complete",n=this.compactionCheckpoint?.goal,a=fe(e),r=a??n,c=void 0!==a&&(void 0===n||a.sourceSequence>n.sourceSequence)?a.sourceSequence:void 0,l=this.compactionCheckpoint?.version===de?this.compactionCheckpoint.semanticState:void 0,u=Ee(l,this.compactionCheckpoint?.constraints??[]),p=1===this.compactionCheckpoint?.version,h=void 0!==this.legacyV1ActiveSnapshot&&Ke(this.legacyV1ActiveSnapshot,this.compactionCheckpoint,this.messages),d=p?h&&void 0!==this.legacyV1ActiveSnapshot?Me(u,{checkpointId:this.legacyV1ActiveSnapshot.checkpointId,fragments:this.legacyV1ActiveSnapshot.fragments}):{state:u,requiredItemIds:[],transcriptFragments:[],complete:!1}:{state:u,requiredItemIds:[],transcriptFragments:[],complete:!0},m=d.state,g=Qe(this.compactionCheckpoint),v=g.messagesThroughSequence,f=g.toolExecutionsThroughSequence,y=e.filter(e=>e.sequence>v),C=new Set(y.filter(e=>e.provenance===Ce[1]&&He(e.message)).map(e=>e.sequence)),b=void 0===r?void 0:e.find(e=>e.sequence===r.sourceSequence),k=ke({messages:[...y.filter(e=>!C.has(e.sequence)),...void 0===b||y.includes(b)?[]:[b]].map(e=>({sequence:e.sequence,role:e.message.role,summary:Ge(e)})),toolExecutions:t.filter(e=>e.sequence>f).map(Be),resources:s.slice().reverse().map(e=>({key:e.key,mode:e.mode})),runningSessions:[]}),T=be(k,m,48e3);return{transcript:e,toolExecutions:t,resources:s,runningWork:o,completeness:i,goal:r,newGoalSourceSequence:c,previousSemanticState:m,registry:k,modelContext:T.prompt,presentedEvidenceIds:T.includedEvidenceIds,expectedActiveMessages:[...this.messages],expectedLatestCheckpointId:this.compactionCheckpoint?.id,previousSemanticEvidenceWatermarks:g,autoCoveredMessageSequences:C,legacySnapshotMigrationComplete:d.complete,legacySnapshotRequiredItemIds:d.requiredItemIds,legacySnapshotTranscriptFragments:d.transcriptFragments,legacySnapshotRemovableRawMessageCount:d.complete&&h?this.legacyV1ActiveSnapshot?.removableRawMessageCount??0:0,evidenceWatermarks:{transcriptMessagesThroughSequence:Math.max(this.compactionCheckpoint?.transcript.messages.throughSequence??-1,e.at(-1)?.sequence??-1),toolExecutionsThroughSequence:Math.max(this.compactionCheckpoint?.transcript.toolExecutions.throughSequence??-1,t.at(-1)?.sequence??-1)}}}buildCompactionDraft(t,s,o,i){const n=we({draft:s??Le,evidenceRegistry:t.registry,presentedEvidenceIds:t.presentedEvidenceIds,...void 0!==t.previousSemanticState?{previousState:t.previousSemanticState}:{},...void 0!==t.goal?{harnessGoal:{verbatimRequest:t.goal.verbatimRequest,sourceSequence:t.goal.sourceSequence}}:{}});let a=Te(t.previousSemanticState,n,{startsNewGoalScope:n.startsNewGoalScope});if(void 0===s){const e=ye(t.transcript,this.compactionCheckpoint?.constraints??[]),s=JSON.stringify(e)!==JSON.stringify(this.compactionCheckpoint?.constraints??[]);a=Se(a,e);const o=void 0===this.compactionCheckpoint?.goal?t.goal:void 0===t.goal||void 0===t.newGoalSourceSequence||s?this.compactionCheckpoint.goal:t.goal;a=xe(a,o)}const r=a.constraints.flatMap(e=>{const t=e.provenance.find(e=>"message"===e.kind)?.messageSequence;return null==t?[]:[{quote:e.text,sourceSequence:t}]}),c=a.goal?.provenance.find(e=>"message"===e.kind)?.messageSequence,l=(null==c?void 0:fe(t.transcript.filter(e=>e.sequence===c)))??this.compactionCheckpoint?.goal??t.goal,u=void 0!==l&&l.sourceSequence!==this.compactionCheckpoint?.goal?.sourceSequence?l.sourceSequence:void 0,p=o?Ie(a):void 0,h=void 0!==p?ge(p):void 0!==i?{status:"fallback",reason:i}:{status:"skipped"},d=new Set(ve(a).items.map(e=>e.itemId)),m=0===t.legacySnapshotRequiredItemIds.length||t.legacySnapshotRequiredItemIds.every(e=>d.has(e)),g=he(t.transcript.map(e=>e.message),t.toolExecutions,32,t.completeness);return{draft:{...l?{goal:l}:{},constraints:[...r],resources:[...t.resources],toolState:g,runningWork:[...t.runningWork],context:{cwd:this.capabilityManifest.dynamicContext.cwd,stableRuleIds:[...this.capabilityManifest.stableContext.rules],systemPromptSha256:e("sha256").update(this.systemPrompt).digest("hex"),skills:this.capabilityManifest.skills.map(e=>({name:e.name,source:e.source})),explicitSkillNames:[...this.latestExplicitSkillNames(t.transcript,u)]},summary:h},semanticState:a,semanticSummaryText:p,semanticRejectionCount:n.rejections.length,legacySnapshotReminderCoverageComplete:m,coveredEvidenceIds:n.coveredEvidenceIds,expectedActiveMessages:t.expectedActiveMessages,expectedLatestCheckpointId:t.expectedLatestCheckpointId,evidenceWatermarks:t.evidenceWatermarks,legacySnapshotTranscriptFragments:t.legacySnapshotTranscriptFragments}}async runCompactionTurn(e,t,s){try{await this.runCompaction(e,s,t,!0)}catch(t){e.push({type:"error",stage:"plan",message:ze(t)})}finally{this.activeTurn===t&&(this.activeTurn=void 0),e.close()}}advanceCompactionSemanticEvidenceWatermarks(e,t){const s=e.previousSemanticEvidenceWatermarks;if(0===t.length)return s;const o=new Set(t.filter(t=>e.presentedEvidenceIds.includes(t))),i=new Map(e.registry.flatMap(e=>"message"===e.provenance.kind&&null!==e.provenance.messageSequence?[[e.provenance.messageSequence,e.evidenceId]]:[]));let n=s.messagesThroughSequence;for(let t=s.messagesThroughSequence+1;;t+=1){if(e.autoCoveredMessageSequences.has(t)){n=t;continue}const s=i.get(t);if(void 0===s||!o.has(s))break;n=t}const a=new Map(e.registry.flatMap(e=>"tool_execution"===e.provenance.kind&&null!==e.provenance.toolExecutionId?[[e.provenance.toolExecutionId,e.evidenceId]]:[])),r=new Map(e.toolExecutions.map(e=>[e.sequence,e]));let c=s.toolExecutionsThroughSequence;for(let e=s.toolExecutionsThroughSequence+1;;e+=1){const t=r.get(e);if(void 0===t)break;const s=a.get(t.id);if(void 0===s||!o.has(s))break;c=e}return{messagesThroughSequence:n,toolExecutionsThroughSequence:c}}countContiguousPresentedMessageEvidence(e){if(!e.legacySnapshotMigrationComplete)return 0;const t=new Set(e.presentedEvidenceIds),s=new Map(e.registry.flatMap(e=>"message"===e.provenance.kind&&null!==e.provenance.messageSequence?[[e.provenance.messageSequence,e.evidenceId]]:[]));let o=e.legacySnapshotRemovableRawMessageCount;for(let i=e.previousSemanticEvidenceWatermarks.messagesThroughSequence+1;;i+=1){if(e.autoCoveredMessageSequences.has(i))continue;const n=s.get(i);if(void 0===n||!t.has(n))break;o+=1}return o}async runCompaction(e,t,s,o=!1){const i=Date.now(),n=this.compaction,a=n?.strategy??"summarize";if(this.debug(e,"compaction","start",i,{reason:t,messages:this.messages.length,timeoutMs:n?.timeoutMs??12e4,maxOutputTokens:n?.maxOutputTokens??8192}),e.push({type:"compaction-start",reason:t}),this.isTurnAborted(s))return o&&void 0!==s&&this.emitCancellation(e,s),!1;if(!n)return e.push({type:"context-compacted",reason:t,strategy:a,removed:0,kept:this.messages.length}),!1;const r=this.lastInputTokens;let c,l=n.strategy;const u=s?.abortController.signal,p=Date.now(),h=this.captureCompactionEvidence();if(this.debug(e,"compaction","evidence ready",p,{transcriptMessages:h.transcript.length,toolExecutions:h.toolExecutions.length}),1===this.compactionCheckpoint?.version&&void 0===this.commitPersistedCompaction)return this.debug(e,"compaction","legacy V1 snapshot requires durable transcript",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;if(!h.legacySnapshotMigrationComplete)return this.debug(e,"compaction","legacy V1 snapshot migration incomplete",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;const d=this.countContiguousPresentedMessageEvidence(h);let m;try{const t=Date.now();try{m=await G({messages:this.messages,strategy:l,keepRecentTurns:n.keepRecentTurns,keepRecentTokens:n.keepRecentTokens,model:this.model,semanticEvidencePrompt:h.modelContext,maxRemovedTranscriptMessages:d,...void 0!==n.timeoutMs?{timeoutMs:n.timeoutMs}:{},...void 0!==n.maxOutputTokens?{maxOutputTokens:n.maxOutputTokens}:{},...this.structuredOutputReasoning?{structuredOutputReasoning:this.structuredOutputReasoning}:{},...this.structuredOutputProviderOptions?{structuredOutputProviderOptions:this.structuredOutputProviderOptions}:{},...u?{abortSignal:u}:{}})}finally{this.debug(e,"compaction","draft generation finished",t,{strategy:l})}if("summarize"===l&&m.removed>0&&void 0===m.semanticDraft)throw new J(z.missingObject,"structured compaction draft is missing")}catch(t){if(this.isTurnAborted(s))return o&&void 0!==s&&this.emitCancellation(e,s),!1;if("summarize"!==l||!J.isInstance(t))throw t;this.debug(e,"compaction","summarize failed, fallback to truncate",i,{message:ze(t)}),c="structured checkpoint generation failed",l="truncate";const a=Date.now();m=await G({messages:this.messages,strategy:l,keepRecentTurns:n.keepRecentTurns,keepRecentTokens:n.keepRecentTokens,model:this.model,maxRemovedTranscriptMessages:d,...u?{abortSignal:u}:{}}),this.debug(e,"compaction","truncate fallback finished",a)}if(this.isTurnAborted(s))return o&&void 0!==s&&this.emitCancellation(e,s),!1;const g=m.removed>0||m.truncatedTools>0,v=Date.now(),f=g?this.buildCompactionDraft(h,m.semanticDraft,"summarize"===l&&m.removed>0,c):void 0;if(this.debug(e,"compaction","checkpoint validation finished",v,{semanticRejections:f?.semanticRejectionCount??0}),void 0!==f&&f.semanticRejectionCount>0&&this.debug(e,"compaction","rejected ungrounded semantic claims",i,{count:f.semanticRejectionCount}),void 0!==f&&!f.legacySnapshotReminderCoverageComplete)return this.debug(e,"compaction","legacy V1 snapshot exceeds reminder projection",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;const y=qe(m.messages),C=[...y.messages];if(y.status===Re.repaired&&this.debug(e,"compaction","repaired malformed active Tool protocol",i,{removedToolCallIds:y.removedToolCallIds.join(","),removedToolResultIds:y.removedToolResultIds.join(",")}),1===this.compactionCheckpoint?.version&&C.some(He))return this.debug(e,"compaction","legacy V1 snapshot remains in active history",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;const b=e=>Buffer.byteLength(JSON.stringify(e),"utf8"),k=g&&b(C)<b(this.messages),T=k?C:[...this.messages],S=k?this.advanceCompactionSemanticEvidenceWatermarks(h,f?.coveredEvidenceIds??[]):h.previousSemanticEvidenceWatermarks;let x;if(k){const t=Date.now();if(void 0!==this.commitPersistedCompaction){if(void 0===f)throw new Error("compaction checkpoint snapshot is missing");x=this.commitPersistedCompaction({messages:T,expectedActiveMessages:f.expectedActiveMessages,expectedLatestCheckpointId:f.expectedLatestCheckpointId,draft:f.draft,semanticState:f.semanticState,semanticEvidenceWatermarks:S,evidenceWatermarks:f.evidenceWatermarks,...f.legacySnapshotTranscriptFragments.length>0?{legacySnapshotTranscriptFragments:f.legacySnapshotTranscriptFragments}:{}})}else{if(void 0===f)throw new Error("compaction checkpoint snapshot is missing");const e=this.compactionCheckpoint;x=me({draft:f.draft,semanticState:f.semanticState,semanticEvidence:S,generation:(e?.generation??0)+1,transcript:{messages:{fromSequenceExclusive:e?.transcript.messages.throughSequence??-1,throughSequence:f.evidenceWatermarks.transcriptMessagesThroughSequence},toolExecutions:{fromSequenceExclusive:e?.transcript.toolExecutions.throughSequence??-1,throughSequence:f.evidenceWatermarks.toolExecutionsThroughSequence},completeness:h.completeness},...void 0!==e?{previousCheckpointId:e.id}:{}}),this.onReplace?.(T)}this.debug(e,"compaction","checkpoint commit finished",t,{durable:void 0!==this.commitPersistedCompaction}),this.messages.splice(0,this.messages.length,...T),void 0!==x&&(this.compactionCheckpoint=x),this.lastInputTokens=void 0,this.needsCompaction=!1}return e.push({type:"context-compacted",reason:t,strategy:l,removed:k?m.removed:0,kept:T.length,...k&&m.truncatedTools>0?{truncatedTools:m.truncatedTools}:{},...void 0!==r?{beforeInputTokens:r}:{},...void 0!==x?{checkpointId:x.id,checkpointGeneration:x.generation,checkpointSummaryStatus:x.summary.status}:{}}),this.debug(e,"compaction","finish",i,{reason:t,strategy:l,removed:k?m.removed:0,kept:T.length,truncatedTools:k?m.truncatedTools:0,progressed:k}),k}async recoverFromContextError(e,t){if(!this.compaction?.enabled)return!1;try{return await this.runCompaction(e,"auto",t)}catch(t){return e.push({type:"error",stage:"plan",message:ze(t)}),!1}}isTurnAborted(e){return!0===e?.aborted||!0===e?.abortController.signal.aborted}execSessionIds(e){return[...e.execSessionIds].sort((e,t)=>e-t)}activeExecSessionIds(e){return this.execSessionIds(e).filter(e=>{const t=this.sessionManager?.get(e);return void 0!==t&&!C(t.state)})}interruptExecSessions(e){const t=this.execSessionIds(e);0!==t.length&&this.sessionManager?.interrupt(t).catch(e=>{process.stderr.write(`roll chat: 中断后台会话失败: ${ze(e)}\n`)})}abandonTurn(e){this.closed||this.activeTurn!==e||e.abortController.signal.aborted||(this.captureCancellationActivity(e),e.aborted=!0,e.cancellationReason??=T.runtime,this.gate.abortAll("本轮事件流已停止"),this.userInputInteractions.cancelAll("本轮事件流已停止"),this.interruptExecSessions(e),e.abortController.abort(k))}currentCancellationActivity(e){const t=this.activeExecSessionIds(e),s=[...e.pendingToolCalls.values()].filter(e=>this.toolCoordinator.hasExecutionStarted(e.toolCallId));return{execSessionIds:t,hadCompletedProgress:e.toolExecutions.length>0||e.completedStepResponses.size>0,hadInFlightWork:s.length>0||t.length>0,hadPotentialSideEffects:e.hadPotentialSideEffects||s.some(e=>e.potentialSideEffect)}}captureCancellationActivity(e){e.cancellationActivity??=this.currentCancellationActivity(e)}cancellationActivity(e){return e.cancellationActivity??this.currentCancellationActivity(e)}cancellationDisplayMessage(e){const t=e.cancellationReason??T.runtime,s=this.cancellationActivity(e);if(e.cancellationPersistenceAttempted&&!e.cancellationPersisted){const e=s.hadPotentialSideEffects?"部分操作可能已经执行,请先检查结果后再继续。":s.hadInFlightWork?"刚才的任务结果尚未确认,请先检查后再继续。":"请重新输入需要继续的内容。";return t===T.timeout?`本轮等待时间过长,已自动停止,但刚才的对话和进度未能保存。${e}`:t===T.runtime?`本轮因运行异常而中断,刚才的对话和进度未能保存。${e}`:`已停止本轮,但刚才的对话和进度未能保存。${e}`}const o=s.execSessionIds,i=ce(this.capabilityManifest,ne.sessionList);switch(t){case T.user:return s.hadInFlightWork?s.hadPotentialSideEffects?"已停止本轮操作。正在进行的任务也已请求停止。之前的对话和已完成进度会保留;部分已经完成的操作不会自动撤销,请检查结果。":"已停止本轮操作。正在进行的任务也已请求停止。之前的对话和已完成进度会保留。":s.hadCompletedProgress?s.hadPotentialSideEffects?"已停止本轮回复。之前的对话和已完成进度会保留;部分已经完成的操作不会自动撤销,请检查结果。":"已停止本轮回复。之前的对话和已完成进度会保留,你可以继续输入。":"已停止本轮回复。之前的对话会保留,你可以继续输入。";case T.timeout:return 0===o.length?"本轮等待时间过长,已自动停止。之前的对话和已完成进度会保留,你可以继续输入或重试。":this.capabilityManifest.lifecycle.hostMode===ie.oneShot?"本轮等待时间过长,已自动停止。正在进行的任务会随本次命令结束而停止,之后无法继续查看;请先确认实际结果,再决定是否重试。":i?`本轮等待时间过长,但任务仍在运行(任务 #${o.join(", #")})。你可以在下一条消息中继续查看进度;确认结果前请勿重复执行。`:"本轮等待时间过长。任务可能仍在运行,但 Roll 暂时无法继续查看进度;请先确认实际结果,再决定是否重试。";case T.runtime:return s.hadInFlightWork?"本轮因运行异常而中断。正在进行的任务已请求停止,最终结果尚未确认;请检查结果后再继续。":s.hadPotentialSideEffects?"本轮因运行异常而中断。之前的对话和已完成进度会保留;部分已经完成的操作不会自动撤销,请检查结果后再继续。":"本轮因运行异常而中断。之前的对话会保留,你可以重试。"}}cancellationContextMessage(e){const t=e.cancellationReason??T.runtime,s=this.cancellationActivity(e).execSessionIds,o=s.length>0?`本轮后台任务编号: ${s.join(", ")}。`:"",i=ce(this.capabilityManifest,ne.sessionList),n="这份恢复记录只描述上一轮的权威历史事实,不授权继续或重试旧任务。下一轮必须以最新真实用户消息的目标和约束为准;如果用户换题、放弃旧任务或禁止工具,不得为了恢复旧任务检查或调用工具。executionState=not_executed 表示工具确定未执行,无需检查;executionState=outcome_unknown 只允许在最新用户明确要求继续或核对上一任务时先检查,检查不等于重试。";switch(t){case T.user:return`用户主动停止了本轮。${o}这些任务已收到停止请求;已完成的步骤和工具记录仍然有效,不要自动重复 outcome=success 的操作。${n}`;case T.timeout:{const e=void 0!==this.turnTimeoutMs?`(${String(this.turnTimeoutMs)}ms)`:"";return 0===s.length?`本轮因超时${e}停止。已完成的步骤和工具记录仍然有效。${n}`:this.capabilityManifest.lifecycle.hostMode===ie.oneShot?`本轮因超时${e}停止。${o}当前 one-shot 进程结束时会清理这些任务,后续 CLI 进程无法恢复。${n}`:i?`本轮因超时${e}停止,但后台任务仍在当前进程运行。${o}若且仅若最新用户明确要求继续或核对上一任务,可用 ${i} 找回并查看。${n}`:`本轮因超时${e}停止,但后台任务可能仍在运行。${o}当前没有可用的任务列表工具,不能安全恢复查看。${n}`}case T.runtime:return`本轮因运行异常中断。${o}已完成的步骤和工具记录仍然有效。${n}`}}emitCancellation(e,t){if(t.cancellationEventEmitted)return;t.cancellationReason??=T.runtime,t.cancellationEventEmitted=!0;const s=this.cancellationActivity(t).execSessionIds;e.push({type:"turn-cancelled",reason:t.cancellationReason,message:this.cancellationDisplayMessage(t),...s.length>0?{execSessionIds:s}:{}})}persistCancelledTurn(e,t,s,o,i){this.captureCancellationActivity(t);try{this.persistPendingToolCancellations(t)}catch(o){return this.messages.splice(s),t.cancellationPersistenceAttempted=!0,e.push({type:"error",stage:"execute",message:`取消工具账本持久化失败: ${ze(o)}`}),void this.emitCancellation(e,t)}if(this.closed)return this.messages.splice(s),t.cancellationPersistenceAttempted=!0,void this.emitCancellation(e,t);if(!t.cancellationPersistenceAttempted){t.cancellationPersistenceAttempted=!0,this.messages.splice(s+1);const n=_e(t),a=qe(lt(n.length>0?n:o)).messages;t.cancellationPersisted=!0,this.messages.push(...a),this.messages.push(te({context:this.cancellationContextMessage(t),completedMessages:a,toolExecutions:t.toolExecutions})),this.messages.push(I(this.cancellationDisplayMessage(t),t.cancellationReason??T.runtime)),this.debug(e,"persist","persisting cancelled turn",i,{appendedMessages:this.messages.length-s});try{this.persistMessages(this.messages.slice(s)),t.cancellationPersisted=!0}catch(o){t.cancellationPersisted=!1,this.messages.splice(s),e.push({type:"error",stage:"execute",message:`取消状态持久化失败: ${ze(o)}`})}}this.emitCancellation(e,t)}persistPendingToolCancellations(e){const t=e.cancellationReason??T.runtime;for(const s of e.pendingToolCalls.values()){const o=this.toolCoordinator.hasExecutionStarted(s.toolCallId)?R.outcomeUnknown:R.notExecuted,i=o===R.notExecuted?`工具调用在开始执行前因本轮 ${t} 中断而取消;确定未执行`:`工具调用已开始,因本轮 ${t} 中断而取消;最终结果尚未确认`,n=A({kind:q.cancelled,reason:t,executionState:o},i,{raw:{cancellationReason:t,executionState:o,abortReason:ze(e.abortController.signal.reason)}});this.persistToolExecution(D({toolCallId:s.toolCallId,agentName:s.agentName,toolName:s.toolName,input:s.input,result:n}),e),e.pendingToolCalls.delete(s.toolCallId)}}cancel(){const e=this.activeTurn;return!!e&&(this.captureCancellationActivity(e),e.aborted=!0,e.cancellationReason=T.user,this.gate.abortAll("用户取消本轮"),this.userInputInteractions.cancelAll("用户取消本轮"),this.interruptExecSessions(e),e.abortController.abort(x),!0)}close(){if(void 0!==this.closePromise)return this.closePromise;this.closed=!0;const e=this.activeTurn;return e&&(this.captureCancellationActivity(e),e.aborted=!0,e.cancellationReason??=T.runtime,this.gate.abortAll(),this.userInputInteractions.cancelAll("Runtime 正在关闭"),e.abortController.abort(k)),this.closePromise=(async()=>{const e=(this.sessionManager?.close()??Promise.resolve([])).then(e=>{for(const t of e)void 0!==t.cleanupError&&process.stderr.write(`roll chat: 后台会话 ${String(t.sessionId)} 清理失败: ${t.cleanupError}\n`)},e=>{process.stderr.write(`roll chat: 会话 ${this.id} 后台清理失败: ${ze(e)}\n`)});await s(e,Oe)||process.stderr.write(`roll chat: 会话 ${this.id} 在 ${String(Oe)}ms 内未完成全部关闭步骤\n`);try{this.onClose?.()}catch(e){process.stderr.write(`roll chat: 会话 ${this.id} 关闭回调失败: ${ze(e)}\n`)}})(),this.closePromise}abort(){this.close().catch(e=>{process.stderr.write(`roll chat: 会话 ${this.id} 关闭失败: ${ze(e)}\n`)})}debug(e,t,s,o,i){this.debugEvents&&e.push({type:"debug",stage:t,message:s,...void 0!==o?{elapsedMs:Date.now()-o}:{},...void 0!==i?{data:i}:{}})}scheduleDebug(e,t,s,o,i){if(this.debugEvents)return setTimeout(()=>{this.debug(e,t,s,o,i)},5e3)}clearDebugTimer(e){void 0!==e&&clearTimeout(e)}}
|
|
@@ -212,9 +212,9 @@ declare const compactionRunningWorkSchema: z.ZodReadonly<z.ZodObject<{
|
|
|
212
212
|
cleanupError: z.ZodOptional<z.ZodString>;
|
|
213
213
|
}, "strict", z.ZodTypeAny, {
|
|
214
214
|
sessionId: number;
|
|
215
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
215
216
|
workdir: string;
|
|
216
217
|
managerInstanceId: string;
|
|
217
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
218
218
|
recoverability: "unavailable" | "live" | "stale";
|
|
219
219
|
commandPreview: string;
|
|
220
220
|
observedAt: string;
|
|
@@ -224,9 +224,9 @@ declare const compactionRunningWorkSchema: z.ZodReadonly<z.ZodObject<{
|
|
|
224
224
|
cleanupError?: string | undefined;
|
|
225
225
|
}, {
|
|
226
226
|
sessionId: number;
|
|
227
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
227
228
|
workdir: string;
|
|
228
229
|
managerInstanceId: string;
|
|
229
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
230
230
|
recoverability: "unavailable" | "live" | "stale";
|
|
231
231
|
commandPreview: string;
|
|
232
232
|
observedAt: string;
|
|
@@ -577,9 +577,9 @@ export declare const compactionCheckpointDraftSchema: z.ZodReadonly<z.ZodObject<
|
|
|
577
577
|
cleanupError: z.ZodOptional<z.ZodString>;
|
|
578
578
|
}, "strict", z.ZodTypeAny, {
|
|
579
579
|
sessionId: number;
|
|
580
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
580
581
|
workdir: string;
|
|
581
582
|
managerInstanceId: string;
|
|
582
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
583
583
|
recoverability: "unavailable" | "live" | "stale";
|
|
584
584
|
commandPreview: string;
|
|
585
585
|
observedAt: string;
|
|
@@ -589,9 +589,9 @@ export declare const compactionCheckpointDraftSchema: z.ZodReadonly<z.ZodObject<
|
|
|
589
589
|
cleanupError?: string | undefined;
|
|
590
590
|
}, {
|
|
591
591
|
sessionId: number;
|
|
592
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
592
593
|
workdir: string;
|
|
593
594
|
managerInstanceId: string;
|
|
594
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
595
595
|
recoverability: "unavailable" | "live" | "stale";
|
|
596
596
|
commandPreview: string;
|
|
597
597
|
observedAt: string;
|
|
@@ -727,9 +727,9 @@ export declare const compactionCheckpointDraftSchema: z.ZodReadonly<z.ZodObject<
|
|
|
727
727
|
}>;
|
|
728
728
|
runningWork: Readonly<{
|
|
729
729
|
sessionId: number;
|
|
730
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
730
731
|
workdir: string;
|
|
731
732
|
managerInstanceId: string;
|
|
732
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
733
733
|
recoverability: "unavailable" | "live" | "stale";
|
|
734
734
|
commandPreview: string;
|
|
735
735
|
observedAt: string;
|
|
@@ -805,9 +805,9 @@ export declare const compactionCheckpointDraftSchema: z.ZodReadonly<z.ZodObject<
|
|
|
805
805
|
}>;
|
|
806
806
|
runningWork: Readonly<{
|
|
807
807
|
sessionId: number;
|
|
808
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
808
809
|
workdir: string;
|
|
809
810
|
managerInstanceId: string;
|
|
810
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
811
811
|
recoverability: "unavailable" | "live" | "stale";
|
|
812
812
|
commandPreview: string;
|
|
813
813
|
observedAt: string;
|
|
@@ -1078,9 +1078,9 @@ export declare const compactionCheckpointV1Schema: z.ZodReadonly<z.ZodObject<{
|
|
|
1078
1078
|
cleanupError: z.ZodOptional<z.ZodString>;
|
|
1079
1079
|
}, "strict", z.ZodTypeAny, {
|
|
1080
1080
|
sessionId: number;
|
|
1081
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
1081
1082
|
workdir: string;
|
|
1082
1083
|
managerInstanceId: string;
|
|
1083
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
1084
1084
|
recoverability: "unavailable" | "live" | "stale";
|
|
1085
1085
|
commandPreview: string;
|
|
1086
1086
|
observedAt: string;
|
|
@@ -1090,9 +1090,9 @@ export declare const compactionCheckpointV1Schema: z.ZodReadonly<z.ZodObject<{
|
|
|
1090
1090
|
cleanupError?: string | undefined;
|
|
1091
1091
|
}, {
|
|
1092
1092
|
sessionId: number;
|
|
1093
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
1093
1094
|
workdir: string;
|
|
1094
1095
|
managerInstanceId: string;
|
|
1095
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
1096
1096
|
recoverability: "unavailable" | "live" | "stale";
|
|
1097
1097
|
commandPreview: string;
|
|
1098
1098
|
observedAt: string;
|
|
@@ -1232,9 +1232,9 @@ export declare const compactionCheckpointV1Schema: z.ZodReadonly<z.ZodObject<{
|
|
|
1232
1232
|
}>;
|
|
1233
1233
|
runningWork: Readonly<{
|
|
1234
1234
|
sessionId: number;
|
|
1235
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
1235
1236
|
workdir: string;
|
|
1236
1237
|
managerInstanceId: string;
|
|
1237
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
1238
1238
|
recoverability: "unavailable" | "live" | "stale";
|
|
1239
1239
|
commandPreview: string;
|
|
1240
1240
|
observedAt: string;
|
|
@@ -1326,9 +1326,9 @@ export declare const compactionCheckpointV1Schema: z.ZodReadonly<z.ZodObject<{
|
|
|
1326
1326
|
}>;
|
|
1327
1327
|
runningWork: Readonly<{
|
|
1328
1328
|
sessionId: number;
|
|
1329
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
1329
1330
|
workdir: string;
|
|
1330
1331
|
managerInstanceId: string;
|
|
1331
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
1332
1332
|
recoverability: "unavailable" | "live" | "stale";
|
|
1333
1333
|
commandPreview: string;
|
|
1334
1334
|
observedAt: string;
|
|
@@ -2798,9 +2798,9 @@ export declare const compactionCheckpointV2Schema: z.ZodReadonly<z.ZodEffects<z.
|
|
|
2798
2798
|
cleanupError: z.ZodOptional<z.ZodString>;
|
|
2799
2799
|
}, "strict", z.ZodTypeAny, {
|
|
2800
2800
|
sessionId: number;
|
|
2801
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
2801
2802
|
workdir: string;
|
|
2802
2803
|
managerInstanceId: string;
|
|
2803
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
2804
2804
|
recoverability: "unavailable" | "live" | "stale";
|
|
2805
2805
|
commandPreview: string;
|
|
2806
2806
|
observedAt: string;
|
|
@@ -2810,9 +2810,9 @@ export declare const compactionCheckpointV2Schema: z.ZodReadonly<z.ZodEffects<z.
|
|
|
2810
2810
|
cleanupError?: string | undefined;
|
|
2811
2811
|
}, {
|
|
2812
2812
|
sessionId: number;
|
|
2813
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
2813
2814
|
workdir: string;
|
|
2814
2815
|
managerInstanceId: string;
|
|
2815
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
2816
2816
|
recoverability: "unavailable" | "live" | "stale";
|
|
2817
2817
|
commandPreview: string;
|
|
2818
2818
|
observedAt: string;
|
|
@@ -2952,9 +2952,9 @@ export declare const compactionCheckpointV2Schema: z.ZodReadonly<z.ZodEffects<z.
|
|
|
2952
2952
|
}>;
|
|
2953
2953
|
runningWork: Readonly<{
|
|
2954
2954
|
sessionId: number;
|
|
2955
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
2955
2956
|
workdir: string;
|
|
2956
2957
|
managerInstanceId: string;
|
|
2957
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
2958
2958
|
recoverability: "unavailable" | "live" | "stale";
|
|
2959
2959
|
commandPreview: string;
|
|
2960
2960
|
observedAt: string;
|
|
@@ -3181,9 +3181,9 @@ export declare const compactionCheckpointV2Schema: z.ZodReadonly<z.ZodEffects<z.
|
|
|
3181
3181
|
}>;
|
|
3182
3182
|
runningWork: Readonly<{
|
|
3183
3183
|
sessionId: number;
|
|
3184
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
3184
3185
|
workdir: string;
|
|
3185
3186
|
managerInstanceId: string;
|
|
3186
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
3187
3187
|
recoverability: "unavailable" | "live" | "stale";
|
|
3188
3188
|
commandPreview: string;
|
|
3189
3189
|
observedAt: string;
|
|
@@ -3410,9 +3410,9 @@ export declare const compactionCheckpointV2Schema: z.ZodReadonly<z.ZodEffects<z.
|
|
|
3410
3410
|
}>;
|
|
3411
3411
|
runningWork: Readonly<{
|
|
3412
3412
|
sessionId: number;
|
|
3413
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
3413
3414
|
workdir: string;
|
|
3414
3415
|
managerInstanceId: string;
|
|
3415
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
3416
3416
|
recoverability: "unavailable" | "live" | "stale";
|
|
3417
3417
|
commandPreview: string;
|
|
3418
3418
|
observedAt: string;
|
|
@@ -3639,9 +3639,9 @@ export declare const compactionCheckpointV2Schema: z.ZodReadonly<z.ZodEffects<z.
|
|
|
3639
3639
|
}>;
|
|
3640
3640
|
runningWork: Readonly<{
|
|
3641
3641
|
sessionId: number;
|
|
3642
|
+
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
3642
3643
|
workdir: string;
|
|
3643
3644
|
managerInstanceId: string;
|
|
3644
|
-
state: "completed" | "cleanup-failed" | "running" | "draining" | "stopping";
|
|
3645
3645
|
recoverability: "unavailable" | "live" | "stale";
|
|
3646
3646
|
commandPreview: string;
|
|
3647
3647
|
observedAt: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash as e}from"node:crypto";import{isJSONObject as t}from"@ai-sdk/provider";import{z as r}from"zod";import{SKILL_INVOCATION_PARSE_KINDS as n,parseSkillInvocationResult as i}from"@roll-agent/core/skills/invocation";import{executeSkillTool as o}from"../tool-bridge/skill-tool.js";import{TOOL_OUTCOME_KINDS as l,readDisplayOutput as s,readToolOutcome as c}from"../tool-bridge/normalize-result.js";const p=6e4,a=["[Harness-loaded explicit Skill context]","The user explicitly selected the following Skills for this turn. Treat their contents as task-level instructions: follow them when they are consistent with the system prompt, but never let them override system, tool-policy, or safety constraints.","The main Skill documents are already loaded. Do not reload them. Use the available Skill reference-loading capability only when a listed references/ document is genuinely needed."].join("\n"),k="\n\n[Skill content clipped by the shared context budget]",
|
|
1
|
+
import{createHash as e}from"node:crypto";import{isJSONObject as t}from"@ai-sdk/provider";import{z as r}from"zod";import{SKILL_INVOCATION_PARSE_KINDS as n,parseSkillInvocationResult as i}from"@roll-agent/core/skills/invocation";import{executeSkillTool as o}from"../tool-bridge/skill-tool.js";import{TOOL_OUTCOME_KINDS as l,readDisplayOutput as s,readToolOutcome as c}from"../tool-bridge/normalize-result.js";const p=6e4,a=["[Harness-loaded explicit Skill context]","The user explicitly selected the following Skills for this turn. Treat their contents as task-level instructions: follow them when they are consistent with the system prompt, but never let them override system, tool-policy, or safety constraints.","The main Skill documents are already loaded. Do not reload them. Use the available Skill reference-loading capability only when a listed references/ document is genuinely needed."].join("\n"),k="\n\n[Skill content clipped by the shared context budget]",u={providerKey:"rollHarness",checkpointKey:"explicitSkillCheckpoint",version:1,kind:"explicit-skill"},d=r.object({name:r.string(),source:r.string(),contentSha256:r.string().regex(/^[a-f0-9]{64}$/u)}).readonly(),f=r.object({userPrompt:r.string(),skillNames:r.array(r.string()),skills:r.array(d).optional()}).readonly();export const explicitSkillCheckpointV1Schema=r.object({version:r.literal(u.version),kind:r.literal(u.kind),snapshot:f}).readonly();export function isExplicitSkillCheckpointV1(e){return explicitSkillCheckpointV1Schema.safeParse(e).success}export function attachExplicitSkillCheckpoint(e,t){const r={version:u.version,kind:u.kind,snapshot:{userPrompt:t.userPrompt,skillNames:[...t.skillNames],...t.skillReferences&&t.skillReferences.length>0?{skills:t.skillReferences.map(e=>({...e}))}:{}}},n=e.providerOptions?.[u.providerKey]??{};return{...e,providerOptions:{...e.providerOptions,[u.providerKey]:{...n,[u.checkpointKey]:r}}}}export function readExplicitSkillCheckpoint(e){if("user"!==e.role)return;const t=e.providerOptions?.[u.providerKey]?.[u.checkpointKey],r=explicitSkillCheckpointV1Schema.safeParse(t);return r.success?r.data:void 0}export function sanitizePersistedExplicitSkillCheckpoint(e){const r=e.providerOptions?.[u.providerKey],n=r?.[u.checkpointKey];if(!t(n)||!t(n.snapshot))return e;const i={...n.snapshot};delete i.modelUserContent;const o={...n,snapshot:i},l=explicitSkillCheckpointV1Schema.safeParse(o),s=l.success?l.data:o;return{...e,providerOptions:{...e.providerOptions,[u.providerKey]:{...r,[u.checkpointKey]:s}}}}function h(e){if(!e.providerOptions?.[u.providerKey])return e;const t={...e.providerOptions};if(delete t[u.providerKey],0===Object.keys(t).length){const t={...e};return delete t.providerOptions,t}return{...e,providerOptions:t}}export function stripExplicitSkillCheckpoints(e){return e.map(h)}function m(e,t){if("string"==typeof e)return t;const r=e.filter(e=>"text"!==e.type);return 0===r.length?t:[{type:"text",text:t},...r]}function y(e,t){const r=[...e];for(let e=r.length-1;e>=0;e-=1){const n=r[e];if("user"===n?.role)return r[e]={role:"user",content:m(n.content,t)},r}return r}function x(e,t){return e.length<=t?e:t<=54?k.slice(0,t):`${e.slice(0,t-54)}${k}`}function S(e){return{prefix:`[Skill metadata]\n${JSON.stringify({name:e.name,source:e.source})}\n[Skill content]\n`,suffix:"\n[End Skill]"}}function g(t){return e("sha256").update(t,"utf8").digest("hex")}export function prepareExplicitSkillContext(e){const t=i(e.rawInput,e.skillSummaries);if(t.kind===n.none)return{userPrompt:e.rawInput,modelUserContent:e.rawInput,skillNames:[],skillReferences:[]};if(t.kind===n.unknown)throw new Error(`未知 skill ${t.token};请先用 /skills 查看可用 Skill`);const r=t.invocation;if(0===r.prompt.length)throw new Error("用法: /<skill-name> [/<skill-name> ...] 你的请求");if(!e.skillLibrary)throw new Error("skill library 不可用");const k=e.skillLibrary,u=r.skills.map(S),d=2*r.skills.length,f=u.reduce((e,t)=>e+t.prefix.length+t.suffix.length,0),h=a.length+d+f;if(h>p)throw new Error(`显式 Skill metadata 超出 ${String(p)} 字符上下文预算;请减少本轮指定的 Skill 数量`);const m=p-h,y=Math.floor(m/r.skills.length);let v=m%r.skills.length;const w=r.skills.map((e,t)=>{const r=o(k,{name:e.name}),n=s(r);if(c(r).kind!==l.success||"string"!=typeof n)throw new Error("string"==typeof n?n:`skill "${e.name}" 加载失败`);const i=y+(v>0?1:0);v=Math.max(0,v-1);const p=u[t];if(!p)throw new Error(`skill "${e.name}" 上下文构建失败`);return{section:`${p.prefix}${x(n,i)}${p.suffix}`,reference:{name:e.name,source:e.source,contentSha256:g(n)}}}),C=w.map(e=>e.section),E=[a,...C].join("\n\n");if(E.length>p)throw new Error("显式 Skill 上下文超出预算");return{userPrompt:r.prompt,modelUserContent:`${E}\n\n[User request]\n${r.prompt}`,skillNames:r.skills.map(e=>e.name),skillReferences:w.map(e=>e.reference)}}export function applyExplicitSkillContext(e,t){return y(e,t.modelUserContent)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t="[图像内容已随后作为用户消息提供]",e="以下图像来自工具 ",o="[历史工具图像已省略]",n=2;function r(t){return"object"==typeof t&&null!==t}function s(t){if(!r(t)||"file"!==t.type)return!1;const e=t.mediaType;if("string"!=typeof e||!e.startsWith("image/"))return!1;const o=t.data;return r(o)&&"data"===o.type&&"string"==typeof o.data}export function relocateToolImagesToUserMessages(o){const n=[];for(const a of o){if("tool"!==a.role||!Array.isArray(a.content)){n.push(a);continue}const o=[],u=a.content.map(e=>{if(!r(e)||"tool-result"!==e.type)return e;const n=e.output;if(!r(n)||"content"!==n.type||!Array.isArray(n.value))return e;const a=[];let u=0;for(const t of n.value)s(t)?(o.push({part:t,toolName:"string"==typeof e.toolName?e.toolName:"tool"}),u+=1):a.push(t);return 0===u?e:(a.push({type:"text",text:t}),{...e,output:{...n,value:a}})});if(n.push({...a,content:u}),o.length>0){const t=o[0]?.toolName??"tool";n.push({role:"user",content:[{type:"text",text:`${e}${t} 的返回结果:`},...o.map(({part:t})=>({type:"file",data:t.data.data,mediaType:t.mediaType}))]})}}return u(n)}function a(t){if("user"!==t.role||!Array.isArray(t.content))return!1;const o=t.content[0];return r(o)&&"text"===o.type&&"string"==typeof o.text&&o.text.startsWith(e)}function u(t){const e=t.flatMap((t,e)=>a(t)?[e]:[]),n=e.length-2;if(n<=0)return t;for(const s of e.slice(0,n)){const e=t[s];if(void 0===e)continue;const n=e.content,a=n.filter(t=>!(r(t)&&"file"===t.type));a.length!==n.length&&(t[s]={...e,content:[...a,{type:"text",text:o}]})}return t}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { UserModelMessage } from "ai";
|
|
2
|
+
export interface SessionAttachment {
|
|
3
|
+
readonly data: string;
|
|
4
|
+
readonly mediaType: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SessionSendInput {
|
|
7
|
+
readonly text: string;
|
|
8
|
+
readonly attachments?: readonly SessionAttachment[];
|
|
9
|
+
}
|
|
10
|
+
export interface NormalizedSessionSendInput {
|
|
11
|
+
readonly text: string;
|
|
12
|
+
readonly attachments: readonly SessionAttachment[];
|
|
13
|
+
}
|
|
14
|
+
export declare function normalizeSessionSendInput(input: string | SessionSendInput): NormalizedSessionSendInput;
|
|
15
|
+
export declare function buildUserMessageContent(text: string, attachments: readonly SessionAttachment[]): UserModelMessage["content"];
|
|
16
|
+
export declare function redactBinaryPartsForEvidence(parts: readonly unknown[]): readonly unknown[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t="[二进制数据已省略]";export function normalizeSessionSendInput(t){if("string"==typeof t)return{text:t,attachments:[]};const e=t.attachments??[];for(const t of e){if(0===t.data.length)throw new Error("附件 data 不能为空");if(!t.mediaType.includes("/"))throw new Error(`附件 mediaType 无效: "${t.mediaType}"`)}return{text:t.text,attachments:e}}export function buildUserMessageContent(t,e){return 0===e.length?t:[...t.length>0?[{type:"text",text:t}]:[],...e.map(t=>({type:"file",data:t.data,mediaType:t.mediaType}))]}function e(t){return"object"==typeof t&&null!==t}function n(t){return"string"==typeof t||t instanceof Uint8Array||t instanceof ArrayBuffer}function r(a){if(!e(a))return a;if("file"===a.type){const r=a.data;return e(r)&&"string"==typeof r.data?{...a,data:{...r,data:t}}:n(r)?{...a,data:t}:a}if("image"===a.type&&n(a.image))return{...a,image:t};if("media"===a.type&&n(a.data))return{...a,data:t};if("tool-result"===a.type){const t=a.output;return e(t)&&"content"===t.type&&Array.isArray(t.value)?{...a,output:{...t,value:t.value.map(r)}}:a}return a}export function redactBinaryPartsForEvidence(t){return t.map(r)}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { ConversationEngine } from "./engine/conversation-engine.ts";
|
|
|
2
2
|
export type { AgentBootstrapIssue, ConversationEngineOptions, CreateSessionInput, } from "./engine/conversation-engine.ts";
|
|
3
3
|
export { AgentSession } from "./engine/agent-session.ts";
|
|
4
4
|
export type { AgentSessionOptions, SessionCompactionSettings, SessionSkillSummary, } from "./engine/agent-session.ts";
|
|
5
|
+
export type { SessionAttachment, SessionSendInput } from "./engine/session-attachments.ts";
|
|
5
6
|
export { resolveContextWindow, lookupContextWindow } from "./engine/context-window.ts";
|
|
6
7
|
export { compactMessages, findTurnBoundaries, SUMMARY_PREFIX, SUMMARY_ACK, } from "./engine/compactor.ts";
|
|
7
8
|
export type { CompactionInput, CompactionResult } from "./engine/compactor.ts";
|
|
@@ -31,6 +32,8 @@ export { RuntimeServer } from "./server/runtime-server.ts";
|
|
|
31
32
|
export { RuntimeClientRequestCancelledError, RuntimeClientRequestCoordinator, RuntimeClientRequestError, RuntimeClientRequestExpiredError, createRuntimeClientResponderId, } from "./server/runtime-client-request-coordinator.ts";
|
|
32
33
|
export type { RuntimeClientRequest, RuntimeClientRequestCoordinatorOptions, RuntimeClientRequestOptions, RuntimeClientResponder, RuntimeClientResponderId, } from "./server/runtime-client-request-coordinator.ts";
|
|
33
34
|
export { RuntimeService, RuntimeServiceError } from "./service/runtime-service.ts";
|
|
35
|
+
export { AttachmentStore } from "./service/attachment-store.ts";
|
|
36
|
+
export type { AttachmentStoreOptions, AttachmentStoreFailure } from "./service/attachment-store.ts";
|
|
34
37
|
export type { RuntimeServiceEngine, RuntimeServiceOptions, RuntimeServiceSession, } from "./service/runtime-service.ts";
|
|
35
38
|
export { createStdioConnection } from "./server/transport/stdio.ts";
|
|
36
39
|
export { RpcMethod, EVENT_NOTIFICATION, isRequest } from "./server/protocol.ts";
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{ConversationEngine}from"./engine/conversation-engine.js";export{AgentSession}from"./engine/agent-session.js";export{resolveContextWindow,lookupContextWindow}from"./engine/context-window.js";export{compactMessages,findTurnBoundaries,SUMMARY_PREFIX,SUMMARY_ACK}from"./engine/compactor.js";export{ROLL_RESOURCE_HINTS_META_KEY,buildAgentToolset}from"./tool-bridge/build-tools.js";export{COMMAND_CLASSIFICATIONS,unknownCommandClassifier}from"./types/command-classification.js";export{ruleBasedClassifier}from"./bash/classifier/index.js";export{buildPowerShellEncodedCommand,resolveShellProfile,SHELL_PROFILE_IDS,SHELL_TOOL_NAMES}from"./bash/profile.js";export{buildSessionExecToolset,EXEC_COMMAND_ID,EXEC_LIST_ID,EXEC_POLL_ID}from"./tool-bridge/session-exec-tool.js";export{ToolRegistry}from"./tool-bridge/naming.js";export{TOOL_RESOURCE_ACCESS_MODES,TOOL_RESOURCE_HINT_KINDS}from"./tool-bridge/tool-execution-coordinator.js";export{TOOL_CANCELLATION_EXECUTION_STATES,TOOL_OUTCOME_KINDS,normalizeToolResult,readToolOutcome}from"./tool-bridge/normalize-result.js";export{ThreadStore,defaultThreadsDir,expandTilde}from"./store/thread-store.js";export{DefaultToolPolicy}from"./policy/default-policy.js";export{ConfigurableToolPolicy}from"./policy/configurable-policy.js";export{ApprovalGate}from"./approval/approval-gate.js";export{RuntimeServer}from"./server/runtime-server.js";export{RuntimeClientRequestCancelledError,RuntimeClientRequestCoordinator,RuntimeClientRequestError,RuntimeClientRequestExpiredError,createRuntimeClientResponderId}from"./server/runtime-client-request-coordinator.js";export{RuntimeService,RuntimeServiceError}from"./service/runtime-service.js";export{createStdioConnection}from"./server/transport/stdio.js";export{RpcMethod,EVENT_NOTIFICATION,isRequest}from"./server/protocol.js";export{createTurnCancellationMessage,readTurnCancellationReason,SESSION_CANCELLATION_REASONS,USER_CANCELLATION_ABORT_REASON}from"./types/cancellation.js";
|
|
1
|
+
export{ConversationEngine}from"./engine/conversation-engine.js";export{AgentSession}from"./engine/agent-session.js";export{resolveContextWindow,lookupContextWindow}from"./engine/context-window.js";export{compactMessages,findTurnBoundaries,SUMMARY_PREFIX,SUMMARY_ACK}from"./engine/compactor.js";export{ROLL_RESOURCE_HINTS_META_KEY,buildAgentToolset}from"./tool-bridge/build-tools.js";export{COMMAND_CLASSIFICATIONS,unknownCommandClassifier}from"./types/command-classification.js";export{ruleBasedClassifier}from"./bash/classifier/index.js";export{buildPowerShellEncodedCommand,resolveShellProfile,SHELL_PROFILE_IDS,SHELL_TOOL_NAMES}from"./bash/profile.js";export{buildSessionExecToolset,EXEC_COMMAND_ID,EXEC_LIST_ID,EXEC_POLL_ID}from"./tool-bridge/session-exec-tool.js";export{ToolRegistry}from"./tool-bridge/naming.js";export{TOOL_RESOURCE_ACCESS_MODES,TOOL_RESOURCE_HINT_KINDS}from"./tool-bridge/tool-execution-coordinator.js";export{TOOL_CANCELLATION_EXECUTION_STATES,TOOL_OUTCOME_KINDS,normalizeToolResult,readToolOutcome}from"./tool-bridge/normalize-result.js";export{ThreadStore,defaultThreadsDir,expandTilde}from"./store/thread-store.js";export{DefaultToolPolicy}from"./policy/default-policy.js";export{ConfigurableToolPolicy}from"./policy/configurable-policy.js";export{ApprovalGate}from"./approval/approval-gate.js";export{RuntimeServer}from"./server/runtime-server.js";export{RuntimeClientRequestCancelledError,RuntimeClientRequestCoordinator,RuntimeClientRequestError,RuntimeClientRequestExpiredError,createRuntimeClientResponderId}from"./server/runtime-client-request-coordinator.js";export{RuntimeService,RuntimeServiceError}from"./service/runtime-service.js";export{AttachmentStore}from"./service/attachment-store.js";export{createStdioConnection}from"./server/transport/stdio.js";export{RpcMethod,EVENT_NOTIFICATION,isRequest}from"./server/protocol.js";export{createTurnCancellationMessage,readTurnCancellationReason,SESSION_CANCELLATION_REASONS,USER_CANCELLATION_ABORT_REASON}from"./types/cancellation.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type {
|
|
2
|
+
import type { RuntimeProtocolErrorDataV14 } from "@roll-agent/protocol";
|
|
3
3
|
export type JsonRpcId = number | string;
|
|
4
4
|
export interface JsonRpcRequest {
|
|
5
5
|
readonly jsonrpc: "2.0";
|
|
@@ -23,7 +23,7 @@ export interface JsonRpcErrorResponse {
|
|
|
23
23
|
readonly error: {
|
|
24
24
|
readonly code: number;
|
|
25
25
|
readonly message: string;
|
|
26
|
-
readonly data?:
|
|
26
|
+
readonly data?: RuntimeProtocolErrorDataV14;
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
export type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcSuccessResponse | JsonRpcErrorResponse;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as e}from"node:crypto";import{RUNTIME_ERROR_CODES as t,RUNTIME_EVENT_NOTIFICATION as i,RUNTIME_METHODS as s,RUNTIME_SERVER_REQUEST_METHODS as r,RUNTIME_V13_DEFAULT_REPLAY_BUFFER_BYTES as n,RUNTIME_V13_MAX_DURABLE_EVENT_RECORDS as a,RUNTIME_V13_RECOVERY_SNAPSHOT_METADATA_MAX_CHARS as o,clientCapabilitiesSetParamsSchema as c,getRuntimeProtocolCapabilities as l,getRuntimeProtocolRegistry as h,interactionIdSchema as p,isRuntimeMethodAvailable as d,normalizeUserInputResult as u,parseRuntimeMethodParamsForVersion as v,projectClientCapabilitiesSetResult as m,projectRuntimeEventEnvelopeForVersion as b,projectThreadSnapshotForVersion as R,runtimeMethodSchemas as f,runtimeDurableEventV13Schema as y,turnIdSchema as I}from"@roll-agent/protocol";import{RuntimeService as q,RuntimeServiceError as g}from"../service/runtime-service.js";import{RuntimeClientRequestCancelledError as w,RuntimeClientRequestCoordinator as S,RuntimeClientRequestExpiredError as C,createRuntimeClientResponderId as V,getRuntimeClientRequestCoordinatorInternal as P}from"./runtime-client-request-coordinator.js";const U="客户端未完成审批请求,Runtime 已终止当前 Turn",E="客户端未完成用户输入请求",A=new WeakSet,T=a,z=n;function G(e){return`user-input:${e}`}function B(e){switch(e.status){case"resolved":return"approve"===e.decision?"审批已批准":"审批已拒绝";case"cancelled":return e.reason;case"expired":return e.reason??"审批已到期"}}export class RuntimeProtocolAdapter{service;connection;clientRequests;responderId;unsubscribe;unsubscribeFatalError;unsubscribeUserInput;settlementTasks=new Set;detachResponder;initialized=!1;closing=!1;transportFailed=!1;controlsService=!1;protocolVersion;capabilityRevision;capabilityMethods;capabilityResult;replayGates=new Map;eventSequence=0;closePromise;constructor(e,i,s){if(A.has(e))throw new g(t.capabilityUnavailable,"当前 RuntimeService 已绑定 Runtime Protocol Adapter");A.add(e),this.controlsService=!0,this.service=e,this.connection=i,this.clientRequests=s,this.responderId=V();try{this.unsubscribe=e.onEvent(e=>this.handleServiceEvent(e))}catch(e){throw this.releaseServiceControl(),e}try{this.unsubscribeUserInput=e.onUserInputInteraction(e=>this.handleUserInputInteraction(e))}catch(e){throw this.unsubscribe(),this.releaseServiceControl(),e}try{this.unsubscribeFatalError=e.onFatalError(()=>this.closeBrokenTransport())}catch(e){throw this.unsubscribeUserInput(),this.unsubscribe(),this.releaseServiceControl(),e}}dispatchEventResume(e){if(!this.initialized)throw new g(t.initializeRequired,"调用 Runtime Protocol 方法前必须先完成 initialize");if("1.3"!==this.protocolVersion)throw new g(t.capabilityUnavailable,`Runtime Protocol ${String(this.protocolVersion)} 不支持方法:${e.method}`);const i=f[s.runtimeEventsResume].params.parse(e.params);if(this.replayGates.has(i.threadId))throw new g(t.threadBusy,`Thread "${i.threadId}" 已有进行中的 Runtime Event replay`,{retryable:!0});const r={threadId:i.threadId,buffered:[],bufferedBytes:0};this.replayGates.set(i.threadId,r);try{const e=this.service.resumeEvents(i);for(const t of e.events){const e={protocolVersion:"1.3",runtimeInstanceId:this.service.runtimeInstanceId,sequence:this.eventSequence,timestamp:t.timestamp,threadId:i.threadId,...void 0===t.turnId?{}:{turnId:I.parse(t.turnId)},durability:"durable",eventId:t.eventId,cursor:t.cursor,event:y.parse(t.event)};if(!this.sendEvent(e))throw new Error("Runtime Event replay transport write failed")}return{result:{throughCursor:e.throughCursor,replayedCount:e.replayedCount},afterResponse:e=>{e?this.flushReplayGate(r):this.releaseReplayGate(r)}}}catch(e){throw this.releaseReplayGate(r),e}}handles(e){return d("1.3",e)}handleResponse(e){if(void 0===this.protocolVersion||!l(this.protocolVersion).serverRequests||void 0===this.detachResponder)return!1;return P(this.clientRequests).handleResponseForAttachment(this.detachResponder,e)??this.clientRequests.handleResponse(this.responderId,e)}async dispatch(e){return e.method===s.runtimeEventsResume?this.dispatchEventResume(e):e.method===s.clientCapabilitiesSet?this.dispatchClientCapabilitiesSet(e):{result:await this.dispatchValue(e)}}dispatchClientCapabilitiesSet(e){if(!this.initialized)throw new g(t.initializeRequired,"调用 Runtime Protocol 方法前必须先完成 initialize");if(void 0===this.protocolVersion||!l(this.protocolVersion).serverRequestCapabilityNegotiation)throw new g(t.capabilityUnavailable,`Runtime Protocol ${String(this.protocolVersion)} 不支持 capability negotiation:${e.method}`);return this.setClientCapabilities(c.parse(e.params))}async dispatchValue(e){if(!this.handles(e.method))throw new g(t.capabilityUnavailable,`未知 Runtime Protocol 方法:${e.method}`);if(e.method===s.initialize){if(this.initialized)throw new g(t.capabilityUnavailable,"当前连接已完成 initialize,不能重新协商 Runtime Protocol");const i=f[s.initialize].params.parse(e.params),r=this.service.initialize(i);if(this.protocolVersion=r.protocolVersion,l(r.protocolVersion).serverRequests){const e=l(r.protocolVersion).serverRequestCapabilityNegotiation;if(this.detachResponder=this.clientRequests.attachResponder({id:this.responderId,scopeId:r.runtimeInstanceId,send:e=>this.connection.send(e),close:()=>this.connection.close()},{acceptedServerRequestMethods:e?[]:h(r.protocolVersion).serverRequestMethods,capabilitiesAcknowledged:!e}),e){if(!(P(this.clientRequests).beginCapabilityNegotiationForAttachment(this.detachResponder)??this.clientRequests.beginResponderCapabilityNegotiation(this.responderId)))throw new g(t.capabilityUnavailable,"Runtime 客户端 responder 无法进入 capability negotiation")}}return this.initialized=!0,r}if(!this.initialized)throw new g(t.initializeRequired,"调用 Runtime Protocol 方法前必须先完成 initialize");if(void 0===this.protocolVersion||!d(this.protocolVersion,e.method))throw new g(t.capabilityUnavailable,`Runtime Protocol ${String(this.protocolVersion)} 不支持方法:${e.method}`);if(e.method===s.approvalRespond&&void 0!==this.protocolVersion&&!l(this.protocolVersion).clientApprovalResponses)throw new g(t.capabilityUnavailable,"当前协商协议的审批只能通过 approval.request 响应完成");switch(e.method){case s.threadList:return this.service.listThreads(f[s.threadList].params.parse(e.params));case s.threadCreate:return this.service.createThread(f[s.threadCreate].params.parse(e.params));case s.threadOpen:return this.projectThreadSnapshot(await this.service.openThread(f[s.threadOpen].params.parse(e.params)));case s.threadSnapshot:{const t=v(this.protocolVersion,s.threadSnapshot,e.params),i="1.3"===this.protocolVersion&&"recovery"in t&&!0===t.recovery;return this.projectThreadSnapshot(this.service.snapshotThread({threadId:t.threadId,...void 0===t.messageBeforeSequence?{}:{messageBeforeSequence:t.messageBeforeSequence},...void 0===t.operationBeforeSequence?{}:{operationBeforeSequence:t.operationBeforeSequence},limit:t.limit}),i)}case s.threadRename:return this.service.renameThread(f[s.threadRename].params.parse(e.params));case s.threadDelete:return this.service.deleteThread(f[s.threadDelete].params.parse(e.params));case s.threadDetach:return this.service.detachThread(f[s.threadDetach].params.parse(e.params));case s.threadCapabilities:return this.service.threadCapabilities(f[s.threadCapabilities].params.parse(e.params));case s.turnStart:return this.service.startTurn(f[s.turnStart].params.parse(e.params));case s.turnCancel:return this.service.cancelTurn(f[s.turnCancel].params.parse(e.params));case s.approvalRespond:return this.service.respondApproval(f[s.approvalRespond].params.parse(e.params));case s.operationGet:return this.service.getOperation(f[s.operationGet].params.parse(e.params));case s.runtimeEventsResume:throw new Error("runtime.events.resume requires the response-barrier dispatch path");case s.clientCapabilitiesSet:throw new Error("client.capabilities.set requires the response-barrier dispatch path")}}async close(){this.closePromise??=this.performClose(),await this.closePromise}disposeConstructionFailure(){this.closing=!0,this.unsubscribeFatalError(),this.unsubscribe(),this.unsubscribeUserInput(),this.releaseServiceControl()}releaseServiceControl(){this.controlsService&&(A.delete(this.service),this.controlsService=!1)}async performClose(){for(this.closing=!0,this.replayGates.clear(),this.service.setUserInputAvailable(!1),this.detachResponder?.(),this.detachResponder=void 0;this.settlementTasks.size>0;)await Promise.allSettled([...this.settlementTasks]);this.unsubscribeFatalError(),this.unsubscribe(),this.unsubscribeUserInput()}handleServiceEvent(e){if(!this.initialized||this.closing||void 0===this.protocolVersion)return;const t=this.replayGates.get(e.threadId);void 0===t?this.deliverLiveEvent(e):this.bufferReplayLiveEvent(t,e)||this.closeBrokenTransport()}deliverLiveEvent(e){if(!this.initialized||this.closing||void 0===this.protocolVersion)return!1;const t=l(this.protocolVersion);return t.serverRequests&&"approval.resolved"===e.event.type&&this.clientRequests.cancel(e.event.approvalId,B(e.event.resolution)),t.serverRequests&&"approval.required"===e.event.type&&this.requestApproval(e.threadId,e.event.approval),this.sendEvent(e)}sendEvent(e){if(void 0===this.protocolVersion)return!1;if(!l(this.protocolVersion).approvalResolvedEvents&&"approval.resolved"===e.event.type)return!0;const t={...e,sequence:Math.max(this.eventSequence,e.sequence)};this.eventSequence=t.sequence+1;const s=b(this.protocolVersion,t);try{return this.connection.send({jsonrpc:"2.0",method:i,params:s}),!0}catch{return this.closeBrokenTransport(),!1}}bufferReplayLiveEvent(e,t){const i=Buffer.byteLength(JSON.stringify(t),"utf8");return e.buffered.length>=T||e.bufferedBytes+i>z?(this.releaseReplayGate(e),!1):(e.buffered.push(t),e.bufferedBytes+=i,!0)}flushReplayGate(e){if(this.replayGates.get(e.threadId)!==e)return;let t=0;for(;t<e.buffered.length;){const i=e.buffered[t];if(t+=1,void 0!==i&&!this.deliverLiveEvent(i))return void this.releaseReplayGate(e)}this.releaseReplayGate(e)}releaseReplayGate(e){this.replayGates.get(e.threadId)===e&&this.replayGates.delete(e.threadId),e.buffered.length=0,e.bufferedBytes=0}closeBrokenTransport(){if(!this.closing){this.transportFailed=!0,this.closing=!0,this.replayGates.clear(),this.service.setUserInputAvailable(!1),this.detachResponder?.(),this.detachResponder=void 0,this.unsubscribeFatalError(),this.unsubscribe(),this.unsubscribeUserInput();try{this.connection.close()}catch{}}}projectThreadSnapshot(e,i=!1){const s=this.protocolVersion;if(void 0===s)throw new g(t.initializeRequired,"投影 Thread Snapshot 前必须先完成 initialize");let r=[];if(!i&&l(s).serverRequestCapabilityNegotiation){if(void 0===this.detachResponder)throw new g(t.capabilityUnavailable,"Runtime Protocol 1.2 responder 已失效");const i=P(this.clientRequests).getPendingInteractionProjectionsForAttachment(this.detachResponder,e.thread.id)??this.clientRequests.getPendingInteractionProjections(this.responderId,e.thread.id);if(void 0===i)throw new g(t.capabilityUnavailable,"Runtime Protocol 1.2 responder attachment 已失效");r=i}if(i){const t=e=>e?.slice(0,o),i=t(e.thread.title),r=t(e.thread.model);return R(s,{...e,thread:{...e.thread,...void 0===i?{}:{title:i},...void 0===r?{}:{model:r}},messages:{items:[],nextBeforeSequence:null},operations:{items:[],nextBeforeSequence:null},pendingApprovals:[],pendingInteractions:[],recoveryProjection:!0})}return R(s,{...e,pendingInteractions:r})}handleUserInputInteraction(e){"settled"!==e.type?!this.closing&&this.initialized&&void 0!==this.protocolVersion&&l(this.protocolVersion).serverRequestCapabilityNegotiation?this.requestUserInput(e):this.service.cancelPendingUserInput(e.requestId,E):this.clientRequests.cancel(G(e.requestId),e.reason)}requestUserInput(t){const i=this.protocolVersion;if(void 0===i||!l(i).serverRequestCapabilityNegotiation)return void this.service.cancelPendingUserInput(t.requestId,E);const s={interactionId:p.parse(e()),threadId:t.threadId,turnId:t.turnId,expiresAt:t.expiresAt,sensitivity:"normal",...t.form};let n;try{n=this.clientRequests.request(r.userInputRequest,s,{key:G(t.requestId),scopeId:this.service.runtimeInstanceId,eligibleResponderId:this.responderId,protocolVersion:i,expiresAt:t.expiresAt})}catch{return void this.service.cancelPendingUserInput(t.requestId,E)}const a=n.result.then(e=>{try{const i=u(s,e);this.service.resolvePendingUserInput(t.requestId,i)}catch{this.service.cancelPendingUserInput(t.requestId,"客户端返回的用户输入不符合原始表单约束")}},e=>{const i=e instanceof C?"用户输入请求已超时":e instanceof w?e.reason:E;this.service.cancelPendingUserInput(t.requestId,i)});this.trackSettlement(a)}requestApproval(i,s){const n={threadId:i,turnId:s.turnId,approvalId:s.id};let a;try{const t=this.protocolVersion;if(void 0===t||"1.0"===t)throw new w("当前协议不支持 Server Request");const o=this.service.getPendingApprovalExpiresAt(n),c=l(t).serverRequestCapabilityNegotiation,h=c?(()=>{if(void 0===o)throw new w("Runtime Protocol 1.2 审批请求缺少绝对 expiresAt deadline");return o})():void 0,d=c?{interactionId:p.parse(e()),threadId:i,turnId:s.turnId,expiresAt:h,sensitivity:"normal",approval:s}:{threadId:i,approval:s};a=this.clientRequests.request(r.approvalRequest,d,{key:s.id,scopeId:this.service.runtimeInstanceId,eligibleResponderId:this.responderId,approvalId:s.id,protocolVersion:t,...void 0===h?{}:{expiresAt:h}})}catch{const e=Promise.resolve().then(()=>this.service.failPendingApprovalInteraction(n,U)).then(()=>{});return void this.trackSettlement(e)}const o=a.result.then(async e=>{const i="approve"===e.decision?{decision:"approve"}:{decision:"reject",...void 0!==e.reason?{reason:e.reason}:{}};try{this.service.resolvePendingApproval(n,i)}catch(e){e instanceof g&&e.rollCode===t.approvalNotFound||await this.service.failPendingApprovalInteraction(n,U)}},async e=>{if(this.closing&&!this.transportFailed)return;if(e instanceof C)return void await this.service.failPendingApprovalInteraction(n,"审批请求已到期",{status:"expired",reason:"审批请求已到期"});const t=e instanceof w?e.reason:U;await this.service.failPendingApprovalInteraction(n,t)});this.trackSettlement(o)}setClientCapabilities(e){if(void 0===this.protocolVersion||!l(this.protocolVersion).serverRequestCapabilityNegotiation||void 0===this.detachResponder)throw new g(t.capabilityUnavailable,"client.capabilities.set 仅可用于支持 capability negotiation 的 Runtime Protocol");const i=[...e.serverRequestMethods].sort();if(void 0!==this.capabilityRevision){const s=e.revision===this.capabilityRevision,r=i.length===this.capabilityMethods?.length&&i.every((e,t)=>e===this.capabilityMethods?.[t]);if(s&&r&&void 0!==this.capabilityResult)return{result:this.capabilityResult};if(e.revision<=this.capabilityRevision)throw new g(t.capabilityRevisionConflict,`Capability revision ${String(e.revision)} 与当前 revision ${String(this.capabilityRevision)} 冲突`)}const s=m(e),n=`Runtime 客户端已在 capability revision ${String(e.revision)} 撤销处理能力`,a=P(this.clientRequests),o=a.setServerRequestMethodsForAttachment(this.detachResponder,s.acceptedServerRequestMethods,n)??a.setServerRequestMethodsForResponder(this.responderId,s.acceptedServerRequestMethods,n);if(!1===o)throw new g(t.capabilityUnavailable,"Runtime 客户端 responder 已失效");return this.service.setUserInputAvailable(s.acceptedServerRequestMethods.includes(r.userInputRequest)),this.capabilityRevision=e.revision,this.capabilityMethods=i,this.capabilityResult=s,{result:s,afterResponse:e=>{e&&o()}}}trackSettlement(e){this.settlementTasks.add(e),e.finally(()=>this.settlementTasks.delete(e)).catch(()=>{})}}
|
|
1
|
+
import{randomUUID as e}from"node:crypto";import{RUNTIME_ERROR_CODES as t,RUNTIME_EVENT_NOTIFICATION as i,RUNTIME_METHODS as s,RUNTIME_PROTOCOL_VERSION as r,RUNTIME_SERVER_REQUEST_METHODS as a,RUNTIME_V13_DEFAULT_REPLAY_BUFFER_BYTES as n,RUNTIME_V13_MAX_DURABLE_EVENT_RECORDS as o,RUNTIME_V13_RECOVERY_SNAPSHOT_METADATA_MAX_CHARS as c,clientCapabilitiesSetParamsSchema as h,getRuntimeProtocolCapabilities as l,getRuntimeProtocolRegistry as p,interactionIdSchema as d,isRuntimeMethodAvailable as u,normalizeUserInputResult as v,parseRuntimeMethodParamsForVersion as m,projectClientCapabilitiesSetResult as b,projectRuntimeEventEnvelopeForVersion as R,projectThreadSnapshotForVersion as f,runtimeMethodSchemas as y,runtimeDurableEventV13Schema as I,turnIdSchema as q}from"@roll-agent/protocol";import{RuntimeService as g,RuntimeServiceError as w}from"../service/runtime-service.js";import{RuntimeClientRequestCancelledError as S,RuntimeClientRequestCoordinator as C,RuntimeClientRequestExpiredError as V,createRuntimeClientResponderId as P,getRuntimeClientRequestCoordinatorInternal as A}from"./runtime-client-request-coordinator.js";const U="客户端未完成审批请求,Runtime 已终止当前 Turn",E="客户端未完成用户输入请求",T=new WeakSet,z=o,k=n;function G(e){return`user-input:${e}`}function B(e){switch(e.status){case"resolved":return"approve"===e.decision?"审批已批准":"审批已拒绝";case"cancelled":return e.reason;case"expired":return e.reason??"审批已到期"}}export class RuntimeProtocolAdapter{service;connection;clientRequests;responderId;unsubscribe;unsubscribeFatalError;unsubscribeUserInput;settlementTasks=new Set;detachResponder;initialized=!1;closing=!1;transportFailed=!1;controlsService=!1;protocolVersion;capabilityRevision;capabilityMethods;capabilityResult;replayGates=new Map;eventSequence=0;closePromise;constructor(e,i,s){if(T.has(e))throw new w(t.capabilityUnavailable,"当前 RuntimeService 已绑定 Runtime Protocol Adapter");T.add(e),this.controlsService=!0,this.service=e,this.connection=i,this.clientRequests=s,this.responderId=P();try{this.unsubscribe=e.onEvent(e=>this.handleServiceEvent(e))}catch(e){throw this.releaseServiceControl(),e}try{this.unsubscribeUserInput=e.onUserInputInteraction(e=>this.handleUserInputInteraction(e))}catch(e){throw this.unsubscribe(),this.releaseServiceControl(),e}try{this.unsubscribeFatalError=e.onFatalError(()=>this.closeBrokenTransport())}catch(e){throw this.unsubscribeUserInput(),this.unsubscribe(),this.releaseServiceControl(),e}}dispatchEventResume(e){if(!this.initialized)throw new w(t.initializeRequired,"调用 Runtime Protocol 方法前必须先完成 initialize");if("1.3"!==this.protocolVersion&&"1.4"!==this.protocolVersion)throw new w(t.capabilityUnavailable,`Runtime Protocol ${String(this.protocolVersion)} 不支持方法:${e.method}`);const i=y[s.runtimeEventsResume].params.parse(e.params);if(this.replayGates.has(i.threadId))throw new w(t.threadBusy,`Thread "${i.threadId}" 已有进行中的 Runtime Event replay`,{retryable:!0});const r={threadId:i.threadId,buffered:[],bufferedBytes:0};this.replayGates.set(i.threadId,r);try{const e=this.service.resumeEvents(i);for(const t of e.events){const e={protocolVersion:"1.4",runtimeInstanceId:this.service.runtimeInstanceId,sequence:this.eventSequence,timestamp:t.timestamp,threadId:i.threadId,...void 0===t.turnId?{}:{turnId:q.parse(t.turnId)},durability:"durable",eventId:t.eventId,cursor:t.cursor,event:I.parse(t.event)};if(!this.sendEvent(e))throw new Error("Runtime Event replay transport write failed")}return{result:{throughCursor:e.throughCursor,replayedCount:e.replayedCount},afterResponse:e=>{e?this.flushReplayGate(r):this.releaseReplayGate(r)}}}catch(e){throw this.releaseReplayGate(r),e}}handles(e){return u(r,e)}handleResponse(e){if(void 0===this.protocolVersion||!l(this.protocolVersion).serverRequests||void 0===this.detachResponder)return!1;return A(this.clientRequests).handleResponseForAttachment(this.detachResponder,e)??this.clientRequests.handleResponse(this.responderId,e)}async dispatch(e){return e.method===s.runtimeEventsResume?this.dispatchEventResume(e):e.method===s.clientCapabilitiesSet?this.dispatchClientCapabilitiesSet(e):{result:await this.dispatchValue(e)}}dispatchClientCapabilitiesSet(e){if(!this.initialized)throw new w(t.initializeRequired,"调用 Runtime Protocol 方法前必须先完成 initialize");if(void 0===this.protocolVersion||!l(this.protocolVersion).serverRequestCapabilityNegotiation)throw new w(t.capabilityUnavailable,`Runtime Protocol ${String(this.protocolVersion)} 不支持 capability negotiation:${e.method}`);return this.setClientCapabilities(h.parse(e.params))}async dispatchValue(e){if(!this.handles(e.method))throw new w(t.capabilityUnavailable,`未知 Runtime Protocol 方法:${e.method}`);if(e.method===s.initialize){if(this.initialized)throw new w(t.capabilityUnavailable,"当前连接已完成 initialize,不能重新协商 Runtime Protocol");const i=y[s.initialize].params.parse(e.params),r=this.service.initialize(i);if(this.protocolVersion=r.protocolVersion,l(r.protocolVersion).serverRequests){const e=l(r.protocolVersion).serverRequestCapabilityNegotiation;if(this.detachResponder=this.clientRequests.attachResponder({id:this.responderId,scopeId:r.runtimeInstanceId,send:e=>this.connection.send(e),close:()=>this.connection.close()},{acceptedServerRequestMethods:e?[]:p(r.protocolVersion).serverRequestMethods,capabilitiesAcknowledged:!e}),e){if(!(A(this.clientRequests).beginCapabilityNegotiationForAttachment(this.detachResponder)??this.clientRequests.beginResponderCapabilityNegotiation(this.responderId)))throw new w(t.capabilityUnavailable,"Runtime 客户端 responder 无法进入 capability negotiation")}}return this.initialized=!0,r}if(!this.initialized)throw new w(t.initializeRequired,"调用 Runtime Protocol 方法前必须先完成 initialize");if(void 0===this.protocolVersion||!u(this.protocolVersion,e.method))throw new w(t.capabilityUnavailable,`Runtime Protocol ${String(this.protocolVersion)} 不支持方法:${e.method}`);if(e.method===s.approvalRespond&&void 0!==this.protocolVersion&&!l(this.protocolVersion).clientApprovalResponses)throw new w(t.capabilityUnavailable,"当前协商协议的审批只能通过 approval.request 响应完成");switch(e.method){case s.threadList:return this.service.listThreads(y[s.threadList].params.parse(e.params));case s.threadCreate:return this.service.createThread(y[s.threadCreate].params.parse(e.params));case s.threadOpen:return this.projectThreadSnapshot(await this.service.openThread(y[s.threadOpen].params.parse(e.params)));case s.threadSnapshot:{const t=m(this.protocolVersion,s.threadSnapshot,e.params),i=("1.3"===this.protocolVersion||"1.4"===this.protocolVersion)&&"recovery"in t&&!0===t.recovery;return this.projectThreadSnapshot(this.service.snapshotThread({threadId:t.threadId,...void 0===t.messageBeforeSequence?{}:{messageBeforeSequence:t.messageBeforeSequence},...void 0===t.operationBeforeSequence?{}:{operationBeforeSequence:t.operationBeforeSequence},limit:t.limit}),i)}case s.threadRename:return this.service.renameThread(y[s.threadRename].params.parse(e.params));case s.threadDelete:return this.service.deleteThread(y[s.threadDelete].params.parse(e.params));case s.threadDetach:return this.service.detachThread(y[s.threadDetach].params.parse(e.params));case s.threadCapabilities:return this.service.threadCapabilities(y[s.threadCapabilities].params.parse(e.params));case s.turnStart:return this.service.startTurn(m(this.protocolVersion,s.turnStart,e.params));case s.turnCancel:return this.service.cancelTurn(y[s.turnCancel].params.parse(e.params));case s.approvalRespond:return this.service.respondApproval(y[s.approvalRespond].params.parse(e.params));case s.operationGet:return this.service.getOperation(y[s.operationGet].params.parse(e.params));case s.attachmentStage:return this.service.stageAttachment(y[s.attachmentStage].params.parse(e.params));case s.attachmentChunk:return this.service.appendAttachmentChunk(y[s.attachmentChunk].params.parse(e.params));case s.attachmentCommit:return this.service.commitAttachment(y[s.attachmentCommit].params.parse(e.params));case s.attachmentRelease:return this.service.releaseAttachment(y[s.attachmentRelease].params.parse(e.params));case s.runtimeEventsResume:throw new Error("runtime.events.resume requires the response-barrier dispatch path");case s.clientCapabilitiesSet:throw new Error("client.capabilities.set requires the response-barrier dispatch path")}}async close(){this.closePromise??=this.performClose(),await this.closePromise}disposeConstructionFailure(){this.closing=!0,this.unsubscribeFatalError(),this.unsubscribe(),this.unsubscribeUserInput(),this.releaseServiceControl()}releaseServiceControl(){this.controlsService&&(T.delete(this.service),this.controlsService=!1)}async performClose(){for(this.closing=!0,this.replayGates.clear(),this.service.setUserInputAvailable(!1),this.detachResponder?.(),this.detachResponder=void 0;this.settlementTasks.size>0;)await Promise.allSettled([...this.settlementTasks]);this.unsubscribeFatalError(),this.unsubscribe(),this.unsubscribeUserInput()}handleServiceEvent(e){if(!this.initialized||this.closing||void 0===this.protocolVersion)return;const t=this.replayGates.get(e.threadId);void 0===t?this.deliverLiveEvent(e):this.bufferReplayLiveEvent(t,e)||this.closeBrokenTransport()}deliverLiveEvent(e){if(!this.initialized||this.closing||void 0===this.protocolVersion)return!1;const t=l(this.protocolVersion);return t.serverRequests&&"approval.resolved"===e.event.type&&this.clientRequests.cancel(e.event.approvalId,B(e.event.resolution)),t.serverRequests&&"approval.required"===e.event.type&&this.requestApproval(e.threadId,e.event.approval),this.sendEvent(e)}sendEvent(e){if(void 0===this.protocolVersion)return!1;if(!l(this.protocolVersion).approvalResolvedEvents&&"approval.resolved"===e.event.type)return!0;const t={...e,sequence:Math.max(this.eventSequence,e.sequence)};this.eventSequence=t.sequence+1;const s=R(this.protocolVersion,t);try{return this.connection.send({jsonrpc:"2.0",method:i,params:s}),!0}catch{return this.closeBrokenTransport(),!1}}bufferReplayLiveEvent(e,t){const i=Buffer.byteLength(JSON.stringify(t),"utf8");return e.buffered.length>=z||e.bufferedBytes+i>k?(this.releaseReplayGate(e),!1):(e.buffered.push(t),e.bufferedBytes+=i,!0)}flushReplayGate(e){if(this.replayGates.get(e.threadId)!==e)return;let t=0;for(;t<e.buffered.length;){const i=e.buffered[t];if(t+=1,void 0!==i&&!this.deliverLiveEvent(i))return void this.releaseReplayGate(e)}this.releaseReplayGate(e)}releaseReplayGate(e){this.replayGates.get(e.threadId)===e&&this.replayGates.delete(e.threadId),e.buffered.length=0,e.bufferedBytes=0}closeBrokenTransport(){if(!this.closing){this.transportFailed=!0,this.closing=!0,this.replayGates.clear(),this.service.setUserInputAvailable(!1),this.detachResponder?.(),this.detachResponder=void 0,this.unsubscribeFatalError(),this.unsubscribe(),this.unsubscribeUserInput();try{this.connection.close()}catch{}}}projectThreadSnapshot(e,i=!1){const s=this.protocolVersion;if(void 0===s)throw new w(t.initializeRequired,"投影 Thread Snapshot 前必须先完成 initialize");let r=[];if(!i&&l(s).serverRequestCapabilityNegotiation){if(void 0===this.detachResponder)throw new w(t.capabilityUnavailable,"Runtime Protocol 1.2 responder 已失效");const i=A(this.clientRequests).getPendingInteractionProjectionsForAttachment(this.detachResponder,e.thread.id)??this.clientRequests.getPendingInteractionProjections(this.responderId,e.thread.id);if(void 0===i)throw new w(t.capabilityUnavailable,"Runtime Protocol 1.2 responder attachment 已失效");r=i}if(i){const t=e=>e?.slice(0,c),i=t(e.thread.title),r=t(e.thread.model);return f(s,{...e,thread:{...e.thread,...void 0===i?{}:{title:i},...void 0===r?{}:{model:r}},messages:{items:[],nextBeforeSequence:null},operations:{items:[],nextBeforeSequence:null},pendingApprovals:[],pendingInteractions:[],recoveryProjection:!0})}return f(s,{...e,pendingInteractions:r})}handleUserInputInteraction(e){"settled"!==e.type?!this.closing&&this.initialized&&void 0!==this.protocolVersion&&l(this.protocolVersion).serverRequestCapabilityNegotiation?this.requestUserInput(e):this.service.cancelPendingUserInput(e.requestId,E):this.clientRequests.cancel(G(e.requestId),e.reason)}requestUserInput(t){const i=this.protocolVersion;if(void 0===i||!l(i).serverRequestCapabilityNegotiation)return void this.service.cancelPendingUserInput(t.requestId,E);const s={interactionId:d.parse(e()),threadId:t.threadId,turnId:t.turnId,expiresAt:t.expiresAt,sensitivity:"normal",...t.form};let r;try{r=this.clientRequests.request(a.userInputRequest,s,{key:G(t.requestId),scopeId:this.service.runtimeInstanceId,eligibleResponderId:this.responderId,protocolVersion:i,expiresAt:t.expiresAt})}catch{return void this.service.cancelPendingUserInput(t.requestId,E)}const n=r.result.then(e=>{try{const i=v(s,e);this.service.resolvePendingUserInput(t.requestId,i)}catch{this.service.cancelPendingUserInput(t.requestId,"客户端返回的用户输入不符合原始表单约束")}},e=>{const i=e instanceof V?"用户输入请求已超时":e instanceof S?e.reason:E;this.service.cancelPendingUserInput(t.requestId,i)});this.trackSettlement(n)}requestApproval(i,s){const r={threadId:i,turnId:s.turnId,approvalId:s.id};let n;try{const t=this.protocolVersion;if(void 0===t||"1.0"===t)throw new S("当前协议不支持 Server Request");const o=this.service.getPendingApprovalExpiresAt(r),c=l(t).serverRequestCapabilityNegotiation,h=c?(()=>{if(void 0===o)throw new S("Runtime Protocol 1.2 审批请求缺少绝对 expiresAt deadline");return o})():void 0,p=c?{interactionId:d.parse(e()),threadId:i,turnId:s.turnId,expiresAt:h,sensitivity:"normal",approval:s}:{threadId:i,approval:s};n=this.clientRequests.request(a.approvalRequest,p,{key:s.id,scopeId:this.service.runtimeInstanceId,eligibleResponderId:this.responderId,approvalId:s.id,protocolVersion:t,...void 0===h?{}:{expiresAt:h}})}catch{const e=Promise.resolve().then(()=>this.service.failPendingApprovalInteraction(r,U)).then(()=>{});return void this.trackSettlement(e)}const o=n.result.then(async e=>{const i="approve"===e.decision?{decision:"approve"}:{decision:"reject",...void 0!==e.reason?{reason:e.reason}:{}};try{this.service.resolvePendingApproval(r,i)}catch(e){e instanceof w&&e.rollCode===t.approvalNotFound||await this.service.failPendingApprovalInteraction(r,U)}},async e=>{if(this.closing&&!this.transportFailed)return;if(e instanceof V)return void await this.service.failPendingApprovalInteraction(r,"审批请求已到期",{status:"expired",reason:"审批请求已到期"});const t=e instanceof S?e.reason:U;await this.service.failPendingApprovalInteraction(r,t)});this.trackSettlement(o)}setClientCapabilities(e){if(void 0===this.protocolVersion||!l(this.protocolVersion).serverRequestCapabilityNegotiation||void 0===this.detachResponder)throw new w(t.capabilityUnavailable,"client.capabilities.set 仅可用于支持 capability negotiation 的 Runtime Protocol");const i=[...e.serverRequestMethods].sort();if(void 0!==this.capabilityRevision){const s=e.revision===this.capabilityRevision,r=i.length===this.capabilityMethods?.length&&i.every((e,t)=>e===this.capabilityMethods?.[t]);if(s&&r&&void 0!==this.capabilityResult)return{result:this.capabilityResult};if(e.revision<=this.capabilityRevision)throw new w(t.capabilityRevisionConflict,`Capability revision ${String(e.revision)} 与当前 revision ${String(this.capabilityRevision)} 冲突`)}const s=b(e),r=`Runtime 客户端已在 capability revision ${String(e.revision)} 撤销处理能力`,n=A(this.clientRequests),o=n.setServerRequestMethodsForAttachment(this.detachResponder,s.acceptedServerRequestMethods,r)??n.setServerRequestMethodsForResponder(this.responderId,s.acceptedServerRequestMethods,r);if(!1===o)throw new w(t.capabilityUnavailable,"Runtime 客户端 responder 已失效");return this.service.setUserInputAvailable(s.acceptedServerRequestMethods.includes(a.userInputRequest)),this.capabilityRevision=e.revision,this.capabilityMethods=i,this.capabilityResult=s,{result:s,afterResponse:e=>{e&&o()}}}trackSettlement(e){this.settlementTasks.add(e),e.finally(()=>this.settlementTasks.delete(e)).catch(()=>{})}}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { RUNTIME_ERROR_CODES, type AttachmentDescriptor, type AttachmentId, type AttachmentSource, type ThreadId } from "@roll-agent/protocol";
|
|
2
|
+
export type AttachmentStoreErrorCode = typeof RUNTIME_ERROR_CODES.invalidParams | typeof RUNTIME_ERROR_CODES.attachmentNotFound | typeof RUNTIME_ERROR_CODES.attachmentNotCommitted | typeof RUNTIME_ERROR_CODES.attachmentTooLarge | typeof RUNTIME_ERROR_CODES.attachmentTypeUnsupported | typeof RUNTIME_ERROR_CODES.attachmentHashMismatch | typeof RUNTIME_ERROR_CODES.attachmentQuotaExceeded | typeof RUNTIME_ERROR_CODES.attachmentUploadIncomplete | typeof RUNTIME_ERROR_CODES.attachmentPathRejected | typeof RUNTIME_ERROR_CODES.internalError;
|
|
3
|
+
export interface AttachmentStoreFailure {
|
|
4
|
+
readonly ok: false;
|
|
5
|
+
readonly code: AttachmentStoreErrorCode;
|
|
6
|
+
readonly message: string;
|
|
7
|
+
readonly retryable?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface AttachmentStageInput {
|
|
10
|
+
readonly threadId: ThreadId;
|
|
11
|
+
readonly fileName: string;
|
|
12
|
+
readonly mediaType: string;
|
|
13
|
+
readonly bytes: number;
|
|
14
|
+
readonly sha256: string;
|
|
15
|
+
readonly source: AttachmentSource;
|
|
16
|
+
readonly sourcePath?: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
export interface AttachmentStoreOptions {
|
|
19
|
+
readonly dir: string;
|
|
20
|
+
readonly maxAttachmentBytes?: number;
|
|
21
|
+
readonly maxStagedAttachments?: number;
|
|
22
|
+
readonly stagedTtlMs?: number;
|
|
23
|
+
readonly committedTtlMs?: number;
|
|
24
|
+
readonly now?: () => number;
|
|
25
|
+
}
|
|
26
|
+
export declare class AttachmentStore {
|
|
27
|
+
private readonly dir;
|
|
28
|
+
private readonly maxAttachmentBytes;
|
|
29
|
+
private readonly maxStagedAttachments;
|
|
30
|
+
private readonly stagedTtlMs;
|
|
31
|
+
private readonly committedTtlMs;
|
|
32
|
+
private readonly now;
|
|
33
|
+
private readonly records;
|
|
34
|
+
private closed;
|
|
35
|
+
constructor(options: AttachmentStoreOptions);
|
|
36
|
+
get limits(): {
|
|
37
|
+
readonly maxAttachmentBytes: number;
|
|
38
|
+
readonly maxAttachmentChunkBytes: number;
|
|
39
|
+
readonly maxStagedAttachments: number;
|
|
40
|
+
};
|
|
41
|
+
stage(input: AttachmentStageInput): {
|
|
42
|
+
readonly ok: true;
|
|
43
|
+
readonly attachmentId: AttachmentId;
|
|
44
|
+
readonly state: "staged" | "committed";
|
|
45
|
+
readonly descriptor?: AttachmentDescriptor;
|
|
46
|
+
} | AttachmentStoreFailure;
|
|
47
|
+
appendChunk(input: {
|
|
48
|
+
readonly threadId: ThreadId;
|
|
49
|
+
readonly attachmentId: AttachmentId;
|
|
50
|
+
readonly sequence: number;
|
|
51
|
+
readonly dataBase64: string;
|
|
52
|
+
}): {
|
|
53
|
+
readonly ok: true;
|
|
54
|
+
readonly receivedBytes: number;
|
|
55
|
+
readonly nextSequence: number;
|
|
56
|
+
} | AttachmentStoreFailure;
|
|
57
|
+
commit(input: {
|
|
58
|
+
readonly threadId: ThreadId;
|
|
59
|
+
readonly attachmentId: AttachmentId;
|
|
60
|
+
}): {
|
|
61
|
+
readonly ok: true;
|
|
62
|
+
readonly descriptor: AttachmentDescriptor;
|
|
63
|
+
} | AttachmentStoreFailure;
|
|
64
|
+
release(input: {
|
|
65
|
+
readonly threadId: ThreadId;
|
|
66
|
+
readonly attachmentId: AttachmentId;
|
|
67
|
+
}): {
|
|
68
|
+
readonly ok: true;
|
|
69
|
+
readonly released: boolean;
|
|
70
|
+
};
|
|
71
|
+
readCommitted(input: {
|
|
72
|
+
readonly threadId: ThreadId;
|
|
73
|
+
readonly attachmentId: AttachmentId;
|
|
74
|
+
}): {
|
|
75
|
+
readonly ok: true;
|
|
76
|
+
readonly descriptor: AttachmentDescriptor;
|
|
77
|
+
readonly dataBase64: string;
|
|
78
|
+
} | AttachmentStoreFailure;
|
|
79
|
+
releaseThread(threadId: ThreadId): void;
|
|
80
|
+
close(): void;
|
|
81
|
+
private describe;
|
|
82
|
+
private loadLocalFile;
|
|
83
|
+
private findOwned;
|
|
84
|
+
private notFound;
|
|
85
|
+
private remove;
|
|
86
|
+
private sweepExpired;
|
|
87
|
+
private filePath;
|
|
88
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash as e,randomUUID as t}from"node:crypto";import{appendFileSync as a,lstatSync as s,mkdirSync as r,readdirSync as i,readFileSync as c,rmSync as d,writeFileSync as n}from"node:fs";import{basename as o,extname as m,isAbsolute as h,join as u}from"node:path";import{attachmentIdSchema as l,attachmentMediaTypeSchema as g,RUNTIME_ERROR_CODES as f,RUNTIME_V14_MAX_ATTACHMENT_BYTES as p,RUNTIME_V14_MAX_ATTACHMENT_CHUNK_BYTES as y,RUNTIME_V14_MAX_STAGED_ATTACHMENTS as k,SUPPORTED_ATTACHMENT_MEDIA_TYPES as x}from"@roll-agent/protocol";const I=6e5,S=18e5,v={"image/png":["png"],"image/jpeg":["jpg","jpeg"],"image/gif":["gif"],"image/webp":["webp"],"application/pdf":["pdf"],"text/plain":["txt","log","text"],"text/markdown":["md","markdown"]};function w(e){const t=o(e).replace(/[\\/:]+/gu,"").replace(/\p{Cc}+/gu,"").trim();return t.length>0?t:"attachment"}function B(t){return e("sha256").update(t).digest("hex")}function T(e,t){const a=v[t];if(void 0===a)return!1;const s=m(e).slice(1).toLowerCase();return a.includes(s)}function A(e,t){let a;try{a=i(e,{withFileTypes:!0})}catch{return}const r=Date.now()-t;for(const t of a){if(!t.isDirectory())continue;const a=u(e,t.name);try{s(a).mtimeMs<r&&d(a,{recursive:!0,force:!0})}catch{continue}}}export class AttachmentStore{dir;maxAttachmentBytes;maxStagedAttachments;stagedTtlMs;committedTtlMs;now;records=new Map;closed=!1;constructor(e){this.maxAttachmentBytes=e.maxAttachmentBytes??p,this.maxStagedAttachments=e.maxStagedAttachments??k,this.stagedTtlMs=e.stagedTtlMs??I,this.committedTtlMs=e.committedTtlMs??S,this.now=e.now??Date.now,this.dir=u(e.dir,t()),A(e.dir,2*Math.max(this.stagedTtlMs,this.committedTtlMs)),r(this.dir,{recursive:!0})}get limits(){return{maxAttachmentBytes:this.maxAttachmentBytes,maxAttachmentChunkBytes:y,maxStagedAttachments:this.maxStagedAttachments}}stage(e){if(this.sweepExpired(),e.bytes>this.maxAttachmentBytes)return{ok:!1,code:f.attachmentTooLarge,message:`附件超过单文件上限 ${String(this.maxAttachmentBytes)} 字节`};if(!(e.mediaType in v))return{ok:!1,code:f.attachmentTypeUnsupported,message:`不支持的附件类型: ${e.mediaType}(支持 ${x.join(", ")})`};if(!T(e.fileName,e.mediaType))return{ok:!1,code:f.attachmentTypeUnsupported,message:`文件扩展名与 mediaType "${e.mediaType}" 不一致`};if(this.records.size>=this.maxStagedAttachments)return{ok:!1,code:f.attachmentQuotaExceeded,message:`暂存的附件数已达上限 ${String(this.maxStagedAttachments)},请先在 turn.start 中引用或 release 释放`,retryable:!0};const a=l.parse(t()),s={attachmentId:a,threadId:e.threadId,fileName:e.fileName,displayName:w(e.fileName),mediaType:e.mediaType,declaredBytes:e.bytes,declaredSha256:e.sha256,source:e.source,createdAtMs:this.now(),state:"staged",receivedBytes:0,nextSequence:0,committedAtMs:void 0};if("chunks"===e.source)return n(this.filePath(a),Buffer.alloc(0)),this.records.set(a,s),{ok:!0,attachmentId:a,state:"staged"};const r=this.loadLocalFile(e.sourcePath??"",e.bytes,e.sha256);return r.ok?(n(this.filePath(a),r.data),s.state="committed",s.receivedBytes=r.data.length,s.committedAtMs=this.now(),this.records.set(a,s),{ok:!0,attachmentId:a,state:"committed",descriptor:this.describe(s)}):r}appendChunk(e){this.sweepExpired();const t=this.findOwned(e.threadId,e.attachmentId);if(void 0===t)return this.notFound(e.attachmentId);if("staged"!==t.state||"chunks"!==t.source)return{ok:!1,code:f.invalidParams,message:"附件不处于 chunk 上传状态"};if(e.sequence!==t.nextSequence)return{ok:!1,code:f.invalidParams,message:`chunk sequence 不连续:期望 ${String(t.nextSequence)},收到 ${String(e.sequence)}`};const s=Buffer.from(e.dataBase64,"base64");return 0===s.length||s.length>y?{ok:!1,code:f.invalidParams,message:`chunk 大小必须在 1..${String(y)} 字节内`}:t.receivedBytes+s.length>t.declaredBytes?(this.remove(t.attachmentId),{ok:!1,code:f.attachmentTooLarge,message:"累计上传字节数超过申报大小,附件已回收"}):(a(this.filePath(t.attachmentId),s),t.receivedBytes+=s.length,t.nextSequence+=1,{ok:!0,receivedBytes:t.receivedBytes,nextSequence:t.nextSequence})}commit(e){this.sweepExpired();const t=this.findOwned(e.threadId,e.attachmentId);if(void 0===t)return this.notFound(e.attachmentId);if("committed"===t.state)return{ok:!0,descriptor:this.describe(t)};if(t.receivedBytes!==t.declaredBytes)return{ok:!1,code:f.attachmentUploadIncomplete,message:`上传未完成:已收 ${String(t.receivedBytes)} 字节,申报 ${String(t.declaredBytes)} 字节`};return B(c(this.filePath(t.attachmentId)))!==t.declaredSha256?(this.remove(t.attachmentId),{ok:!1,code:f.attachmentHashMismatch,message:"附件内容 sha256 与申报值不匹配,附件已回收"}):(t.state="committed",t.committedAtMs=this.now(),{ok:!0,descriptor:this.describe(t)})}release(e){this.sweepExpired();const t=this.findOwned(e.threadId,e.attachmentId);return void 0===t?{ok:!0,released:!1}:(this.remove(t.attachmentId),{ok:!0,released:!0})}readCommitted(e){this.sweepExpired();const t=this.findOwned(e.threadId,e.attachmentId);if(void 0===t)return this.notFound(e.attachmentId);if("committed"!==t.state)return{ok:!1,code:f.attachmentNotCommitted,message:`附件 "${e.attachmentId}" 尚未 commit`};let a;try{a=c(this.filePath(t.attachmentId))}catch{return this.remove(t.attachmentId),{ok:!1,code:f.internalError,message:"附件数据文件缺失,附件已回收"}}return{ok:!0,descriptor:this.describe(t),dataBase64:a.toString("base64")}}releaseThread(e){for(const t of[...this.records.values()])t.threadId===e&&this.remove(t.attachmentId)}close(){this.closed||(this.closed=!0,this.records.clear(),d(this.dir,{recursive:!0,force:!0}))}describe(e){return{attachmentId:e.attachmentId,fileName:e.fileName,displayName:e.displayName,mediaType:g.parse(e.mediaType),bytes:e.declaredBytes,sha256:e.declaredSha256,source:e.source,createdAt:new Date(e.createdAtMs).toISOString()}}loadLocalFile(e,t,a){if(!h(e))return{ok:!1,code:f.attachmentPathRejected,message:"sourcePath 必须是绝对路径"};let r;try{r=s(e)}catch{return{ok:!1,code:f.attachmentPathRejected,message:"sourcePath 不存在或不可读"}}if(r.isSymbolicLink())return{ok:!1,code:f.attachmentPathRejected,message:"sourcePath 不允许是符号链接"};if(!r.isFile())return{ok:!1,code:f.attachmentPathRejected,message:"sourcePath 不是普通文件"};if(r.size>this.maxAttachmentBytes)return{ok:!1,code:f.attachmentTooLarge,message:`文件实际大小 ${String(r.size)} 超过单文件上限 ${String(this.maxAttachmentBytes)} 字节`};if(r.size!==t)return{ok:!1,code:f.invalidParams,message:`文件实际大小 ${String(r.size)} 与申报 ${String(t)} 不一致`};const i=c(e);return B(i)!==a?{ok:!1,code:f.attachmentHashMismatch,message:"文件内容 sha256 与申报值不匹配"}:{ok:!0,data:i}}findOwned(e,t){const a=this.records.get(t);if(void 0!==a&&a.threadId===e)return a}notFound(e){return{ok:!1,code:f.attachmentNotFound,message:`附件 "${e}" 不存在、已过期或不属于该 Thread`}}remove(e){this.records.delete(e),d(this.filePath(e),{force:!0})}sweepExpired(){const e=this.now();for(const t of[...this.records.values()]){const a="staged"===t.state?this.stagedTtlMs:this.committedTtlMs;e-(t.committedAtMs??t.createdAtMs)>a&&this.remove(t.attachmentId)}}filePath(e){return u(this.dir,`${e}.bin`)}}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { RUNTIME_METHODS, approvalIdSchema, type ApprovalResolution, type InitializeParams, type InitializeResult, type
|
|
1
|
+
import { RUNTIME_METHODS, approvalIdSchema, type ApprovalResolution, type InitializeParams, type InitializeResult, type RuntimeEventEnvelopeV14, type RuntimeEventsResumeParams, type RuntimeEventsResumeResult, type RuntimeInstanceId, type LatestRuntimeMethodParams, type LatestRuntimeMethodResult, type RuntimeMethodParams, type RuntimeMethodResult, type RuntimeProtocolErrorDataV14, type ThreadSnapshotV14Full, type ThreadId, type TurnId, type UserInputForm, type UserInputResult } from "@roll-agent/protocol";
|
|
2
2
|
import type { AgentSession } from "../engine/agent-session.ts";
|
|
3
|
+
import { AttachmentStore } from "./attachment-store.ts";
|
|
3
4
|
import type { SessionUserInputRequestId } from "../interaction/user-input-interaction-manager.ts";
|
|
4
5
|
import { ThreadStore, type StoredRuntimeEvent } from "../store/thread-store.ts";
|
|
5
|
-
type RollErrorCode =
|
|
6
|
+
type RollErrorCode = RuntimeProtocolErrorDataV14["rollCode"];
|
|
6
7
|
export type RuntimeServiceSession = Pick<AgentSession, "id" | "send" | "approve" | "reject" | "cancel" | "close" | "getCapabilityManifest" | "getCapabilityTurnContext"> & Partial<Pick<AgentSession, "setUserInputAvailable" | "resolveUserInput" | "cancelUserInput">>;
|
|
7
8
|
export interface RuntimeServiceEngine {
|
|
8
9
|
createSession(input?: {
|
|
@@ -21,6 +22,8 @@ export interface RuntimeServiceOptions {
|
|
|
21
22
|
* Supplying this enables the `reasoning-summary` capability.
|
|
22
23
|
*/
|
|
23
24
|
readonly reasoningSummaryProjector?: (delta: string) => string | undefined;
|
|
25
|
+
/** Supplying this enables the Protocol 1.4 `attachments` capability. */
|
|
26
|
+
readonly attachmentStore?: AttachmentStore;
|
|
24
27
|
}
|
|
25
28
|
export interface RuntimeApprovalIdentity {
|
|
26
29
|
readonly threadId: ThreadId;
|
|
@@ -33,7 +36,7 @@ export type RuntimeApprovalDecision = {
|
|
|
33
36
|
readonly decision: "reject";
|
|
34
37
|
readonly reason?: string;
|
|
35
38
|
};
|
|
36
|
-
export type RuntimeThreadSnapshot = Omit<
|
|
39
|
+
export type RuntimeThreadSnapshot = Omit<ThreadSnapshotV14Full, "pendingInteractions">;
|
|
37
40
|
export interface RuntimeEventReplayBatch extends RuntimeEventsResumeResult {
|
|
38
41
|
readonly events: readonly StoredRuntimeEvent[];
|
|
39
42
|
}
|
|
@@ -60,13 +63,13 @@ export declare class RuntimeServiceError extends Error {
|
|
|
60
63
|
readonly details?: unknown;
|
|
61
64
|
});
|
|
62
65
|
}
|
|
63
|
-
type MutationRuntimeMethod = typeof RUNTIME_METHODS.threadCreate | typeof RUNTIME_METHODS.threadRename | typeof RUNTIME_METHODS.threadDelete | typeof RUNTIME_METHODS.threadDetach | typeof RUNTIME_METHODS.turnStart | typeof RUNTIME_METHODS.turnCancel | typeof RUNTIME_METHODS.approvalRespond;
|
|
66
|
+
type MutationRuntimeMethod = typeof RUNTIME_METHODS.threadCreate | typeof RUNTIME_METHODS.threadRename | typeof RUNTIME_METHODS.threadDelete | typeof RUNTIME_METHODS.threadDetach | typeof RUNTIME_METHODS.turnStart | typeof RUNTIME_METHODS.turnCancel | typeof RUNTIME_METHODS.approvalRespond | typeof RUNTIME_METHODS.attachmentStage | typeof RUNTIME_METHODS.attachmentChunk | typeof RUNTIME_METHODS.attachmentCommit | typeof RUNTIME_METHODS.attachmentRelease;
|
|
64
67
|
export declare class MutationRequestCache {
|
|
65
68
|
private readonly maxEntries;
|
|
66
69
|
private readonly inFlight;
|
|
67
70
|
private readonly settled;
|
|
68
71
|
constructor(maxEntries: number);
|
|
69
|
-
run<TMethod extends MutationRuntimeMethod>(requestId: string, method: TMethod, params:
|
|
72
|
+
run<TMethod extends MutationRuntimeMethod>(requestId: string, method: TMethod, params: LatestRuntimeMethodParams<TMethod>, action: () => LatestRuntimeMethodResult<TMethod> | Promise<LatestRuntimeMethodResult<TMethod>>): Promise<LatestRuntimeMethodResult<TMethod>>;
|
|
70
73
|
clear(): void;
|
|
71
74
|
private assertMatchingRequest;
|
|
72
75
|
private settle;
|
|
@@ -81,6 +84,7 @@ export declare class RuntimeService {
|
|
|
81
84
|
private readonly maxFrameBytes;
|
|
82
85
|
private readonly idempotencyCacheEntries;
|
|
83
86
|
private readonly reasoningSummaryProjector;
|
|
87
|
+
private readonly attachmentStore;
|
|
84
88
|
private readonly sessions;
|
|
85
89
|
private readonly activeTurns;
|
|
86
90
|
private readonly activeTurnOwners;
|
|
@@ -96,7 +100,7 @@ export declare class RuntimeService {
|
|
|
96
100
|
private userInputAvailable;
|
|
97
101
|
constructor(engine: RuntimeServiceEngine, store: ThreadStore, options?: RuntimeServiceOptions);
|
|
98
102
|
initialize(params: InitializeParams): InitializeResult;
|
|
99
|
-
onEvent(listener: (event:
|
|
103
|
+
onEvent(listener: (event: RuntimeEventEnvelopeV14) => void): () => void;
|
|
100
104
|
onFatalError(listener: (error: unknown) => void): () => void;
|
|
101
105
|
onUserInputInteraction(listener: (event: RuntimeUserInputInteractionEvent) => void): () => void;
|
|
102
106
|
setUserInputAvailable(available: boolean): void;
|
|
@@ -109,11 +113,18 @@ export declare class RuntimeService {
|
|
|
109
113
|
deleteThread(params: RuntimeMethodParams<"thread.delete">): Promise<RuntimeMethodResult<"thread.delete">>;
|
|
110
114
|
detachThread(params: RuntimeMethodParams<"thread.detach">): Promise<RuntimeMethodResult<"thread.detach">>;
|
|
111
115
|
threadCapabilities(params: RuntimeMethodParams<"thread.capabilities">): Promise<RuntimeMethodResult<"thread.capabilities">>;
|
|
112
|
-
startTurn(params:
|
|
116
|
+
startTurn(params: LatestRuntimeMethodParams<"turn.start">): Promise<LatestRuntimeMethodResult<"turn.start">>;
|
|
113
117
|
cancelTurn(params: RuntimeMethodParams<"turn.cancel">): Promise<RuntimeMethodResult<"turn.cancel">>;
|
|
114
118
|
respondApproval(params: RuntimeMethodParams<"approval.respond">): Promise<RuntimeMethodResult<"approval.respond">>;
|
|
115
119
|
resolvePendingApproval(identity: RuntimeApprovalIdentity, decision: RuntimeApprovalDecision): RuntimeMethodResult<"approval.respond">;
|
|
116
120
|
getPendingApprovalExpiresAt(identity: RuntimeApprovalIdentity): string | undefined;
|
|
121
|
+
stageAttachment(params: LatestRuntimeMethodParams<"attachment.stage">): Promise<LatestRuntimeMethodResult<"attachment.stage">>;
|
|
122
|
+
appendAttachmentChunk(params: LatestRuntimeMethodParams<"attachment.chunk">): Promise<LatestRuntimeMethodResult<"attachment.chunk">>;
|
|
123
|
+
commitAttachment(params: LatestRuntimeMethodParams<"attachment.commit">): Promise<LatestRuntimeMethodResult<"attachment.commit">>;
|
|
124
|
+
releaseAttachment(params: LatestRuntimeMethodParams<"attachment.release">): Promise<LatestRuntimeMethodResult<"attachment.release">>;
|
|
125
|
+
private requireAttachmentStore;
|
|
126
|
+
private throwAttachmentFailure;
|
|
127
|
+
private resolveTurnAttachments;
|
|
117
128
|
private cancelPendingApproval;
|
|
118
129
|
failPendingApprovalInteraction(identity: RuntimeApprovalIdentity, reason: string, resolution?: Extract<ApprovalResolution, {
|
|
119
130
|
readonly status: "cancelled" | "expired";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash as e,randomUUID as t}from"node:crypto";import{APPROVAL_EXPLANATION_PREVIEW_KEY as r,RUNTIME_ERROR_CODES as s,RUNTIME_FEATURES as n,RUNTIME_METHODS as i,RUNTIME_PROTOCOL_VERSION as a,SUPPORTED_RUNTIME_PROTOCOL_VERSIONS as o,approvalExplanationSchema as d,approvalIdSchema as u,normalizeUserInputResultForForm as l,operationIdSchema as c,parseRuntimeMethodResult as h,runtimeDurableEventV13Schema as p,runtimeEphemeralEventV13Schema as I,runtimeInstanceIdSchema as m,runtimeProtocolVersionSchema as v,streamIdSchema as g,threadIdSchema as f,userInputFormSchema as y}from"@roll-agent/protocol";import{createSafeCapabilitySnapshot as T}from"../engine/capability-manifest.js";import{isSensitiveFieldName as w,redactSecretText as A,toRedactedToolExecutionRecordSummary as q}from"../tool-bridge/tool-execution-record.js";import{RuntimeEventCursorExpiredError as S,RuntimeEventCursorGapError as E,ThreadStore as R}from"../store/thread-store.js";const x=4194304,b=500,O=1e4,P=16e3,U=64,C=128,N=16,F=new Set(["_meta","authorization","cookie","password","provideroptions","raw","secret","token"]);export class RuntimeServiceError extends Error{rollCode;retryable;details;constructor(e,t,r={}){super(t),this.name="RuntimeServiceError",this.rollCode=e,this.retryable=r.retryable??!1,this.details=r.details}}function j(t){const r=JSON.stringify(t);if(void 0===r)throw new RuntimeServiceError(s.invalidParams,"无法为 mutation 请求生成稳定指纹");return e("sha256").update(r).digest("hex")}export class MutationRequestCache{maxEntries;inFlight=new Map;settled=new Map;constructor(e){if(!Number.isInteger(e)||e<1)throw new Error("idempotencyCacheEntries must be a positive integer");this.maxEntries=e}run(e,t,r,s){const n=j(r),i=this.inFlight.get(e);if(void 0!==i)return this.assertMatchingRequest(e,t,n,i),i.response.then(e=>h(t,e));const a=this.settled.get(e);if(void 0!==a)return this.assertMatchingRequest(e,t,n,a),this.settled.delete(e),this.settled.set(e,a),a.response.then(e=>h(t,e));const o=Promise.resolve().then(s),d={method:t,fingerprint:n,response:o};return this.inFlight.set(e,d),o.then(()=>{this.settle(e,d)},()=>{this.settle(e,d)}),o}clear(){this.inFlight.clear(),this.settled.clear()}assertMatchingRequest(e,t,r,n){if(n.method!==t||n.fingerprint!==r)throw new RuntimeServiceError(s.invalidParams,`requestId "${e}" 已用于不同请求`)}settle(e,t){if(this.inFlight.get(e)===t)for(this.inFlight.delete(e),this.settled.set(e,t);this.settled.size>this.maxEntries;){const e=this.settled.keys().next().value;if(void 0===e)return;this.settled.delete(e)}}}function M(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function V(e,t,r=0){if(void 0!==t&&(F.has(t.toLowerCase())||w(t)))return"[redacted]";if(r>=N)return"[nested value omitted]";if(null===e||"boolean"==typeof e)return e;if("string"==typeof e){const t=A(e);return t.length<=P?t:`${t.slice(0,P)}\n[value clipped]`}if("number"==typeof e)return Number.isFinite(e)?e:String(e);if(Array.isArray(e)){const t=e.slice(0,U).map(e=>V(e,void 0,r+1));return e.length>U&&t.push(`[${String(e.length-U)} items omitted]`),t}if(M(e)){const t={};for(const[s,n]of Object.entries(e).slice(0,C))void 0!==n&&(t[s]=V(n,s,r+1));return Object.keys(e).length>C&&(t._omitted=`${String(Object.keys(e).length-C)} keys omitted`),t}return void 0===e?null:String(e)}function $(e){return e instanceof Error?e.message:String(e)}function k(e){const t=e.content;return"string"==typeof t?t:Array.isArray(t)?t.flatMap(e=>M(e)&&"text"===e.type&&"string"==typeof e.text?[e.text]:[]).join(""):""}function B(e){return f.parse(e)}function L(e,t){return{id:B(t.id),...void 0!==t.title?{title:t.title}:{},...void 0!==t.model?{model:t.model}:{},createdAt:t.createdAt,updatedAt:t.updatedAt,messageCount:e.countTranscriptMessages(t.id)}}function z(e){if("user"!==e.message.role&&"assistant"!==e.message.role)return;const t=A(k(e.message));return{sequence:e.sequence,role:e.message.role,createdAt:e.createdAt,parts:t.length>0?[{type:"text",text:t}]:[]}}function D(e){const t=q(e);return{id:c.parse(t.id),sequence:e.sequence,toolCallId:t.toolCallId,agentName:t.agentName,toolName:t.toolName,createdAt:t.createdAt,outcome:{kind:t.outcome.kind,..."reason"in t.outcome&&void 0!==t.outcome.reason?{reason:t.outcome.reason}:{}},display:V(t.display,void 0)}}function _(e,t){const s=V(t.input,void 0),n=void 0===t.explanation?void 0:d.safeParse(A(t.explanation)),i=!0===n?.success&&M(s)?{...s,[r]:n.data}:s;return{id:u.parse(t.approvalId),turnId:e,agentName:t.agentName,toolName:t.toolName,preview:i,...void 0!==t.reason?{reason:A(t.reason)}:{}}}export class RuntimeService{runtimeInstanceId;engine;store;serverName;serverVersion;runtimeVersion;maxFrameBytes;idempotencyCacheEntries;reasoningSummaryProjector;sessions=new Map;activeTurns=new Map;activeTurnOwners=new Map;settledTurnOwners=new Map;pendingApprovals=new Map;pendingUserInputs=new Map;listeners=new Set;fatalErrorListeners=new Set;userInputListeners=new Set;mutationRequests;sequence=0;closing=!1;userInputAvailable=!1;constructor(e,r,s={}){this.engine=e,this.store=r,this.serverName=s.serverName??"roll-runtime",this.serverVersion=s.serverVersion??"1.0",this.runtimeVersion=s.runtimeVersion??"development",this.maxFrameBytes=s.maxFrameBytes??x,this.idempotencyCacheEntries=s.idempotencyCacheEntries??O,this.mutationRequests=new MutationRequestCache(this.idempotencyCacheEntries),this.reasoningSummaryProjector=s.reasoningSummaryProjector,this.runtimeInstanceId=m.parse(t())}initialize(e){let t;this.assertOpen();for(const r of e.protocolVersions){const e=v.safeParse(r);if(e.success){t=e.data;break}}if(void 0===t)throw new RuntimeServiceError(s.protocolVersionUnsupported,`不支持客户端协议版本:${e.protocolVersions.join(", ")}`,{details:{supportedVersions:o}});const r={protocolVersion:t,runtimeInstanceId:this.runtimeInstanceId,server:{name:this.serverName,version:this.serverVersion,runtimeVersion:this.runtimeVersion},features:n.filter(e=>"reasoning-summary"!==e||void 0!==this.reasoningSummaryProjector),limits:{maxFrameBytes:this.maxFrameBytes,maxPageSize:b,idempotencyCacheEntries:this.idempotencyCacheEntries}};return"1.3"===t?{...r,protocolVersion:t,limits:{...r.limits,eventReplay:!0}}:{...r,protocolVersion:t,limits:{...r.limits,eventReplay:!1}}}onEvent(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}onFatalError(e){return this.fatalErrorListeners.add(e),()=>{this.fatalErrorListeners.delete(e)}}onUserInputInteraction(e){return this.userInputListeners.add(e),()=>{this.userInputListeners.delete(e)}}setUserInputAvailable(e){if(this.userInputAvailable!==e){this.userInputAvailable=e,e||this.cancelAllPendingUserInputs("当前客户端已撤销用户输入处理能力");for(const t of this.sessions.values())t.setUserInputAvailable?.(e)}}listThreads(e){this.assertOpen();const t=void 0===e.cursor?0:Number(e.cursor);if(!Number.isSafeInteger(t)||t<0)throw new RuntimeServiceError(s.invalidParams,"thread.list cursor 超出安全整数范围");const r=this.store.listThreads(),n=r.slice(t,t+e.limit).map(e=>L(this.store,e)),i=t+n.length;return{items:n,nextCursor:i<r.length?String(i):null}}async createThread(e){return this.mutationRequests.run(e.requestId,i.threadCreate,e,async()=>{this.assertOpen();const t=await this.engine.createSession(void 0!==e.title?{title:e.title}:{});return this.rememberSession(t),{thread:L(this.store,this.requireThread(t.id))}})}async openThread(e){return await this.requireSession(e.threadId),this.snapshotThread({threadId:e.threadId,limit:100})}snapshotThread(e){this.assertOpen();const t=this.requireThread(e.threadId),r=this.store.listRecentTranscriptMessages(e.threadId,{...void 0!==e.messageBeforeSequence?{beforeSequence:e.messageBeforeSequence}:{},limit:e.limit}),s=this.store.listRecentToolExecutions(e.threadId,{...void 0!==e.operationBeforeSequence?{beforeSequence:e.operationBeforeSequence}:{},limit:e.limit}),n=this.activeTurns.get(e.threadId),i=void 0===n?void 0:{id:n.turnId,status:n.status,startedAt:n.startedAt};return{thread:L(this.store,t),messages:{items:r.entries.flatMap(e=>{const t=z(e);return void 0===t?[]:[t]}),nextBeforeSequence:r.nextBeforeSequence??null},operations:{items:s.entries.map(D),nextBeforeSequence:s.nextBeforeSequence??null},...void 0!==i?{activeTurn:i}:{},pendingApprovals:[...this.pendingApprovals.values()].filter(t=>t.threadId===e.threadId).map(e=>e.approval),transcriptCompleteness:this.store.getTranscriptCompleteness(e.threadId),eventCursor:this.store.getRuntimeEventCursor(e.threadId)}}resumeEvents(e){this.assertOpen(),this.requireThread(e.threadId);try{return this.store.resumeRuntimeEvents(e.threadId,e.afterCursor)}catch(t){if(t instanceof S)throw new RuntimeServiceError(s.eventCursorExpired,"Runtime Event cursor 已超出当前 Thread 的保留窗口,请回退到 thread.snapshot",{details:{threadId:e.threadId}});if(t instanceof E)throw new RuntimeServiceError(s.eventCursorGap,"Runtime Event cursor 与当前 Thread 事件日志不连续,请回退到 thread.snapshot",{details:{threadId:e.threadId}});throw t}}async renameThread(e){return this.mutationRequests.run(e.requestId,i.threadRename,e,()=>(this.assertOpen(),this.requireThread(e.threadId),this.store.updateTitle(e.threadId,e.title),{thread:L(this.store,this.requireThread(e.threadId))}))}async deleteThread(e){return this.mutationRequests.run(e.requestId,i.threadDelete,e,async()=>{if(this.assertOpen(),this.requireThread(e.threadId),this.activeTurns.has(e.threadId))throw new RuntimeServiceError(s.threadBusy,`Thread "${e.threadId}" 仍有活动 Turn`,{retryable:!0});const t=this.sessions.get(e.threadId);return void 0!==t&&(await t.close(),this.sessions.delete(e.threadId)),this.store.deleteThread(e.threadId),{deleted:!0}})}async detachThread(e){return this.mutationRequests.run(e.requestId,i.threadDetach,e,async()=>{if(this.assertOpen(),this.requireThread(e.threadId),this.activeTurns.has(e.threadId))return{detached:!1};const t=this.sessions.get(e.threadId);return void 0===t?{detached:!1}:(await t.close(),this.sessions.delete(e.threadId),{detached:!0})})}async threadCapabilities(e){const t=await this.requireSession(e.threadId);return{manifest:V(T(t.getCapabilityManifest(),t.getCapabilityTurnContext()),void 0)}}async startTurn(e){return this.mutationRequests.run(e.requestId,i.turnStart,e,async()=>{this.assertOpen();const t=this.findTurnOwner(e.turnId,e.threadId);if(void 0!==t){if(t===e.threadId)return{accepted:!0,turnId:e.turnId};throw new RuntimeServiceError(s.turnAlreadyActive,`Turn "${e.turnId}" 已属于另一个 Thread`)}if(this.activeTurns.has(e.threadId))throw new RuntimeServiceError(s.turnAlreadyActive,`Thread "${e.threadId}" 已有活动 Turn`,{retryable:!0});const r=await this.requireSession(e.threadId),n=this.findTurnOwner(e.turnId,e.threadId);if(void 0!==n){if(n===e.threadId)return{accepted:!0,turnId:e.turnId};throw new RuntimeServiceError(s.turnAlreadyActive,`Turn "${e.turnId}" 已属于另一个 Thread`)}if(this.activeTurns.has(e.threadId))throw new RuntimeServiceError(s.turnAlreadyActive,`Thread "${e.threadId}" 已有活动 Turn`,{retryable:!0});const i={threadId:e.threadId,turnId:e.turnId,session:r,startedAt:(new Date).toISOString(),status:"running",interactionFailure:void 0};this.activeTurns.set(e.threadId,i),this.activeTurnOwners.set(e.turnId,e.threadId);try{this.emit(e.threadId,e.turnId,{type:"turn.started"})}catch(t){throw this.activeTurns.delete(e.threadId),this.activeTurnOwners.delete(e.turnId),t}return this.driveTurn(i,e.input.text).catch(()=>{}),{accepted:!0,turnId:e.turnId}})}async cancelTurn(e){return this.mutationRequests.run(e.requestId,i.turnCancel,e,()=>{this.assertOpen();const t=this.activeTurns.get(e.threadId);if(void 0===t||t.turnId!==e.turnId)throw new RuntimeServiceError(s.turnNotFound,`Turn "${e.turnId}" 不存在或已结束`);t.status="cancelling";let r=!1;try{this.cancelPendingUserInputsForTurn(e.turnId,"Turn 已由客户端取消"),this.cancelPendingApprovalsForTurn(e.turnId,{status:"cancelled",reason:"Turn 已由客户端取消"})}finally{r=t.session.cancel()}return{cancelling:r}})}async respondApproval(e){return this.mutationRequests.run(e.requestId,i.approvalRespond,e,()=>this.resolvePendingApproval(e,"approve"===e.decision?{decision:"approve"}:{decision:"reject",...void 0!==e.reason?{reason:e.reason}:{}}))}resolvePendingApproval(e,t){this.assertOpen();const r=this.requirePendingApproval(e),n="approve"===t.decision?r.session.approve(e.approvalId):r.session.reject(e.approvalId,t.reason);if(this.pendingApprovals.delete(e.approvalId),!n)throw this.emitApprovalResolved(r,{status:"cancelled",reason:"审批已失效"}),new RuntimeServiceError(s.approvalNotFound,`Approval "${e.approvalId}" 已失效`);return this.emitApprovalResolved(r,"approve"===t.decision?{status:"resolved",decision:"approve"}:{status:"resolved",decision:"reject",...void 0!==t.reason?{reason:t.reason}:{}}),{resolved:!0}}getPendingApprovalExpiresAt(e){return this.findPendingApproval(e)?.expiresAt}cancelPendingApproval(e,t){const r=this.findPendingApproval(e);return void 0!==r&&(this.pendingApprovals.delete(e.approvalId),this.emitApprovalResolved(r,t),!0)}async failPendingApprovalInteraction(e,t,r={status:"cancelled",reason:t}){const s=this.findPendingApproval(e);if(void 0===s)return!1;const n=this.activeTurns.get(e.threadId);void 0!==n&&n.turnId===e.turnId&&(n.interactionFailure=t),this.pendingApprovals.delete(e.approvalId);try{this.emitApprovalResolved(s,r)}finally{s.session.cancel()}return!0}resolvePendingUserInput(e,t){const r=this.pendingUserInputs.get(e);if(void 0===r)return!1;let s;try{s=l(r.form,t)}catch{return this.cancelPendingUserInput(e,"用户输入不符合原始表单约束"),!1}this.pendingUserInputs.delete(e);const n=r.session.resolveUserInput?.(e,s)??!1;if(!n){const t="用户输入请求已失效";return r.session.cancelUserInput?.(e,t),this.restoreRunningStatus(r),this.emitUserInputInteraction({type:"settled",requestId:e,threadId:r.threadId,turnId:r.turnId,reason:t}),!1}return this.restoreRunningStatus(r),this.emitUserInputInteraction({type:"settled",requestId:e,threadId:r.threadId,turnId:r.turnId,reason:"submitted"===s.status?"用户已提交输入":"用户已取消输入"}),n}cancelPendingUserInput(e,t){const r=this.pendingUserInputs.get(e);if(void 0===r)return!1;this.pendingUserInputs.delete(e);const s=r.session.cancelUserInput?.(e,t)??!1;return this.restoreRunningStatus(r),this.emitUserInputInteraction({type:"settled",requestId:e,threadId:r.threadId,turnId:r.turnId,reason:t}),s}getOperation(e){this.assertOpen(),this.requireThread(e.threadId);const t=this.store.getToolExecution(e.threadId,e.operationId);return{operation:void 0===t?null:D(t)}}async close(){if(this.closing)return;this.closing=!0,this.setUserInputAvailable(!1);for(const e of this.activeTurns.values())e.session.cancel();const e=[...this.sessions.values()];this.sessions.clear(),this.activeTurns.clear(),this.activeTurnOwners.clear(),this.settledTurnOwners.clear(),this.pendingApprovals.clear(),this.pendingUserInputs.clear(),this.userInputListeners.clear(),await Promise.allSettled(e.map(e=>e.close())),this.mutationRequests.clear(),this.fatalErrorListeners.clear()}assertOpen(){if(this.closing)throw new RuntimeServiceError(s.runtimeClosing,"Runtime 正在关闭",{retryable:!0})}requireThread(e){const t=this.store.getThread(e);if(void 0===t)throw new RuntimeServiceError(s.threadNotFound,`Thread "${e}" 不存在`);return t}async requireSession(e){this.assertOpen(),this.requireThread(e);const t=this.sessions.get(e);if(void 0!==t)return t;const r=await this.engine.resumeSession(e);return this.rememberSession(r),r}rememberSession(e){e.setUserInputAvailable?.(this.userInputAvailable),this.sessions.set(e.id,e)}emit(e,t,r){const s=(new Date).toISOString(),n={protocolVersion:a,runtimeInstanceId:this.runtimeInstanceId,sequence:this.sequence,timestamp:s,threadId:e,...void 0!==t?{turnId:t}:{}},i=p.safeParse(r),o=i.success?(()=>{let r;try{r=this.store.appendRuntimeEvent({threadId:e,...void 0===t?{}:{turnId:t},timestamp:s,event:i.data})}catch(e){throw this.notifyFatalError(e),e}return{...n,durability:"durable",eventId:r.eventId,cursor:r.cursor,event:i.data}})():{...n,durability:"ephemeral",event:I.parse(r)};this.sequence+=1;for(const e of this.listeners)try{e(o)}catch{}}notifyFatalError(e){for(const e of this.activeTurns.values())try{e.session.cancel()}catch{}for(const t of this.fatalErrorListeners)try{t(e)}catch{}}async driveTurn(e,t){const r={streamId:void 0,text:"",terminalEvent:void 0};let s;try{for await(const s of e.session.send(t))this.projectSessionEvent(e,r,s);s=r.terminalEvent??{type:"turn.completed"}}catch(e){s=r.terminalEvent??{type:"turn.failed",stage:"execute",message:A($(e))}}finally{this.activeTurns.get(e.threadId)===e&&this.activeTurns.delete(e.threadId),this.settleTurnOwner(e)}this.emitTurnTerminal(e,s)}findTurnOwner(e,t){const r=this.activeTurnOwners.get(e);if(void 0!==r)return r;const s=this.settledTurnOwners.get(e);return s===t&&(this.settledTurnOwners.delete(e),this.settledTurnOwners.set(e,s)),s}settleTurnOwner(e){if(this.activeTurnOwners.get(e.turnId)===e.threadId&&(this.activeTurnOwners.delete(e.turnId),!this.closing))for(this.settledTurnOwners.set(e.turnId,e.threadId);this.settledTurnOwners.size>this.idempotencyCacheEntries;){const e=this.settledTurnOwners.keys().next().value;if(void 0===e)return;this.settledTurnOwners.delete(e)}}projectSessionEvent(e,r,s){switch(s.type){case"message-start":{const s=g.parse(t());return r.streamId=s,r.text="",void this.emit(e.threadId,e.turnId,{type:"message.started",streamId:s})}case"text-delta":{const n=r.streamId??g.parse(t());return void 0===r.streamId&&(r.streamId=n,this.emit(e.threadId,e.turnId,{type:"message.started",streamId:n})),r.text+=s.delta,void this.emit(e.threadId,e.turnId,{type:"message.delta",streamId:n,delta:s.delta})}case"message-finish":{const n=r.streamId??g.parse(t());return this.emit(e.threadId,e.turnId,{type:"message.completed",streamId:n,text:A(s.text||r.text)}),void(r.terminalEvent??={type:"turn.completed"})}case"reasoning-delta":{const t=this.reasoningSummaryProjector?.(s.delta);return void(void 0!==t&&t.length>0&&this.emit(e.threadId,e.turnId,{type:"reasoning.summary.delta",reasoningId:s.reasoningId,delta:A(t)}))}case"tool-call":return void this.emit(e.threadId,e.turnId,{type:"tool.started",toolCallId:s.toolCallId,agentName:s.agentName,toolName:s.toolName,input:V(s.input,void 0)});case"tool-output-delta":return void this.emit(e.threadId,e.turnId,{type:"tool.output",toolCallId:s.toolCallId,agentName:s.agentName,toolName:s.toolName,stream:s.stream,delta:A(s.delta)});case"tool-result":return void this.emit(e.threadId,e.turnId,{type:"tool.completed",toolCallId:s.toolCallId,agentName:s.agentName,toolName:s.toolName,...void 0!==s.executionId?{operationId:c.parse(s.executionId)}:{},...void 0!==s.outcome?{outcome:this.projectToolOutcome(s.outcome)}:{},display:V(s.display,void 0)});case"confirmation-required":{const t=_(e.turnId,s);return this.pendingApprovals.set(t.id,{threadId:e.threadId,session:e.session,approval:t,expiresAt:s.expiresAt}),void this.emit(e.threadId,e.turnId,{type:"approval.required",approval:t})}case"user-input-required":{if(!this.userInputAvailable||void 0===e.session.resolveUserInput||void 0===e.session.cancelUserInput)return void e.session.cancelUserInput?.(s.requestId,"当前客户端未协商用户输入处理能力");const t=y.parse(s.form),r=y.parse(t),n={requestId:s.requestId,threadId:e.threadId,turnId:e.turnId,session:e.session,form:t,expiresAt:s.expiresAt};this.pendingUserInputs.set(s.requestId,n),e.status="waiting-for-user";return void(this.emitUserInputInteraction({type:"required",requestId:s.requestId,threadId:e.threadId,turnId:e.turnId,form:r,expiresAt:s.expiresAt})||this.cancelPendingUserInput(s.requestId,"当前没有可处理用户输入请求的客户端"))}case"user-input-settled":{const e=this.pendingUserInputs.get(s.requestId);if(void 0===e)return;return this.pendingUserInputs.delete(s.requestId),this.restoreRunningStatus(e),void this.emitUserInputInteraction({type:"settled",requestId:e.requestId,threadId:e.threadId,turnId:e.turnId,reason:"submitted"===s.status?"用户输入请求已结算":"用户输入请求已取消"})}case"turn-cancelled":r.terminalEvent??=void 0===e.interactionFailure?{type:"turn.cancelled",reason:s.reason,message:A(s.message)}:{type:"turn.failed",stage:"execute",message:A(e.interactionFailure)};break;case"error":r.terminalEvent??={type:"turn.failed",stage:s.stage,message:A(s.message)}}}projectToolOutcome(e){return{kind:e.kind,..."reason"in e&&void 0!==e.reason?{reason:A(e.reason)}:{}}}findPendingApproval(e){const t=this.pendingApprovals.get(e.approvalId);if(void 0!==t&&t.threadId===e.threadId&&t.approval.turnId===e.turnId)return t}requirePendingApproval(e){const t=this.findPendingApproval(e);if(void 0===t)throw new RuntimeServiceError(s.approvalNotFound,`Approval "${e.approvalId}" 不存在或已失效`);return t}emitApprovalResolved(e,t){const r="reason"in t&&void 0!==t.reason?{...t,reason:A(t.reason)}:t;this.emit(e.threadId,e.approval.turnId,{type:"approval.resolved",approvalId:e.approval.id,resolution:r})}cancelPendingApprovalsForTurn(e,t){const r=[...this.pendingApprovals.values()].filter(t=>t.approval.turnId===e);let s;for(const e of r)try{this.cancelPendingApproval({threadId:e.threadId,turnId:e.approval.turnId,approvalId:e.approval.id},t)}catch(e){s??=e}if(void 0!==s)throw s}cancelPendingUserInputsForTurn(e,t){const r=[...this.pendingUserInputs.values()].filter(t=>t.turnId===e).map(e=>e.requestId);for(const e of r)this.cancelPendingUserInput(e,t)}cancelAllPendingUserInputs(e){for(const t of[...this.pendingUserInputs.keys()])this.cancelPendingUserInput(t,e)}restoreRunningStatus(e){const t=this.activeTurns.get(e.threadId);void 0!==t&&t.turnId===e.turnId&&"waiting-for-user"===t.status&&(t.status="running")}emitUserInputInteraction(e){let t=!1;for(const r of this.userInputListeners)try{r(e),t=!0}catch{}return t}emitTurnTerminal(e,t){this.cancelPendingUserInputsForTurn(e.turnId,`Turn 已终止:${t.type}`),this.cancelPendingApprovalsForTurn(e.turnId,"turn.cancelled"===t.type&&"timeout"===t.reason?{status:"expired",reason:"Turn 已超时"}:{status:"cancelled",reason:`Turn 已终止:${t.type}`}),this.emit(e.threadId,e.turnId,t)}}
|
|
1
|
+
import{createHash as e,randomUUID as t}from"node:crypto";import{APPROVAL_EXPLANATION_PREVIEW_KEY as r,RUNTIME_ERROR_CODES as s,RUNTIME_FEATURES_V13 as n,RUNTIME_METHODS as i,RUNTIME_PROTOCOL_VERSION as a,RUNTIME_V14_MAX_ATTACHMENT_BYTES as o,RUNTIME_V14_MAX_ATTACHMENT_CHUNK_BYTES as d,RUNTIME_V14_MAX_STAGED_ATTACHMENTS as u,RUNTIME_V14_MAX_TURN_ATTACHMENTS as h,SUPPORTED_RUNTIME_PROTOCOL_VERSIONS as c,approvalExplanationSchema as l,approvalIdSchema as p,normalizeUserInputResultForForm as m,operationIdSchema as I,parseLatestRuntimeMethodResult as v,runtimeDurableEventV13Schema as g,runtimeEphemeralEventV13Schema as f,runtimeInstanceIdSchema as y,runtimeProtocolVersionSchema as T,streamIdSchema as A,threadIdSchema as w,userInputFormSchema as q}from"@roll-agent/protocol";import{AttachmentStore as S}from"./attachment-store.js";import{createSafeCapabilitySnapshot as R}from"../engine/capability-manifest.js";import{isSensitiveFieldName as x,redactSecretText as E,toRedactedToolExecutionRecordSummary as b}from"../tool-bridge/tool-execution-record.js";import{RuntimeEventCursorExpiredError as O,RuntimeEventCursorGapError as C,ThreadStore as P}from"../store/thread-store.js";const U=4194304,N=500,F=1e4,k=16e3,B=64,j=128,M=16,V=new Set(["_meta","authorization","cookie","password","provideroptions","raw","secret","token"]);export class RuntimeServiceError extends Error{rollCode;retryable;details;constructor(e,t,r={}){super(t),this.name="RuntimeServiceError",this.rollCode=e,this.retryable=r.retryable??!1,this.details=r.details}}function $(t){const r=JSON.stringify(t);if(void 0===r)throw new RuntimeServiceError(s.invalidParams,"无法为 mutation 请求生成稳定指纹");return e("sha256").update(r).digest("hex")}export class MutationRequestCache{maxEntries;inFlight=new Map;settled=new Map;constructor(e){if(!Number.isInteger(e)||e<1)throw new Error("idempotencyCacheEntries must be a positive integer");this.maxEntries=e}run(e,t,r,s){const n=$(r),i=this.inFlight.get(e);if(void 0!==i)return this.assertMatchingRequest(e,t,n,i),i.response.then(e=>v(t,e));const a=this.settled.get(e);if(void 0!==a)return this.assertMatchingRequest(e,t,n,a),this.settled.delete(e),this.settled.set(e,a),a.response.then(e=>v(t,e));const o=Promise.resolve().then(s),d={method:t,fingerprint:n,response:o};return this.inFlight.set(e,d),o.then(()=>{this.settle(e,d)},()=>{this.settle(e,d)}),o}clear(){this.inFlight.clear(),this.settled.clear()}assertMatchingRequest(e,t,r,n){if(n.method!==t||n.fingerprint!==r)throw new RuntimeServiceError(s.invalidParams,`requestId "${e}" 已用于不同请求`)}settle(e,t){if(this.inFlight.get(e)===t)for(this.inFlight.delete(e),this.settled.set(e,t);this.settled.size>this.maxEntries;){const e=this.settled.keys().next().value;if(void 0===e)return;this.settled.delete(e)}}}function L(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function z(e,t,r=0){if(void 0!==t&&(V.has(t.toLowerCase())||x(t)))return"[redacted]";if(r>=M)return"[nested value omitted]";if(null===e||"boolean"==typeof e)return e;if("string"==typeof e){const t=E(e);return t.length<=k?t:`${t.slice(0,k)}\n[value clipped]`}if("number"==typeof e)return Number.isFinite(e)?e:String(e);if(Array.isArray(e)){const t=e.slice(0,B).map(e=>z(e,void 0,r+1));return e.length>B&&t.push(`[${String(e.length-B)} items omitted]`),t}if(L(e)){const t={};for(const[s,n]of Object.entries(e).slice(0,j))void 0!==n&&(t[s]=z(n,s,r+1));return Object.keys(e).length>j&&(t._omitted=`${String(Object.keys(e).length-j)} keys omitted`),t}return void 0===e?null:String(e)}function D(e){return e instanceof Error?e.message:String(e)}function W(e){const t=e.content;return"string"==typeof t?t:Array.isArray(t)?t.flatMap(e=>L(e)&&"text"===e.type&&"string"==typeof e.text?[e.text]:[]).join(""):""}function _(e){const t=e.endsWith("==")?2:e.endsWith("=")?1:0;return Math.max(0,3*Math.floor(e.length/4)-t)}function G(e){const t=e.content;return"user"===e.role&&Array.isArray(t)?t.flatMap(e=>L(e)?"file"===e.type&&"string"==typeof e.mediaType&&"string"==typeof e.data?[{type:"attachment",mediaType:e.mediaType,bytes:_(e.data)}]:"image"===e.type&&"string"==typeof e.image?[{type:"attachment",mediaType:"image/*",bytes:_(e.image)}]:[]:[]):[]}function J(e){return w.parse(e)}function H(e,t){return{id:J(t.id),...void 0!==t.title?{title:t.title}:{},...void 0!==t.model?{model:t.model}:{},createdAt:t.createdAt,updatedAt:t.updatedAt,messageCount:e.countTranscriptMessages(t.id)}}function K(e){if("user"!==e.message.role&&"assistant"!==e.message.role)return;const t=E(W(e.message));return{sequence:e.sequence,role:e.message.role,createdAt:e.createdAt,parts:[...t.length>0?[{type:"text",text:t}]:[],...G(e.message)]}}function Q(e){const t=b(e);return{id:I.parse(t.id),sequence:e.sequence,toolCallId:t.toolCallId,agentName:t.agentName,toolName:t.toolName,createdAt:t.createdAt,outcome:{kind:t.outcome.kind,..."reason"in t.outcome&&void 0!==t.outcome.reason?{reason:t.outcome.reason}:{}},display:z(t.display,void 0)}}function X(e,t){const s=z(t.input,void 0),n=void 0===t.explanation?void 0:l.safeParse(E(t.explanation)),i=!0===n?.success&&L(s)?{...s,[r]:n.data}:s;return{id:p.parse(t.approvalId),turnId:e,agentName:t.agentName,toolName:t.toolName,preview:i,...void 0!==t.reason?{reason:E(t.reason)}:{}}}export class RuntimeService{runtimeInstanceId;engine;store;serverName;serverVersion;runtimeVersion;maxFrameBytes;idempotencyCacheEntries;reasoningSummaryProjector;attachmentStore;sessions=new Map;activeTurns=new Map;activeTurnOwners=new Map;settledTurnOwners=new Map;pendingApprovals=new Map;pendingUserInputs=new Map;listeners=new Set;fatalErrorListeners=new Set;userInputListeners=new Set;mutationRequests;sequence=0;closing=!1;userInputAvailable=!1;constructor(e,r,s={}){this.engine=e,this.store=r,this.serverName=s.serverName??"roll-runtime",this.serverVersion=s.serverVersion??"1.0",this.runtimeVersion=s.runtimeVersion??"development",this.maxFrameBytes=s.maxFrameBytes??U,this.idempotencyCacheEntries=s.idempotencyCacheEntries??F,this.mutationRequests=new MutationRequestCache(this.idempotencyCacheEntries),this.reasoningSummaryProjector=s.reasoningSummaryProjector,this.attachmentStore=s.attachmentStore,this.runtimeInstanceId=y.parse(t())}initialize(e){let t;this.assertOpen();for(const r of e.protocolVersions){const e=T.safeParse(r);if(e.success){t=e.data;break}}if(void 0===t)throw new RuntimeServiceError(s.protocolVersionUnsupported,`不支持客户端协议版本:${e.protocolVersions.join(", ")}`,{details:{supportedVersions:c}});const r=n.filter(e=>"reasoning-summary"!==e||void 0!==this.reasoningSummaryProjector),i={protocolVersion:t,runtimeInstanceId:this.runtimeInstanceId,server:{name:this.serverName,version:this.serverVersion,runtimeVersion:this.runtimeVersion},features:r,limits:{maxFrameBytes:this.maxFrameBytes,maxPageSize:N,idempotencyCacheEntries:this.idempotencyCacheEntries}};if("1.4"===t){const e=this.attachmentStore?.limits;return{...i,protocolVersion:t,features:void 0!==this.attachmentStore?[...r,"attachments"]:[...r],limits:{...i.limits,eventReplay:!0,maxAttachmentBytes:e?.maxAttachmentBytes??o,maxAttachmentChunkBytes:e?.maxAttachmentChunkBytes??d,maxTurnAttachments:h,maxStagedAttachments:e?.maxStagedAttachments??u}}}return"1.3"===t?{...i,protocolVersion:t,limits:{...i.limits,eventReplay:!0}}:{...i,protocolVersion:t,limits:{...i.limits,eventReplay:!1}}}onEvent(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}onFatalError(e){return this.fatalErrorListeners.add(e),()=>{this.fatalErrorListeners.delete(e)}}onUserInputInteraction(e){return this.userInputListeners.add(e),()=>{this.userInputListeners.delete(e)}}setUserInputAvailable(e){if(this.userInputAvailable!==e){this.userInputAvailable=e,e||this.cancelAllPendingUserInputs("当前客户端已撤销用户输入处理能力");for(const t of this.sessions.values())t.setUserInputAvailable?.(e)}}listThreads(e){this.assertOpen();const t=void 0===e.cursor?0:Number(e.cursor);if(!Number.isSafeInteger(t)||t<0)throw new RuntimeServiceError(s.invalidParams,"thread.list cursor 超出安全整数范围");const r=this.store.listThreads(),n=r.slice(t,t+e.limit).map(e=>H(this.store,e)),i=t+n.length;return{items:n,nextCursor:i<r.length?String(i):null}}async createThread(e){return this.mutationRequests.run(e.requestId,i.threadCreate,e,async()=>{this.assertOpen();const t=await this.engine.createSession(void 0!==e.title?{title:e.title}:{});return this.rememberSession(t),{thread:H(this.store,this.requireThread(t.id))}})}async openThread(e){return await this.requireSession(e.threadId),this.snapshotThread({threadId:e.threadId,limit:100})}snapshotThread(e){this.assertOpen();const t=this.requireThread(e.threadId),r=this.store.listRecentTranscriptMessages(e.threadId,{...void 0!==e.messageBeforeSequence?{beforeSequence:e.messageBeforeSequence}:{},limit:e.limit}),s=this.store.listRecentToolExecutions(e.threadId,{...void 0!==e.operationBeforeSequence?{beforeSequence:e.operationBeforeSequence}:{},limit:e.limit}),n=this.activeTurns.get(e.threadId),i=void 0===n?void 0:{id:n.turnId,status:n.status,startedAt:n.startedAt};return{thread:H(this.store,t),messages:{items:r.entries.flatMap(e=>{const t=K(e);return void 0===t?[]:[t]}),nextBeforeSequence:r.nextBeforeSequence??null},operations:{items:s.entries.map(Q),nextBeforeSequence:s.nextBeforeSequence??null},...void 0!==i?{activeTurn:i}:{},pendingApprovals:[...this.pendingApprovals.values()].filter(t=>t.threadId===e.threadId).map(e=>e.approval),transcriptCompleteness:this.store.getTranscriptCompleteness(e.threadId),eventCursor:this.store.getRuntimeEventCursor(e.threadId)}}resumeEvents(e){this.assertOpen(),this.requireThread(e.threadId);try{return this.store.resumeRuntimeEvents(e.threadId,e.afterCursor)}catch(t){if(t instanceof O)throw new RuntimeServiceError(s.eventCursorExpired,"Runtime Event cursor 已超出当前 Thread 的保留窗口,请回退到 thread.snapshot",{details:{threadId:e.threadId}});if(t instanceof C)throw new RuntimeServiceError(s.eventCursorGap,"Runtime Event cursor 与当前 Thread 事件日志不连续,请回退到 thread.snapshot",{details:{threadId:e.threadId}});throw t}}async renameThread(e){return this.mutationRequests.run(e.requestId,i.threadRename,e,()=>(this.assertOpen(),this.requireThread(e.threadId),this.store.updateTitle(e.threadId,e.title),{thread:H(this.store,this.requireThread(e.threadId))}))}async deleteThread(e){return this.mutationRequests.run(e.requestId,i.threadDelete,e,async()=>{if(this.assertOpen(),this.requireThread(e.threadId),this.activeTurns.has(e.threadId))throw new RuntimeServiceError(s.threadBusy,`Thread "${e.threadId}" 仍有活动 Turn`,{retryable:!0});const t=this.sessions.get(e.threadId);return void 0!==t&&(await t.close(),this.sessions.delete(e.threadId)),this.attachmentStore?.releaseThread(e.threadId),this.store.deleteThread(e.threadId),{deleted:!0}})}async detachThread(e){return this.mutationRequests.run(e.requestId,i.threadDetach,e,async()=>{if(this.assertOpen(),this.requireThread(e.threadId),this.activeTurns.has(e.threadId))return{detached:!1};const t=this.sessions.get(e.threadId);return void 0===t?{detached:!1}:(await t.close(),this.sessions.delete(e.threadId),{detached:!0})})}async threadCapabilities(e){const t=await this.requireSession(e.threadId);return{manifest:z(R(t.getCapabilityManifest(),t.getCapabilityTurnContext()),void 0)}}async startTurn(e){return this.mutationRequests.run(e.requestId,i.turnStart,e,async()=>{this.assertOpen();const t=this.findTurnOwner(e.turnId,e.threadId);if(void 0!==t){if(t===e.threadId)return{accepted:!0,turnId:e.turnId};throw new RuntimeServiceError(s.turnAlreadyActive,`Turn "${e.turnId}" 已属于另一个 Thread`)}if(this.activeTurns.has(e.threadId))throw new RuntimeServiceError(s.turnAlreadyActive,`Thread "${e.threadId}" 已有活动 Turn`,{retryable:!0});const r=await this.requireSession(e.threadId),n=this.findTurnOwner(e.turnId,e.threadId);if(void 0!==n){if(n===e.threadId)return{accepted:!0,turnId:e.turnId};throw new RuntimeServiceError(s.turnAlreadyActive,`Turn "${e.turnId}" 已属于另一个 Thread`)}if(this.activeTurns.has(e.threadId))throw new RuntimeServiceError(s.turnAlreadyActive,`Thread "${e.threadId}" 已有活动 Turn`,{retryable:!0});const i=this.resolveTurnAttachments(e),a={threadId:e.threadId,turnId:e.turnId,session:r,startedAt:(new Date).toISOString(),status:"running",interactionFailure:void 0};this.activeTurns.set(e.threadId,a),this.activeTurnOwners.set(e.turnId,e.threadId);try{this.emit(e.threadId,e.turnId,{type:"turn.started"})}catch(t){throw this.activeTurns.delete(e.threadId),this.activeTurnOwners.delete(e.turnId),t}return this.driveTurn(a,e.input.text,i).catch(()=>{}),{accepted:!0,turnId:e.turnId}})}async cancelTurn(e){return this.mutationRequests.run(e.requestId,i.turnCancel,e,()=>{this.assertOpen();const t=this.activeTurns.get(e.threadId);if(void 0===t||t.turnId!==e.turnId)throw new RuntimeServiceError(s.turnNotFound,`Turn "${e.turnId}" 不存在或已结束`);t.status="cancelling";let r=!1;try{this.cancelPendingUserInputsForTurn(e.turnId,"Turn 已由客户端取消"),this.cancelPendingApprovalsForTurn(e.turnId,{status:"cancelled",reason:"Turn 已由客户端取消"})}finally{r=t.session.cancel()}return{cancelling:r}})}async respondApproval(e){return this.mutationRequests.run(e.requestId,i.approvalRespond,e,()=>this.resolvePendingApproval(e,"approve"===e.decision?{decision:"approve"}:{decision:"reject",...void 0!==e.reason?{reason:e.reason}:{}}))}resolvePendingApproval(e,t){this.assertOpen();const r=this.requirePendingApproval(e),n="approve"===t.decision?r.session.approve(e.approvalId):r.session.reject(e.approvalId,t.reason);if(this.pendingApprovals.delete(e.approvalId),!n)throw this.emitApprovalResolved(r,{status:"cancelled",reason:"审批已失效"}),new RuntimeServiceError(s.approvalNotFound,`Approval "${e.approvalId}" 已失效`);return this.emitApprovalResolved(r,"approve"===t.decision?{status:"resolved",decision:"approve"}:{status:"resolved",decision:"reject",...void 0!==t.reason?{reason:t.reason}:{}}),{resolved:!0}}getPendingApprovalExpiresAt(e){return this.findPendingApproval(e)?.expiresAt}async stageAttachment(e){return this.mutationRequests.run(e.requestId,i.attachmentStage,e,()=>{this.assertOpen(),this.requireThread(e.threadId);const t=this.requireAttachmentStore().stage({threadId:e.threadId,fileName:e.fileName,mediaType:e.mediaType,bytes:e.bytes,sha256:e.sha256,source:e.source,sourcePath:e.sourcePath});return t.ok||this.throwAttachmentFailure(t),{attachmentId:t.attachmentId,state:t.state,...void 0!==t.descriptor?{descriptor:t.descriptor}:{}}})}async appendAttachmentChunk(e){return this.mutationRequests.run(e.requestId,i.attachmentChunk,e,()=>{this.assertOpen();const t=this.requireAttachmentStore().appendChunk({threadId:e.threadId,attachmentId:e.attachmentId,sequence:e.sequence,dataBase64:e.dataBase64});return t.ok||this.throwAttachmentFailure(t),{receivedBytes:t.receivedBytes,nextSequence:t.nextSequence}})}async commitAttachment(e){return this.mutationRequests.run(e.requestId,i.attachmentCommit,e,()=>{this.assertOpen();const t=this.requireAttachmentStore().commit({threadId:e.threadId,attachmentId:e.attachmentId});return t.ok||this.throwAttachmentFailure(t),{descriptor:t.descriptor}})}async releaseAttachment(e){return this.mutationRequests.run(e.requestId,i.attachmentRelease,e,()=>{this.assertOpen();return{released:this.requireAttachmentStore().release({threadId:e.threadId,attachmentId:e.attachmentId}).released}})}requireAttachmentStore(){if(void 0===this.attachmentStore)throw new RuntimeServiceError(s.capabilityUnavailable,"Runtime 未配置附件存储,attachments 能力不可用");return this.attachmentStore}throwAttachmentFailure(e){throw new RuntimeServiceError(e.code,e.message,{...void 0!==e.retryable?{retryable:e.retryable}:{}})}resolveTurnAttachments(e){const t=e.input.attachments;if(void 0===t||0===t.length)return;const r=this.requireAttachmentStore(),n=new Set,i=[];for(const a of t){if(n.has(a))throw new RuntimeServiceError(s.invalidParams,`附件 "${a}" 在同一 Turn 中重复引用`);n.add(a);const t=r.readCommitted({threadId:e.threadId,attachmentId:a});t.ok||this.throwAttachmentFailure(t),i.push({data:t.dataBase64,mediaType:t.descriptor.mediaType})}return i}cancelPendingApproval(e,t){const r=this.findPendingApproval(e);return void 0!==r&&(this.pendingApprovals.delete(e.approvalId),this.emitApprovalResolved(r,t),!0)}async failPendingApprovalInteraction(e,t,r={status:"cancelled",reason:t}){const s=this.findPendingApproval(e);if(void 0===s)return!1;const n=this.activeTurns.get(e.threadId);void 0!==n&&n.turnId===e.turnId&&(n.interactionFailure=t),this.pendingApprovals.delete(e.approvalId);try{this.emitApprovalResolved(s,r)}finally{s.session.cancel()}return!0}resolvePendingUserInput(e,t){const r=this.pendingUserInputs.get(e);if(void 0===r)return!1;let s;try{s=m(r.form,t)}catch{return this.cancelPendingUserInput(e,"用户输入不符合原始表单约束"),!1}this.pendingUserInputs.delete(e);const n=r.session.resolveUserInput?.(e,s)??!1;if(!n){const t="用户输入请求已失效";return r.session.cancelUserInput?.(e,t),this.restoreRunningStatus(r),this.emitUserInputInteraction({type:"settled",requestId:e,threadId:r.threadId,turnId:r.turnId,reason:t}),!1}return this.restoreRunningStatus(r),this.emitUserInputInteraction({type:"settled",requestId:e,threadId:r.threadId,turnId:r.turnId,reason:"submitted"===s.status?"用户已提交输入":"用户已取消输入"}),n}cancelPendingUserInput(e,t){const r=this.pendingUserInputs.get(e);if(void 0===r)return!1;this.pendingUserInputs.delete(e);const s=r.session.cancelUserInput?.(e,t)??!1;return this.restoreRunningStatus(r),this.emitUserInputInteraction({type:"settled",requestId:e,threadId:r.threadId,turnId:r.turnId,reason:t}),s}getOperation(e){this.assertOpen(),this.requireThread(e.threadId);const t=this.store.getToolExecution(e.threadId,e.operationId);return{operation:void 0===t?null:Q(t)}}async close(){if(this.closing)return;this.closing=!0,this.setUserInputAvailable(!1);for(const e of this.activeTurns.values())e.session.cancel();const e=[...this.sessions.values()];this.sessions.clear(),this.activeTurns.clear(),this.activeTurnOwners.clear(),this.settledTurnOwners.clear(),this.pendingApprovals.clear(),this.pendingUserInputs.clear(),this.userInputListeners.clear(),this.attachmentStore?.close(),await Promise.allSettled(e.map(e=>e.close())),this.mutationRequests.clear(),this.fatalErrorListeners.clear()}assertOpen(){if(this.closing)throw new RuntimeServiceError(s.runtimeClosing,"Runtime 正在关闭",{retryable:!0})}requireThread(e){const t=this.store.getThread(e);if(void 0===t)throw new RuntimeServiceError(s.threadNotFound,`Thread "${e}" 不存在`);return t}async requireSession(e){this.assertOpen(),this.requireThread(e);const t=this.sessions.get(e);if(void 0!==t)return t;const r=await this.engine.resumeSession(e);return this.rememberSession(r),r}rememberSession(e){e.setUserInputAvailable?.(this.userInputAvailable),this.sessions.set(e.id,e)}emit(e,t,r){const s=(new Date).toISOString(),n={protocolVersion:a,runtimeInstanceId:this.runtimeInstanceId,sequence:this.sequence,timestamp:s,threadId:e,...void 0!==t?{turnId:t}:{}},i=g.safeParse(r),o=i.success?(()=>{let r;try{r=this.store.appendRuntimeEvent({threadId:e,...void 0===t?{}:{turnId:t},timestamp:s,event:i.data})}catch(e){throw this.notifyFatalError(e),e}return{...n,durability:"durable",eventId:r.eventId,cursor:r.cursor,event:i.data}})():{...n,durability:"ephemeral",event:f.parse(r)};this.sequence+=1;for(const e of this.listeners)try{e(o)}catch{}}notifyFatalError(e){for(const e of this.activeTurns.values())try{e.session.cancel()}catch{}for(const t of this.fatalErrorListeners)try{t(e)}catch{}}async driveTurn(e,t,r){const s={streamId:void 0,text:"",terminalEvent:void 0},n=void 0!==r&&r.length>0?{text:t,attachments:r}:t;let i;try{for await(const t of e.session.send(n))this.projectSessionEvent(e,s,t);i=s.terminalEvent??{type:"turn.completed"}}catch(e){i=s.terminalEvent??{type:"turn.failed",stage:"execute",message:E(D(e))}}finally{this.activeTurns.get(e.threadId)===e&&this.activeTurns.delete(e.threadId),this.settleTurnOwner(e)}this.emitTurnTerminal(e,i)}findTurnOwner(e,t){const r=this.activeTurnOwners.get(e);if(void 0!==r)return r;const s=this.settledTurnOwners.get(e);return s===t&&(this.settledTurnOwners.delete(e),this.settledTurnOwners.set(e,s)),s}settleTurnOwner(e){if(this.activeTurnOwners.get(e.turnId)===e.threadId&&(this.activeTurnOwners.delete(e.turnId),!this.closing))for(this.settledTurnOwners.set(e.turnId,e.threadId);this.settledTurnOwners.size>this.idempotencyCacheEntries;){const e=this.settledTurnOwners.keys().next().value;if(void 0===e)return;this.settledTurnOwners.delete(e)}}projectSessionEvent(e,r,s){switch(s.type){case"message-start":{const s=A.parse(t());return r.streamId=s,r.text="",void this.emit(e.threadId,e.turnId,{type:"message.started",streamId:s})}case"text-delta":{const n=r.streamId??A.parse(t());return void 0===r.streamId&&(r.streamId=n,this.emit(e.threadId,e.turnId,{type:"message.started",streamId:n})),r.text+=s.delta,void this.emit(e.threadId,e.turnId,{type:"message.delta",streamId:n,delta:s.delta})}case"message-finish":{const n=r.streamId??A.parse(t());return this.emit(e.threadId,e.turnId,{type:"message.completed",streamId:n,text:E(s.text||r.text)}),void(r.terminalEvent??={type:"turn.completed"})}case"reasoning-delta":{const t=this.reasoningSummaryProjector?.(s.delta);return void(void 0!==t&&t.length>0&&this.emit(e.threadId,e.turnId,{type:"reasoning.summary.delta",reasoningId:s.reasoningId,delta:E(t)}))}case"tool-call":return void this.emit(e.threadId,e.turnId,{type:"tool.started",toolCallId:s.toolCallId,agentName:s.agentName,toolName:s.toolName,input:z(s.input,void 0)});case"tool-output-delta":return void this.emit(e.threadId,e.turnId,{type:"tool.output",toolCallId:s.toolCallId,agentName:s.agentName,toolName:s.toolName,stream:s.stream,delta:E(s.delta)});case"tool-result":return void this.emit(e.threadId,e.turnId,{type:"tool.completed",toolCallId:s.toolCallId,agentName:s.agentName,toolName:s.toolName,...void 0!==s.executionId?{operationId:I.parse(s.executionId)}:{},...void 0!==s.outcome?{outcome:this.projectToolOutcome(s.outcome)}:{},display:z(s.display,void 0)});case"confirmation-required":{const t=X(e.turnId,s);return this.pendingApprovals.set(t.id,{threadId:e.threadId,session:e.session,approval:t,expiresAt:s.expiresAt}),void this.emit(e.threadId,e.turnId,{type:"approval.required",approval:t})}case"user-input-required":{if(!this.userInputAvailable||void 0===e.session.resolveUserInput||void 0===e.session.cancelUserInput)return void e.session.cancelUserInput?.(s.requestId,"当前客户端未协商用户输入处理能力");const t=q.parse(s.form),r=q.parse(t),n={requestId:s.requestId,threadId:e.threadId,turnId:e.turnId,session:e.session,form:t,expiresAt:s.expiresAt};this.pendingUserInputs.set(s.requestId,n),e.status="waiting-for-user";return void(this.emitUserInputInteraction({type:"required",requestId:s.requestId,threadId:e.threadId,turnId:e.turnId,form:r,expiresAt:s.expiresAt})||this.cancelPendingUserInput(s.requestId,"当前没有可处理用户输入请求的客户端"))}case"user-input-settled":{const e=this.pendingUserInputs.get(s.requestId);if(void 0===e)return;return this.pendingUserInputs.delete(s.requestId),this.restoreRunningStatus(e),void this.emitUserInputInteraction({type:"settled",requestId:e.requestId,threadId:e.threadId,turnId:e.turnId,reason:"submitted"===s.status?"用户输入请求已结算":"用户输入请求已取消"})}case"turn-cancelled":r.terminalEvent??=void 0===e.interactionFailure?{type:"turn.cancelled",reason:s.reason,message:E(s.message)}:{type:"turn.failed",stage:"execute",message:E(e.interactionFailure)};break;case"error":r.terminalEvent??={type:"turn.failed",stage:s.stage,message:E(s.message)}}}projectToolOutcome(e){return{kind:e.kind,..."reason"in e&&void 0!==e.reason?{reason:E(e.reason)}:{}}}findPendingApproval(e){const t=this.pendingApprovals.get(e.approvalId);if(void 0!==t&&t.threadId===e.threadId&&t.approval.turnId===e.turnId)return t}requirePendingApproval(e){const t=this.findPendingApproval(e);if(void 0===t)throw new RuntimeServiceError(s.approvalNotFound,`Approval "${e.approvalId}" 不存在或已失效`);return t}emitApprovalResolved(e,t){const r="reason"in t&&void 0!==t.reason?{...t,reason:E(t.reason)}:t;this.emit(e.threadId,e.approval.turnId,{type:"approval.resolved",approvalId:e.approval.id,resolution:r})}cancelPendingApprovalsForTurn(e,t){const r=[...this.pendingApprovals.values()].filter(t=>t.approval.turnId===e);let s;for(const e of r)try{this.cancelPendingApproval({threadId:e.threadId,turnId:e.approval.turnId,approvalId:e.approval.id},t)}catch(e){s??=e}if(void 0!==s)throw s}cancelPendingUserInputsForTurn(e,t){const r=[...this.pendingUserInputs.values()].filter(t=>t.turnId===e).map(e=>e.requestId);for(const e of r)this.cancelPendingUserInput(e,t)}cancelAllPendingUserInputs(e){for(const t of[...this.pendingUserInputs.keys()])this.cancelPendingUserInput(t,e)}restoreRunningStatus(e){const t=this.activeTurns.get(e.threadId);void 0!==t&&t.turnId===e.turnId&&"waiting-for-user"===t.status&&(t.status="running")}emitUserInputInteraction(e){let t=!1;for(const r of this.userInputListeners)try{r(e),t=!0}catch{}return t}emitTurnTerminal(e,t){this.cancelPendingUserInputsForTurn(e.turnId,`Turn 已终止:${t.type}`),this.cancelPendingApprovalsForTurn(e.turnId,"turn.cancelled"===t.type&&"timeout"===t.reason?{status:"expired",reason:"Turn 已超时"}:{status:"cancelled",reason:`Turn 已终止:${t.type}`}),this.emit(e.threadId,e.turnId,t)}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{extractTextContent as t,isToolErrorResult as e}from"@roll-agent/core/cli/utils/tool-results";export const TOOL_OUTCOME_KINDS={success:"success",userRejected:"user_rejected",policyDenied:"policy_denied",invalidInput:"invalid_input",cancelled:"cancelled",toolFailed:"tool_failed"};export const TOOL_CANCELLATION_EXECUTION_STATES={notExecuted:"not_executed",outcomeUnknown:"outcome_unknown"};const n=16e3,o=64,r=6e4,i=
|
|
1
|
+
import{extractTextContent as t,isToolErrorResult as e}from"@roll-agent/core/cli/utils/tool-results";export const TOOL_OUTCOME_KINDS={success:"success",userRejected:"user_rejected",policyDenied:"policy_denied",invalidInput:"invalid_input",cancelled:"cancelled",toolFailed:"tool_failed"};export const TOOL_CANCELLATION_EXECUTION_STATES={notExecuted:"not_executed",outcomeUnknown:"outcome_unknown"};const n=16e3,o=64,r=6e4,i=12e6,u=128,s="\n\n[工具模型内容已截断;完整结果仍保留在 raw 视图]";function l(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function c(t){if(null===t||"string"==typeof t||"boolean"==typeof t||"number"==typeof t&&Number.isFinite(t))return t;if(Array.isArray(t))return t.map(c);if(l(t)){const e={};for(const[n,o]of Object.entries(t))e[n]=c(o);return e}return void 0===t?null:String(t)}function a(t){if(!t)return!1;const e=t.replace(/([a-z0-9])([A-Z])/gu,"$1_$2").toLowerCase().split(/[^a-z0-9]+/u).filter(t=>t.length>0);return 1===e.length&&["data","image","audio"].includes(e[0]??"")||e.some(t=>["base64","blob","bytes"].includes(t))||e.includes("data")&&e.some(t=>["image","audio","binary"].includes(t))}function p(t){return`[binary content omitted: ${String(t.length)} chars]`}function d(t,e,n){return t.length<=e?t:e<=n.length?n.slice(0,e):`${t.slice(0,e-n.length)}${n}`}function f(t,e,n=0){if(n>16)return"[nested value omitted]";if("string"==typeof t)return a(e)||/^data:[^,]+;base64,/iu.test(t)?p(t):d(t,r,s);if(null===t||"boolean"==typeof t||"number"==typeof t)return Number.isFinite(t)||"number"!=typeof t?t:String(t);if(Array.isArray(t)){const e=t.slice(0,u).map(t=>f(t,void 0,n+1));return t.length>u&&e.push(`[${String(t.length-u)} items omitted]`),e}if(l(t)){const e={},o=Object.entries(t).filter(([t])=>"_meta"!==t);for(const[t,r]of o.slice(0,u))e[t]=f(r,t,n+1);return o.length>u&&(e["[omitted]"]=`${String(o.length-u)} fields`),e}return void 0===t?null:String(t)}function y(t,e,r=0){if(r>12)return"[nested value omitted]";if("string"==typeof t)return a(e)||/^data:[^,]+;base64,/iu.test(t)?p(t):t.length<=n?t:g(t);if(null===t||"boolean"==typeof t||"number"==typeof t)return Number.isFinite(t)||"number"!=typeof t?t:String(t);if(Array.isArray(t)){const e=t.slice(0,o).map(t=>y(t,void 0,r+1));return t.length>o&&e.push(`[${String(t.length-o)} items omitted]`),e}if(l(t)){const e={},n=Object.entries(t).filter(([t])=>"_meta"!==t);for(const[t,i]of n.slice(0,o))e[t]=y(i,t,r+1);return n.length>o&&(e["[omitted]"]=`${String(n.length-o)} fields`),e}return void 0===t?null:String(t)}function O(t){return"string"==typeof t?t:JSON.stringify(c(t),null,2)}function g(t){return t.length<=n?t:`${t.slice(0,n)}\n\n[工具展示内容已截断]`}function m(t,e=!1){const n=f(t),o=O(n);if(o.length<=r)return e?{type:"error-json",value:n}:{type:"json",value:n};const i=d(o,r,s);return e?{type:"error-text",value:i}:{type:"text",value:i}}function T(t,e=!1){const n=d(t,r,s);return e?{type:"error-text",value:n}:{type:"text",value:n}}function x(t){const e=[];let n=r,o=i;for(const r of t.slice(0,u)){if(n<=0&&o<=0)break;if("text"===r.type){if(n<=0)continue;const t=d(r.text,n,s);e.push({type:"text",text:t}),n-=t.length;continue}const t=r.data.data.length;if(t<=o){e.push(r),o-=t;continue}if(n<=0)continue;const i=d(`[${r.mediaType} file omitted from model projection: ${String(t)} chars]`,n,s);e.push({type:"text",text:i}),n-=i.length}if(t.length>u&&n>0){const o=d(`[${String(t.length-u)} model content parts omitted]`,n,s);e.push({type:"text",text:o})}return e}function _(t,e){return e.kind===TOOL_OUTCOME_KINDS.userRejected?{type:"execution-denied",...void 0!==e.reason?{reason:e.reason}:{}}:e.kind===TOOL_OUTCOME_KINDS.success?"string"==typeof t?T(t):m(t):m({code:e.kind,message:"string"==typeof t?t:O(t),...void 0!==e.reason?{reason:e.reason}:{}},!0)}export function createToolResult(t,e,n={}){const o=t.kind!==TOOL_OUTCOME_KINDS.success;return{raw:Object.prototype.hasOwnProperty.call(n,"raw")?n.raw:e,model:n.model??_(e,t),display:e,outcome:t,output:e,isError:o}}export function successfulToolResult(t,e={}){return createToolResult({kind:TOOL_OUTCOME_KINDS.success},t,e)}export function failedToolResult(t,e,n={}){return createToolResult({kind:t,...void 0!==n.reason?{reason:n.reason}:{}},e,n)}function h(t){return l(t)?t.content:void 0}function v(t){return l(t)?t.structuredContent:void 0}function S(t){return l(t)&&"toolResult"in t?{value:t.toolResult,wrapped:!0}:{value:t,wrapped:!1}}function b(t){if("string"==typeof t.data&&"string"==typeof t.mimeType)return{type:"file",data:{type:"data",data:t.data},mediaType:t.mimeType}}function E(t){const e=l(t.resource)?t.resource:void 0;if(!e)return;const n="string"==typeof e.uri?e.uri:"embedded resource";return"string"==typeof e.text?{type:"text",text:`[${n}]\n${e.text}`}:"string"==typeof e.blob?{type:"file",data:{type:"data",data:e.blob},mediaType:"string"==typeof e.mimeType?e.mimeType:"application/octet-stream",filename:n}:void 0}function N(t){if("string"!=typeof t.uri)return;const e="string"==typeof t.name?t.name:"resource",n="string"==typeof t.description?` — ${t.description}`:"";return{type:"text",text:`[${e}] ${t.uri}${n}`}}function C(t){if(l(t)&&"string"==typeof t.type)return"text"===t.type&&"string"==typeof t.text?{type:"text",text:t.text}:"image"===t.type||"audio"===t.type?b(t):"resource"===t.type?E(t):"resource_link"===t.type?N(t):void 0}function $(t,e,n,o){if(n)return _(e,{kind:TOOL_OUTCOME_KINDS.toolFailed});const r=h(t),i=Array.isArray(r)?r.flatMap(t=>{const e=C(t);return void 0===e?[]:[e]}):[],u=v(t);return void 0!==u&&i.length>0&&i.push({type:"text",text:`[structuredContent]\n${O(f(u))}`}),i.length>0?{type:"content",value:x(i)}:void 0!==u?m(u):o?"string"==typeof t?T(t):m(t):_(e,{kind:TOOL_OUTCOME_KINDS.success})}function L(e,r,i){const u=t(h(e));if(u.length>0)return g(u.join("\n"));const s=v(e);if(void 0!==s){const t=y(s),e=O(t);return e.length<=n?t:g(e)}const c=h(e);if(Array.isArray(c)&&c.length>0){const t=c.slice(0,o).flatMap(t=>{if(!l(t)||"string"!=typeof t.type)return[];const e="string"==typeof t.mimeType?` ${t.mimeType}`:"";return[`[${t.type}${e}]`]});if(t.length>0){const e=c.length-Math.min(c.length,o);return`${t.join("\n")}${e>0?`\n[另有 ${String(e)} 项未展示]`:""}`}}if(i){const t=y(e),o=O(t);return o.length<=n?t:g(o)}return r?"工具执行失败(无可展示的文本结果)":"工具执行完成(无可展示的文本结果)"}export function normalizeToolResult(t){const n=S(t),o=e(n.value),r=L(n.value,o,n.wrapped);return createToolResult(o?{kind:TOOL_OUTCOME_KINDS.toolFailed}:{kind:TOOL_OUTCOME_KINDS.success},r,{raw:t,model:$(n.value,r,o,n.wrapped)})}export function isToolCancellationExecutionState(t){return Object.values(TOOL_CANCELLATION_EXECUTION_STATES).some(e=>e===t)}function k(t){return!(!l(t)||"string"!=typeof t.kind)&&(!!Object.values(TOOL_OUTCOME_KINDS).some(e=>e===t.kind)&&((void 0===t.reason||"string"==typeof t.reason)&&(t.kind!==TOOL_OUTCOME_KINDS.cancelled?void 0===t.executionState:void 0===t.executionState||isToolCancellationExecutionState(t.executionState))))}export function isNormalizedToolResult(t){return l(t)&&"raw"in t&&"model"in t&&"display"in t&&k(t.outcome)}export function readToolOutcome(t){return isNormalizedToolResult(t)?t.outcome:l(t)&&"execution-denied"===t.type?{kind:TOOL_OUTCOME_KINDS.userRejected,..."string"==typeof t.reason?{reason:t.reason}:{}}:l(t)&&"string"==typeof t.type&&t.type.startsWith("error")||I(t)?{kind:TOOL_OUTCOME_KINDS.toolFailed}:{kind:TOOL_OUTCOME_KINDS.success}}function I(t){return l(t)&&!0===t.isError}export function readIsError(t){return readToolOutcome(t).kind!==TOOL_OUTCOME_KINDS.success}export function readDisplayOutput(t){return isNormalizedToolResult(t)?t.display:l(t)&&"output"in t?t.output:t}export function toolResultToModelOutput(t){return t.model}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roll-agent/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
31
31
|
"ai": "^7.0.9",
|
|
32
32
|
"zod": "^3.25.76",
|
|
33
|
-
"@roll-agent/protocol": "0.
|
|
33
|
+
"@roll-agent/protocol": "0.5.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^22.0.0",
|
|
37
|
-
"@roll-agent/core": "0.
|
|
37
|
+
"@roll-agent/core": "0.30.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/obfuscate.mjs",
|