billion-context 0.1.149 → 0.1.150

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/index.js CHANGED
@@ -457,7 +457,7 @@ ${text}`;else if(Array.isArray(c))last.content=[...c,{type:"text",text}];else la
457
457
  ${tail}
458
458
  ---
459
459
 
460
- Continue the response seamlessly from exactly where that text ends (mid-sentence if necessary). Do not repeat any part of the received text and do not start over \u2014 just pick up where it stopped and finish the response.`;function isLoopThinking(m2){return m2.contentType==="reasoning"&&typeof m2.id==="string"&&m2.id.startsWith("acp_loop_")}function stripLoopThinking(messages){return messages.filter(m2=>!isLoopThinking(m2))}function canonicalArgs(args){try{return JSON.stringify(sortKeys(JSON.parse(args)))}catch{return args}}function sortKeys(v2){if(Array.isArray(v2))return v2.map(sortKeys);if(v2!==null&&typeof v2==="object"){const src=v2;const out={};for(const k2 of Object.keys(src).sort())out[k2]=sortKeys(src[k2]);return out}return v2}function executeProxyTool(toolName,args,ctx,callId){if(toolName==="compress"){return applyRanges(parseCompressInput(args,callId),ctx)}if(toolName==="decompress"){return resolveDecompress(args,ctx)}if(toolName==="search_context"){return executeSearchContextTarget(args,ctx.core,ctx.session.id,ctx.session.state)}if(toolName==="acp_status"){return handleAcpStatus(args,ctx)}if(toolName==="acp_cache"){return handleAcpCache(ctx.session,args)}const absorb=effectiveAbsorbConfig(ctx.session,ctx.config);if(absorb?.enabled===true&&toolName===(absorb.toolName??ABSORB_TOOL_NAME)){return executeAbsorb(args,callId,absorb,ctx)}if(effectiveRulesConfig(ctx.session,ctx.config)?.enabled===true&&toolName===RULE_TOOL_NAME){return executeRule(args,ctx)}if(ccrEnabled(ctx.session)&&toolName===retrieveToolName(ctx.session)){return executeRetrieve(args,ctx.session)}if(imageCompressionEnabled(ctx.session)&&toolName===IMAGE_FULL_TOOL_NAME){return executeImageFull(args,ctx.session,ctx.config,callId)}return`[Unknown proxy tool: ${toolName}]`}function recordUsage(ctx,usage,round){const prompt=usage.inputTokens;const cached=usage.cachedTokens;const out=usage.outputTokens;const total=promptInputTotal(ctx.protocol,prompt,cached,usage.creationTokens);if(total>0)ctx.session.stats.inputTokens+=total;if(total>0){ctx.session.stats.lastInputTokens=Math.max(0,total-(ctx.session.stats.compressCreditTokens??0));ctx.session.stats.lastInputTokensSource="usage";delete ctx.session.stats.overflowArmTokens}if(typeof cached==="number"&&total>0){ctx.session.stats.cachedTokens+=cached;ctx.session.stats.cacheSamples+=1}if(typeof out==="number")ctx.session.stats.outputTokens+=out;const hitPct=typeof cached==="number"&&total>0?Math.round(cached/total*100):0;warnCacheCollapse(ctx.session,total,cached??0);const foldNew=ctx.session.stats.pendingFoldUsage===true;if(foldNew)ctx.session.stats.pendingFoldUsage=false;ctx.log(`[acp-usage] round ${round} input=${total} cached=${cached??0} (cache hit ${hitPct}%)${foldNew?" fold=new":""}${total<=0?" (zero-total: lastInputTokens kept)":""}${imageUsageSuffix(ctx.session)}`);if(total>0||typeof cached==="number"){recordCacheSample(ctx.session,{at:Date.now(),input:total,cached:cached??0,output:out})}}async function*runCompressLoop(upstream,ctx,requestBody,requestOptions,adapter,systemPrompt,signal){let activeClearTimer=null;let currentUpstream=upstream;let roundBody=requestBody;const coreMessages=[...ctx.messages];let degradedRetried=false;let truncationRetried=false;let continuationRetried=false;let degenerateRetried=false;const seenMarkers=new Set;const fetchUpstream=body=>fetchWithRetry(requestOptions.url,{method:"POST",headers:requestOptions.headers,body:JSON.stringify(requestOptions.wireTransform?requestOptions.wireTransform(body):body),...ctx.proxyUrl?{dispatcher:proxyDispatcher(ctx.proxyUrl)}:{}},void 0,signal,info=>{const lc=lastCompressSuffix(ctx.session.lastCompress);ctx.log(`[acp-proxy: upstream rejected replay (HTTP ${info.status}: ${info.detail.slice(0,120)}); likely provider risk-control \u2014 retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}]`);log("warn",`[acp-loop] upstream rejected replay (HTTP ${info.status}); retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}`)});const failedSignatures=new Set;let strictEchoOrigin;try{strictEchoOrigin=new URL(requestOptions.url).origin}catch{strictEchoOrigin=void 0}try{for(let round=1;round<=MAX_LOOP_ROUNDS;round++){if(signal?.aborted)break;let assistantText="";let assistantReasoning="";const reasoningSegments=[];let reasoningSealed=true;const calls=[];let usage={};let finishReason;let streamError;let sawDone=false;let suppressCompletion=false;let truncatedDone=false;let sawThinking=false;let forwardedAny=false;let forwardedVisible=false;const fwd=(chunk,visible=false)=>{forwardedAny=true;if(visible)forwardedVisible=true;return chunk};for(;;){assistantText="";assistantReasoning="";reasoningSegments.length=0;reasoningSealed=true;calls.length=0;usage={};finishReason=void 0;streamError=void 0;sawDone=false;suppressCompletion=false;truncatedDone=false;sawThinking=false;forwardedAny=false;forwardedVisible=false;for await(const ev of adapter.parseStream(currentUpstream,round)){if(signal?.aborted)break;if(ev.kind==="text"){assistantText+=ev.delta;if(!ctx.textProtocol&&ev.raw){yield fwd(ev.raw,true)}else if(!ctx.textProtocol&&round>1&&ev.delta.length>0){yield fwd(adapter.emitText(ev.delta),true)}}else if(ev.kind==="reasoning"){assistantReasoning+=ev.delta;let seg=reasoningSegments[reasoningSegments.length-1];if(reasoningSealed||!seg){seg={text:"",signature:""};reasoningSegments.push(seg);reasoningSealed=false}seg.text+=ev.delta;if(ev.signature)seg.signature+=ev.signature;if(ev.blockEnd)reasoningSealed=true;if(!ctx.textProtocol){if(ev.raw){yield fwd(ev.raw)}else if(round>1&&ev.delta.length>0&&adapter.emitReasoning){yield fwd(adapter.emitReasoning(ev.delta))}}}else if(ev.kind==="tool_call"){calls.push({name:ev.name,callId:ev.callId,arguments:ev.arguments,passthrough:ev.passthrough,signature:ev.signature})}else if(ev.kind==="usage"){usage={inputTokens:ev.inputTokens,outputTokens:ev.outputTokens,cachedTokens:ev.cachedTokens,creationTokens:ev.creationTokens}}else if(ev.kind==="done"){sawDone=true;finishReason=ev.finishReason;suppressCompletion=ev.suppressCompletion===true;truncatedDone=ev.truncated===true;sawThinking=ev.thinking===true}else if(ev.kind==="error"){streamError=ev.message}else if(ev.kind==="meta"){if(round===1||!ev.firstRoundOnly){yield fwd(ev.chunk)}}}if(streamError!==void 0){ctx.log(`[acp-loop] round ${round}: upstream stream error: ${streamError}`);log("warn",`[acp-loop] upstream stream error: ${streamError}`)}if((!sawDone||truncatedDone)&&(!forwardedAny||ctx.protocol==="openai"&&!forwardedVisible)&&calls.length===0&&!(ctx.textProtocol&&assistantText.length>0)&&!signal?.aborted&&!truncationRetried){truncationRetried=true;ctx.log(`[acp-loop] round ${round}: upstream truncated with no visible output reaching the client; retrying fetch once`);try{const respResult2=await fetchUpstream(roundBody);if(!respResult2.response.body){respResult2.clearTimer();throw new UpstreamHttpError(respResult2.response.status,"(empty response body)",1)}currentUpstream=respResult2.response.body;if(activeClearTimer)activeClearTimer();activeClearTimer=respResult2.clearTimer;continue}catch(e){if(e instanceof UpstreamHttpError){const suffix=e.attempts>1?` after ${e.attempts} attempt(s)`:"";ctx.log(`[acp-loop] round ${round}: truncation retry failed (upstream error ${e.status}${suffix}: ${e.body.slice(0,200)})`)}else{ctx.log(`[acp-loop] round ${round}: truncation retry failed (${e instanceof Error?e.message:String(e)})`)}}}if((!sawDone||truncatedDone)&&forwardedVisible&&ctx.protocol==="openai"&&!ctx.textProtocol&&assistantText.length>0&&calls.length===0&&!signal?.aborted&&!continuationRetried){continuationRetried=true;const tail=assistantText.length<=TRUNCATION_CONTINUATION_TAIL_CHARS?assistantText:`\u2026${assistantText.slice(-TRUNCATION_CONTINUATION_TAIL_CHARS)}`;ctx.log(`[acp-loop] round ${round}: upstream truncated after ${assistantText.length} text chars reached the client; retrying once with continuation nudge`);const nudge={id:`acp_truncation_retry_r${round}`,role:"user",contentType:"text",text:truncationContinuationNudge(tail)};try{const retryBody=adapter.buildRequest([...coreMessages,nudge],systemPrompt,requestBody);const respResult2=await fetchUpstream(retryBody);if(!respResult2.response.body){respResult2.clearTimer();throw new UpstreamHttpError(respResult2.response.status,"(empty response body)",1)}currentUpstream=respResult2.response.body;roundBody=retryBody;if(activeClearTimer)activeClearTimer();activeClearTimer=respResult2.clearTimer;continue}catch(e){if(e instanceof UpstreamHttpError){ctx.log(`[acp-loop] round ${round}: continuation retry failed (upstream error ${e.status}: ${e.body.slice(0,200)}); falling back to the truncation error`)}else{ctx.log(`[acp-loop] round ${round}: continuation retry failed (${e instanceof Error?e.message:String(e)}); falling back to the truncation error`)}log("warn",`[acp-loop] truncation continuation retry failed round ${round}: ${e instanceof Error?e.message:String(e)}`)}}if(!degenerateRetried&&sawDone&&sawThinking&&!truncatedDone&&!suppressCompletion&&typeof finishReason==="string"&&finishReason!=="failed"&&finishReason!=="incomplete"&&finishReason!=="error"&&assistantText.length===0&&calls.length===0&&!forwardedVisible&&!signal?.aborted){degenerateRetried=true;ctx.log(`[acp-loop] round ${round}: degenerate terminal turn (completed, zero visible output); retrying once with continuation nudge (#732/#821)`);log("warn",`[acp-loop] degenerate-turn auto-retry round ${round} (session ${ctx.session.id})`);const nudge={id:`acp_degenerate_retry_r${round}`,role:"user",contentType:"text",text:DEGENERATE_RETRY_NUDGE};try{const retryBody=adapter.buildRequest([...coreMessages,nudge],systemPrompt,requestBody);const respResult2=await fetchUpstream(retryBody);if(!respResult2.response.body){respResult2.clearTimer();throw new UpstreamHttpError(respResult2.response.status,"(empty response body)",1)}currentUpstream=respResult2.response.body;roundBody=retryBody;if(activeClearTimer)activeClearTimer();activeClearTimer=respResult2.clearTimer;continue}catch(e){if(e instanceof UpstreamHttpError){ctx.log(`[acp-loop] round ${round}: degenerate retry failed (upstream error ${e.status}: ${e.body.slice(0,200)}); passing the empty turn through`)}else{ctx.log(`[acp-loop] round ${round}: degenerate retry failed (${e instanceof Error?e.message:String(e)}); passing the empty turn through`)}log("warn",`[acp-loop] degenerate-turn auto-retry failed round ${round}: ${e instanceof Error?e.message:String(e)}`)}}break}if(usage.inputTokens!==void 0||usage.outputTokens!==void 0||usage.cachedTokens!==void 0){recordUsage(ctx,usage,round)}let resolvedText=assistantText;let allCalls=calls;if(ctx.textProtocol&&assistantText.length>0&&adapter.extractTextTriggers){const extracted=adapter.extractTextTriggers(assistantText);resolvedText=extracted.clean;allCalls=[...calls,...extracted.calls]}const functionCallIds=new Set(allCalls.map(c=>c.callId));if(ctx.textProtocol&&resolvedText.length>0){yield adapter.emitText(resolvedText)}let realCalls=0;const realToolCalls=[];const proxyResults=[];for(const call of allCalls){if(isProxyToolFor(call.name,ctx.session,ctx.config)){let parsedArgs;try{parsedArgs=call.arguments.length>0?JSON.parse(call.arguments):{}}catch{parsedArgs={}}const result=await withSessionLock(ctx.session,()=>executeProxyTool(call.name,parsedArgs,ctx,call.callId));proxyResults.push({name:call.name,callId:call.callId,result,arguments:call.arguments,signature:call.signature});if(ctx.visibilityMarkers!==false){const markerKey=`${call.name}\0${result}`;if(seenMarkers.has(markerKey)){ctx.log(`[acp-loop] suppressed duplicate ${call.name} status marker (identical failure repeated this request)`)}else{seenMarkers.add(markerKey);yield adapter.emitMarker(call.name,result)}}}else{realToolCalls.push(call);realCalls+=1}}if(ctx.debug){const callSummary=allCalls.map(c=>{const argSnippet=c.arguments.length>200?c.arguments.slice(0,200)+"...":c.arguments;return`${c.name}(${argSnippet})`}).join(" | ");ctx.log(`[acp-loop] round ${round}: ${allCalls.length} call(s): ${callSummary||"(none)"}`);for(const pr2 of proxyResults){const resSnippet=pr2.result.length>300?pr2.result.slice(0,300)+"...":pr2.result;ctx.log(`[acp-loop] \u2192 ${pr2.name} result: ${resSnippet}`)}if(realCalls>0)ctx.log(`[acp-loop] round ${round}: ${realCalls} real tool call(s) forwarded to client`)}if(proxyResults.length>0){const requiresThinkingSignature=ctx.protocol==="anthropic"||ctx.protocol==="google";if(reasoningSegments.length>0){for(let i=0;i<reasoningSegments.length;i++){const seg=reasoningSegments[i];if(seg.text.length===0||requiresThinkingSignature&&seg.signature.length===0)continue;const reasoningMsg={id:i===0?`acp_loop_r${round}_reasoning`:`acp_loop_r${round}_reasoning_${i+1}`,role:"assistant",contentType:"reasoning",text:seg.text,reasoningContent:seg.text,...seg.signature.length>0?ctx.protocol==="google"?{googleThoughtSignature:seg.signature}:{thinkingSignature:seg.signature}:{}};coreMessages.push(reasoningMsg)}}if(assistantText.length>0){coreMessages.push({id:`acp_loop_r${round}_asst`,role:"assistant",contentType:"text",text:assistantText})}for(const pr2 of proxyResults){if(functionCallIds.has(pr2.callId)){coreMessages.push({id:`acp_loop_r${round}_asst_tc_${pr2.callId}`,role:"assistant",contentType:"tool-call",toolName:pr2.name,toolCallId:pr2.callId,text:pr2.arguments,...ctx.protocol==="google"&&pr2.signature?{googleThoughtSignature:pr2.signature}:{}});coreMessages.push({id:`acp_loop_r${round}_tool_${pr2.callId}`,role:"tool",contentType:"tool-result",toolName:pr2.name,toolCallId:pr2.callId,text:pr2.result})}else if(ctx.visibilityMarkers!==false){coreMessages.push({id:`acp_loop_r${round}_marker_${pr2.callId}`,role:"system",contentType:"text",text:buildVisibilityMarker(pr2.name,pr2.result)})}}for(const injection of drainPendingRetrievals(ctx.session)){coreMessages.push(injection)}const anyCompressFailed=proxyResults.some(pr2=>(pr2.name==="compress"||pr2.name==="decompress")&&pr2.result.includes("FAILED"));if(!ctx.textProtocol&&!anyCompressFailed){const hidden=hideConsumedCompressCalls(ctx.session.state,coreMessages);if(hidden.hidden>0){ctx.log(`[acp-loop] round ${round} hideConsumed hid ${hidden.hidden} compress record(s)`);coreMessages.length=0;coreMessages.push(...hidden.messages)}}const absorbName=(()=>{const a=effectiveAbsorbConfig(ctx.session,ctx.config);return a?.enabled===true?a.toolName??ABSORB_TOOL_NAME:void 0})();if(ctx.refreshFolded&&proxyResults.some(pr2=>(pr2.name==="compress"||pr2.name===absorbName)&&!pr2.result.includes("FAILED"))){try{const refreshed=await ctx.refreshFolded(coreMessages);if(refreshed.length>0){coreMessages.length=0;coreMessages.push(...refreshed);ctx.session.stats.compressCreditTokens=0;ctx.log(`[acp-loop] round ${round}: re-request view refreshed to post-compress fold`)}}catch(err2){ctx.log(`[acp-loop] round ${round}: fold refresh failed (${String(err2)}); keeping pre-compress view`);log("warn",`[acp-loop] fold refresh failed: ${String(err2)}`)}}}for(const tc of realToolCalls){if(tc.passthrough)continue;yield adapter.emitToolCall(tc)}const reRequest=proxyResults.length>0&&realCalls===0;if(!reRequest){if(!sawDone){const partialText=assistantText.length;const partialReasoning=assistantReasoning.length;const detail=streamError!==void 0?`upstream stream error: ${streamError}`:"no completion event";const msg=`upstream stream truncated (${detail}; round ${round}, ${partialText} text chars + ${partialReasoning} reasoning chars received)`;ctx.log(`[acp-loop] round ${round}: ${msg}`);yield adapter.emitError(msg);return}if(!suppressCompletion){yield adapter.emitCompletion({finishReason,usage})}return}if(round>=MAX_LOOP_ROUNDS){ctx.log(`[acp-loop] round ${round} hit MAX_LOOP_ROUNDS; completing gracefully`);log("warn",`[acp-loop] loop limit (${MAX_LOOP_ROUNDS}) reached; completing gracefully`);yield adapter.emitCompletion({finishReason:"length",usage});return}const failedMutating=proxyResults.filter(pr2=>(pr2.name==="compress"||pr2.name==="decompress")&&pr2.result.includes("FAILED"));if(reRequest&&failedMutating.length>0&&failedMutating.length===proxyResults.length){const repeated=failedMutating.some(pr2=>failedSignatures.has(`${pr2.name}\0${canonicalArgs(pr2.arguments)}`));if(repeated){ctx.log(`[acp-loop] round ${round}: model re-submitted an already-failed compress with identical arguments; stopping after ${round} round(s) instead of ${MAX_LOOP_ROUNDS}`);log("warn",`[acp-loop] repeated identical compress failure at round ${round}; breaking early (#156)`);yield adapter.emitCompletion({finishReason:"length",usage});return}}for(const pr2 of failedMutating)failedSignatures.add(`${pr2.name}\0${canonicalArgs(pr2.arguments)}`);ctx.log(`[acp-loop] round ${round}: proxy tool executed; re-requesting so the model sees the result`);if(signal?.aborted)break;let newBody=adapter.buildRequest(coreMessages,systemPrompt,requestBody);newBody=normalizeStrictEchoBody(newBody,isStrictReasoningEcho(ctx.session,strictEchoOrigin,modelIdOf(requestBody)),(level,msg)=>log(level,`[acp-loop] ${msg}`),ctx.session.id??"unknown");if(process.env.ACP_DUMP_BODY==="1"){try{const fs18=await import("fs");const path29=await import("path");const dumpDir=dumpsDir();fs18.mkdirSync(dumpDir,{recursive:true});const sid=(ctx.session.id??"unknown").replace(/[^a-zA-Z0-9_-]/g,"_");fs18.writeFileSync(path29.join(dumpDir,`req-${Date.now()}-${sid}-REREQUEST.json`),JSON.stringify(newBody,null,2))}catch{}}let respResult;try{try{respResult=await fetchUpstream(newBody)}catch(e){if(!(e instanceof UpstreamHttpError)||e.status<400||e.status>=500||degradedRetried||ctx.protocol!=="anthropic"&&ctx.protocol!=="google"||!coreMessages.some(isLoopThinking)){throw e}degradedRetried=true;const stripped=stripLoopThinking(coreMessages);coreMessages.length=0;coreMessages.push(...stripped);ctx.log(`[acp-loop] round ${round}: re-request rejected (${e.status}: ${e.body.slice(0,200)}); retrying without replayed thinking blocks`);log("warn",`[acp-loop] re-request rejected (${e.status}); retrying without thinking replay: ${e.body.slice(0,200)}`);newBody=adapter.buildRequest(coreMessages,systemPrompt,requestBody);respResult=await fetchUpstream(newBody)}}catch(e){if(!(e instanceof UpstreamHttpError))throw e;if(e.status===400&&/reasoning_content/i.test(e.body)&&ctx.session.metadata.strictReasoningEcho!==true){ctx.session.metadata.strictReasoningEcho=true;ctx.log(`[acp-loop] 400 mentions reasoning_content \u2014 learned strict reasoning echo for this session; #651 reasoning-drop disabled (#684)`);log("warn",`[acp-loop] learned strictReasoningEcho (session ${ctx.session.id}); reasoning-drop disabled (#684)`)}if(e.status>=400&&e.status<500){dumpRejectedBody(e.status,ctx.session.id??"unknown",JSON.stringify(newBody))}const suffix=e.attempts>1?` after ${e.attempts} attempt(s)`:"";ctx.log(`[acp-proxy: compress loop upstream error ${e.status}${suffix}: ${e.body.slice(0,200)}]`);log("error",`[acp-loop] upstream error ${e.status}${suffix}: ${e.body.slice(0,200)}`);yield adapter.emitError(`upstream error ${e.status}${suffix}: ${e.body.slice(0,200)}`);return}if(!respResult.response.body){respResult.clearTimer();yield adapter.emitError(`upstream error ${respResult.response.status}: empty response body`);return}currentUpstream=respResult.response.body;roundBody=newBody;if(activeClearTimer)activeClearTimer();activeClearTimer=respResult.clearTimer}}finally{if(activeClearTimer){activeClearTimer();activeClearTimer=null}}}function degenerateTurnWarning(o){if((o.reason??o.terminalReason)!==o.terminalReason)return null;if(o.toolCalls>0)return null;if(o.text.outputChars>0)return null;const bits=[];if(o.sawThinking)bits.push("thinking present");if(o.text.dropped&&o.text.inputChars>0)bits.push(`${o.text.inputChars} chars of emitted text stripped as render-tag echo`);else bits.push("no visible text emitted");return`[degenerate-turn] ${o.wire}: turn ended ${o.terminalReason} with zero visible text and zero tool calls (${bits.join("; ")}) \u2014 the agent receives an empty turn and may stall until nudged (#673)`}var RESPONSES_ITEM_ID_MAX=64;function normalizeResponsesMessageItems(input){if(!Array.isArray(input))return 0;let typed=0;for(const item of input){if(typeof item!=="object"||item===null)continue;const rec=item;if(rec["type"]!==void 0)continue;if(rec["role"]!=="user"&&rec["role"]!=="assistant")continue;if(rec["content"]===void 0)continue;rec["type"]="message";typed++}return typed}function sanitizeResponsesInputIds(input){if(!Array.isArray(input))return;for(const item of input){const rec=item;if(rec?.type==="message"&&rec.role==="assistant"&&typeof rec.id==="string"&&rec.id.startsWith("msg-proxy-")&&(typeof rec.content==="string"||Array.isArray(rec.content))){delete rec.id;continue}if(typeof rec?.id==="string"&&rec.id.length>RESPONSES_ITEM_ID_MAX){rec.id=`msg-fix-${hashId(rec.id)}`}}}function dropWhitespaceResponsesMessages(input){if(!Array.isArray(input))return 0;let dropped=0;for(let i=input.length-1;i>=0;i--){const rec=input[i];if(rec===null||typeof rec!=="object")continue;const type=rec.type;if(type!=="message"&&type!==void 0)continue;const role=rec.role;if(role!=="user"&&role!=="assistant")continue;const content=rec.content;let text;if(typeof content==="string")text=content;else if(Array.isArray(content)){let mixed=false;let joined="";for(const part of content){const p2=part;if(p2===null||typeof p2!=="object"){mixed=true;break}const pt2=p2.type;if(pt2!==void 0&&pt2!=="input_text"&&pt2!=="output_text"&&pt2!=="text"){mixed=true;break}if(typeof p2.text==="string")joined+=p2.text}if(!mixed)text=joined}if(text!==void 0&&stripRenderTags(text).trim()===""){input.splice(i,1);dropped++}}return dropped}var RENDER_TAG_RE=new RegExp("<"+ACP_NAME_ALT+"\\s[^>]*>[^<]*<\\/"+ACP_NAME_ALT+">|<"+ACP_NAME_ALT+"\\s[^>]*\\/>","g");function stripRenderTags(text){return text.replace(RENDER_TAG_RE,"")}function rewriteItemEvent(type,obj,mapped){const item={...obj.item??{},id:mapped.id};return Buffer.from(`event: ${type}
460
+ Continue the response seamlessly from exactly where that text ends (mid-sentence if necessary). Do not repeat any part of the received text and do not start over \u2014 just pick up where it stopped and finish the response.`;function isLoopThinking(m2){return m2.contentType==="reasoning"&&typeof m2.id==="string"&&m2.id.startsWith("acp_loop_")}function stripLoopThinking(messages){return messages.filter(m2=>!isLoopThinking(m2))}function canonicalArgs(args){try{return JSON.stringify(sortKeys(JSON.parse(args)))}catch{return args}}function sortKeys(v2){if(Array.isArray(v2))return v2.map(sortKeys);if(v2!==null&&typeof v2==="object"){const src=v2;const out={};for(const k2 of Object.keys(src).sort())out[k2]=sortKeys(src[k2]);return out}return v2}function executeProxyTool(toolName,args,ctx,callId){if(toolName==="compress"){return applyRanges(parseCompressInput(args,callId),ctx)}if(toolName==="decompress"){return resolveDecompress(args,ctx)}if(toolName==="search_context"){return executeSearchContextTarget(args,ctx.core,ctx.session.id,ctx.session.state)}if(toolName==="acp_status"){return handleAcpStatus(args,ctx)}if(toolName==="acp_cache"){return handleAcpCache(ctx.session,args)}const absorb=effectiveAbsorbConfig(ctx.session,ctx.config);if(absorb?.enabled===true&&toolName===(absorb.toolName??ABSORB_TOOL_NAME)){return executeAbsorb(args,callId,absorb,ctx)}if(effectiveRulesConfig(ctx.session,ctx.config)?.enabled===true&&toolName===RULE_TOOL_NAME){return executeRule(args,ctx)}if(ccrEnabled(ctx.session)&&toolName===retrieveToolName(ctx.session)){return executeRetrieve(args,ctx.session)}if(imageCompressionEnabled(ctx.session)&&toolName===IMAGE_FULL_TOOL_NAME){return executeImageFull(args,ctx.session,ctx.config,callId)}return`[Unknown proxy tool: ${toolName}]`}function recordUsage(ctx,usage,round){const prompt=usage.inputTokens;const cached=usage.cachedTokens;const out=usage.outputTokens;const total=promptInputTotal(ctx.protocol,prompt,cached,usage.creationTokens);if(total>0)ctx.session.stats.inputTokens+=total;if(total>0){ctx.session.stats.lastInputTokens=Math.max(0,total-(ctx.session.stats.compressCreditTokens??0));ctx.session.stats.lastInputTokensSource="usage";delete ctx.session.stats.overflowArmTokens}if(typeof cached==="number"&&total>0){ctx.session.stats.cachedTokens+=cached;ctx.session.stats.cacheSamples+=1}if(typeof out==="number")ctx.session.stats.outputTokens+=out;const hitPct=typeof cached==="number"&&total>0?Math.round(cached/total*100):0;warnCacheCollapse(ctx.session,total,cached??0);const foldNew=ctx.session.stats.pendingFoldUsage===true;if(foldNew)ctx.session.stats.pendingFoldUsage=false;ctx.log(`[acp-usage] round ${round} input=${total} cached=${cached??0} (cache hit ${hitPct}%)${foldNew?" fold=new":""}${total<=0?" (zero-total: lastInputTokens kept)":""}${imageUsageSuffix(ctx.session)}`);if(total>0||typeof cached==="number"){recordCacheSample(ctx.session,{at:Date.now(),input:total,cached:cached??0,output:out})}}async function*runCompressLoop(upstream,ctx,requestBody,requestOptions,adapter,systemPrompt,signal){let activeClearTimer=null;let currentUpstream=upstream;let roundBody=requestBody;const coreMessages=[...ctx.messages];let degradedRetried=false;let truncationRetried=false;let continuationRetried=false;let degenerateRetried=false;const seenMarkers=new Set;const fetchUpstream=body=>fetchWithRetry(requestOptions.url,{method:"POST",headers:requestOptions.headers,body:JSON.stringify(requestOptions.wireTransform?requestOptions.wireTransform(body):body),...ctx.proxyUrl?{dispatcher:proxyDispatcher(ctx.proxyUrl)}:{}},void 0,signal,info=>{const lc=lastCompressSuffix(ctx.session.lastCompress);ctx.log(`[acp-proxy: upstream rejected replay (HTTP ${info.status}: ${info.detail.slice(0,120)}); likely provider risk-control \u2014 retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}]`);log("warn",`[acp-loop] upstream rejected replay (HTTP ${info.status}); retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}`)});const failedSignatures=new Set;let strictEchoOrigin;try{strictEchoOrigin=new URL(requestOptions.url).origin}catch{strictEchoOrigin=void 0}try{for(let round=1;round<=MAX_LOOP_ROUNDS;round++){if(signal?.aborted)break;let assistantText="";let assistantReasoning="";const reasoningSegments=[];let reasoningSealed=true;const calls=[];let usage={};let finishReason;let streamError;let sawDone=false;let suppressCompletion=false;let truncatedDone=false;let sawThinking=false;let forwardedAny=false;let forwardedVisible=false;const fwd=(chunk,visible=false)=>{forwardedAny=true;if(visible)forwardedVisible=true;return chunk};for(;;){assistantText="";assistantReasoning="";reasoningSegments.length=0;reasoningSealed=true;calls.length=0;usage={};finishReason=void 0;streamError=void 0;sawDone=false;suppressCompletion=false;truncatedDone=false;sawThinking=false;forwardedAny=false;forwardedVisible=false;for await(const ev of adapter.parseStream(currentUpstream,round)){if(signal?.aborted)break;if(ev.kind==="text"){assistantText+=ev.delta;if(!ctx.textProtocol&&ev.raw){yield fwd(ev.raw,true)}else if(!ctx.textProtocol&&round>1&&ev.delta.length>0){yield fwd(adapter.emitText(ev.delta),true)}}else if(ev.kind==="reasoning"){assistantReasoning+=ev.delta;let seg=reasoningSegments[reasoningSegments.length-1];if(reasoningSealed||!seg){seg={text:"",signature:""};reasoningSegments.push(seg);reasoningSealed=false}seg.text+=ev.delta;if(ev.signature)seg.signature+=ev.signature;if(ev.blockEnd)reasoningSealed=true;if(!ctx.textProtocol){if(ev.raw){yield fwd(ev.raw)}else if(round>1&&ev.delta.length>0&&adapter.emitReasoning){yield fwd(adapter.emitReasoning(ev.delta))}}}else if(ev.kind==="tool_call"){calls.push({name:ev.name,callId:ev.callId,arguments:ev.arguments,passthrough:ev.passthrough,signature:ev.signature})}else if(ev.kind==="usage"){usage={inputTokens:ev.inputTokens,outputTokens:ev.outputTokens,cachedTokens:ev.cachedTokens,creationTokens:ev.creationTokens}}else if(ev.kind==="done"){sawDone=true;finishReason=ev.finishReason;suppressCompletion=ev.suppressCompletion===true;truncatedDone=ev.truncated===true;sawThinking=ev.thinking===true}else if(ev.kind==="error"){streamError=ev.message}else if(ev.kind==="meta"){if(round===1||!ev.firstRoundOnly){yield fwd(ev.chunk)}}}if(streamError!==void 0){ctx.log(`[acp-loop] round ${round}: upstream stream error: ${streamError}`);log("warn",`[acp-loop] upstream stream error: ${streamError}`)}if((!sawDone||truncatedDone)&&(!forwardedAny||ctx.protocol==="openai"&&!forwardedVisible)&&calls.length===0&&!(ctx.textProtocol&&assistantText.length>0)&&!signal?.aborted&&!truncationRetried){truncationRetried=true;ctx.log(`[acp-loop] round ${round}: upstream truncated with no visible output reaching the client; retrying fetch once`);try{const respResult2=await fetchUpstream(roundBody);if(!respResult2.response.body){respResult2.clearTimer();throw new UpstreamHttpError(respResult2.response.status,"(empty response body)",1)}currentUpstream=respResult2.response.body;if(activeClearTimer)activeClearTimer();activeClearTimer=respResult2.clearTimer;continue}catch(e){if(e instanceof UpstreamHttpError){const suffix=e.attempts>1?` after ${e.attempts} attempt(s)`:"";ctx.log(`[acp-loop] round ${round}: truncation retry failed (upstream error ${e.status}${suffix}: ${e.body.slice(0,200)})`)}else{ctx.log(`[acp-loop] round ${round}: truncation retry failed (${e instanceof Error?e.message:String(e)})`)}}}if((!sawDone||truncatedDone)&&forwardedVisible&&ctx.protocol==="openai"&&!ctx.textProtocol&&assistantText.length>0&&calls.length===0&&!signal?.aborted&&!continuationRetried){continuationRetried=true;const tail=assistantText.length<=TRUNCATION_CONTINUATION_TAIL_CHARS?assistantText:`\u2026${assistantText.slice(-TRUNCATION_CONTINUATION_TAIL_CHARS)}`;ctx.log(`[acp-loop] round ${round}: upstream truncated after ${assistantText.length} text chars reached the client; retrying once with continuation nudge`);const nudge={id:`acp_truncation_retry_r${round}`,role:"user",contentType:"text",text:truncationContinuationNudge(tail)};try{const retryBody=adapter.buildRequest([...coreMessages,nudge],systemPrompt,requestBody);const respResult2=await fetchUpstream(retryBody);if(!respResult2.response.body){respResult2.clearTimer();throw new UpstreamHttpError(respResult2.response.status,"(empty response body)",1)}currentUpstream=respResult2.response.body;roundBody=retryBody;if(activeClearTimer)activeClearTimer();activeClearTimer=respResult2.clearTimer;continue}catch(e){if(e instanceof UpstreamHttpError){ctx.log(`[acp-loop] round ${round}: continuation retry failed (upstream error ${e.status}: ${e.body.slice(0,200)}); falling back to the truncation error`)}else{ctx.log(`[acp-loop] round ${round}: continuation retry failed (${e instanceof Error?e.message:String(e)}); falling back to the truncation error`)}log("warn",`[acp-loop] truncation continuation retry failed round ${round}: ${e instanceof Error?e.message:String(e)}`)}}if(!degenerateRetried&&sawDone&&sawThinking&&!truncatedDone&&!suppressCompletion&&typeof finishReason==="string"&&finishReason!=="failed"&&finishReason!=="incomplete"&&finishReason!=="error"&&assistantText.length===0&&calls.length===0&&!forwardedVisible&&!signal?.aborted){degenerateRetried=true;ctx.log(`[acp-loop] round ${round}: degenerate terminal turn (completed, zero visible output); retrying once with continuation nudge (#732/#821)`);log("warn",`[acp-loop] degenerate-turn auto-retry round ${round} (session ${ctx.session.id})`);const nudge={id:`acp_degenerate_retry_r${round}`,role:"user",contentType:"text",text:DEGENERATE_RETRY_NUDGE};try{const retryBody=adapter.buildRequest([...coreMessages,nudge],systemPrompt,requestBody);const respResult2=await fetchUpstream(retryBody);if(!respResult2.response.body){respResult2.clearTimer();throw new UpstreamHttpError(respResult2.response.status,"(empty response body)",1)}currentUpstream=respResult2.response.body;roundBody=retryBody;if(activeClearTimer)activeClearTimer();activeClearTimer=respResult2.clearTimer;continue}catch(e){if(e instanceof UpstreamHttpError){ctx.log(`[acp-loop] round ${round}: degenerate retry failed (upstream error ${e.status}: ${e.body.slice(0,200)}); passing the empty turn through`)}else{ctx.log(`[acp-loop] round ${round}: degenerate retry failed (${e instanceof Error?e.message:String(e)}); passing the empty turn through`)}log("warn",`[acp-loop] degenerate-turn auto-retry failed round ${round}: ${e instanceof Error?e.message:String(e)}`)}}break}if(usage.inputTokens!==void 0||usage.outputTokens!==void 0||usage.cachedTokens!==void 0){recordUsage(ctx,usage,round)}let resolvedText=assistantText;let allCalls=calls;if(ctx.textProtocol&&assistantText.length>0&&adapter.extractTextTriggers){const extracted=adapter.extractTextTriggers(assistantText);resolvedText=extracted.clean;allCalls=[...calls,...extracted.calls]}const functionCallIds=new Set(allCalls.map(c=>c.callId));if(ctx.textProtocol&&resolvedText.length>0){yield adapter.emitText(resolvedText)}let realCalls=0;const realToolCalls=[];const proxyResults=[];for(const call of allCalls){if(isProxyToolFor(call.name,ctx.session,ctx.config)){let parsedArgs;try{parsedArgs=call.arguments.length>0?JSON.parse(call.arguments):{}}catch{ctx.log(`[acp-loop] proxy tool ${call.name}: arguments not parseable JSON (len=${call.arguments.length}${call.arguments.length>0?`, head=${call.arguments.slice(0,200)}`:""}) \u2014 executing with {}`);parsedArgs={}}const result=await withSessionLock(ctx.session,()=>executeProxyTool(call.name,parsedArgs,ctx,call.callId));proxyResults.push({name:call.name,callId:call.callId,result,arguments:call.arguments,signature:call.signature});if(ctx.visibilityMarkers!==false){const markerKey=`${call.name}\0${result}`;if(seenMarkers.has(markerKey)){ctx.log(`[acp-loop] suppressed duplicate ${call.name} status marker (identical failure repeated this request)`)}else{seenMarkers.add(markerKey);yield adapter.emitMarker(call.name,result)}}}else{realToolCalls.push(call);realCalls+=1}}if(ctx.debug){const callSummary=allCalls.map(c=>{const argSnippet=c.arguments.length>200?c.arguments.slice(0,200)+"...":c.arguments;return`${c.name}(${argSnippet})`}).join(" | ");ctx.log(`[acp-loop] round ${round}: ${allCalls.length} call(s): ${callSummary||"(none)"}`);for(const pr2 of proxyResults){const resSnippet=pr2.result.length>300?pr2.result.slice(0,300)+"...":pr2.result;ctx.log(`[acp-loop] \u2192 ${pr2.name} result: ${resSnippet}`)}if(realCalls>0)ctx.log(`[acp-loop] round ${round}: ${realCalls} real tool call(s) forwarded to client`)}if(proxyResults.length>0){const requiresThinkingSignature=ctx.protocol==="anthropic"||ctx.protocol==="google";if(reasoningSegments.length>0){for(let i=0;i<reasoningSegments.length;i++){const seg=reasoningSegments[i];if(seg.text.length===0||requiresThinkingSignature&&seg.signature.length===0)continue;const reasoningMsg={id:i===0?`acp_loop_r${round}_reasoning`:`acp_loop_r${round}_reasoning_${i+1}`,role:"assistant",contentType:"reasoning",text:seg.text,reasoningContent:seg.text,...seg.signature.length>0?ctx.protocol==="google"?{googleThoughtSignature:seg.signature}:{thinkingSignature:seg.signature}:{}};coreMessages.push(reasoningMsg)}}if(assistantText.length>0){coreMessages.push({id:`acp_loop_r${round}_asst`,role:"assistant",contentType:"text",text:assistantText})}for(const pr2 of proxyResults){if(functionCallIds.has(pr2.callId)){coreMessages.push({id:`acp_loop_r${round}_asst_tc_${pr2.callId}`,role:"assistant",contentType:"tool-call",toolName:pr2.name,toolCallId:pr2.callId,text:pr2.arguments,...ctx.protocol==="google"&&pr2.signature?{googleThoughtSignature:pr2.signature}:{}});coreMessages.push({id:`acp_loop_r${round}_tool_${pr2.callId}`,role:"tool",contentType:"tool-result",toolName:pr2.name,toolCallId:pr2.callId,text:pr2.result})}else if(ctx.visibilityMarkers!==false){coreMessages.push({id:`acp_loop_r${round}_marker_${pr2.callId}`,role:"system",contentType:"text",text:buildVisibilityMarker(pr2.name,pr2.result)})}}for(const injection of drainPendingRetrievals(ctx.session)){coreMessages.push(injection)}const anyCompressFailed=proxyResults.some(pr2=>(pr2.name==="compress"||pr2.name==="decompress")&&pr2.result.includes("FAILED"));if(!ctx.textProtocol&&!anyCompressFailed){const hidden=hideConsumedCompressCalls(ctx.session.state,coreMessages);if(hidden.hidden>0){ctx.log(`[acp-loop] round ${round} hideConsumed hid ${hidden.hidden} compress record(s)`);coreMessages.length=0;coreMessages.push(...hidden.messages)}}const absorbName=(()=>{const a=effectiveAbsorbConfig(ctx.session,ctx.config);return a?.enabled===true?a.toolName??ABSORB_TOOL_NAME:void 0})();if(ctx.refreshFolded&&proxyResults.some(pr2=>(pr2.name==="compress"||pr2.name===absorbName)&&!pr2.result.includes("FAILED"))){try{const refreshed=await ctx.refreshFolded(coreMessages);if(refreshed.length>0){coreMessages.length=0;coreMessages.push(...refreshed);ctx.session.stats.compressCreditTokens=0;ctx.log(`[acp-loop] round ${round}: re-request view refreshed to post-compress fold`)}}catch(err2){ctx.log(`[acp-loop] round ${round}: fold refresh failed (${String(err2)}); keeping pre-compress view`);log("warn",`[acp-loop] fold refresh failed: ${String(err2)}`)}}}for(const tc of realToolCalls){if(tc.passthrough)continue;yield adapter.emitToolCall(tc)}const reRequest=proxyResults.length>0&&realCalls===0;if(!reRequest){if(!sawDone){const partialText=assistantText.length;const partialReasoning=assistantReasoning.length;const detail=streamError!==void 0?`upstream stream error: ${streamError}`:"no completion event";const msg=`upstream stream truncated (${detail}; round ${round}, ${partialText} text chars + ${partialReasoning} reasoning chars received)`;ctx.log(`[acp-loop] round ${round}: ${msg}`);yield adapter.emitError(msg);return}if(!suppressCompletion){yield adapter.emitCompletion({finishReason,usage})}return}if(round>=MAX_LOOP_ROUNDS){ctx.log(`[acp-loop] round ${round} hit MAX_LOOP_ROUNDS; completing gracefully`);log("warn",`[acp-loop] loop limit (${MAX_LOOP_ROUNDS}) reached; completing gracefully`);yield adapter.emitCompletion({finishReason:"length",usage});return}const failedMutating=proxyResults.filter(pr2=>(pr2.name==="compress"||pr2.name==="decompress")&&pr2.result.includes("FAILED"));if(reRequest&&failedMutating.length>0&&failedMutating.length===proxyResults.length){const repeated=failedMutating.some(pr2=>failedSignatures.has(`${pr2.name}\0${canonicalArgs(pr2.arguments)}`));if(repeated){ctx.log(`[acp-loop] round ${round}: model re-submitted an already-failed compress with identical arguments; stopping after ${round} round(s) instead of ${MAX_LOOP_ROUNDS}`);log("warn",`[acp-loop] repeated identical compress failure at round ${round}; breaking early (#156)`);yield adapter.emitCompletion({finishReason:"length",usage});return}}for(const pr2 of failedMutating)failedSignatures.add(`${pr2.name}\0${canonicalArgs(pr2.arguments)}`);ctx.log(`[acp-loop] round ${round}: proxy tool executed; re-requesting so the model sees the result`);if(signal?.aborted)break;let newBody=adapter.buildRequest(coreMessages,systemPrompt,requestBody);newBody=normalizeStrictEchoBody(newBody,isStrictReasoningEcho(ctx.session,strictEchoOrigin,modelIdOf(requestBody)),(level,msg)=>log(level,`[acp-loop] ${msg}`),ctx.session.id??"unknown");if(process.env.ACP_DUMP_BODY==="1"){try{const fs18=await import("fs");const path29=await import("path");const dumpDir=dumpsDir();fs18.mkdirSync(dumpDir,{recursive:true});const sid=(ctx.session.id??"unknown").replace(/[^a-zA-Z0-9_-]/g,"_");fs18.writeFileSync(path29.join(dumpDir,`req-${Date.now()}-${sid}-REREQUEST.json`),JSON.stringify(newBody,null,2))}catch{}}let respResult;try{try{respResult=await fetchUpstream(newBody)}catch(e){if(!(e instanceof UpstreamHttpError)||e.status<400||e.status>=500||degradedRetried||ctx.protocol!=="anthropic"&&ctx.protocol!=="google"||!coreMessages.some(isLoopThinking)){throw e}degradedRetried=true;const stripped=stripLoopThinking(coreMessages);coreMessages.length=0;coreMessages.push(...stripped);ctx.log(`[acp-loop] round ${round}: re-request rejected (${e.status}: ${e.body.slice(0,200)}); retrying without replayed thinking blocks`);log("warn",`[acp-loop] re-request rejected (${e.status}); retrying without thinking replay: ${e.body.slice(0,200)}`);newBody=adapter.buildRequest(coreMessages,systemPrompt,requestBody);respResult=await fetchUpstream(newBody)}}catch(e){if(!(e instanceof UpstreamHttpError))throw e;if(e.status===400&&/reasoning_content/i.test(e.body)&&ctx.session.metadata.strictReasoningEcho!==true){ctx.session.metadata.strictReasoningEcho=true;ctx.log(`[acp-loop] 400 mentions reasoning_content \u2014 learned strict reasoning echo for this session; #651 reasoning-drop disabled (#684)`);log("warn",`[acp-loop] learned strictReasoningEcho (session ${ctx.session.id}); reasoning-drop disabled (#684)`)}if(e.status>=400&&e.status<500){dumpRejectedBody(e.status,ctx.session.id??"unknown",JSON.stringify(newBody))}const suffix=e.attempts>1?` after ${e.attempts} attempt(s)`:"";ctx.log(`[acp-proxy: compress loop upstream error ${e.status}${suffix}: ${e.body.slice(0,200)}]`);log("error",`[acp-loop] upstream error ${e.status}${suffix}: ${e.body.slice(0,200)}`);yield adapter.emitError(`upstream error ${e.status}${suffix}: ${e.body.slice(0,200)}`);return}if(!respResult.response.body){respResult.clearTimer();yield adapter.emitError(`upstream error ${respResult.response.status}: empty response body`);return}currentUpstream=respResult.response.body;roundBody=newBody;if(activeClearTimer)activeClearTimer();activeClearTimer=respResult.clearTimer}}finally{if(activeClearTimer){activeClearTimer();activeClearTimer=null}}}function degenerateTurnWarning(o){if((o.reason??o.terminalReason)!==o.terminalReason)return null;if(o.toolCalls>0)return null;if(o.text.outputChars>0)return null;const bits=[];if(o.sawThinking)bits.push("thinking present");if(o.text.dropped&&o.text.inputChars>0)bits.push(`${o.text.inputChars} chars of emitted text stripped as render-tag echo`);else bits.push("no visible text emitted");return`[degenerate-turn] ${o.wire}: turn ended ${o.terminalReason} with zero visible text and zero tool calls (${bits.join("; ")}) \u2014 the agent receives an empty turn and may stall until nudged (#673)`}var RESPONSES_ITEM_ID_MAX=64;function normalizeResponsesMessageItems(input){if(!Array.isArray(input))return 0;let typed=0;for(const item of input){if(typeof item!=="object"||item===null)continue;const rec=item;if(rec["type"]!==void 0)continue;if(rec["role"]!=="user"&&rec["role"]!=="assistant")continue;if(rec["content"]===void 0)continue;rec["type"]="message";typed++}return typed}function sanitizeResponsesInputIds(input){if(!Array.isArray(input))return;for(const item of input){const rec=item;if(rec?.type==="message"&&rec.role==="assistant"&&typeof rec.id==="string"&&rec.id.startsWith("msg-proxy-")&&(typeof rec.content==="string"||Array.isArray(rec.content))){delete rec.id;continue}if(typeof rec?.id==="string"&&rec.id.length>RESPONSES_ITEM_ID_MAX){rec.id=`msg-fix-${hashId(rec.id)}`}}}function dropWhitespaceResponsesMessages(input){if(!Array.isArray(input))return 0;let dropped=0;for(let i=input.length-1;i>=0;i--){const rec=input[i];if(rec===null||typeof rec!=="object")continue;const type=rec.type;if(type!=="message"&&type!==void 0)continue;const role=rec.role;if(role!=="user"&&role!=="assistant")continue;const content=rec.content;let text;if(typeof content==="string")text=content;else if(Array.isArray(content)){let mixed=false;let joined="";for(const part of content){const p2=part;if(p2===null||typeof p2!=="object"){mixed=true;break}const pt2=p2.type;if(pt2!==void 0&&pt2!=="input_text"&&pt2!=="output_text"&&pt2!=="text"){mixed=true;break}if(typeof p2.text==="string")joined+=p2.text}if(!mixed)text=joined}if(text!==void 0&&stripRenderTags(text).trim()===""){input.splice(i,1);dropped++}}return dropped}var RENDER_TAG_RE=new RegExp("<"+ACP_NAME_ALT+"\\s[^>]*>[^<]*<\\/"+ACP_NAME_ALT+">|<"+ACP_NAME_ALT+"\\s[^>]*\\/>","g");function stripRenderTags(text){return text.replace(RENDER_TAG_RE,"")}function rewriteItemEvent(type,obj,mapped){const item={...obj.item??{},id:mapped.id};return Buffer.from(`event: ${type}
461
461
  data: ${JSON.stringify({...obj,output_index:mapped.index,item})}
462
462
 
463
463
  `,"utf8")}function rewriteRefEvent(type,obj,mapped){return Buffer.from(`event: ${type}
@@ -520,7 +520,7 @@ data: ${JSON.stringify({type:"response.failed",response:resp})}
520
520
 
521
521
  `,"utf8")};const buildFinish=(finishReason,usage)=>Buffer.from(`data: ${JSON.stringify({...makeBase(),choices:[{index:0,delta:{},finish_reason:finishReason}],...usage?{usage}:{}})}
522
522
 
523
- `,"utf8");return{buildRequest(coreMessages,systemPrompt,body){const messages=systemToUser(hardenOpenaiAssistantContent(coreToOpenai(coreMessages)));const withSys=injectOpenaiSystem(messages,[clientSystem,systemPrompt].filter(p2=>typeof p2==="string"&&p2.length>0));const withNotes=notes&&notes.length>0?[...withSys,...notes.map(text=>({role:"user",content:text}))]:withSys;return{...body,messages:withNotes}},async*parseStream(upstream,_round){const pending=new Map;const tagFilter=composeStreamFilters(createTagEchoFilter(snippet=>{log("warn",`[tag-echo] stripped model-emitted render tag: ${snippet.slice(0,80).replace(/\n/g," ")}`)}),createMarkerLineFilter(snippet=>{log("warn",`[marker-echo] stripped model-emitted ACP confirmation marker: ${snippet.slice(0,80).replace(/\n/g," ")}`)}));const flushFilter=function*(){const tail=tagFilter.flush();if(tail.length>0){yield{kind:"text",delta:tail,raw:buildContent(tail)}}};let sawReasoning=false;let toolCallsEmitted=0;let degenerateWarned=false;const maybeWarnDegenerate=reason=>{if(degenerateWarned)return;const msg=degenerateTurnWarning({reason,terminalReason:"stop",toolCalls:toolCallsEmitted,text:tagFilter.stats(),sawThinking:sawReasoning,wire:"openai"});if(msg){degenerateWarned=true;log("warn",msg)}};const rawToolChunks=[];let sawRealToolCall=false;const flushPendingAsStructured=function*(){for(const[,tc]of pending){if(tc.name.length>0||tc.id.length>0){toolCallsEmitted++;yield{kind:"tool_call",name:tc.name,callId:tc.id,arguments:tc.arguments}}}pending.clear()};const settleToolCalls=function*(){const realIndexes=new Set;for(const[idx,tc]of pending){if(tc.name.length>0&&!PROXY_TOOL_SET.has(tc.name)&&tc.name!==absorbName)realIndexes.add(idx)}sawRealToolCall=realIndexes.size>0;if(!sawRealToolCall){yield*flushPendingAsStructured();return}for(const[idx,tc]of pending){if(realIndexes.has(idx)&&(tc.name.length>0||tc.id.length>0)){toolCallsEmitted++;yield{kind:"tool_call",name:tc.name,callId:tc.id,arguments:tc.arguments,passthrough:true}}}for(const{json,parsed}of rawToolChunks){const filtered=filterRealToolFragments(parsed,realIndexes);if(filtered==="keep"){yield{kind:"meta",chunk:Buffer.from("data: "+json+"\n\n","utf8")}}else if(filtered!==null){yield{kind:"meta",chunk:Buffer.from("data: "+JSON.stringify(filtered)+"\n\n","utf8")}}}for(const[idx,tc]of pending){if(!realIndexes.has(idx)&&tc.name.length>0){toolCallsEmitted++;yield{kind:"tool_call",name:tc.name,callId:tc.id,arguments:tc.arguments}}}pending.clear()};for await(const eventStr of iterSseChunks(upstream)){const dataLine2=eventStr.split("\n").find(l=>l.startsWith("data:"));if(!dataLine2)continue;const jsonStr=dataLine2.slice(5).trim();if(jsonStr==="[DONE]"){yield*flushFilter();if(sawRealToolCall){yield{kind:"meta",chunk:Buffer.from(eventStr+"\n\n","utf8")};continue}yield*settleToolCalls();if(sawRealToolCall){yield{kind:"meta",chunk:Buffer.from(eventStr+"\n\n","utf8")}}maybeWarnDegenerate("stop");yield{kind:"done",finishReason:"stop",thinking:sawReasoning,...sawRealToolCall?{suppressCompletion:true}:{}};continue}let parsed;try{parsed=JSON.parse(jsonStr)}catch{continue}const rawBuf=Buffer.from(eventStr+"\n\n","utf8");const streamError=parsed.error;if(streamError!==void 0&&streamError!==null){let message;if(typeof streamError==="string"){message=streamError}else if(typeof streamError==="object"){const error=streamError;const detail=typeof error.message==="string"?error.message:JSON.stringify(streamError);const code=typeof error.code==="string"?error.code:void 0;message=code&&detail?`${code}: ${detail}`:detail}else{message=String(streamError)}yield{kind:"error",message};return}const choices=parsed.choices;const choice=choices?.[0];if(!choice){if(parsed.usage){const u2=parsed.usage;yield{kind:"usage",inputTokens:typeof u2.prompt_tokens==="number"?u2.prompt_tokens:void 0,outputTokens:typeof u2.completion_tokens==="number"?u2.completion_tokens:void 0,cachedTokens:openaiCachedTokens(u2)};if(sawRealToolCall){yield{kind:"meta",chunk:rawBuf}}}continue}const delta=choice.delta;const finishReason=typeof choice.finish_reason==="string"?choice.finish_reason:void 0;if(finishReason){yield*flushFilter();const hadToolCalls=[...pending.values()].some(tc=>tc.name.length>0||tc.id.length>0);yield*settleToolCalls();const u2=parsed.usage;yield{kind:"usage",inputTokens:typeof u2?.prompt_tokens==="number"?u2.prompt_tokens:void 0,outputTokens:typeof u2?.completion_tokens==="number"?u2.completion_tokens:void 0,cachedTokens:u2?openaiCachedTokens(u2):void 0};if(sawRealToolCall){const chunk=rawBuf;yield{kind:"meta",chunk};maybeWarnDegenerate(finishReason);yield{kind:"done",finishReason,suppressCompletion:true,thinking:sawReasoning};continue}else{maybeWarnDegenerate(finishReason);yield{kind:"done",finishReason:hadToolCalls&&finishReason==="stop"?"tool_calls":finishReason,thinking:sawReasoning}}}if(!delta)continue;if(typeof delta.reasoning_content==="string"&&delta.reasoning_content.length>0){sawReasoning=true;yield{kind:"reasoning",delta:delta.reasoning_content,raw:finishReason?stripFinishReasonChunk(rawBuf):rawBuf}}if(delta.tool_calls){const tcs=delta.tool_calls;rawToolChunks.push({json:jsonStr,parsed});for(const tc of tcs){const idx=typeof tc.index==="number"?tc.index:0;const fn=tc.function;const name=typeof fn?.name==="string"?fn.name:"";const id=typeof tc.id==="string"?tc.id:"";const rawArgs=fn?.arguments;const args=typeof rawArgs==="string"?rawArgs:rawArgs!==null&&typeof rawArgs==="object"?JSON.stringify(rawArgs):"";let buf=pending.get(idx);if(!buf){buf={index:idx,id,name,arguments:args};pending.set(idx,buf)}else{if(id)buf.id=id;buf.name+=name;buf.arguments+=args}}if(typeof delta.content==="string"&&delta.content.length>0){const clean=tagFilter.push(delta.content);if(clean.length>0){yield{kind:"text",delta:clean}}}continue}const hasReasoning=typeof delta.reasoning_content==="string"&&delta.reasoning_content.length>0;if(typeof delta.content==="string"&&delta.content.length>0){const clean=tagFilter.push(delta.content);if(clean.length>0){const raw=clean===delta.content?rawBuf:rewriteContentChunk(parsed,clean);yield{kind:"text",delta:clean,...hasReasoning?{}:{raw:finishReason?stripFinishReasonChunk(raw):raw}}}}else if(!hasReasoning&&(delta.role||Object.keys(delta).length===0&&!finishReason)){yield{kind:"meta",chunk:finishReason?stripFinishReasonChunk(rawBuf):rawBuf,firstRoundOnly:true}}}},emitText(delta){return buildContent(delta)},emitReasoning(delta){return buildReasoning(delta)},emitToolCall(call){return buildToolCall(call)},emitMarker(toolName,result){return buildContent(buildVisibilityMarker(toolName,result))},emitCompletion(opts){const finishReason=opts?.finishReason??"stop";const usage=opts?.usage?{prompt_tokens:opts.usage.inputTokens??0,completion_tokens:opts.usage.outputTokens??0,total_tokens:(opts.usage.inputTokens??0)+(opts.usage.outputTokens??0),...typeof opts.usage.cachedTokens==="number"?{prompt_tokens_details:{cached_tokens:opts.usage.cachedTokens}}:{}}:null;return Buffer.concat([buildFinish(finishReason,usage),Buffer.from("data: [DONE]\n\n","utf8")])},emitError(message){return Buffer.concat([buildContent(`
523
+ `,"utf8");return{buildRequest(coreMessages,systemPrompt,body){const messages=systemToUser(hardenOpenaiAssistantContent(coreToOpenai(coreMessages)));const withSys=injectOpenaiSystem(messages,[clientSystem,systemPrompt].filter(p2=>typeof p2==="string"&&p2.length>0));const withNotes=notes&&notes.length>0?[...withSys,...notes.map(text=>({role:"user",content:text}))]:withSys;return{...body,messages:withNotes}},async*parseStream(upstream,_round){const pending=new Map;const tagFilter=composeStreamFilters(createTagEchoFilter(snippet=>{log("warn",`[tag-echo] stripped model-emitted render tag: ${snippet.slice(0,80).replace(/\n/g," ")}`)}),createMarkerLineFilter(snippet=>{log("warn",`[marker-echo] stripped model-emitted ACP confirmation marker: ${snippet.slice(0,80).replace(/\n/g," ")}`)}));const flushFilter=function*(){const tail=tagFilter.flush();if(tail.length>0){yield{kind:"text",delta:tail,raw:buildContent(tail)}}};let sawReasoning=false;let toolCallsEmitted=0;let degenerateWarned=false;const maybeWarnDegenerate=reason=>{if(degenerateWarned)return;const msg=degenerateTurnWarning({reason,terminalReason:"stop",toolCalls:toolCallsEmitted,text:tagFilter.stats(),sawThinking:sawReasoning,wire:"openai"});if(msg){degenerateWarned=true;log("warn",msg)}};const rawToolChunks=[];let sawRealToolCall=false;const absorbToolCallDeltas=tcs=>{for(const tc of tcs){const idx=typeof tc.index==="number"?tc.index:0;const fn=tc.function;const name=typeof fn?.name==="string"?fn.name:"";const id=typeof tc.id==="string"?tc.id:"";const rawArgs=fn?.arguments;const args=typeof rawArgs==="string"?rawArgs:rawArgs!==null&&typeof rawArgs==="object"?JSON.stringify(rawArgs):"";let buf=pending.get(idx);if(!buf){buf={index:idx,id,name,arguments:args};pending.set(idx,buf)}else{if(id)buf.id=id;buf.name+=name;buf.arguments+=args}}};const flushPendingAsStructured=function*(){for(const[idx,tc]of pending){if(tc.name.length>0||tc.id.length>0){toolCallsEmitted++;yield{kind:"tool_call",name:tc.name,callId:tc.id,arguments:tc.arguments}}else if(tc.arguments.length>0){log("warn",`[acp-openai] dropping orphan tool_call buffer idx=${idx} argsLen=${tc.arguments.length} (#1306)`)}}pending.clear()};const settleToolCalls=function*(){const realIndexes=new Set;for(const[idx,tc]of pending){if(tc.name.length>0&&!PROXY_TOOL_SET.has(tc.name)&&tc.name!==absorbName)realIndexes.add(idx)}sawRealToolCall=realIndexes.size>0;if(!sawRealToolCall){yield*flushPendingAsStructured();return}for(const[idx,tc]of pending){if(realIndexes.has(idx)&&(tc.name.length>0||tc.id.length>0)){toolCallsEmitted++;yield{kind:"tool_call",name:tc.name,callId:tc.id,arguments:tc.arguments,passthrough:true}}}for(const{json,parsed}of rawToolChunks){const filtered=filterRealToolFragments(parsed,realIndexes);if(filtered==="keep"){yield{kind:"meta",chunk:Buffer.from("data: "+json+"\n\n","utf8")}}else if(filtered!==null){yield{kind:"meta",chunk:Buffer.from("data: "+JSON.stringify(filtered)+"\n\n","utf8")}}}for(const[idx,tc]of pending){if(!realIndexes.has(idx)&&tc.name.length>0){toolCallsEmitted++;yield{kind:"tool_call",name:tc.name,callId:tc.id,arguments:tc.arguments}}}pending.clear()};for await(const eventStr of iterSseChunks(upstream)){const dataLine2=eventStr.split("\n").find(l=>l.startsWith("data:"));if(!dataLine2)continue;const jsonStr=dataLine2.slice(5).trim();if(jsonStr==="[DONE]"){yield*flushFilter();if(sawRealToolCall){yield{kind:"meta",chunk:Buffer.from(eventStr+"\n\n","utf8")};continue}yield*settleToolCalls();if(sawRealToolCall){yield{kind:"meta",chunk:Buffer.from(eventStr+"\n\n","utf8")}}maybeWarnDegenerate("stop");yield{kind:"done",finishReason:"stop",thinking:sawReasoning,...sawRealToolCall?{suppressCompletion:true}:{}};continue}let parsed;try{parsed=JSON.parse(jsonStr)}catch{continue}const rawBuf=Buffer.from(eventStr+"\n\n","utf8");const streamError=parsed.error;if(streamError!==void 0&&streamError!==null){let message;if(typeof streamError==="string"){message=streamError}else if(typeof streamError==="object"){const error=streamError;const detail=typeof error.message==="string"?error.message:JSON.stringify(streamError);const code=typeof error.code==="string"?error.code:void 0;message=code&&detail?`${code}: ${detail}`:detail}else{message=String(streamError)}yield{kind:"error",message};return}const choices=parsed.choices;const choice=choices?.[0];if(!choice){if(parsed.usage){const u2=parsed.usage;yield{kind:"usage",inputTokens:typeof u2.prompt_tokens==="number"?u2.prompt_tokens:void 0,outputTokens:typeof u2.completion_tokens==="number"?u2.completion_tokens:void 0,cachedTokens:openaiCachedTokens(u2)};if(sawRealToolCall){yield{kind:"meta",chunk:rawBuf}}}continue}const delta=choice.delta;const finishReason=typeof choice.finish_reason==="string"?choice.finish_reason:void 0;const frameToolCalls=delta?.tool_calls;if(frameToolCalls)absorbToolCallDeltas(frameToolCalls);if(finishReason){yield*flushFilter();const hadToolCalls=[...pending.values()].some(tc=>tc.name.length>0||tc.id.length>0);yield*settleToolCalls();const u2=parsed.usage;yield{kind:"usage",inputTokens:typeof u2?.prompt_tokens==="number"?u2.prompt_tokens:void 0,outputTokens:typeof u2?.completion_tokens==="number"?u2.completion_tokens:void 0,cachedTokens:u2?openaiCachedTokens(u2):void 0};if(sawRealToolCall){const chunk=rawBuf;yield{kind:"meta",chunk};maybeWarnDegenerate(finishReason);yield{kind:"done",finishReason,suppressCompletion:true,thinking:sawReasoning};continue}else{maybeWarnDegenerate(finishReason);yield{kind:"done",finishReason:hadToolCalls&&finishReason==="stop"?"tool_calls":finishReason,thinking:sawReasoning}}}if(!delta)continue;if(typeof delta.reasoning_content==="string"&&delta.reasoning_content.length>0){sawReasoning=true;yield{kind:"reasoning",delta:delta.reasoning_content,raw:finishReason?stripFinishReasonChunk(rawBuf):rawBuf}}if(frameToolCalls){rawToolChunks.push({json:jsonStr,parsed});if(typeof delta.content==="string"&&delta.content.length>0){const clean=tagFilter.push(delta.content);if(clean.length>0){yield{kind:"text",delta:clean}}}continue}const hasReasoning=typeof delta.reasoning_content==="string"&&delta.reasoning_content.length>0;if(typeof delta.content==="string"&&delta.content.length>0){const clean=tagFilter.push(delta.content);if(clean.length>0){const raw=clean===delta.content?rawBuf:rewriteContentChunk(parsed,clean);yield{kind:"text",delta:clean,...hasReasoning?{}:{raw:finishReason?stripFinishReasonChunk(raw):raw}}}}else if(!hasReasoning&&(delta.role||Object.keys(delta).length===0&&!finishReason)){yield{kind:"meta",chunk:finishReason?stripFinishReasonChunk(rawBuf):rawBuf,firstRoundOnly:true}}}},emitText(delta){return buildContent(delta)},emitReasoning(delta){return buildReasoning(delta)},emitToolCall(call){return buildToolCall(call)},emitMarker(toolName,result){return buildContent(buildVisibilityMarker(toolName,result))},emitCompletion(opts){const finishReason=opts?.finishReason??"stop";const usage=opts?.usage?{prompt_tokens:opts.usage.inputTokens??0,completion_tokens:opts.usage.outputTokens??0,total_tokens:(opts.usage.inputTokens??0)+(opts.usage.outputTokens??0),...typeof opts.usage.cachedTokens==="number"?{prompt_tokens_details:{cached_tokens:opts.usage.cachedTokens}}:{}}:null;return Buffer.concat([buildFinish(finishReason,usage),Buffer.from("data: [DONE]\n\n","utf8")])},emitError(message){return Buffer.concat([buildContent(`
524
524
  [acp-proxy: ${message}]
525
525
  `),buildFinish("stop",null),Buffer.from("data: [DONE]\n\n","utf8")])}}}async function*iterSseEvents2(stream2){const reader=stream2.getReader();const decoder=new TextDecoder;let buf="";try{while(true){let done;let value;try{({done,value}=await reader.read())}catch{break}if(done)break;buf+=decoder.decode(value,{stream:true});buf=buf.replace(/\r\n|\r/g,"\n");let idx;while((idx=buf.indexOf("\n\n"))>=0){const raw=buf.slice(0,idx);buf=buf.slice(idx+2);if(raw.trim().length>0)yield raw}}if(buf.trim().length>0)yield buf}finally{reader.releaseLock()}}function parseAnthropicSse(eventStr){const lines=eventStr.split("\n");let type="";const dataLines=[];for(const l of lines){if(l.startsWith("event:"))type=l.slice(6).trim();else if(l.startsWith("data:"))dataLines.push(l.slice(5).replace(/^ /,""))}if(!type)return null;const jsonStr=dataLines.join("\n").trim();if(!jsonStr)return{type,data:{}};try{return{type,data:JSON.parse(jsonStr)}}catch{return{type,data:{}}}}function remapIndexInEvent(eventStr,newIndex){const lines=eventStr.split("\n");const rebuilt=[];let touched=false;for(const l of lines){if(!touched&&l.startsWith("data:")){const jsonStr=l.slice(5).replace(/^ /,"");try{const obj=JSON.parse(jsonStr);if(typeof obj==="object"&&obj!==null&&typeof obj.index==="number"){obj.index=newIndex;rebuilt.push(`data: ${JSON.stringify(obj)}`);touched=true;continue}}catch{}}rebuilt.push(l)}return Buffer.from(rebuilt.join("\n")+"\n\n","utf8")}function rewriteTextDeltaEvent(eventStr,newIndex,newText){const lines=eventStr.split("\n");const rebuilt=[];for(const l of lines){if(l.startsWith("data:")){const jsonStr=l.slice(5).replace(/^ /,"");try{const obj=JSON.parse(jsonStr);if(typeof obj==="object"&&obj!==null&&typeof obj.index==="number"){obj.index=newIndex;const d=obj.delta;if(d&&typeof d.text==="string")d.text=newText;rebuilt.push(`data: ${JSON.stringify(obj)}`);continue}}catch{}}rebuilt.push(l)}return Buffer.from(rebuilt.join("\n")+"\n\n","utf8")}function buildTextDeltaEvent(index,text){return Buffer.from(`event: content_block_delta
526
526
  data: ${JSON.stringify({type:"content_block_delta",index,delta:{type:"text_delta",text}})}