@north-light/cli 0.3.577 → 0.3.578

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.
Files changed (36) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/api/dto/run-events.d.ts +10 -4
  4. package/dist/builtin-memory/crouter-sdk/release-notes.md +8 -0
  5. package/dist/builtin-memory/crouter-sdk/streaming.md +2 -0
  6. package/dist/builtin-memory/northlight-apps/concepts/runs.md +2 -0
  7. package/dist/builtin-memory/northlight-apps/guides/stream-a-run.md +1 -1
  8. package/dist/builtin-memory/northlight-apps/guides/test-your-app.md +1 -1
  9. package/dist/core/llm/refusal.d.ts +2 -1
  10. package/dist/core/llm/refusal.js +1 -1
  11. package/dist/core/runs/operations.d.ts +1 -1
  12. package/dist/core/runs/operations.js +5 -5
  13. package/dist/core/runtime/broker/held-deferred-inbox.d.ts +4 -0
  14. package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
  15. package/dist/core/runtime/broker/rebind.d.ts +3 -0
  16. package/dist/core/runtime/broker/rebind.js +1 -1
  17. package/dist/core/runtime/broker/run-steer.d.ts +15 -0
  18. package/dist/core/runtime/broker/run-steer.js +1 -0
  19. package/dist/core/runtime/broker/run-turn-queue.d.ts +2 -0
  20. package/dist/core/runtime/broker/run-turn-queue.js +1 -1
  21. package/dist/core/runtime/broker.js +1 -1
  22. package/dist/core/runtime/runner/blaxel/client.d.ts +2 -2
  23. package/dist/core/runtime/runner/blaxel/client.js +1 -1
  24. package/dist/core/runtime/runner/blaxel/runner.js +2 -2
  25. package/dist/daemon/api/handlers/runs.js +1 -1
  26. package/dist/daemon/runs/append-section.js +2 -2
  27. package/dist/pi-extensions/canvas-bash-valve.js +6 -6
  28. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
  29. package/docs/apps/concepts/runs.md +2 -0
  30. package/docs/apps/guides/stream-a-run.md +1 -1
  31. package/docs/apps/guides/test-your-app.md +1 -1
  32. package/docs/sdk/release-notes.md +8 -0
  33. package/docs/sdk/streaming.md +2 -0
  34. package/package.json +2 -2
  35. package/packages/crouter-identity/package.json +1 -1
  36. package/runtime.lock.json +162 -665
@@ -1 +1 @@
1
- var Ne=Object.defineProperty;var t=(n,o)=>Ne(n,"name",{value:o,configurable:!0});import{envInboxBetweenTurns as Fe,envModelIntent as Le,envNodeCwd as Be,envNodeId as Ue,envProfileId as $e,envScopes as We,scopeAllowed as Ke}from"../../shared/env.js";import{createServer as Ge}from"node:net";import{WebSocketServer as je,createWebSocketStream as qe}from"ws";import{BROKER_VIEW_PORT as Ve}from"./view-socket.js";import{existsSync as Re,readFileSync as Xe,unlinkSync as Te}from"node:fs";import{join as Ee}from"node:path";import{randomUUID as M}from"node:crypto";import{mark as He}from"../timing.js";import{SUBMIT_RESULT_TOOL as Ye}from"./structured-output.js";import{flushConversation as ke,installSessionManagerStorePatch as ze}from"../conversation-store/index.js";import{getAgentDir as J,ModelRegistry as Je,ModelRuntime as Qe}from"@earendil-works/pi-coding-agent";import{nodeRunDir as Ze,viewSocketPath as en}from"../canvas/paths.js";import{bindBrokerEventSource as nn}from"../events/source.js";import{emitEvent as b,flushBrokerLog as tn}from"../events/emit.js";import{operationIdContext as rn}from"../events/operation-id.js";import{findMissingPackagePaths as on}from"./package-health.js";import{installBrokerConfigSnapshot as sn}from"../config.js";import{FRONT_DOOR_ENV as an}from"./front-door.js";import{CANVAS_EXTENSIONS as oe,piInvocationToSdkConfig as dn}from"./launch.js";import{assertEngineVersion as ln,loadBrokerEngine as cn}from"./broker-sdk.js";import{initCrtrTheme as mn}from"./crtr-theme.js";import{resolveBundledPiPackageDir as un}from"./pi-cli.js";import{captureSessionChatCapabilities as fn}from"./command-surface.js";import{InMemoryCredentialStore as pn}from"@earendil-works/pi-ai";import{configureBrokerModelRuntime as gn}from"./broker/model-adapter.js";import{brokerApiUrl as vn,mutateBrokerFault as Q,recordBrokerTurn as Ce}from"./broker/daemon-ops.js";import{fetchRunAttachments as _n}from"./broker/attachments-fetch.js";import{modelRequestFromConfig as hn,expandModelCandidates as wn,parseModelSpec as Sn,unregisteredConcreteModel as bn}from"../model-routes.js";import{KNOWN_STREAM_WAIT_UI as yn}from"./stream-watchdog.js";import{probeOnline as Rn}from"./connectivity.js";import{CRTR_CYCLE_CUSTOM_TYPE as Tn,cycleAwareMessages as En,endedByAbort as kn}from"./session-cycles.js";import{turnErrorOf as Cn}from"../llm/refusal.js";import{projectTranscript as xn}from"./session-visibility.js";import{currentTurnVisibility as Mn}from"./turn-visibility.js";import{isUnknownModel as ie}from"./model-selection.js";import{BrokerClientRegistry as An}from"./broker/client-registry.js";import{ToolGroupTracker as Pn}from"./broker/tool-groups.js";import{TurnReplyTracker as On}from"./broker/turn-replies.js";import{onNodeNamed as Dn}from"./broker/node-named.js";import{FaultRetry as In,willDaemonRetry as Nn}from"./broker/fault-retry.js";import{RunTurnEnds as Fn}from"./broker/run-turn-end.js";import{createTurnAdmissionGate as Ln}from"./broker/turn-admission.js";import{BROKER_DISPOSE_AND_EXIT as Bn,BROKER_IDLE_TURN_START as Un,BROKER_RUN_TURN_BUSY as $n}from"./broker/held-deferred-inbox.js";import{prependRunDeferredMail as Wn,settleRunDeferredMail as Kn}from"./broker/run-deferred-mail.js";import{createRunTurnQueue as Gn}from"./broker/run-turn-queue.js";import{EventProjection as jn}from"./broker/event-projection.js";import{createFrameDispatchContext as qn,handleFrame as Vn}from"./broker/frame-dispatch.js";import{hydratePersistedAdvertisedCommandMessages as Xn,installAdvertisedCommandInvocationContract as Hn}from"./advertised-command-invocation.js";import{RebindDriver as Yn}from"./broker/rebind.js";import{beginRuntimeTeardown as zn}from"./broker/extension-abort.js";import{receiveNodeToken as Jn}from"./broker/node-token.js";import{serveCliRelay as Qn}from"./broker/cli-relay.js";import{recordBrokerRelease as Zn}from"./lifecycle.js";import{setTurnCauseSource as et}from"./broker/turn-cause.js";import{decodeKickoffOrigin as nt,KICKOFF_ORIGIN_ENV as tt}from"./stamp/protocol.js";import{announceMessageOrigin as rt}from"./stamp/channel.js";import{commitBrokerModel as ot,recordConversationWriteFault as xe}from"./broker/daemon-ops.js";import{bootExtensionState as it}from"./broker/boot-state.js";import{composerNames as st}from"./broker/frame-memory-refs.js";import{BROKER_READ_CAPS as at,FrameOverflowError as dt,normalizeSnapshotTail as lt}from"./broker-protocol.js";import{ReviewBoundaryNavigationError as Ar,REVIEW_BOUNDARY_NAVIGATION_CODE as Pr}from"./broker/frame-replies.js";function se(n){return new Je(n.modelRuntime)}t(se,"registryOf");function $(n,o){if(n==null||ie(n))return null;const s=o!==void 0&&o!==""&&o!=="off"?`:${o}`:"";return`${n.provider}/${n.id}${s}`}t($,"formatModelSpec");function ct(n,o){const s=$(n,void 0);return s===null?null:`${s}:${o===void 0||o===""?"off":o}`}t(ct,"formatExactModelSpec");const mt=12e4;let Z=null;import{brokerSignalClient as W}from"./broker/signal-stream.js";function ut(){W().stop();const n=Z;if(Z=null,n!==null)try{n.dispose()}catch{}}t(ut,"disposeActiveSession");function ft(n,o){const s=Ke(o,"crtr:act")?void 0:["bash"],f=n!==void 0&&n.length>0?[...new Set([...n,Ye])]:n===void 0?void 0:[...n];return{...f!==void 0?{tools:f}:{},...s!==void 0?{excludeTools:s}:{}}}t(ft,"brokerToolOptions");async function Cr(n,o=process.hrtime.bigint(),s=new Date().toISOString()){nn(n);const f=await Jn(),i=await Qn(n,f);process.env.CRTR_API_SOCKET=i.path,rn.fresh(()=>{b({level:"info",event:"broker.startup.cli_entry",ts:s,duration_ms:0,fields:{runtime_version:process.version,engine_version:null,model:null}}),b({level:"info",event:"broker.startup.module_loaded",duration_ms:Number(process.hrtime.bigint()-o)/1e6,fields:{runtime_version:process.version,engine_version:null,model:null}})});const a=new Set;let c=null;const d=t((e,r=null)=>{a.has(e)||(a.add(e),He(e,{duration_ms:Number(process.hrtime.bigint()-o)/1e6}),b({level:"info",event:e,duration_ms:Number(process.hrtime.bigint()-o)/1e6,fields:{runtime_version:process.version,engine_version:c,model:r}}))},"emitStartupMilestone");process.env[an]="1",process.env.CRTR_BROKER_PID=String(process.pid),delete process.env.TMUX_PANE,delete process.env.TMUX,ze(n);const v=await cn();ln(v.VERSION),c=v.VERSION;const w=Ze(n),E=Ee(w,"broker-launch.json"),S=JSON.parse(Xe(E,"utf8"));if(!S.configSnapshot)throw new Error("broker launch is missing its config snapshot");sn(S.configSnapshot);const g=dn(S);g.modelExact===!0?process.env.CRTR_MODEL_EXACT="1":delete process.env.CRTR_MODEL_EXACT;const y=await st(n);let R,A;const X=t(e=>{R!==void 0?R(e):A??=e},"onSignal");process.on("SIGTERM",()=>X("SIGTERM")),process.on("SIGINT",()=>X("SIGINT")),process.on("SIGUSR1",()=>{const e=process.env.CRTR_EXECUTION_ID;e!==void 0&&e!==""&&W().start(n,e)});let{session:K,services:P,resuming:de,runtime:G}=await _t(v,g);d("broker.startup.session_built");const k=(await it(n)).node.review_binding?.review_id,T=Fe();Z=K;const F=Rn;try{process.env.PI_PACKAGE_DIR=un(),mn(P.settingsManager.getTheme(),J(),!1)}catch(e){b({level:"error",event:"broker.theme.initialization_failed",error:e})}try{for(const e of on(J()))b({level:"warn",event:"broker.startup.warning",fields:{stage:"package_health",source:e.source,resolved_path:e.resolved}})}catch{}const j=en(n),_=new An(n),H=new Map,D=new Pn(n,e=>_.broadcast(e)),q=new On(n,e=>_.broadcast(e));Dn(e=>_.broadcast({type:"node_named",...e}));let I=!1,le=!1,Y,z,ce,m,V;const ee=new Fn;let L,me=t(()=>{},"notifyTurnAccepted"),ue=t(()=>{},"resetFrameState");const ne=Ln();let fe=t(()=>0,"pendingPromptDispatches"),pe;const B=Gn({runNode:T,session:t(()=>m.session(),"session"),isReplacing:t(()=>m.isReplacing(),"isReplacing"),pendingRebind:t(()=>m.pendingRebind(),"pendingRebind"),isAgentRunActive:t(()=>m.isAgentRunActive(),"isAgentRunActive"),pendingDispatches:t(()=>fe(),"pendingDispatches"),turnAdmission:ne,prependDeferred:t(e=>Wn(n,e),"prependDeferred"),onError:t(e=>b({level:"error",event:"broker.inbox.idle_prompt_failed",error:e}),"onError")});et(()=>m.session());const ge=B.drain;g.firstPrompt!==void 0&&g.firstPrompt!==""&&B.holdForKickoff(),globalThis[Un]=B.start,globalThis[$n]=B.busy,m=new Yn({nodeId:n,cfg:g,session:K,services:P,runtime:G,registry:_,toolGroups:D,turnReplies:q,transcriptMessages:t(e=>ae(e,k)?.map(({message:r})=>r)??e.sessionManager.buildSessionContext().messages,"transcriptMessages"),uiContext:t(()=>ce,"uiContext"),faultRetry:t(()=>V,"faultRetry"),projection:t(()=>L,"projection"),setActiveSession:t(e=>{if(Z=e,pe?.(),T||process.env.CRTR_RUN_ID){let r="stop",l,p=!1;pe=e.subscribe(h=>{if(h.type==="agent_start"&&(r="stop",l=void 0,p=!1,ee.started()),h.type==="agent_end"){const u=[...h.messages??[]].reverse().find(U=>U.role==="assistant");u&&(r=u.stopReason,l=kn(u)?void 0:Cn(u),p=u.role==="assistant"&&Nn(u,e.model?.contextWindow))}h.type==="agent_settled"&&(ee.settled(e.sessionManager,{stop_reason:r,...l?{error:l}:{}},p),T&&(ke().then(()=>Kn(n,e)).catch(async u=>{if(String(u?.message).startsWith("conversation_write_failed")){b({level:"error",event:"broker.exited",node_id:n,error:u,fields:{fault:"conversation_write_failed"}}),await xe(n,u),C("conversation_write_failed",1);return}b({level:"error",event:"broker.run_deferred_mail.settle_failed",error:u})}),queueMicrotask(()=>{m.session()===e&&ge()})))})}},"setActiveSession"),disposeAndExit:t((e,r)=>C(e,r),"disposeAndExit"),onSessionRebind:t(()=>{ue()},"onSessionRebind"),onReplacementIdle:t(()=>{T&&ge()},"onReplacementIdle"),reWelcomeAll:t(()=>te(),"reWelcomeAll")});const ve=t(async(e,r=!1)=>{const l=m.session(),p=$(l.model,l.thinkingLevel);if(p===null)return null;const h=await ot(n,{spec:p,...e!==void 0?{intent:e}:{},userSelected:r});return r&&(g.model=h.modelOverride,e!==void 0?(process.env.CRTR_MODEL_INTENT=`${e.family}/${e.strength}`,delete process.env.CRTR_MODEL_EXACT,g.modelExact=void 0):(delete process.env.CRTR_MODEL_INTENT,process.env.CRTR_MODEL_EXACT="1",g.modelExact=!0)),h.modelOverride},"persistModelChoice"),_e=t(async(e=!0,r,l=!1)=>{const p=m.session(),h=$(p.model,p.thinkingLevel),u=e?await ve(r,l):h;_.broadcast({type:"model_changed",model:ie(p.model)?void 0:p.model,spec:u})},"broadcastModelChanged");V=new In({nodeId:n,cfg:g,retryProbe:F,currentSession:t(()=>m.session(),"currentSession"),currentServices:t(()=>m.services(),"currentServices"),installedGeneration:t(()=>m.installedGeneration(),"installedGeneration"),registryOf:se,formatModelSpec:$,broadcastModelChanged:_e,turnAdmission:ne,reWelcomeAll:t(()=>te(),"reWelcomeAll"),...T?{turnEnds:ee}:{}});const he=t(()=>{V.rearmIfStranded().catch(e=>{b({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry re-arm failed: ${e instanceof Error?e.message:String(e)}`}})})},"rearmStrandedRetry");W().subscribe("resync",he),W().subscribe("fault-retry",he),L=new jn({registry:_,faultRetry:V,installedGeneration:t(()=>m.installedGeneration(),"installedGeneration"),currentSession:t(()=>m.session(),"currentSession"),notifyTurnAccepted:t(()=>me(),"notifyTurnAccepted"),emitStartupMilestone:d,formatModelSpec:$});const Ae=t(e=>{const r=m.session(),l=ae(r,k),p=lt(e.snapshotTail),h=l===void 0||p===void 0?l:l.slice(-p),u=h?.map(({message:N})=>N)??r.sessionManager.buildSessionContext().messages,U=D.summaries(),ye=h!==void 0&&p!==void 0?pt(u):void 0,Ie=ye===void 0?U:Object.fromEntries(Object.entries(U).filter(([N])=>ye.has(N)));return{messages:u,...h===void 0?{}:{messageIds:h.map(({id:N})=>N)},messageVisibility:h?.map(({visibility:N})=>N)??u.map(()=>"visible"),turnVisibility:Mn(),stats:r.getSessionStats(),state:{model:ie(r.model)?void 0:r.model,thinkingLevel:r.thinkingLevel,isStreaming:r.isStreaming,isCompacting:r.isCompacting,steeringMode:r.steeringMode,followUpMode:r.followUpMode,sessionFile:r.sessionFile,sessionId:r.sessionId,sessionName:r.sessionName,autoCompactionEnabled:r.autoCompactionEnabled,messageCount:r.messages.length,pendingMessageCount:r.pendingMessageCount},display:{..._.displaySnapshot()},toolGroupSummaries:Ie,turnReplies:q.snapshot(),workingActivity:L.currentWorkingActivity(),queued:{steering:[...r.getSteeringMessages()],followUp:[...r.getFollowUpMessages()]}}},"buildSnapshot"),we=t(e=>{const r=e.role==="controller"?H.values().next().value:void 0;_.sendFrame(e,{type:"welcome",snapshot:Ae(e),role:e.role,pending_dialog:r!==void 0?r.request:null,agentDir:J()},!0)},"sendWelcome"),Pe=t(e=>{const r=[...H.values()];for(let l=1;l<r.length;l++)_.sendFrame(e,r[l].request)},"replayExtraPendingDialogsTo"),te=t(()=>{try{const e=m.session();q.seed(ae(e,k)?.map(({message:r})=>r)??e.sessionManager.buildSessionContext().messages)}catch(e){b({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`turn reply reseed failed: ${String(e)}`}})}_.forEachHelloed(we)},"reWelcomeAll"),Se=t(async e=>{if(I)return;I=!0;const r=W().latestNode;r!==void 0&&(r.intent==="idle-release"||r.status==="done")&&Zn(n,process.env.CRTR_EXECUTION_ID??null),W().stop(),e===0&&await ke().catch(async l=>{e=1,b({level:"error",event:"broker.exited",node_id:n,error:l,fields:{fault:"conversation_write_failed"}}),await xe(n,l)}),D.persistPendingSummary(),L.dispose(),ut();try{Y?.close(),z?.close()}catch{}i.close(),_.dispose();try{Re(j)&&Te(j)}catch{}_.clearAttachState(),await tn(),process.exit(e)},"completeExit"),C=t((e,r=0)=>{if(I)return;const l=e==="shutdown-hook"||e==="shutdown-after-settle";if(l&&D.finishOpenToolGroup(!0),l&&(D.hasSummaryTasks()||q.hasTasks())){if(le)return;le=!0,Promise.allSettled([D.waitForSummaryTasks(),q.waitForTasks()]).finally(()=>Se(r));return}Se(r)},"disposeAndExit");globalThis[Bn]=C,ce=ht({writable:t(()=>_.writableClients(),"writable"),forward:t((e,r)=>_.sendFrame(e,r),"forward"),pending:H,broadcast:t(e=>_.broadcastUi(e),"broadcast"),onKnownStreamWait:t(e=>L.knownStreamWait(e),"onKnownStreamWait")}),m.setRebindSession(),await m.enqueueRebind(K,P),d("broker.startup.extensions_bound");const x=qn({nodeId:n,registry:_,toolGroups:D,projection:L,rebind:m,boundaryReviewId:k,pendingDialogs:H,sendWelcome:we,replayExtraPendingDialogsTo:Pe,reWelcomeAll:te,disposeAndExit:C,persistModelChoice:ve,broadcastModelChanged:_e,registryOf:se,formatModelSpec:$,formatExactModelSpec:ct,turnAdmission:ne,memoryInventory:y,runNode:T,onKickoffAdmitted:B.releaseKickoff});me=x.notifyTurnAccepted,ue=x.resetForSession,fe=x.pendingPromptDispatches;const Oe=2500;let re=!1;const De=t(async e=>{if(I||re)return;if(re=!0,m.isAgentRunActive()||m.session().isStreaming||x.pendingPromptDispatches()>0){const l=Date.now()+Oe;for(;x.pendingPromptDispatches()>0&&!m.session().isStreaming&&Date.now()<l;)await new Promise(p=>setTimeout(p,25));if(m.session().isStreaming){const p=l-Date.now();if(p>0){let h;try{zn(),await Promise.race([m.session().abort().then(()=>!0,()=>!1),new Promise(U=>{h=setTimeout(()=>U(!1),p)})])&&await Ce(n,{event:"closed",end_reason:"teardown-aborted"})}finally{h!==void 0&&clearTimeout(h)}}}else(x.pendingPromptDispatches()>0||m.isAgentRunActive())&&await Ce(n,{event:"interrupted"})}C(e)},"abortAndExit");if(R=t(e=>{De(e)},"signalTeardown"),A!==void 0){R(A);return}try{Re(j)&&Te(j)}catch{}const be=t(e=>{const r=_.create(e);e.on("data",p=>{let h;try{h=r.decoder.push(p)}catch(u){u instanceof dt?(_.sendFrame(r,{type:"error",code:"frame_overflow",message:u.message}),_.dropClient(r,`frame overflow: ${u.message}`)):_.dropClient(r,`decoder error: ${String(u)}`);return}for(const u of h)try{Vn(x,r,u)}catch{}});const l=t(()=>{_.removeClient(r)},"drop");e.on("close",l),e.on("error",()=>{})},"serveViewer");Y=Ge(be),vn()!==void 0&&(z=new je({host:"::",port:Ve,path:"/view",maxPayload:at.maxLineBytes}),z.on("connection",e=>be(qe(e))),z.on("error",e=>{I||(b({level:"error",event:"broker.socket.failed",error:e}),C("socket-error",1))})),Y.on("error",e=>{I||(b({level:"error",event:"broker.socket.failed",error:e}),C("socket-error",1))}),Y.listen(j,async()=>{Promise.all([Q(n,{op:"clear",link:"viewer\u2194broker"}),Q(n,{op:"clear",link:"relay\u2194broker"})]).catch(r=>b({level:"error",event:"broker.daemon.operation.failed",node_id:n,error:r,fields:{operation:"broker_socket_bound_fault_clear"}}));let e;try{e=await V.recoverOnStartup()}catch(r){b({level:"error",event:"broker.daemon.operation.failed",node_id:n,error:r,fields:{operation:"recovery_startup"}}),C("recovery-startup-failed",1);return}if(!(I||re))if(e==="none"&&g.firstPrompt!==void 0&&g.firstPrompt!==""){const r=nt(process.env[tt]);r!==null&&rt(g.firstPrompt,r);try{await _n(n)}catch(p){b({level:"error",event:"broker.daemon.operation.failed",node_id:n,error:p,fields:{operation:"attachments_fetch"}}),C("attachments-fetch-failed",1);return}const l=process.env.CRTR_KICKOFF_MESSAGE_IDS;x.kickoff(g.firstPrompt,l?JSON.parse(l):[])}else B.releaseKickoff()}),_.persistAttachState()}t(Cr,"runBroker");function pt(n){const o=new Set;for(const s of n)if(!(s.role!=="assistant"||!Array.isArray(s.content)))for(const f of s.content)f!==null&&typeof f=="object"&&f.type==="toolCall"&&typeof f.id=="string"&&o.add(f.id);return o}t(pt,"toolCallIds");function ae(n,o){const s=n.sessionManager,f=En(s);return f!==void 0&&Xn(f.map(({message:i})=>i)),f===void 0?void 0:xn(f,{boundaryReviewId:o,sessionEntries:s.getEntries?.()})}t(ae,"snapshotMessages");function Me(){const n=Ue();if(n===void 0||n==="")throw new Error("broker launch is missing CRTR_NODE_ID");return n}t(Me,"currentBrokerNodeId");async function gt(n,o){const s=hn(o.model,Le(),o.modelExact===!0);if(s===void 0)return;const f=se(n),i={cwd:Be()??o.cwd,profileId:$e()},a=await bn(s,f,i);if(a!==void 0)throw await Q(Me(),{op:"record",input:{link:"crtr\u2192pi",op:"model resolution at launch",kind:"model-not-found",retry:{disposition:"fatal"},message:`model '${a.request.spec}' is not registered in the serving model registry`}}),new Error(`[broker] FATAL: model '${a.request.spec}' is not registered in the serving model registry`);const c=await wn(s,i.cwd,i.profileId),d=Sn(s.spec);for(const v of c){const w=f.find(v.providerId,v.modelId);if(w===void 0)continue;const S=d?.providerId===v.providerId&&d.modelId===v.modelId?d.thinkingLevel??(v.thinkingLevel===void 0?void 0:"off"):v.thinkingLevel;return{model:w,thinkingLevel:S}}if(o.modelExact===!0){const v=c.map(w=>`'${w.providerId}/${w.modelId}' unregistered`).join("; ");throw await Q(Me(),{op:"record",input:{link:"crtr\u2192pi",op:"model resolution at launch",kind:"model-not-found",retry:{disposition:"fatal"},message:`exact-consult model '${o.model}' is unavailable \u2014 refusing to substitute (${v})`}}),new Error(`[broker] FATAL: exact-consult model '${o.model}' is unavailable`)}b({level:"warn",event:"broker.model.none_viable",fields:{rejected_candidates:c.map(v=>({provider:v.providerId,model:v.modelId,reason:"unregistered"})).slice(0,16)}})}t(gt,"resolveLaunchModel");function vt(n){let o=!0;return{runtime:new Proxy(n,{get(f,i,a){return o&&i==="getAvailableSnapshot"?()=>[]:o&&i==="getAvailable"?async()=>[]:o&&i==="getModel"?()=>{}:Reflect.get(f,i,a)}}),reveal:t(()=>{o=!1},"reveal")}}t(vt,"modelLessRegistry");async function _t(n,o){const s=J(),f=t(async S=>{const g=await Qe.create({credentials:new pn,modelsPath:Ee(S.agentDir,"models.json")});gn(g);const y=await n.createAgentSessionServices({cwd:S.cwd,agentDir:S.agentDir,modelRuntime:g,resourceLoaderOptions:{additionalExtensionPaths:o.extensionPaths,appendSystemPrompt:o.appendSystemPromptPath!==void 0?[o.appendSystemPromptPath]:void 0,noContextFiles:!0,extensionsOverride:t(O=>({...O,extensions:O.extensions.filter(k=>!/[/\\]provider-rotation\.(?:ts|js)$/.test(k.path))}),"extensionsOverride")}}),R=y.resourceLoader.getExtensions();if(o.extensionPaths.some(O=>oe.includes(O))){const O=new Set(R.extensions.map(T=>T.path)),k=oe.filter(T=>!O.has(T));if(k.length>0){const T=R.errors.filter(F=>oe.includes(F.path)).map(F=>`${F.path}: ${F.error}`).join("; ");throw new Error(`[broker] FATAL: mandatory canvas extensions failed to load: ${k.join(", ")}${T===""?"":` (${T})`}`)}}y.settingsManager.getRetryEnabled=()=>!1;const A=await gt(y,o),X=A?.model,K=A?.thinkingLevel,P=A===void 0?vt(y.modelRuntime):void 0,de=P!==void 0?{...y,modelRuntime:P.runtime}:y,G=await n.createAgentSessionFromServices({services:de,sessionManager:S.sessionManager,model:X,thinkingLevel:K,...ft(o.tools,We()),sessionStartEvent:S.sessionStartEvent});return Hn(G.session),fn(G.session),P?.reveal(),{...G,services:y}},"buildForManager"),i=o.resumeSessionPath,a=o.forkFrom!==void 0&&o.forkFrom!=="";if(!a&&(i===void 0||i==="")&&o.resumeSessionId!==void 0&&o.resumeSessionId!=="")throw new Error(`[broker] resume requires a session .jsonl PATH; got only a bare id '${o.resumeSessionId}' (pi_session_file was never captured for this node)`);const c=!a&&i!==void 0&&i!=="",d=c&&o.newCycle===!0,v=c&&!d;let w;if(a)w=await n.SessionManager.forkFrom(o.forkFrom,o.cwd);else if(v||d){if(w=await n.SessionManager.open(i),d){const S=w.getLeafId();w.resetLeaf(),w.appendCustomEntry(Tn,{cycle:Number(process.env.CRTR_CYCLES),fromLeaf:S})}}else w=await n.SessionManager.create(o.cwd);if(n.createAgentSessionRuntime!==void 0){const S=t(async y=>{const R=await f({cwd:y.cwd,agentDir:y.agentDir,sessionManager:y.sessionManager,sessionStartEvent:y.sessionStartEvent});return{...R,diagnostics:R.services.diagnostics??[]}},"factory"),g=await n.createAgentSessionRuntime(S,{cwd:o.cwd,agentDir:s,sessionManager:w});return{session:g.session,services:g.services,resuming:v,runtime:g}}const E=await f({cwd:o.cwd,agentDir:s,sessionManager:w});return{session:E.session,services:E.services,resuming:v,runtime:void 0}}t(_t,"buildBrokerSession");function ht(n){const o=t((i,a,c,d)=>{if(d?.signal?.aborted)return Promise.resolve(i);const v=n.writable();return v.length===0?Promise.resolve(i):new Promise(w=>{let E;const S=t(()=>{E!==void 0&&clearTimeout(E),d?.signal?.removeEventListener("abort",g),n.pending.delete(a.id)},"cleanup"),g=t(()=>{S(),n.broadcast({type:"extension_ui_dismiss",id:a.id}),w(i)},"onAbort");d?.signal?.addEventListener("abort",g,{once:!0});const y=d?.timeout!==void 0?d.timeout:mt;E=setTimeout(()=>{S(),n.broadcast({type:"extension_ui_dismiss",id:a.id}),w(i)},y),typeof E.unref=="function"&&E.unref(),n.pending.set(a.id,{request:a,resolve:t(R=>{S(),n.broadcast({type:"extension_ui_dismiss",id:a.id}),w(c(R))},"resolve")});for(const R of v)n.forward(R,a)})},"dialogPromise"),s=t(()=>{},"noop");return{[yn]:n.onKnownStreamWait??(i=>()=>{}),get theme(){const i=globalThis[Symbol.for("@earendil-works/pi-coding-agent:theme")];if(i==null)throw new Error("Theme not initialized. Call initTheme() first.");return i},select:t((i,a,c)=>o(void 0,{type:"extension_ui_request",id:M(),method:"select",title:i,options:a,timeout:c?.timeout},d=>"cancelled"in d&&d.cancelled?void 0:"value"in d?d.value:void 0,c),"select"),confirm:t((i,a,c)=>o(!1,{type:"extension_ui_request",id:M(),method:"confirm",title:i,message:a,timeout:c?.timeout},d=>"cancelled"in d&&d.cancelled?!1:"confirmed"in d?d.confirmed:!1,c),"confirm"),input:t((i,a,c)=>o(void 0,{type:"extension_ui_request",id:M(),method:"input",title:i,placeholder:a,timeout:c?.timeout},d=>"cancelled"in d&&d.cancelled?void 0:"value"in d?d.value:void 0,c),"input"),editor:t((i,a)=>o(void 0,{type:"extension_ui_request",id:M(),method:"editor",title:i,prefill:a},c=>"cancelled"in c&&c.cancelled?void 0:"value"in c?c.value:void 0),"editor"),setStatus:t((i,a)=>n.broadcast({type:"extension_ui_request",id:M(),method:"setStatus",statusKey:i,statusText:a}),"setStatus"),setWidget:t((i,a,c)=>{typeof a!="function"&&n.broadcast({type:"extension_ui_request",id:M(),method:"setWidget",widgetKey:i,widgetLines:a,widgetPlacement:c?.placement})},"setWidget"),setTitle:t(i=>n.broadcast({type:"extension_ui_request",id:M(),method:"setTitle",title:i}),"setTitle"),notify:t((i,a)=>{const c=a==="warn"?"warning":a;n.broadcast({type:"extension_ui_request",id:M(),method:"notify",message:i,notifyType:c})},"notify"),onTerminalInput:t(()=>s,"onTerminalInput"),setWorkingMessage:s,setWorkingVisible:s,setWorkingIndicator:s,setHiddenThinkingLabel:s,setFooter:s,setHeader:s,custom:t(()=>Promise.resolve(void 0),"custom"),pasteToEditor:s,setEditorText:s,getEditorText:t(()=>"","getEditorText"),addAutocompleteProvider:s,setEditorComponent:s,getEditorComponent:t(()=>{},"getEditorComponent"),getAllThemes:t(()=>[],"getAllThemes"),getTheme:t(()=>{},"getTheme"),setTheme:s,getToolsExpanded:t(()=>!1,"getToolsExpanded"),setToolsExpanded:s}}t(ht,"makeBrokerUiContext");export{Pr as REVIEW_BOUNDARY_NAVIGATION_CODE,Ar as ReviewBoundaryNavigationError,ft as brokerToolOptions,_t as buildBrokerSession,ut as disposeActiveSession,ht as makeBrokerUiContext,Cr as runBroker,ae as snapshotMessages};
1
+ var Fe=Object.defineProperty;var n=(t,o)=>Fe(t,"name",{value:o,configurable:!0});import{envInboxBetweenTurns as Le,envModelIntent as Be,envNodeCwd as Ue,envNodeId as $e,envProfileId as Ke,envScopes as We,scopeAllowed as Ge}from"../../shared/env.js";import{createServer as je}from"node:net";import{WebSocketServer as qe,createWebSocketStream as Ve}from"ws";import{BROKER_VIEW_PORT as Xe}from"./view-socket.js";import{existsSync as Te,readFileSync as He,unlinkSync as Ee}from"node:fs";import{join as ke}from"node:path";import{randomUUID as A}from"node:crypto";import{mark as Ye}from"../timing.js";import{SUBMIT_RESULT_TOOL as ze}from"./structured-output.js";import{flushConversation as Ce,installSessionManagerStorePatch as Je}from"../conversation-store/index.js";import{getAgentDir as J,ModelRegistry as Qe,ModelRuntime as Ze}from"@earendil-works/pi-coding-agent";import{nodeRunDir as et,viewSocketPath as tt}from"../canvas/paths.js";import{bindBrokerEventSource as nt}from"../events/source.js";import{emitEvent as b,flushBrokerLog as rt}from"../events/emit.js";import{operationIdContext as ot}from"../events/operation-id.js";import{findMissingPackagePaths as it}from"./package-health.js";import{installBrokerConfigSnapshot as st}from"../config.js";import{FRONT_DOOR_ENV as at}from"./front-door.js";import{CANVAS_EXTENSIONS as oe,piInvocationToSdkConfig as dt}from"./launch.js";import{assertEngineVersion as lt,loadBrokerEngine as ct}from"./broker-sdk.js";import{initCrtrTheme as mt}from"./crtr-theme.js";import{resolveBundledPiPackageDir as ut}from"./pi-cli.js";import{captureSessionChatCapabilities as ft}from"./command-surface.js";import{InMemoryCredentialStore as pt}from"@earendil-works/pi-ai";import{configureBrokerModelRuntime as gt,refusalRetryAt as vt}from"./broker/model-adapter.js";import{brokerApiUrl as _t,mutateBrokerFault as Q,recordBrokerTurn as xe}from"./broker/daemon-ops.js";import{fetchRunAttachments as ht}from"./broker/attachments-fetch.js";import{modelRequestFromConfig as wt,expandModelCandidates as St,parseModelSpec as bt,unregisteredConcreteModel as yt}from"../model-routes.js";import{KNOWN_STREAM_WAIT_UI as Rt}from"./stream-watchdog.js";import{probeOnline as Tt}from"./connectivity.js";import{CRTR_CYCLE_CUSTOM_TYPE as Et,cycleAwareMessages as kt,endedByAbort as Ct}from"./session-cycles.js";import{turnErrorOf as xt}from"../llm/refusal.js";import{projectTranscript as At}from"./session-visibility.js";import{currentTurnVisibility as Mt}from"./turn-visibility.js";import{isUnknownModel as ie}from"./model-selection.js";import{BrokerClientRegistry as Pt}from"./broker/client-registry.js";import{ToolGroupTracker as Ot}from"./broker/tool-groups.js";import{TurnReplyTracker as Dt}from"./broker/turn-replies.js";import{onNodeNamed as It}from"./broker/node-named.js";import{FaultRetry as Nt,willDaemonRetry as Ft}from"./broker/fault-retry.js";import{RunTurnEnds as Lt}from"./broker/run-turn-end.js";import{createTurnAdmissionGate as Bt}from"./broker/turn-admission.js";import{BROKER_DISPOSE_AND_EXIT as Ut,BROKER_IDLE_TURN_START as $t,BROKER_RUN_TURN_BUSY as Kt,BROKER_RUN_STEER as Wt}from"./broker/held-deferred-inbox.js";import{createRunSteerBridge as Gt}from"./broker/run-steer.js";import{prependRunDeferredMail as jt,settleRunDeferredMail as qt}from"./broker/run-deferred-mail.js";import{createRunTurnQueue as Vt}from"./broker/run-turn-queue.js";import{EventProjection as Xt}from"./broker/event-projection.js";import{createFrameDispatchContext as Ht,handleFrame as Yt}from"./broker/frame-dispatch.js";import{hydratePersistedAdvertisedCommandMessages as zt,installAdvertisedCommandInvocationContract as Jt}from"./advertised-command-invocation.js";import{RebindDriver as Qt}from"./broker/rebind.js";import{beginRuntimeTeardown as Zt}from"./broker/extension-abort.js";import{receiveNodeToken as en}from"./broker/node-token.js";import{serveCliRelay as tn}from"./broker/cli-relay.js";import{recordBrokerRelease as nn}from"./lifecycle.js";import{setTurnCauseSource as rn}from"./broker/turn-cause.js";import{decodeKickoffOrigin as on,KICKOFF_ORIGIN_ENV as sn}from"./stamp/protocol.js";import{announceMessageOrigin as an}from"./stamp/channel.js";import{commitBrokerModel as dn,recordConversationWriteFault as Ae}from"./broker/daemon-ops.js";import{bootExtensionState as ln}from"./broker/boot-state.js";import{composerNames as cn}from"./broker/frame-memory-refs.js";import{BROKER_READ_CAPS as mn,FrameOverflowError as un,normalizeSnapshotTail as fn}from"./broker-protocol.js";import{ReviewBoundaryNavigationError as Nr,REVIEW_BOUNDARY_NAVIGATION_CODE as Fr}from"./broker/frame-replies.js";function se(t){return new Qe(t.modelRuntime)}n(se,"registryOf");function $(t,o){if(t==null||ie(t))return null;const s=o!==void 0&&o!==""&&o!=="off"?`:${o}`:"";return`${t.provider}/${t.id}${s}`}n($,"formatModelSpec");function pn(t,o){const s=$(t,void 0);return s===null?null:`${s}:${o===void 0||o===""?"off":o}`}n(pn,"formatExactModelSpec");const gn=12e4;let Z=null;import{brokerSignalClient as K}from"./broker/signal-stream.js";function vn(){K().stop();const t=Z;if(Z=null,t!==null)try{t.dispose()}catch{}}n(vn,"disposeActiveSession");function _n(t,o){const s=Ge(o,"crtr:act")?void 0:["bash"],f=t!==void 0&&t.length>0?[...new Set([...t,ze])]:t===void 0?void 0:[...t];return{...f!==void 0?{tools:f}:{},...s!==void 0?{excludeTools:s}:{}}}n(_n,"brokerToolOptions");async function Or(t,o=process.hrtime.bigint(),s=new Date().toISOString()){nt(t);const f=await en(),i=await tn(t,f);process.env.CRTR_API_SOCKET=i.path,ot.fresh(()=>{b({level:"info",event:"broker.startup.cli_entry",ts:s,duration_ms:0,fields:{runtime_version:process.version,engine_version:null,model:null}}),b({level:"info",event:"broker.startup.module_loaded",duration_ms:Number(process.hrtime.bigint()-o)/1e6,fields:{runtime_version:process.version,engine_version:null,model:null}})});const a=new Set;let c=null;const d=n((e,r=null)=>{a.has(e)||(a.add(e),Ye(e,{duration_ms:Number(process.hrtime.bigint()-o)/1e6}),b({level:"info",event:e,duration_ms:Number(process.hrtime.bigint()-o)/1e6,fields:{runtime_version:process.version,engine_version:c,model:r}}))},"emitStartupMilestone");process.env[at]="1",process.env.CRTR_BROKER_PID=String(process.pid),delete process.env.TMUX_PANE,delete process.env.TMUX,Je(t);const v=await ct();lt(v.VERSION),c=v.VERSION;const w=et(t),E=ke(w,"broker-launch.json"),S=JSON.parse(He(E,"utf8"));if(!S.configSnapshot)throw new Error("broker launch is missing its config snapshot");st(S.configSnapshot);const g=dt(S);g.modelExact===!0?process.env.CRTR_MODEL_EXACT="1":delete process.env.CRTR_MODEL_EXACT;const y=await cn(t);let R,M;const X=n(e=>{R!==void 0?R(e):M??=e},"onSignal");process.on("SIGTERM",()=>X("SIGTERM")),process.on("SIGINT",()=>X("SIGINT")),process.on("SIGUSR1",()=>{const e=process.env.CRTR_EXECUTION_ID;e!==void 0&&e!==""&&K().start(t,e)});let{session:W,services:P,resuming:de,runtime:G}=await bn(v,g);d("broker.startup.session_built");const k=(await ln(t)).node.review_binding?.review_id,T=Le();Z=W;const L=Tt;try{process.env.PI_PACKAGE_DIR=ut(),mt(P.settingsManager.getTheme(),J(),!1)}catch(e){b({level:"error",event:"broker.theme.initialization_failed",error:e})}try{for(const e of it(J()))b({level:"warn",event:"broker.startup.warning",fields:{stage:"package_health",source:e.source,resolved_path:e.resolved}})}catch{}const j=tt(t),_=new Pt(t),H=new Map,D=new Ot(t,e=>_.broadcast(e)),q=new Dt(t,e=>_.broadcast(e));It(e=>_.broadcast({type:"node_named",...e}));let I=!1,le=!1,Y,z,ce,u,V;const ee=new Lt;let B,me=n(()=>{},"notifyTurnAccepted"),ue=n(()=>{},"resetFrameState");const te=Bt();let fe=n(()=>0,"pendingPromptDispatches"),pe;const N=Vt({runNode:T,session:n(()=>u.session(),"session"),isReplacing:n(()=>u.isReplacing(),"isReplacing"),pendingRebind:n(()=>u.pendingRebind(),"pendingRebind"),isAgentRunActive:n(()=>u.isAgentRunActive(),"isAgentRunActive"),pendingDispatches:n(()=>fe(),"pendingDispatches"),turnAdmission:te,prependDeferred:n(e=>jt(t,e),"prependDeferred"),onError:n(e=>b({level:"error",event:"broker.inbox.idle_prompt_failed",error:e}),"onError")}),ge=Gt(N.steerable);globalThis[Wt]=ge,rn(()=>u.session());const ve=N.drain;g.firstPrompt!==void 0&&g.firstPrompt!==""&&N.holdForKickoff(),globalThis[$t]=N.start,globalThis[Kt]=N.busy,u=new Qt({nodeId:t,cfg:g,session:W,services:P,runtime:G,registry:_,toolGroups:D,turnReplies:q,runSteer:ge,runNode:T,transcriptMessages:n(e=>ae(e,k)?.map(({message:r})=>r)??e.sessionManager.buildSessionContext().messages,"transcriptMessages"),uiContext:n(()=>ce,"uiContext"),faultRetry:n(()=>V,"faultRetry"),projection:n(()=>B,"projection"),setActiveSession:n(e=>{if(Z=e,pe?.(),T||process.env.CRTR_RUN_ID){let r="stop",l,p=!1;pe=e.subscribe(h=>{if(h.type==="agent_start"&&(r="stop",l=void 0,p=!1,ee.started()),h.type==="agent_end"){const m=[...h.messages??[]].reverse().find(U=>U.role==="assistant");m&&(r=m.stopReason,l=Ct(m)?void 0:xt(m,vt(m)),p=m.role==="assistant"&&Ft(m,e.model?.contextWindow))}h.type==="agent_settled"&&(ee.settled(e.sessionManager,{stop_reason:r,...l?{error:l}:{}},p),T&&(Ce().then(()=>qt(t,e)).catch(async m=>{if(String(m?.message).startsWith("conversation_write_failed")){b({level:"error",event:"broker.exited",node_id:t,error:m,fields:{fault:"conversation_write_failed"}}),await Ae(t,m),C("conversation_write_failed",1);return}b({level:"error",event:"broker.run_deferred_mail.settle_failed",error:m})}),queueMicrotask(()=>{u.session()===e&&ve()})))})}},"setActiveSession"),disposeAndExit:n((e,r)=>C(e,r),"disposeAndExit"),onSessionRebind:n(()=>{ue()},"onSessionRebind"),onReplacementIdle:n(()=>{T&&ve()},"onReplacementIdle"),reWelcomeAll:n(()=>ne(),"reWelcomeAll")});const _e=n(async(e,r=!1)=>{const l=u.session(),p=$(l.model,l.thinkingLevel);if(p===null)return null;const h=await dn(t,{spec:p,...e!==void 0?{intent:e}:{},userSelected:r});return r&&(g.model=h.modelOverride,e!==void 0?(process.env.CRTR_MODEL_INTENT=`${e.family}/${e.strength}`,delete process.env.CRTR_MODEL_EXACT,g.modelExact=void 0):(delete process.env.CRTR_MODEL_INTENT,process.env.CRTR_MODEL_EXACT="1",g.modelExact=!0)),h.modelOverride},"persistModelChoice"),he=n(async(e=!0,r,l=!1)=>{const p=u.session(),h=$(p.model,p.thinkingLevel),m=e?await _e(r,l):h;_.broadcast({type:"model_changed",model:ie(p.model)?void 0:p.model,spec:m})},"broadcastModelChanged");V=new Nt({nodeId:t,cfg:g,retryProbe:L,currentSession:n(()=>u.session(),"currentSession"),currentServices:n(()=>u.services(),"currentServices"),installedGeneration:n(()=>u.installedGeneration(),"installedGeneration"),registryOf:se,formatModelSpec:$,broadcastModelChanged:he,turnAdmission:te,reWelcomeAll:n(()=>ne(),"reWelcomeAll"),...T?{turnEnds:ee}:{}});const we=n(()=>{V.rearmIfStranded().catch(e=>{b({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry re-arm failed: ${e instanceof Error?e.message:String(e)}`}})})},"rearmStrandedRetry");K().subscribe("resync",we),K().subscribe("fault-retry",we),B=new Xt({registry:_,faultRetry:V,installedGeneration:n(()=>u.installedGeneration(),"installedGeneration"),currentSession:n(()=>u.session(),"currentSession"),notifyTurnAccepted:n(()=>me(),"notifyTurnAccepted"),emitStartupMilestone:d,formatModelSpec:$});const Pe=n(e=>{const r=u.session(),l=ae(r,k),p=fn(e.snapshotTail),h=l===void 0||p===void 0?l:l.slice(-p),m=h?.map(({message:F})=>F)??r.sessionManager.buildSessionContext().messages,U=D.summaries(),Re=h!==void 0&&p!==void 0?hn(m):void 0,Ne=Re===void 0?U:Object.fromEntries(Object.entries(U).filter(([F])=>Re.has(F)));return{messages:m,...h===void 0?{}:{messageIds:h.map(({id:F})=>F)},messageVisibility:h?.map(({visibility:F})=>F)??m.map(()=>"visible"),turnVisibility:Mt(),stats:r.getSessionStats(),state:{model:ie(r.model)?void 0:r.model,thinkingLevel:r.thinkingLevel,isStreaming:r.isStreaming,isCompacting:r.isCompacting,steeringMode:r.steeringMode,followUpMode:r.followUpMode,sessionFile:r.sessionFile,sessionId:r.sessionId,sessionName:r.sessionName,autoCompactionEnabled:r.autoCompactionEnabled,messageCount:r.messages.length,pendingMessageCount:r.pendingMessageCount},display:{..._.displaySnapshot()},toolGroupSummaries:Ne,turnReplies:q.snapshot(),workingActivity:B.currentWorkingActivity(),queued:{steering:[...r.getSteeringMessages()],followUp:[...r.getFollowUpMessages()]}}},"buildSnapshot"),Se=n(e=>{const r=e.role==="controller"?H.values().next().value:void 0;_.sendFrame(e,{type:"welcome",snapshot:Pe(e),role:e.role,pending_dialog:r!==void 0?r.request:null,agentDir:J()},!0)},"sendWelcome"),Oe=n(e=>{const r=[...H.values()];for(let l=1;l<r.length;l++)_.sendFrame(e,r[l].request)},"replayExtraPendingDialogsTo"),ne=n(()=>{try{const e=u.session();q.seed(ae(e,k)?.map(({message:r})=>r)??e.sessionManager.buildSessionContext().messages)}catch(e){b({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`turn reply reseed failed: ${String(e)}`}})}_.forEachHelloed(Se)},"reWelcomeAll"),be=n(async e=>{if(I)return;I=!0;const r=K().latestNode;r!==void 0&&(r.intent==="idle-release"||r.status==="done")&&nn(t,process.env.CRTR_EXECUTION_ID??null),K().stop(),e===0&&await Ce().catch(async l=>{e=1,b({level:"error",event:"broker.exited",node_id:t,error:l,fields:{fault:"conversation_write_failed"}}),await Ae(t,l)}),D.persistPendingSummary(),B.dispose(),vn();try{Y?.close(),z?.close()}catch{}i.close(),_.dispose();try{Te(j)&&Ee(j)}catch{}_.clearAttachState(),await rt(),process.exit(e)},"completeExit"),C=n((e,r=0)=>{if(I)return;const l=e==="shutdown-hook"||e==="shutdown-after-settle";if(l&&D.finishOpenToolGroup(!0),l&&(D.hasSummaryTasks()||q.hasTasks())){if(le)return;le=!0,Promise.allSettled([D.waitForSummaryTasks(),q.waitForTasks()]).finally(()=>be(r));return}be(r)},"disposeAndExit");globalThis[Ut]=C,ce=yn({writable:n(()=>_.writableClients(),"writable"),forward:n((e,r)=>_.sendFrame(e,r),"forward"),pending:H,broadcast:n(e=>_.broadcastUi(e),"broadcast"),onKnownStreamWait:n(e=>B.knownStreamWait(e),"onKnownStreamWait")}),u.setRebindSession(),await u.enqueueRebind(W,P),d("broker.startup.extensions_bound");const x=Ht({nodeId:t,registry:_,toolGroups:D,projection:B,rebind:u,boundaryReviewId:k,pendingDialogs:H,sendWelcome:Se,replayExtraPendingDialogsTo:Oe,reWelcomeAll:ne,disposeAndExit:C,persistModelChoice:_e,broadcastModelChanged:he,registryOf:se,formatModelSpec:$,formatExactModelSpec:pn,turnAdmission:te,memoryInventory:y,runNode:T,onKickoffAdmitted:N.releaseKickoff});me=x.notifyTurnAccepted,ue=x.resetForSession,fe=x.pendingPromptDispatches;const De=2500;let re=!1;const Ie=n(async e=>{if(I||re)return;if(re=!0,u.isAgentRunActive()||u.session().isStreaming||x.pendingPromptDispatches()>0){const l=Date.now()+De;for(;x.pendingPromptDispatches()>0&&!u.session().isStreaming&&Date.now()<l;)await new Promise(p=>setTimeout(p,25));if(u.session().isStreaming){const p=l-Date.now();if(p>0){let h;try{Zt(),await Promise.race([u.session().abort().then(()=>!0,()=>!1),new Promise(U=>{h=setTimeout(()=>U(!1),p)})])&&await xe(t,{event:"closed",end_reason:"teardown-aborted"})}finally{h!==void 0&&clearTimeout(h)}}}else(x.pendingPromptDispatches()>0||u.isAgentRunActive())&&await xe(t,{event:"interrupted"})}C(e)},"abortAndExit");if(R=n(e=>{Ie(e)},"signalTeardown"),M!==void 0){R(M);return}try{Te(j)&&Ee(j)}catch{}const ye=n(e=>{const r=_.create(e);e.on("data",p=>{let h;try{h=r.decoder.push(p)}catch(m){m instanceof un?(_.sendFrame(r,{type:"error",code:"frame_overflow",message:m.message}),_.dropClient(r,`frame overflow: ${m.message}`)):_.dropClient(r,`decoder error: ${String(m)}`);return}for(const m of h)try{Yt(x,r,m)}catch{}});const l=n(()=>{_.removeClient(r)},"drop");e.on("close",l),e.on("error",()=>{})},"serveViewer");Y=je(ye),_t()!==void 0&&(z=new qe({host:"::",port:Xe,path:"/view",maxPayload:mn.maxLineBytes}),z.on("connection",e=>ye(Ve(e))),z.on("error",e=>{I||(b({level:"error",event:"broker.socket.failed",error:e}),C("socket-error",1))})),Y.on("error",e=>{I||(b({level:"error",event:"broker.socket.failed",error:e}),C("socket-error",1))}),Y.listen(j,async()=>{Promise.all([Q(t,{op:"clear",link:"viewer\u2194broker"}),Q(t,{op:"clear",link:"relay\u2194broker"})]).catch(r=>b({level:"error",event:"broker.daemon.operation.failed",node_id:t,error:r,fields:{operation:"broker_socket_bound_fault_clear"}}));let e;try{e=await V.recoverOnStartup()}catch(r){b({level:"error",event:"broker.daemon.operation.failed",node_id:t,error:r,fields:{operation:"recovery_startup"}}),C("recovery-startup-failed",1);return}if(!(I||re))if(e==="none"&&g.firstPrompt!==void 0&&g.firstPrompt!==""){const r=on(process.env[sn]);r!==null&&an(g.firstPrompt,r);try{await ht(t)}catch(p){b({level:"error",event:"broker.daemon.operation.failed",node_id:t,error:p,fields:{operation:"attachments_fetch"}}),C("attachments-fetch-failed",1);return}const l=process.env.CRTR_KICKOFF_MESSAGE_IDS;x.kickoff(g.firstPrompt,l?JSON.parse(l):[])}else N.releaseKickoff()}),_.persistAttachState()}n(Or,"runBroker");function hn(t){const o=new Set;for(const s of t)if(!(s.role!=="assistant"||!Array.isArray(s.content)))for(const f of s.content)f!==null&&typeof f=="object"&&f.type==="toolCall"&&typeof f.id=="string"&&o.add(f.id);return o}n(hn,"toolCallIds");function ae(t,o){const s=t.sessionManager,f=kt(s);return f!==void 0&&zt(f.map(({message:i})=>i)),f===void 0?void 0:At(f,{boundaryReviewId:o,sessionEntries:s.getEntries?.()})}n(ae,"snapshotMessages");function Me(){const t=$e();if(t===void 0||t==="")throw new Error("broker launch is missing CRTR_NODE_ID");return t}n(Me,"currentBrokerNodeId");async function wn(t,o){const s=wt(o.model,Be(),o.modelExact===!0);if(s===void 0)return;const f=se(t),i={cwd:Ue()??o.cwd,profileId:Ke()},a=await yt(s,f,i);if(a!==void 0)throw await Q(Me(),{op:"record",input:{link:"crtr\u2192pi",op:"model resolution at launch",kind:"model-not-found",retry:{disposition:"fatal"},message:`model '${a.request.spec}' is not registered in the serving model registry`}}),new Error(`[broker] FATAL: model '${a.request.spec}' is not registered in the serving model registry`);const c=await St(s,i.cwd,i.profileId),d=bt(s.spec);for(const v of c){const w=f.find(v.providerId,v.modelId);if(w===void 0)continue;const S=d?.providerId===v.providerId&&d.modelId===v.modelId?d.thinkingLevel??(v.thinkingLevel===void 0?void 0:"off"):v.thinkingLevel;return{model:w,thinkingLevel:S}}if(o.modelExact===!0){const v=c.map(w=>`'${w.providerId}/${w.modelId}' unregistered`).join("; ");throw await Q(Me(),{op:"record",input:{link:"crtr\u2192pi",op:"model resolution at launch",kind:"model-not-found",retry:{disposition:"fatal"},message:`exact-consult model '${o.model}' is unavailable \u2014 refusing to substitute (${v})`}}),new Error(`[broker] FATAL: exact-consult model '${o.model}' is unavailable`)}b({level:"warn",event:"broker.model.none_viable",fields:{rejected_candidates:c.map(v=>({provider:v.providerId,model:v.modelId,reason:"unregistered"})).slice(0,16)}})}n(wn,"resolveLaunchModel");function Sn(t){let o=!0;return{runtime:new Proxy(t,{get(f,i,a){return o&&i==="getAvailableSnapshot"?()=>[]:o&&i==="getAvailable"?async()=>[]:o&&i==="getModel"?()=>{}:Reflect.get(f,i,a)}}),reveal:n(()=>{o=!1},"reveal")}}n(Sn,"modelLessRegistry");async function bn(t,o){const s=J(),f=n(async S=>{const g=await Ze.create({credentials:new pt,modelsPath:ke(S.agentDir,"models.json")});gt(g);const y=await t.createAgentSessionServices({cwd:S.cwd,agentDir:S.agentDir,modelRuntime:g,resourceLoaderOptions:{additionalExtensionPaths:o.extensionPaths,appendSystemPrompt:o.appendSystemPromptPath!==void 0?[o.appendSystemPromptPath]:void 0,noContextFiles:!0,extensionsOverride:n(O=>({...O,extensions:O.extensions.filter(k=>!/[/\\]provider-rotation\.(?:ts|js)$/.test(k.path))}),"extensionsOverride")}}),R=y.resourceLoader.getExtensions();if(o.extensionPaths.some(O=>oe.includes(O))){const O=new Set(R.extensions.map(T=>T.path)),k=oe.filter(T=>!O.has(T));if(k.length>0){const T=R.errors.filter(L=>oe.includes(L.path)).map(L=>`${L.path}: ${L.error}`).join("; ");throw new Error(`[broker] FATAL: mandatory canvas extensions failed to load: ${k.join(", ")}${T===""?"":` (${T})`}`)}}y.settingsManager.getRetryEnabled=()=>!1;const M=await wn(y,o),X=M?.model,W=M?.thinkingLevel,P=M===void 0?Sn(y.modelRuntime):void 0,de=P!==void 0?{...y,modelRuntime:P.runtime}:y,G=await t.createAgentSessionFromServices({services:de,sessionManager:S.sessionManager,model:X,thinkingLevel:W,..._n(o.tools,We()),sessionStartEvent:S.sessionStartEvent});return Jt(G.session),ft(G.session),P?.reveal(),{...G,services:y}},"buildForManager"),i=o.resumeSessionPath,a=o.forkFrom!==void 0&&o.forkFrom!=="";if(!a&&(i===void 0||i==="")&&o.resumeSessionId!==void 0&&o.resumeSessionId!=="")throw new Error(`[broker] resume requires a session .jsonl PATH; got only a bare id '${o.resumeSessionId}' (pi_session_file was never captured for this node)`);const c=!a&&i!==void 0&&i!=="",d=c&&o.newCycle===!0,v=c&&!d;let w;if(a)w=await t.SessionManager.forkFrom(o.forkFrom,o.cwd);else if(v||d){if(w=await t.SessionManager.open(i),d){const S=w.getLeafId();w.resetLeaf(),w.appendCustomEntry(Et,{cycle:Number(process.env.CRTR_CYCLES),fromLeaf:S})}}else w=await t.SessionManager.create(o.cwd);if(t.createAgentSessionRuntime!==void 0){const S=n(async y=>{const R=await f({cwd:y.cwd,agentDir:y.agentDir,sessionManager:y.sessionManager,sessionStartEvent:y.sessionStartEvent});return{...R,diagnostics:R.services.diagnostics??[]}},"factory"),g=await t.createAgentSessionRuntime(S,{cwd:o.cwd,agentDir:s,sessionManager:w});return{session:g.session,services:g.services,resuming:v,runtime:g}}const E=await f({cwd:o.cwd,agentDir:s,sessionManager:w});return{session:E.session,services:E.services,resuming:v,runtime:void 0}}n(bn,"buildBrokerSession");function yn(t){const o=n((i,a,c,d)=>{if(d?.signal?.aborted)return Promise.resolve(i);const v=t.writable();return v.length===0?Promise.resolve(i):new Promise(w=>{let E;const S=n(()=>{E!==void 0&&clearTimeout(E),d?.signal?.removeEventListener("abort",g),t.pending.delete(a.id)},"cleanup"),g=n(()=>{S(),t.broadcast({type:"extension_ui_dismiss",id:a.id}),w(i)},"onAbort");d?.signal?.addEventListener("abort",g,{once:!0});const y=d?.timeout!==void 0?d.timeout:gn;E=setTimeout(()=>{S(),t.broadcast({type:"extension_ui_dismiss",id:a.id}),w(i)},y),typeof E.unref=="function"&&E.unref(),t.pending.set(a.id,{request:a,resolve:n(R=>{S(),t.broadcast({type:"extension_ui_dismiss",id:a.id}),w(c(R))},"resolve")});for(const R of v)t.forward(R,a)})},"dialogPromise"),s=n(()=>{},"noop");return{[Rt]:t.onKnownStreamWait??(i=>()=>{}),get theme(){const i=globalThis[Symbol.for("@earendil-works/pi-coding-agent:theme")];if(i==null)throw new Error("Theme not initialized. Call initTheme() first.");return i},select:n((i,a,c)=>o(void 0,{type:"extension_ui_request",id:A(),method:"select",title:i,options:a,timeout:c?.timeout},d=>"cancelled"in d&&d.cancelled?void 0:"value"in d?d.value:void 0,c),"select"),confirm:n((i,a,c)=>o(!1,{type:"extension_ui_request",id:A(),method:"confirm",title:i,message:a,timeout:c?.timeout},d=>"cancelled"in d&&d.cancelled?!1:"confirmed"in d?d.confirmed:!1,c),"confirm"),input:n((i,a,c)=>o(void 0,{type:"extension_ui_request",id:A(),method:"input",title:i,placeholder:a,timeout:c?.timeout},d=>"cancelled"in d&&d.cancelled?void 0:"value"in d?d.value:void 0,c),"input"),editor:n((i,a)=>o(void 0,{type:"extension_ui_request",id:A(),method:"editor",title:i,prefill:a},c=>"cancelled"in c&&c.cancelled?void 0:"value"in c?c.value:void 0),"editor"),setStatus:n((i,a)=>t.broadcast({type:"extension_ui_request",id:A(),method:"setStatus",statusKey:i,statusText:a}),"setStatus"),setWidget:n((i,a,c)=>{typeof a!="function"&&t.broadcast({type:"extension_ui_request",id:A(),method:"setWidget",widgetKey:i,widgetLines:a,widgetPlacement:c?.placement})},"setWidget"),setTitle:n(i=>t.broadcast({type:"extension_ui_request",id:A(),method:"setTitle",title:i}),"setTitle"),notify:n((i,a)=>{const c=a==="warn"?"warning":a;t.broadcast({type:"extension_ui_request",id:A(),method:"notify",message:i,notifyType:c})},"notify"),onTerminalInput:n(()=>s,"onTerminalInput"),setWorkingMessage:s,setWorkingVisible:s,setWorkingIndicator:s,setHiddenThinkingLabel:s,setFooter:s,setHeader:s,custom:n(()=>Promise.resolve(void 0),"custom"),pasteToEditor:s,setEditorText:s,getEditorText:n(()=>"","getEditorText"),addAutocompleteProvider:s,setEditorComponent:s,getEditorComponent:n(()=>{},"getEditorComponent"),getAllThemes:n(()=>[],"getAllThemes"),getTheme:n(()=>{},"getTheme"),setTheme:s,getToolsExpanded:n(()=>!1,"getToolsExpanded"),setToolsExpanded:s}}n(yn,"makeBrokerUiContext");export{Fr as REVIEW_BOUNDARY_NAVIGATION_CODE,Nr as ReviewBoundaryNavigationError,_n as brokerToolOptions,bn as buildBrokerSession,vn as disposeActiveSession,yn as makeBrokerUiContext,Or as runBroker,ae as snapshotMessages};
@@ -13,7 +13,7 @@ export declare class BlaxelApiError extends Error {
13
13
  readonly code: string | null;
14
14
  constructor(status: number, code: string | null, message: string);
15
15
  }
16
- /** Blaxel's sandbox API uses this 404 when the workload URL itself no longer exists. */
16
+ /** The sandbox gateway answers 404 `WORKLOAD_UNAVAILABLE` for a sandbox URL whose workload no longer exists (deleted, or never existed). */
17
17
  export declare const isWorkloadUnavailable: (error: unknown) => error is BlaxelApiError;
18
18
  export interface SandboxSpec {
19
19
  name: string;
@@ -63,7 +63,7 @@ export interface BlaxelClient {
63
63
  createSandbox(spec: SandboxSpec): Promise<SandboxInfo & {
64
64
  created: boolean;
65
65
  }>;
66
- /** Null when the sandbox does not exist. */
66
+ /** Null when the sandbox does not exist or is being deleted. */
67
67
  getSandbox(name: string): Promise<SandboxInfo | null>;
68
68
  /** Deleting an absent sandbox is not an error. */
69
69
  deleteSandbox(name: string): Promise<void>;
@@ -1 +1 @@
1
- var E=Object.defineProperty;var d=(s,o)=>E(s,"name",{value:o,configurable:!0});function h(s=process.env){const o=d(i=>{const f=s[i];if(!f)throw new Error(`${i} is required for the Blaxel runner`);return f},"need");return{apiKey:o("BLAXEL_API_KEY"),workspace:o("BLAXEL_WORKSPACE"),region:o("BLAXEL_REGION")}}d(h,"blaxelConfigFromEnv");class w extends Error{static{d(this,"BlaxelApiError")}status;code;constructor(o,i,f){super(f),this.status=o,this.code=i,this.name="BlaxelApiError"}}const b=d(s=>s instanceof w&&s.status===404&&s.code==="WORKLOAD_UNAVAILABLE","isWorkloadUnavailable");function $(s){const o=s.metadata.url;if(!o)throw new Error(`Blaxel sandbox ${s.metadata.name} has no url`);return{name:s.metadata.name,url:o,state:s.state??null,status:s.status??null}}d($,"toInfo");function A(s){const o=s.fetch??fetch,i=s.controlUrl??"https://api.blaxel.ai/v0",f=d(()=>({authorization:`Bearer ${s.apiKey}`,"x-blaxel-workspace":s.workspace,"content-type":"application/json"}),"headers");async function c(e,t,a,r=[]){const n=await o(e,{method:t,headers:f(),...a===void 0?{}:{body:JSON.stringify(a)}}),l=await n.text();let x=null;if(l!=="")try{x=JSON.parse(l)}catch{x={raw:l}}if(!n.ok&&!r.includes(n.status)){const p=x??{},m=typeof p.message=="string"?p.message:typeof p.error=="string"?p.error:l.slice(0,300);throw new w(n.status,typeof p.code=="string"?p.code:null,`Blaxel ${t} ${e} failed (${n.status}): ${m}`)}return{status:n.status,json:x}}d(c,"call");const u=encodeURIComponent;return{config:s,async createSandbox(e){const t=[...e.ports??[]].map(l=>({name:l.name,target:l.target,protocol:"HTTP"})),a={metadata:{name:e.name},spec:{runtime:{image:e.image,memory:e.memoryMb,...t.length>0?{ports:t}:{}},region:s.region}},{status:r,json:n}=await c(`${i}/sandboxes`,"POST",a,[409]);if(r===409){const l=await this.getSandbox(e.name);if(l===null)throw new Error(`Blaxel sandbox ${e.name} reported as existing but cannot be read`);return{...l,created:!1}}return{...$(n),created:!0}},async getSandbox(e){const{status:t,json:a}=await c(`${i}/sandboxes/${u(e)}`,"GET",void 0,[404]);return t===404?null:$(a)},async deleteSandbox(e){await c(`${i}/sandboxes/${u(e)}`,"DELETE",void 0,[404])},async startProcess(e,t){const{json:a}=await c(`${e.url}/process`,"POST",t);return a},async getProcess(e,t){const{status:a,json:r}=await c(`${e.url}/process/${u(t)}`,"GET",void 0,[404]);if(a===404){const n=r?.code;if(n==="WORKLOAD_UNAVAILABLE")throw new w(404,n,`Blaxel sandbox ${e.name} is unavailable`);return null}return r},async stopProcess(e,t){await c(`${e.url}/process/${u(t)}`,"DELETE",void 0,[404])},async killProcess(e,t){await c(`${e.url}/process/${u(t)}/kill`,"DELETE",void 0,[404])},async exec(e,t,a,r,n){return await this.startProcess(e,{name:t,command:a,waitForCompletion:!0,timeout:r,...n?{env:n}:{}})},async putFile(e,t,a,r="0644"){await c(`${e.url}/filesystem/${t.replace(/^\/+/,"").split("/").map(u).join("/")}`,"PUT",{content:a,permissions:r})},async ensurePreview(e,t,a){const{json:r}=await c(`${i}/sandboxes/${u(e)}/previews`,"POST",{metadata:{name:t},spec:{port:a,public:!1}},[409]),n=r?.spec?.url;if(n)return n;const x=(await c(`${i}/sandboxes/${u(e)}/previews`,"GET")).json.find(p=>p.metadata.name===t);if(!x)throw new Error(`Blaxel preview ${t} of ${e} not found`);return x.spec.url},async createPreviewToken(e,t,a){const{json:r}=await c(`${i}/sandboxes/${u(e)}/previews/${u(t)}/tokens`,"POST",{spec:{expiresAt:a.toISOString()}}),n=r.spec;return{token:n.token,expiresAt:n.expiresAt}}}}d(A,"createBlaxelClient");export{w as BlaxelApiError,h as blaxelConfigFromEnv,A as createBlaxelClient,b as isWorkloadUnavailable};
1
+ var $=Object.defineProperty;var p=(s,c)=>$(s,"name",{value:c,configurable:!0});function A(s=process.env){const c=p(n=>{const d=s[n];if(!d)throw new Error(`${n} is required for the Blaxel runner`);return d},"need");return{apiKey:c("BLAXEL_API_KEY"),workspace:c("BLAXEL_WORKSPACE"),region:c("BLAXEL_REGION")}}p(A,"blaxelConfigFromEnv");class w extends Error{static{p(this,"BlaxelApiError")}status;code;constructor(c,n,d){super(d),this.status=c,this.code=n,this.name="BlaxelApiError"}}const g=p(s=>s instanceof w&&s.status===404&&s.code==="WORKLOAD_UNAVAILABLE","isWorkloadUnavailable");function E(s){const c=s.metadata.url;if(!c)throw new Error(`Blaxel sandbox ${s.metadata.name} has no url`);return{name:s.metadata.name,url:c,state:s.state??null,status:s.status??null}}p(E,"toInfo");function y(s,c){const n=s??{},d=typeof n.error=="object"&&n.error!==null?n.error:{},i=typeof d.code=="string"?d.code:typeof n.code=="string"?n.code:null,l=[d.message,n.message,n.error].find(e=>typeof e=="string")??c.slice(0,300);return{code:i,message:l}}p(y,"errorFields");function T(s){const c=s.fetch??fetch,n=s.controlUrl??"https://api.blaxel.ai/v0",d=p(()=>({authorization:`Bearer ${s.apiKey}`,"x-blaxel-workspace":s.workspace,"content-type":"application/json"}),"headers");async function i(e,t,a,r=[]){const o=await c(e,{method:t,headers:d(),...a===void 0?{}:{body:JSON.stringify(a)}}),u=await o.text();let f=null;if(u!=="")try{f=JSON.parse(u)}catch{f={raw:u}}if(!o.ok){const{code:x,message:m}=y(f,u);if(o.status===404&&x==="WORKLOAD_UNAVAILABLE"||!r.includes(o.status))throw new w(o.status,x,`Blaxel ${t} ${e} failed (${o.status}): ${m}`)}return{status:o.status,json:f}}p(i,"call");const l=encodeURIComponent;return{config:s,async createSandbox(e){const t=[...e.ports??[]].map(u=>({name:u.name,target:u.target,protocol:"HTTP"})),a={metadata:{name:e.name},spec:{runtime:{image:e.image,memory:e.memoryMb,...t.length>0?{ports:t}:{}},region:s.region}},{status:r,json:o}=await i(`${n}/sandboxes`,"POST",a,[409]);if(r===409){const u=await this.getSandbox(e.name);if(u===null)throw new Error(`Blaxel sandbox ${e.name} reported as existing but cannot be read`);return{...u,created:!1}}return{...E(o),created:!0}},async getSandbox(e){const{status:t,json:a}=await i(`${n}/sandboxes/${l(e)}`,"GET",void 0,[404]);if(t===404)return null;const r=a;return r.status==="DELETING"||r.status==="TERMINATED"?null:E(r)},async deleteSandbox(e){await i(`${n}/sandboxes/${l(e)}`,"DELETE",void 0,[404])},async startProcess(e,t){const{json:a}=await i(`${e.url}/process`,"POST",t);return a},async getProcess(e,t){const{status:a,json:r}=await i(`${e.url}/process/${l(t)}`,"GET",void 0,[404]);return a===404?null:r},async stopProcess(e,t){await i(`${e.url}/process/${l(t)}`,"DELETE",void 0,[404])},async killProcess(e,t){await i(`${e.url}/process/${l(t)}/kill`,"DELETE",void 0,[404])},async exec(e,t,a,r,o){return await this.startProcess(e,{name:t,command:a,waitForCompletion:!0,timeout:r,...o?{env:o}:{}})},async putFile(e,t,a,r="0644"){await i(`${e.url}/filesystem/${t.replace(/^\/+/,"").split("/").map(l).join("/")}`,"PUT",{content:a,permissions:r})},async ensurePreview(e,t,a){const{json:r}=await i(`${n}/sandboxes/${l(e)}/previews`,"POST",{metadata:{name:t},spec:{port:a,public:!1}},[409]),o=r?.spec?.url;if(o)return o;const f=(await i(`${n}/sandboxes/${l(e)}/previews`,"GET")).json.find(x=>x.metadata.name===t);if(!f)throw new Error(`Blaxel preview ${t} of ${e} not found`);return f.spec.url},async createPreviewToken(e,t,a){const{json:r}=await i(`${n}/sandboxes/${l(e)}/previews/${l(t)}/tokens`,"POST",{spec:{expiresAt:a.toISOString()}}),o=r.spec;return{token:o.token,expiresAt:o.expiresAt}}}}p(T,"createBlaxelClient");export{w as BlaxelApiError,A as blaxelConfigFromEnv,T as createBlaxelClient,g as isWorkloadUnavailable};
@@ -1,3 +1,3 @@
1
- var g=Object.defineProperty;var o=(t,a)=>g(t,"name",{value:a,configurable:!0});import{withCanvasWrite as R,openDb as I}from"../../../canvas/db.js";import{mintNodeToken as k}from"../../../canvas/node-tokens.js";import{BlaxelApiError as T,isWorkloadUnavailable as b}from"./client.js";const x=4180,l="view",S="node /opt/crtr/current/scripts/sandbox-start.mjs --wake",C=3,N=/^crtr-sandbox-start: failed (\S+)/m,m=o(t=>`broker-${t}`,"brokerProcessName"),f=o(t=>t.sandboxName??`${t.nodeId}-${t.attempt}`,"sandboxNameFor"),y=o(async t=>await R(a=>k(a,t.executionId,t.nodeId,new Date().toISOString())),"defaultMint");async function P(t){return(await(await I()).prepare("SELECT DISTINCT sandbox_name FROM executions WHERE node_id = ? AND sandbox_name IS NOT NULL").all(t)).map(u=>u.sandbox_name)}o(P,"defaultSandboxNames");function $(t){const a=`${t.logs}
1
+ var g=Object.defineProperty;var o=(t,a)=>g(t,"name",{value:a,configurable:!0});import{withCanvasWrite as R,openDb as I}from"../../../canvas/db.js";import{mintNodeToken as k}from"../../../canvas/node-tokens.js";import{BlaxelApiError as T,isWorkloadUnavailable as x}from"./client.js";const f=4180,l="view",S="node /opt/crtr/current/scripts/sandbox-start.mjs --wake",C=3,N=/^crtr-sandbox-start: failed (\S+)/m,m=o(t=>`broker-${t}`,"brokerProcessName"),E=o(t=>t.sandboxName??`${t.nodeId}-${t.attempt}`,"sandboxNameFor"),y=o(async t=>await R(a=>k(a,t.executionId,t.nodeId,new Date().toISOString())),"defaultMint");async function P(t){return(await(await I()).prepare("SELECT DISTINCT sandbox_name FROM executions WHERE node_id = ? AND sandbox_name IS NOT NULL").all(t)).map(u=>u.sandbox_name)}o(P,"defaultSandboxNames");function $(t){const a=`${t.logs}
2
2
  ${t.stderr}
3
- ${t.stdout}`;return{state:"failed",reason:N.exec(a)?.[1]??"start_failed",code:t.exitCode}}o($,"failureOf");function v(t){const{client:a}=t,u=(t.stopGraceS??10)*1e3,E=new Map;async function d(n){return await a.getSandbox(f(n))}return o(d,"sandboxOf"),{kind:"blaxel",async start(n){const r=await t.launchContext(n),e=f(n),s=await a.createSandbox({name:e,image:r.image,memoryMb:r.memoryMb,ports:[{name:l,target:x}]}),i=m(n.executionId);if((await a.getProcess(s,i))?.status==="running")return{pid:null,exited:null};await a.ensurePreview(e,l,x);const w=await(t.mintToken??y)(n),p={...r.secrets,CRTR_API_URL:r.apiUrl,CRTR_EXECUTION_ID:n.executionId,CRTR_NODE_TOKEN:w,CRTR_RELEASE:n.release,CRTR_RELEASE_URL:r.releaseUrl,CRTR_RELEASE_SHA256:r.releaseSha256};return await a.startProcess(s,{name:i,command:t.bootstrap(),env:p,waitForCompletion:!1}),{pid:null,exited:null}},async wake(n){const r=await d(n);if(r===null)return"no_broker";try{const e=await a.exec(r,`wake-${n.executionId}-${Date.now()}`,`${S} ${n.executionId}`,30);if(e.exitCode===0)return"signalled";if(e.exitCode===C)return"no_broker";throw new Error(`wake of ${n.executionId} failed (exit ${e.exitCode}): ${(e.stderr||e.logs).slice(0,300)}`)}catch(e){if(b(e))return"no_broker";throw e}},async stop(n,r){try{const e=await d(n);if(e===null)return{confirmed:!0};const s=m(n.executionId),i=o(async()=>(await a.getProcess(e,s))?.status==="running","running");if(!await i())return{confirmed:!0};await a.stopProcess(e,s);const c=Date.now()+u;for(;Date.now()<c;){if(!await i())return{confirmed:!0};await new Promise(w=>setTimeout(w,250))}return await a.killProcess(e,s),{confirmed:!await i()}}catch(e){return{confirmed:!1,error:e instanceof Error?e:new Error(String(e))}}},async status(n){try{const r=await d(n);if(r===null)return{state:"gone"};const e=await a.getProcess(r,m(n.executionId));return e===null?{state:"gone"}:e.status==="running"?{state:"running",pid:null}:e.status==="failed"||e.status==="completed"&&e.exitCode!==0?$(e):{state:"gone"}}catch(r){return b(r)?{state:"gone"}:{state:"unknown",error:r instanceof Error?r.message:String(r)}}},async delete(n){for(const r of await(t.sandboxNamesForNode??P)(n))await a.deleteSandbox(r)},async viewerAddress(n){const r=f(n),e=E.get(r);if(e&&e.expiresAt-Date.now()>5*6e4)return _(e.url,e.token);const s=await a.ensurePreview(r,l,x),i=new Date(Date.now()+60*6e4),{token:c}=await a.createPreviewToken(r,l,i);return E.set(r,{url:s,token:c,expiresAt:i.getTime()}),_(s,c)},async sandboxState(n){return(await d(n))?.state??null}}}o(v,"createBlaxelRunner");function _(t,a){return{kind:"wss",url:`${t.replace(/^https:/,"wss:")}/view`,headers:{"x-blaxel-preview-token":a}}}o(_,"viewer");export{x as BROKER_VIEW_PORT,T as BlaxelApiError,C as EXIT_NO_BROKER,N as FAILURE_LINE,S as WAKE_COMMAND,m as brokerProcessName,v as createBlaxelRunner,f as sandboxNameFor};
3
+ ${t.stdout}`;return{state:"failed",reason:N.exec(a)?.[1]??"start_failed",code:t.exitCode}}o($,"failureOf");function v(t){const{client:a}=t,u=(t.stopGraceS??10)*1e3,b=new Map;async function d(n){return await a.getSandbox(E(n))}return o(d,"sandboxOf"),{kind:"blaxel",async start(n){const r=await t.launchContext(n),e=E(n),s=await a.createSandbox({name:e,image:r.image,memoryMb:r.memoryMb,ports:[{name:l,target:f}]}),i=m(n.executionId);if((await a.getProcess(s,i))?.status==="running")return{pid:null,exited:null};await a.ensurePreview(e,l,f);const w=await(t.mintToken??y)(n),p={...r.secrets,CRTR_API_URL:r.apiUrl,CRTR_EXECUTION_ID:n.executionId,CRTR_NODE_TOKEN:w,CRTR_RELEASE:n.release,CRTR_RELEASE_URL:r.releaseUrl,CRTR_RELEASE_SHA256:r.releaseSha256};return await a.startProcess(s,{name:i,command:t.bootstrap(),env:p,waitForCompletion:!1}),{pid:null,exited:null}},async wake(n){const r=await d(n);if(r===null)return"no_broker";try{const e=await a.exec(r,`wake-${n.executionId}-${Date.now()}`,`${S} ${n.executionId}`,30);if(e.exitCode===0)return"signalled";if(e.exitCode===C)return"no_broker";throw new Error(`wake of ${n.executionId} failed (exit ${e.exitCode}): ${(e.stderr||e.logs).slice(0,300)}`)}catch(e){if(x(e))return"no_broker";throw e}},async stop(n,r){try{const e=await d(n);if(e===null)return{confirmed:!0};const s=m(n.executionId),i=o(async()=>(await a.getProcess(e,s))?.status==="running","running");if(!await i())return{confirmed:!0};await a.stopProcess(e,s);const c=Date.now()+u;for(;Date.now()<c;){if(!await i())return{confirmed:!0};await new Promise(w=>setTimeout(w,250))}return await a.killProcess(e,s),{confirmed:!await i()}}catch(e){return x(e)?{confirmed:!0}:{confirmed:!1,error:e instanceof Error?e:new Error(String(e))}}},async status(n){try{const r=await d(n);if(r===null)return{state:"gone"};const e=await a.getProcess(r,m(n.executionId));return e===null?{state:"gone"}:e.status==="running"?{state:"running",pid:null}:e.status==="failed"||e.status==="completed"&&e.exitCode!==0?$(e):{state:"gone"}}catch(r){return x(r)?{state:"gone"}:{state:"unknown",error:r instanceof Error?r.message:String(r)}}},async delete(n){for(const r of await(t.sandboxNamesForNode??P)(n))await a.deleteSandbox(r)},async viewerAddress(n){const r=E(n),e=b.get(r);if(e&&e.expiresAt-Date.now()>5*6e4)return _(e.url,e.token);const s=await a.ensurePreview(r,l,f),i=new Date(Date.now()+60*6e4),{token:c}=await a.createPreviewToken(r,l,i);return b.set(r,{url:s,token:c,expiresAt:i.getTime()}),_(s,c)},async sandboxState(n){return(await d(n))?.state??null}}}o(v,"createBlaxelRunner");function _(t,a){return{kind:"wss",url:`${t.replace(/^https:/,"wss:")}/view`,headers:{"x-blaxel-preview-token":a}}}o(_,"viewer");export{f as BROKER_VIEW_PORT,T as BlaxelApiError,C as EXIT_NO_BROKER,N as FAILURE_LINE,S as WAKE_COMMAND,m as brokerProcessName,v as createBlaxelRunner,E as sandboxNameFor};
@@ -1 +1 @@
1
- var S=Object.defineProperty;var c=(a,t)=>S(a,"name",{value:t,configurable:!0});import{callerGrant as g}from"../principal.js";import{grantVersion as b,payerOfGrant as O}from"../../../core/grants/mirror.js";import{holdGrant as $}from"../../../core/grants/watch.js";import{RunError as n,idempotencyKeyOf as v,cancelRun as N,cloudPersonScope as A,deleteRun as j,interruptRun as x,listRuns as B,messageRun as J,renameRun as z,requestedScopes as D,startRun as E,traceRun as G,waitRun as K}from"../../../core/runs/operations.js";import{canReadRun as y,requireRun as w,runCallerApp as M}from"../router.js";import{waitSeconds as q}from"../long-poll-wait.js";import{outputSchemaProblem as P}from"../../../core/runtime/structured-output.js";function m(a){if(!a||typeof a!="object"||Array.isArray(a))throw new n(400,"invalid_request","a JSON object is required");return a}c(m,"object");function k(a,t,i,u,l){if(a===null)return l;if(!/^(0|[1-9]\d*)$/.test(a)||!Number.isSafeInteger(Number(a))||Number(a)<i||Number(a)>u)throw new n(400,"invalid_request",`invalid ${t}`,t);return Number(a)}c(k,"integer");const T=256*1024;async function Y(a,t,i,u,l,_,h){switch(a.name){case"grant.get":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=q("grant.get",u),s=u.get("since");if(s!==null&&!/^[A-Za-z0-9_-]{1,64}$/.test(s))throw new n(400,"invalid_request","invalid since","since");const d=e&&s!==null?$(t.grant):void 0;let o;try{if(o=await g(t),d){const f=Date.now()+e*1e3;for(;b(o)===s&&Date.now()<f;)await d.changed(Math.max(0,(f-Date.now())/1e3)),o=await g(t)}}finally{d?.release()}return{status:200,body:{sub:t.sub,grantee:t.grantee,client_id:t.client_id,grant:t.grant,rev:o.rev,resolved_scopes:o.resolved_scopes,spend_cap:o.spend_cap,exp:t.exp,payer:await O(t.grant),dependencies:o.dependencies??{},version:b(o)}}}case"run.start":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=m(l),s=["mode","parent","fork_from","node_id","env","tools","system_prompt"];for(const r of s)if(e[r]!==void 0)throw new n(403,"owner_only",`${r} is owner-only`,r);const d=["payer","model_policy","kind"];for(const r of d)if(e[r]!==void 0)throw new n(400,"invalid_request",`${r} is not available`,r);for(const r of Object.keys(e))if(!["prompt","scopes","metadata","profile","isolated","cwd","worktree","output_schema","message","model","deadline"].includes(r))throw new n(400,"invalid_request",`unknown field ${r}`,r);if(e.model!==void 0&&(typeof e.model!="string"||!e.model.trim()))throw new n(400,"invalid_request","model must be a non-empty string","model");if(e.deadline!==void 0&&(typeof e.deadline!="string"||!e.deadline.trim()))throw new n(400,"invalid_request","deadline must be a duration string such as 10m","deadline");if(e.profile!==void 0&&(typeof e.profile!="string"||!e.profile))throw new n(400,"invalid_request","profile must be a profile id","profile");if(e.isolated!==void 0&&typeof e.isolated!="boolean")throw new n(400,"invalid_request","isolated must be a boolean","isolated");if(e.cwd!==void 0&&(e.isolated!==!0||typeof e.cwd!="string"||!e.cwd))throw new n(400,"invalid_request","cwd is available only for an isolated run and must be an absolute path inside its root folder","cwd");if(e.worktree!==void 0&&e.isolated!==!0)throw new n(400,"invalid_request","worktree is not available","worktree");if(e.isolated===!0&&e.worktree!==void 0)throw new n(400,"invalid_request","worktree is unavailable for an isolated run","worktree");if(typeof e.prompt!="string"||!e.prompt.trim())throw new n(400,"invalid_request","prompt is required","prompt");if(e.message!==void 0&&(typeof e.message!="string"||!e.message.trim()))throw new n(400,"invalid_request","message must be a non-empty string","message");const o=e.metadata===void 0?{}:m(e.metadata);if(Buffer.byteLength(JSON.stringify(o))>4096)throw new n(400,"invalid_request","metadata exceeds 4 KB","metadata");let f;if(e.output_schema!==void 0){const r=e.output_schema;if(!r||typeof r!="object"||Array.isArray(r))throw new n(400,"invalid_request","output_schema must be a JSON Schema object","output_schema");if(Buffer.byteLength(JSON.stringify(r))>T)throw new n(400,"invalid_request","output_schema exceeds 256 KB","output_schema");const p=await P(r);if(p!==null)throw new n(400,"invalid_request",`output_schema is not usable: ${p}`,"output_schema");f=r}const R=D(t.resolved,e.scopes,t.grantee);return{status:201,body:await E(t,{prompt:e.prompt,scopes:R,metadata:o,profile:e.profile,isolated:e.isolated,cwd:e.cwd,...e.model===void 0?{}:{model:e.model},...e.deadline===void 0?{}:{deadline:e.deadline},...f===void 0?{}:{output_schema:f},...e.message===void 0?{}:{message:e.message},...h===void 0?{}:{causedBy:h}},v(_))}}case"run.list":{const e=u.get("status")??void 0;if(e&&!["running","waiting_on_user","idle","settled"].includes(e))throw new n(400,"invalid_request","invalid status","status");const s=u.get("app");if(s!==null&&!/^(?:app:)?[a-z0-9][a-z0-9-]*$/.test(s))throw new n(400,"invalid_request","invalid app","app");const d=s===null?M(t):s.startsWith("app:")?s:`app:${s}`;if(d!==null&&!await y(t,d))throw new n(404,"not_found","run not found");const o=A(t.sub);return{status:200,body:await B(d,{status:e,...o===void 0?{}:{sub:o},after:u.get("after")??void 0,limit:k(u.get("limit"),"limit",1,100,50),..."class"in t&&(t.class==="broker"||t.class==="cron")&&d!==null?{visible:c(async f=>await y(t,d,f),"visible")}:{}})}}case"run.get":{const e=q("run.get",u),s=await w(t,i),d=await K(i,s.grantee,e);return await w(t,i),{status:200,body:d}}case"run.message":{const e=m(l);if(typeof e.message!="string"||!e.message.trim())throw new n(400,"invalid_request","message is required","message");if(e.start_turn!==void 0&&typeof e.start_turn!="boolean")throw new n(400,"invalid_request","start_turn must be a boolean","start_turn");return{status:202,body:await J(await w(t,i,!0),e.message,void 0,e.start_turn,v(_))}}case"run.interrupt":{const e=m(l===void 0?{}:l);if(e.children!==void 0&&e.children!=="stop"&&e.children!=="continue")throw new n(400,"invalid_request","invalid children","children");return{status:200,body:await x(await w(t,i,!0),e.children)}}case"run.cancel":{const e=m(l===void 0?{}:l);if(e.keep_schedules!==void 0&&typeof e.keep_schedules!="boolean")throw new n(400,"invalid_request","invalid keep_schedules","keep_schedules");return{status:200,body:await N(await w(t,i,!0),{keepSchedules:e.keep_schedules})}}case"run.rename":{const e=m(l);for(const s of Object.keys(e))if(s!=="name")throw new n(400,"invalid_request",`unknown field ${s}`,s);return{status:200,body:await z(await w(t,i,!0),e.name)}}case"run.delete":{const{uploads_pending:e}=await j(await w(t,i,!0,!0));return{status:200,body:{run_id:i,uploads_pending:e}}}case"trace.get":{const e=u.get("format")??void 0;if(e&&e!=="entries")throw new n(400,"invalid_request","unknown trace format","format");const s=await w(t,i);return{status:200,body:await G(i,s.grantee,e)}}default:throw new n(404,"not_found","unknown operation")}}c(Y,"handleRunOperation");export{Y as handleRunOperation,k as integer};
1
+ var S=Object.defineProperty;var c=(a,t)=>S(a,"name",{value:t,configurable:!0});import{callerGrant as g}from"../principal.js";import{grantVersion as b,payerOfGrant as O}from"../../../core/grants/mirror.js";import{holdGrant as $}from"../../../core/grants/watch.js";import{RunError as n,idempotencyKeyOf as v,cancelRun as N,cloudPersonScope as A,deleteRun as j,interruptRun as x,listRuns as B,messageRun as J,renameRun as z,requestedScopes as D,startRun as E,traceRun as G,waitRun as K}from"../../../core/runs/operations.js";import{canReadRun as y,requireRun as w,runCallerApp as M}from"../router.js";import{waitSeconds as q}from"../long-poll-wait.js";import{outputSchemaProblem as P}from"../../../core/runtime/structured-output.js";function m(a){if(!a||typeof a!="object"||Array.isArray(a))throw new n(400,"invalid_request","a JSON object is required");return a}c(m,"object");function k(a,t,i,u,l){if(a===null)return l;if(!/^(0|[1-9]\d*)$/.test(a)||!Number.isSafeInteger(Number(a))||Number(a)<i||Number(a)>u)throw new n(400,"invalid_request",`invalid ${t}`,t);return Number(a)}c(k,"integer");const T=256*1024;async function Y(a,t,i,u,l,_,h){switch(a.name){case"grant.get":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=q("grant.get",u),s=u.get("since");if(s!==null&&!/^[A-Za-z0-9_-]{1,64}$/.test(s))throw new n(400,"invalid_request","invalid since","since");const d=e&&s!==null?$(t.grant):void 0;let o;try{if(o=await g(t),d){const f=Date.now()+e*1e3;for(;b(o)===s&&Date.now()<f;)await d.changed(Math.max(0,(f-Date.now())/1e3)),o=await g(t)}}finally{d?.release()}return{status:200,body:{sub:t.sub,grantee:t.grantee,client_id:t.client_id,grant:t.grant,rev:o.rev,resolved_scopes:o.resolved_scopes,spend_cap:o.spend_cap,exp:t.exp,payer:await O(t.grant),dependencies:o.dependencies??{},version:b(o)}}}case"run.start":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=m(l),s=["mode","parent","fork_from","node_id","env","tools","system_prompt"];for(const r of s)if(e[r]!==void 0)throw new n(403,"owner_only",`${r} is owner-only`,r);const d=["payer","model_policy","kind"];for(const r of d)if(e[r]!==void 0)throw new n(400,"invalid_request",`${r} is not available`,r);for(const r of Object.keys(e))if(!["prompt","scopes","metadata","profile","isolated","cwd","worktree","output_schema","message","model","deadline"].includes(r))throw new n(400,"invalid_request",`unknown field ${r}`,r);if(e.model!==void 0&&(typeof e.model!="string"||!e.model.trim()))throw new n(400,"invalid_request","model must be a non-empty string","model");if(e.deadline!==void 0&&(typeof e.deadline!="string"||!e.deadline.trim()))throw new n(400,"invalid_request","deadline must be a duration string such as 10m","deadline");if(e.profile!==void 0&&(typeof e.profile!="string"||!e.profile))throw new n(400,"invalid_request","profile must be a profile id","profile");if(e.isolated!==void 0&&typeof e.isolated!="boolean")throw new n(400,"invalid_request","isolated must be a boolean","isolated");if(e.cwd!==void 0&&(e.isolated!==!0||typeof e.cwd!="string"||!e.cwd))throw new n(400,"invalid_request","cwd is available only for an isolated run and must be an absolute path inside its root folder","cwd");if(e.worktree!==void 0&&e.isolated!==!0)throw new n(400,"invalid_request","worktree is not available","worktree");if(e.isolated===!0&&e.worktree!==void 0)throw new n(400,"invalid_request","worktree is unavailable for an isolated run","worktree");if(typeof e.prompt!="string"||!e.prompt.trim())throw new n(400,"invalid_request","prompt is required","prompt");if(e.message!==void 0&&(typeof e.message!="string"||!e.message.trim()))throw new n(400,"invalid_request","message must be a non-empty string","message");const o=e.metadata===void 0?{}:m(e.metadata);if(Buffer.byteLength(JSON.stringify(o))>4096)throw new n(400,"invalid_request","metadata exceeds 4 KB","metadata");let f;if(e.output_schema!==void 0){const r=e.output_schema;if(!r||typeof r!="object"||Array.isArray(r))throw new n(400,"invalid_request","output_schema must be a JSON Schema object","output_schema");if(Buffer.byteLength(JSON.stringify(r))>T)throw new n(400,"invalid_request","output_schema exceeds 256 KB","output_schema");const p=await P(r);if(p!==null)throw new n(400,"invalid_request",`output_schema is not usable: ${p}`,"output_schema");f=r}const R=D(t.resolved,e.scopes,t.grantee);return{status:201,body:await E(t,{prompt:e.prompt,scopes:R,metadata:o,profile:e.profile,isolated:e.isolated,cwd:e.cwd,...e.model===void 0?{}:{model:e.model},...e.deadline===void 0?{}:{deadline:e.deadline},...f===void 0?{}:{output_schema:f},...e.message===void 0?{}:{message:e.message},...h===void 0?{}:{causedBy:h}},v(_))}}case"run.list":{const e=u.get("status")??void 0;if(e&&!["running","waiting_on_user","idle","settled"].includes(e))throw new n(400,"invalid_request","invalid status","status");const s=u.get("app");if(s!==null&&!/^(?:app:)?[a-z0-9][a-z0-9-]*$/.test(s))throw new n(400,"invalid_request","invalid app","app");const d=s===null?M(t):s.startsWith("app:")?s:`app:${s}`;if(d!==null&&!await y(t,d))throw new n(404,"not_found","run not found");const o=A(t.sub);return{status:200,body:await B(d,{status:e,...o===void 0?{}:{sub:o},after:u.get("after")??void 0,limit:k(u.get("limit"),"limit",1,100,50),..."class"in t&&(t.class==="broker"||t.class==="cron")&&d!==null?{visible:c(async f=>await y(t,d,f),"visible")}:{}})}}case"run.get":{const e=q("run.get",u),s=await w(t,i),d=await K(i,s.grantee,e);return await w(t,i),{status:200,body:d}}case"run.message":{const e=m(l);if(typeof e.message!="string"||!e.message.trim())throw new n(400,"invalid_request","message is required","message");if(e.start_turn!==void 0&&typeof e.start_turn!="boolean")throw new n(400,"invalid_request","start_turn must be a boolean","start_turn");if(e.steer!==void 0&&typeof e.steer!="boolean")throw new n(400,"invalid_request","steer must be a boolean","steer");if(e.start_turn===!1&&e.steer===!0)throw new n(400,"invalid_request","steer requires start_turn","steer");return{status:202,body:await J(await w(t,i,!0),e.message,void 0,e.start_turn,v(_),e.steer)}}case"run.interrupt":{const e=m(l===void 0?{}:l);if(e.children!==void 0&&e.children!=="stop"&&e.children!=="continue")throw new n(400,"invalid_request","invalid children","children");return{status:200,body:await x(await w(t,i,!0),e.children)}}case"run.cancel":{const e=m(l===void 0?{}:l);if(e.keep_schedules!==void 0&&typeof e.keep_schedules!="boolean")throw new n(400,"invalid_request","invalid keep_schedules","keep_schedules");return{status:200,body:await N(await w(t,i,!0),{keepSchedules:e.keep_schedules})}}case"run.rename":{const e=m(l);for(const s of Object.keys(e))if(s!=="name")throw new n(400,"invalid_request",`unknown field ${s}`,s);return{status:200,body:await z(await w(t,i,!0),e.name)}}case"run.delete":{const{uploads_pending:e}=await j(await w(t,i,!0,!0));return{status:200,body:{run_id:i,uploads_pending:e}}}case"trace.get":{const e=u.get("format")??void 0;if(e&&e!=="entries")throw new n(400,"invalid_request","unknown trace format","format");const s=await w(t,i);return{status:200,body:await G(i,s.grantee,e)}}default:throw new n(404,"not_found","unknown operation")}}c(Y,"handleRunOperation");export{Y as handleRunOperation,k as integer};
@@ -1,2 +1,2 @@
1
- var T=Object.defineProperty;var _=(t,e)=>T(t,"name",{value:e,configurable:!0});import{appendRunEvent as c}from"../../core/runs/events.js";import{openQuestionIds as x}from"../../core/runs/questions.js";import{getNode as N}from"../../core/canvas/canvas.js";import{payerOfGrant as h}from"../../core/grants/mirror.js";import{runSchemaRepromptPending as S}from"../../core/runtime/stop-guard.js";const k="crtr-turn-input",D="crtr-system-prompt",g="crtr-turn-end",w=_(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"zero");function O(t){return typeof t=="string"?t:Array.isArray(t)?t.filter(e=>typeof e?.text=="string").map(e=>e.text).join(`
2
- `):""}_(O,"text");function j(t){if(t==null)return;const{code:e,message:n}=t;if(e!=="content_refused"&&e!=="model_error"||typeof n!="string")throw new Error("invalid crtr-turn-end error");return{code:e,message:n}}_(j,"turnEndError");function A(t){return{input_tokens:t.input??0,output_tokens:t.output??0,cache_read_tokens:t.cacheRead??0,cache_write_tokens:t.cacheWrite??0,cost_usd:t.cost?.total??0}}_(A,"usageParts");async function v(t,e,n){if(e!==t||!n.some(f=>f.type==="custom"&&f.customType===g))return!1;const m=await N(t);return m!==null&&await S(m)}_(v,"rootRepromptPending");async function Y(t,e,n,m,f,R){let d=f;for(const r of m){if(r.type==="custom"&&r.customType===k){const o=r.data?.message_ids;if(!Array.isArray(o)||!o.every(i=>typeof i=="string"))throw new Error("invalid crtr-turn-input message_ids");d=[...new Set([...d,...o])]}if(r.type==="custom"&&r.customType===D){const o=r.data?.text;if(typeof o!="string")throw new Error("invalid crtr-system-prompt text");await c(t,e,n,"node.system_prompt.set",{entry_id:r.id,text:o})}if(r.type==="custom"&&r.customType===g){const o=r.data,i=o?.stop_reason;if(typeof i!="string")throw new Error("invalid crtr-turn-end stop_reason");const a=j(o?.error),s=await t.prepare("SELECT body_json FROM run_events WHERE run_id = ? AND node_id = ? AND type = 'node.turn.started' ORDER BY sequence_number DESC LIMIT 1").get(e,n);if(!s)throw new Error("crtr-turn-end without node.turn.started");const{input:p}=JSON.parse(s.body_json),y=await t.prepare("SELECT status, pi_pid FROM node_rows WHERE run_id = ? AND node_id != ?").all(e,n),u=R??await v(e,n,[r]),l=y.some(E=>E.pi_pid!==null&&E.status==="active")||u?"running":(await x(e,t)).length?"waiting_on_user":"idle";await c(t,e,n,"node.turn.completed",{entry_id:r.id,message_ids:p.message_ids,run_status:l,stop_reason:i,...a?{error:a}:{}}),n===e&&await t.prepare("UPDATE nodes SET last_turn_error_json = ? WHERE node_id = ?").run(a?JSON.stringify(a):null,e)}if(r.type==="message"){const o=r.message;if(o.role==="user"){const i=await t.prepare("SELECT type, body_json FROM run_events WHERE run_id = ? AND node_id = ? AND type IN ('node.turn.started', 'node.turn.completed') ORDER BY sequence_number DESC LIMIT 1").get(e,n),a=i?.type==="node.turn.started"?JSON.parse(i.body_json).input.message_ids:[];await c(t,e,n,"node.turn.started",{entry_id:r.id,input:{text:O(o.content),message_ids:[...new Set([...a,...d])]}}),d=[]}if(o.role==="assistant"){let i=!1;const a=_(async()=>{if(i)return;i=!0;const s=o.content.filter(p=>p.type==="text").map(p=>p.text).join("");await c(t,e,n,"node.output_text.done",{entry_id:r.id,text:s,stop_reason:o.stopReason})},"doneText");for(const s of o.content)s.type==="thinking"&&await c(t,e,n,"node.thinking.done",{entry_id:r.id,text:s.thinking}),s.type==="text"&&await a(),s.type==="toolCall"&&(await a(),await c(t,e,n,"node.tool_call.started",{entry_id:r.id,tool_call_id:s.id,name:s.name,arguments:s.arguments}));if(await a(),o.usage){const s=A(o.usage),p=await t.prepare("SELECT usage_json, grant_id FROM nodes WHERE node_id = ?").get(e),y=p.usage_json?JSON.parse(p.usage_json):w(),u=w();for(const l of Object.keys(u))u[l]=y[l]+s[l];await t.prepare("UPDATE nodes SET usage_json = ? WHERE node_id = ?").run(JSON.stringify(u),e),await c(t,e,n,"usage",{entry_id:r.id,served_model:o.model,payer:await h(p.grant_id),...s,usage:u})}await t.prepare("DELETE FROM run_events WHERE run_id = ? AND node_id = ? AND type IN ('node.thinking.delta', 'node.output_text.delta')").run(e,n)}o.role==="toolResult"&&await c(t,e,n,"node.tool_call.completed",{entry_id:r.id,tool_call_id:o.toolCallId,name:o.toolName,result:o.content,status:o.isError?"error":"ok"})}}return d}_(Y,"appendConversationEvents");export{D as SYSTEM_PROMPT_TYPE,g as TURN_END_TYPE,k as TURN_INPUT_TYPE,Y as appendConversationEvents,v as rootRepromptPending};
1
+ var T=Object.defineProperty;var _=(t,e)=>T(t,"name",{value:e,configurable:!0});import{appendRunEvent as c}from"../../core/runs/events.js";import{openQuestionIds as x}from"../../core/runs/questions.js";import{getNode as h}from"../../core/canvas/canvas.js";import{payerOfGrant as N}from"../../core/grants/mirror.js";import{runSchemaRepromptPending as S}from"../../core/runtime/stop-guard.js";const k="crtr-turn-input",D="crtr-system-prompt",g="crtr-turn-end",w=_(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"zero");function O(t){return typeof t=="string"?t:Array.isArray(t)?t.filter(e=>typeof e?.text=="string").map(e=>e.text).join(`
2
+ `):""}_(O,"text");function j(t){if(t==null)return;const{code:e,message:o,retry_at:u}=t;if(e!=="content_refused"&&e!=="model_error"&&e!=="rate_limited"||typeof o!="string")throw new Error("invalid crtr-turn-end error");if(e==="rate_limited"){if(typeof u!="string")throw new Error("invalid crtr-turn-end error");return{code:e,message:o,retry_at:u}}return{code:e,message:o}}_(j,"turnEndError");function v(t){return{input_tokens:t.input??0,output_tokens:t.output??0,cache_read_tokens:t.cacheRead??0,cache_write_tokens:t.cacheWrite??0,cost_usd:t.cost?.total??0}}_(v,"usageParts");async function A(t,e,o){if(e!==t||!o.some(f=>f.type==="custom"&&f.customType===g))return!1;const u=await h(t);return u!==null&&await S(u)}_(A,"rootRepromptPending");async function Y(t,e,o,u,f,R){let l=f;for(const n of u){if(n.type==="custom"&&n.customType===k){const r=n.data?.message_ids;if(!Array.isArray(r)||!r.every(i=>typeof i=="string"))throw new Error("invalid crtr-turn-input message_ids");l=[...new Set([...l,...r])]}if(n.type==="custom"&&n.customType===D){const r=n.data?.text;if(typeof r!="string")throw new Error("invalid crtr-system-prompt text");await c(t,e,o,"node.system_prompt.set",{entry_id:n.id,text:r})}if(n.type==="custom"&&n.customType===g){const r=n.data,i=r?.stop_reason;if(typeof i!="string")throw new Error("invalid crtr-turn-end stop_reason");const a=j(r?.error),s=await t.prepare("SELECT body_json FROM run_events WHERE run_id = ? AND node_id = ? AND type = 'node.turn.started' ORDER BY sequence_number DESC LIMIT 1").get(e,o);if(!s)throw new Error("crtr-turn-end without node.turn.started");const{input:p}=JSON.parse(s.body_json),y=await t.prepare("SELECT status, pi_pid FROM node_rows WHERE run_id = ? AND node_id != ?").all(e,o),d=R??await A(e,o,[n]),m=y.some(E=>E.pi_pid!==null&&E.status==="active")||d?"running":(await x(e,t)).length?"waiting_on_user":"idle";await c(t,e,o,"node.turn.completed",{entry_id:n.id,message_ids:p.message_ids,run_status:m,stop_reason:i,...a?{error:a}:{}}),o===e&&await t.prepare("UPDATE nodes SET last_turn_error_json = ? WHERE node_id = ?").run(a?JSON.stringify(a):null,e)}if(n.type==="message"){const r=n.message;if(r.role==="user"){const i=await t.prepare("SELECT type, body_json FROM run_events WHERE run_id = ? AND node_id = ? AND type IN ('node.turn.started', 'node.turn.completed') ORDER BY sequence_number DESC LIMIT 1").get(e,o),a=i?.type==="node.turn.started"?JSON.parse(i.body_json).input.message_ids:[];await c(t,e,o,"node.turn.started",{entry_id:n.id,input:{text:O(r.content),message_ids:[...new Set([...a,...l])]}}),l=[]}if(r.role==="assistant"){let i=!1;const a=_(async()=>{if(i)return;i=!0;const s=r.content.filter(p=>p.type==="text").map(p=>p.text).join("");await c(t,e,o,"node.output_text.done",{entry_id:n.id,text:s,stop_reason:r.stopReason})},"doneText");for(const s of r.content)s.type==="thinking"&&await c(t,e,o,"node.thinking.done",{entry_id:n.id,text:s.thinking}),s.type==="text"&&await a(),s.type==="toolCall"&&(await a(),await c(t,e,o,"node.tool_call.started",{entry_id:n.id,tool_call_id:s.id,name:s.name,arguments:s.arguments}));if(await a(),r.usage){const s=v(r.usage),p=await t.prepare("SELECT usage_json, grant_id FROM nodes WHERE node_id = ?").get(e),y=p.usage_json?JSON.parse(p.usage_json):w(),d=w();for(const m of Object.keys(d))d[m]=y[m]+s[m];await t.prepare("UPDATE nodes SET usage_json = ? WHERE node_id = ?").run(JSON.stringify(d),e),await c(t,e,o,"usage",{entry_id:n.id,served_model:r.model,payer:await N(p.grant_id),...s,usage:d})}await t.prepare("DELETE FROM run_events WHERE run_id = ? AND node_id = ? AND type IN ('node.thinking.delta', 'node.output_text.delta')").run(e,o)}r.role==="toolResult"&&await c(t,e,o,"node.tool_call.completed",{entry_id:n.id,tool_call_id:r.toolCallId,name:r.toolName,result:r.content,status:r.isError?"error":"ok"})}}return l}_(Y,"appendConversationEvents");export{D as SYSTEM_PROMPT_TYPE,g as TURN_END_TYPE,k as TURN_INPUT_TYPE,Y as appendConversationEvents,A as rootRepromptPending};
@@ -1,7 +1,7 @@
1
- var ce=Object.defineProperty;var o=(e,n)=>ce(e,"name",{value:n,configurable:!0});import{envBashAutoBgDeadlineMs as z,envBashValveMs as ue,envNodeId as de}from"../shared/env.js";import{closeSync as le,existsSync as v,mkdirSync as K,openSync as fe,readFileSync as W,readSync as he,rmSync as q,statSync as X,writeFileSync as $}from"node:fs";import{homedir as be}from"node:os";import{dirname as me}from"node:path";import{performance as N}from"node:perf_hooks";import{backgroundBashJob as Y,bashJobPaths as pe,clearJobDeadlineFile as ge,writeJobDeadlineFile as we,cancelBashJobProcessTree as ye,countJobLogLines as ve,formatBashElapsed as Q,newBashJobId as Ee,normalizeBashJobPurpose as je,tailJobLog as Ie}from"../core/bash-jobs.js";import{MAX_BACKGROUND_BASH_LOG_BYTES as ht,BACKGROUND_BASH_LOG_TRUNCATION_MARKER as bt}from"../core/bash-job-supervisor.js";import{brokerApiUrl as _e,recordBackgroundedJob as Be,reportBrokerJob as Se,stopBrokerBashJob as ke}from"../core/runtime/broker/daemon-ops.js";import{bashJobsUseLauncher as Le,launchBashJobSupervisor as Ae}from"../core/runtime/broker/bash-job-launch.js";import{JobReporter as xe}from"../core/runtime/broker/job-reporter.js";import{bashCommandRefusal as Oe}from"../core/bash-guard.js";import{readConfig as Je}from"../core/config.js";import{PREVIEW_RESULT_PATH_ENV as Re,previewResultPath as Te}from"../core/preview-result-path.js";import{Type as _}from"typebox";import{createBashToolDefinition as Z}from"@earendil-works/pi-coding-agent";const $e=200;function Ne(){return ue()}o(Ne,"resolveValveMs");function Ce(e){return pe(e,Ee())}o(Ce,"makeJobPaths");const B=new Set;let ee=!1;function te(){for(const e of[...B])try{Y(e),B.delete(e)}catch(n){process.stderr.write(`crtr: failed to background bash job ${e.jobId} during broker termination: ${n.message}
2
- `)}}o(te,"backgroundLiveJobsForTermination");function Me(){ee||(ee=!0,process.prependListener("SIGTERM",te),process.platform!=="win32"&&process.prependListener("SIGHUP",te))}o(Me,"installTerminationHandoff");const De=64*1024;function C(e,n,i){let s;try{s=X(e)}catch{return n}if(s.size<=n)return n;const c=fe(e,"r");try{for(;n<s.size;){const b=Math.min(De,s.size-n),m=Buffer.allocUnsafe(b),u=he(c,m,0,b,n);if(u===0)break;i(m.subarray(0,u)),n+=u}return n}finally{le(c)}}o(C,"tailOnce");function E(e){try{q(e,{recursive:!0,force:!0})}catch{}}o(E,"cleanupJobDir");function Pe(e){try{const n=W(e,"utf8").trim(),i=Number(n);return Number.isFinite(i)?i:null}catch{return null}}o(Pe,"readExitCode");function Ue(e){try{if(X(e).isDirectory())return{cwd:e}}catch{}const n=be();return{cwd:n,warning:`[working directory no longer exists: ${e}; running from ${n}]
3
- `}}o(Ue,"resolveExecutionCwd");function ne(e,n,i=()=>null,s,c=()=>{}){return{exec:o(async(b,m,{onData:u,signal:f,timeout:p,env:S})=>new Promise((j,d)=>{const w=i();if(f?.aborted){d(new Error("aborted"));return}const k=Oe(b,m);if(k!==null){d(new Error(k));return}const t=Ce(n);K(t.dir,{recursive:!0}),$(t.cmdSh,b),$(t.jobLog,""),$(t.jobRun,"");let l=0,g=!1,I,L,A,M=!1;const oe=Le(),D=N.now(),re=Ne();let y;const ie=o(()=>{y&&clearInterval(y),f&&f.removeEventListener("abort",G),B.delete(t)},"teardown"),h=o(r=>{g||(g=!0,ie(),r())},"finish");let P=!1;function x(r=!1){if(g||P)return;P=!0,y&&clearInterval(y);const a=r?Date.now()+z():void 0;a!==void 0&&we(t,a),Be(e,t.jobId,{command:W(t.cmdSh,"utf8"),...w===null?{}:{purpose:w},...a===void 0?{}:{deadline_at_ms:a}}).then(()=>{try{ge(t)}catch{}U(r,"")},T=>U(r,`
4
- [crtr could not record job ${t.jobId} (${T.message}); it will NOT message you when it exits. Watch its log.]`))}o(x,"finishBackground");function U(r,a){const T=Q(N.now()-D);let H="";if(r){const ae=z();H=`It will be CANCELED in ${Q(ae)} unless you claim it: crtr node bash extend ${t.jobId} --for <duration> (or --keep to run to completion). `}const V=`
5
- [backgrounded as job ${t.jobId} after ${T} in the foreground${r?" (auto)":""}. `+H+(a===""?"It will interrupt you with a message when it exits. ":"")+`Watch: tail -f ${t.jobLog} ; stop: crtr node bash kill ${t.jobId}]`;u(Buffer.from(V+a)),c({jobId:t.jobId,logPath:t.jobLog,notice:V+a}),h(()=>j({exitCode:0}))}o(U,"handOff");function F(){l=C(t.jobLog,l,u);const r=Pe(t.jobExit);h(()=>j({exitCode:r})),E(t.dir)}o(F,"finishForeground");function se(r=!1){const a=Y(t);if(a==="backgrounded"||a==="already-backgrounded"){x(r);return}if(a==="finished"){F();return}h(()=>d(new Error(`bash job ${t.jobId} disappeared before it could be backgrounded`))),E(t.dir)}o(se,"requestBackground");function O(r){if(!(g||M)){if(l=C(t.jobLog,l,u),v(t.jobBg)){x();return}if(!v(t.jobExit)){if(I===void 0){A??=r;return}if(oe){M=!0,ke(e,t.jobId).then(()=>{h(()=>d(r)),E(t.dir)},a=>h(()=>d(a)));return}I!==void 0&&L!==void 0&&ye(I,L),h(()=>d(r)),E(t.dir)}}}o(O,"requestStop");function G(){O(new Error("aborted"))}o(G,"onAbort"),f&&f.addEventListener("abort",G,{once:!0});const J=Ue(m);J.warning&&u(Buffer.from(J.warning));let R=S??process.env;s!==void 0&&(K(me(s),{recursive:!0}),q(s,{force:!0}),R={...R,[Re]:s}),Ae(e,t,n,J.cwd,R).then(r=>{I=r.pgid,L=r.identity,B.add(t),A!==void 0&&O(A)},r=>{h(()=>d(r)),E(t.dir)}),y=setInterval(()=>{if(l=C(t.jobLog,l,u),v(t.jobDone)){F();return}if(v(t.jobBg)){x();return}if(v(t.jobExit))return;const r=N.now()-D;if(p!==void 0&&p>0){r>=p*1e3&&O(new Error(`timeout:${p}`));return}r>=re&&se(!0)},$e)}),"exec")}}o(ne,"createValveOperations");const Fe=["One short clause naming what this step is FOR, in the words of the person you",'are working for \u2014 "Reading the Acme thread", not "Fetching emails" and never','"Running GMAIL_FETCH_EMAILS". A person reads this while the command runs; it',"is the only thing they see of this step. Under about eight words. Omit it for","steps that only touch your own scratch work."].join(" "),Ge=3;function He(e){const n=Ie(e.logPath,Ge),i=Math.max(0,ve(e.logPath)-n.length),s=n.length===0?"(no output yet)":n.join(`
1
+ var fe=Object.defineProperty;var o=(e,n)=>fe(e,"name",{value:n,configurable:!0});import{envBashAutoBgDeadlineMs as q,envBashValveMs as he,envNodeId as be}from"../shared/env.js";import{closeSync as me,existsSync as v,mkdirSync as X,openSync as pe,readFileSync as Y,readSync as ge,rmSync as Q,statSync as Z,writeFileSync as M}from"node:fs";import{homedir as we}from"node:os";import{dirname as ye}from"node:path";import{performance as N}from"node:perf_hooks";import{backgroundBashJob as ee,bashJobPaths as ve,clearJobDeadlineFile as Ee,writeJobDeadlineFile as _e,cancelBashJobProcessTree as je,countJobLogLines as Se,formatBashElapsed as te,newBashJobId as ke,normalizeBashJobPurpose as Ie,tailJobLog as Ae}from"../core/bash-jobs.js";import{MAX_BACKGROUND_BASH_LOG_BYTES as gt,BACKGROUND_BASH_LOG_TRUNCATION_MARKER as wt}from"../core/bash-job-supervisor.js";import{brokerApiUrl as Be,recordBackgroundedJob as Le,reportBrokerJob as xe,stopBrokerBashJob as Oe}from"../core/runtime/broker/daemon-ops.js";import{bashJobsUseLauncher as Te,launchBashJobSupervisor as Je}from"../core/runtime/broker/bash-job-launch.js";import{JobReporter as Re}from"../core/runtime/broker/job-reporter.js";import{bashCommandRefusal as $e}from"../core/bash-guard.js";import{readConfig as Me}from"../core/config.js";import{PREVIEW_RESULT_PATH_ENV as Ne,previewResultPath as Ce}from"../core/preview-result-path.js";import{Type as S}from"typebox";import{createBashToolDefinition as ne}from"@earendil-works/pi-coding-agent";const oe=200,De=5e3;function Pe(){return he()}o(Pe,"resolveValveMs");function Ue(e){return ve(e,ke())}o(Ue,"makeJobPaths");const k=new Set;let re=!1;function ie(){for(const e of[...k])try{ee(e),k.delete(e)}catch(n){process.stderr.write(`crtr: failed to background bash job ${e.jobId} during broker termination: ${n.message}
2
+ `)}}o(ie,"backgroundLiveJobsForTermination");function Fe(){re||(re=!0,process.prependListener("SIGTERM",ie),process.platform!=="win32"&&process.prependListener("SIGHUP",ie))}o(Fe,"installTerminationHandoff");const Ge=64*1024;function C(e,n,i){let s;try{s=Z(e)}catch{return n}if(s.size<=n)return n;const c=pe(e,"r");try{for(;n<s.size;){const b=Math.min(Ge,s.size-n),m=Buffer.allocUnsafe(b),u=ge(c,m,0,b,n);if(u===0)break;i(m.subarray(0,u)),n+=u}return n}finally{me(c)}}o(C,"tailOnce");function E(e){try{Q(e,{recursive:!0,force:!0})}catch{}}o(E,"cleanupJobDir");function He(e){try{const n=Y(e,"utf8").trim(),i=Number(n);return Number.isFinite(i)?i:null}catch{return null}}o(He,"readExitCode");function Ve(e){try{if(Z(e).isDirectory())return{cwd:e}}catch{}const n=we();return{cwd:n,warning:`[working directory no longer exists: ${e}; running from ${n}]
3
+ `}}o(Ve,"resolveExecutionCwd");function se(e,n,i=()=>null,s,c=()=>{}){return{exec:o(async(b,m,{onData:u,signal:f,timeout:p,env:I})=>new Promise((_,d)=>{const w=i();if(f?.aborted){d(new Error("aborted"));return}const A=$e(b,m);if(A!==null){d(new Error(A));return}const t=Ue(n);X(t.dir,{recursive:!0}),M(t.cmdSh,b),M(t.jobLog,""),M(t.jobRun,"");let l=0,g=!1,j,B,L,D=!1;const ae=Te(),P=N.now();let U=0,x=P;const F=o(()=>N.now()-P-U,"foregroundMs"),ce=Pe();let y;const ue=o(()=>{y&&clearInterval(y),f&&f.removeEventListener("abort",z),k.delete(t)},"teardown"),h=o(r=>{g||(g=!0,ue(),r())},"finish");let G=!1;function O(r=!1){if(g||G)return;G=!0,y&&clearInterval(y);const a=r?Date.now()+q():void 0;a!==void 0&&_e(t,a),Le(e,t.jobId,{command:Y(t.cmdSh,"utf8"),...w===null?{}:{purpose:w},...a===void 0?{}:{deadline_at_ms:a}}).then(()=>{try{Ee(t)}catch{}H(r,"")},$=>H(r,`
4
+ [crtr could not record job ${t.jobId} (${$.message}); it will NOT message you when it exits. Watch its log.]`))}o(O,"finishBackground");function H(r,a){const $=te(F());let K="";if(r){const le=q();K=`It will be CANCELED in ${te(le)} unless you claim it: crtr node bash extend ${t.jobId} --for <duration> (or --keep to run to completion). `}const W=`
5
+ [backgrounded as job ${t.jobId} after ${$} in the foreground${r?" (auto)":""}. `+K+(a===""?"It will interrupt you with a message when it exits. ":"")+`Watch: tail -f ${t.jobLog} ; stop: crtr node bash kill ${t.jobId}]`;u(Buffer.from(W+a)),c({jobId:t.jobId,logPath:t.jobLog,notice:W+a}),h(()=>_({exitCode:0}))}o(H,"handOff");function V(){l=C(t.jobLog,l,u);const r=He(t.jobExit);h(()=>_({exitCode:r})),E(t.dir)}o(V,"finishForeground");function de(r=!1){const a=ee(t);if(a==="backgrounded"||a==="already-backgrounded"){O(r);return}if(a==="finished"){V();return}h(()=>d(new Error(`bash job ${t.jobId} disappeared before it could be backgrounded`))),E(t.dir)}o(de,"requestBackground");function T(r){if(!(g||D)){if(l=C(t.jobLog,l,u),v(t.jobBg)){O();return}if(!v(t.jobExit)){if(j===void 0){L??=r;return}if(ae){D=!0,Oe(e,t.jobId).then(()=>{h(()=>d(r)),E(t.dir)},a=>h(()=>d(a)));return}j!==void 0&&B!==void 0&&je(j,B),h(()=>d(r)),E(t.dir)}}}o(T,"requestStop");function z(){T(new Error("aborted"))}o(z,"onAbort"),f&&f.addEventListener("abort",z,{once:!0});const J=Ve(m);J.warning&&u(Buffer.from(J.warning));let R=I??process.env;s!==void 0&&(X(ye(s),{recursive:!0}),Q(s,{force:!0}),R={...R,[Ne]:s}),Je(e,t,n,J.cwd,R).then(r=>{j=r.pgid,B=r.identity,k.add(t),L!==void 0&&T(L)},r=>{h(()=>d(r)),E(t.dir)}),y=setInterval(()=>{const r=N.now();if(r-x>De&&(U+=r-x-oe),x=r,l=C(t.jobLog,l,u),v(t.jobDone)){V();return}if(v(t.jobBg)){O();return}if(v(t.jobExit))return;const a=F();if(p!==void 0&&p>0){a>=p*1e3&&T(new Error(`timeout:${p}`));return}a>=ce&&de(!0)},oe)}),"exec")}}o(se,"createValveOperations");const ze=["One short clause naming what this step is FOR, in the words of the person you",'are working for \u2014 "Reading the Acme thread", not "Fetching emails" and never','"Running GMAIL_FETCH_EMAILS". A person reads this while the command runs; it',"is the only thing they see of this step. Under about eight words. Omit it for","steps that only touch your own scratch work."].join(" "),Ke=3;function We(e){const n=Ae(e.logPath,Ke),i=Math.max(0,Se(e.logPath)-n.length),s=n.length===0?"(no output yet)":n.join(`
6
6
  `),c=i===0?"":`
7
- [${i} earlier output line${i===1?"":"s"} are in the job log only]`;return{content:[{type:"text",text:`${s}${c}${e.notice}`}],details:void 0}}o(He,"handoffResult");function Ve(e){return{...e,parameters:_.Object({purpose:_.Optional(_.String({description:Fe})),...e.parameters.properties})}}o(Ve,"withPurpose");const ze=["A command that needs longer than one call \u2014 a test lane, a build, a soak \u2014 is started with","`crtr node bash run '<command>'`, which returns at once and wakes you with the exit code; the job is a canvas","object others can watch. Do not use `&`, `nohup`, `setsid`, or a cron that polls a log. A foreground command","past 5 minutes is moved to a job automatically; a `timeout` kills it and everything it started."].join(" ");function Ke(e){const{command:n,...i}=e.parameters.properties;return{...e,parameters:_.Object({command:{...n,description:`${n.description??""} ${ze}`.trim()},...i})}}o(Ke,"withJobGuidance");function We(e,n,i,s){const c=Ke(Z(i,{operations:ne(e,n)}));return{...s?Ve(c):c,execute:o(async(u,f,p,S,j)=>{const d=s?je(f.purpose):null;let w=null;const t=await Z(i,{operations:ne(e,n,()=>d,Te(u),g=>{w=g})}).execute(u,f,p,S,j),l=w;return l===null?t:He(l)},"execute")}}o(We,"createValveToolDefinition");function qe(e){const n=de();if(n===void 0||n.trim()==="")return;const i=process.env.CRTR_CONTEXT_DIR;i===void 0||i.trim()===""||(Me(),_e()!==void 0&&new xe(i,(s,c)=>Se(n,s,c)).start(),e.on("session_start",(s,c)=>{e.registerTool(We(n,i,c.cwd,Je("user").bash_tool_purpose))}))}o(qe,"default");export{bt as BACKGROUND_BASH_LOG_TRUNCATION_MARKER,ht as MAX_BACKGROUND_BASH_LOG_BYTES,ne as createValveOperations,We as createValveToolDefinition,qe as default};
7
+ [${i} earlier output line${i===1?"":"s"} are in the job log only]`;return{content:[{type:"text",text:`${s}${c}${e.notice}`}],details:void 0}}o(We,"handoffResult");function qe(e){return{...e,parameters:S.Object({purpose:S.Optional(S.String({description:ze})),...e.parameters.properties})}}o(qe,"withPurpose");const Xe=["A command that needs longer than one call \u2014 a test lane, a build, a soak \u2014 is started with","`crtr node bash run '<command>'`, which returns at once and wakes you with the exit code; the job is a canvas","object others can watch. Do not use `&`, `nohup`, `setsid`, or a cron that polls a log. A foreground command","past 5 minutes is moved to a job automatically; a `timeout` kills it and everything it started."].join(" ");function Ye(e){const{command:n,...i}=e.parameters.properties;return{...e,parameters:S.Object({command:{...n,description:`${n.description??""} ${Xe}`.trim()},...i})}}o(Ye,"withJobGuidance");function Qe(e,n,i,s){const c=Ye(ne(i,{operations:se(e,n)}));return{...s?qe(c):c,execute:o(async(u,f,p,I,_)=>{const d=s?Ie(f.purpose):null;let w=null;const t=await ne(i,{operations:se(e,n,()=>d,Ce(u),g=>{w=g})}).execute(u,f,p,I,_),l=w;return l===null?t:We(l)},"execute")}}o(Qe,"createValveToolDefinition");function Ze(e){const n=be();if(n===void 0||n.trim()==="")return;const i=process.env.CRTR_CONTEXT_DIR;i===void 0||i.trim()===""||(Fe(),Be()!==void 0&&new Re(i,(s,c)=>xe(n,s,c)).start(),e.on("session_start",(s,c)=>{e.registerTool(Qe(n,i,c.cwd,Me("user").bash_tool_purpose))}))}o(Ze,"default");export{wt as BACKGROUND_BASH_LOG_TRUNCATION_MARKER,gt as MAX_BACKGROUND_BASH_LOG_BYTES,se as createValveOperations,Qe as createValveToolDefinition,Ze as default};
@@ -1,2 +1,2 @@
1
- var U=Object.defineProperty;var f=(s,t)=>U(s,"name",{value:t,configurable:!0});import{envInboxBetweenTurns as q,envNodeId as B,envProfileId as W,envWatcherDebounceMs as J,envWatcherTickMs as $,envWatcherBackstopMs as G}from"../shared/env.js";import{coalesceBrokerInbox as X,entryCards as j}from"../core/runtime/broker/inbox.js";import{acknowledgeBrokerMail as z,claimBrokerMail as L,brokerExtensionState as F,recordConversationWriteFault as V}from"../core/runtime/broker/daemon-ops.js";import{materializeSpilledBody as Q}from"../core/runtime/broker/spilled-body.js";import{brokerSignalClient as S}from"../core/runtime/broker/signal-stream.js";import{wasTurnRefused as Y}from"../core/runtime/broker/extension-abort.js";import{isDaemonTransportApiError as R,isTokenRevokedApiError as K}from"../api/errors.js";import{announceMessageOrigin as Z,messageSignature as _}from"../core/runtime/stamp/channel.js";import{captureGoalIfAbsent as ee,generateAndCommitInitialName as te}from"./broker-local.js";import{emitEvent as O}from"../core/events/emit.js";import{errorClassFromError as ie}from"../core/events/errors.js";import{operationIdContext as m}from"../core/events/operation-id.js";import{SITUATIONAL_CONTEXT_CUSTOM_TYPE as I}from"../shared/generated-context.js";import{isProfilePaused as ne}from"../core/profiles/manifest.js";import{brokerDisposeAndExit as N,brokerIdleTurnStart as re,brokerRunTurnBusy as se,HELD_DEFERRED_INBOX_PROMPT_JOIN as ae}from"../core/runtime/broker/held-deferred-inbox.js";const b=64,A=16;let p,oe=0;class D extends Error{static{f(this,"WatcherFatalError")}constructor(t,e){super(t,{cause:e}),this.name="WatcherFatalError"}}function de(){return{tickMs:$(),debounceMs:J()}}f(de,"resolveCadence");function E(){const s=B();return s===void 0||s.trim()===""?void 0:s}f(E,"currentNodeId");function P(){const s=W();return s!==void 0&&s!==""&&ne(s)}f(P,"profilePaused");function k(s){try{return ie(s)}catch{return{class:"unknown"}}}f(k,"safeErrorClass");function v(s){try{O(s)}catch{}}f(v,"emitBestEffort");function w(s){try{m.fresh(t=>{v({...s,operation_id:t})})}catch{}}f(w,"emitFreshBestEffort");function C(s,t){try{m.run(s,()=>{v({...t,operation_id:s})})}catch{}}f(C,"emitStoredBestEffort");function le(s){return s.kind==="human"?{type:"human"}:{type:"runtime",kind:"inbox",entries:s.entries.map(t=>({...t.from===null?{}:{from:t.from},...t.from_name===void 0?{}:{fromName:t.from_name},kind:t.kind,tier:t.tier,...t.ref===void 0?{}:{ref:t.ref}}))}}f(le,"deliveryOrigin");function x(s,t){if(s.length===0)throw new Error(`cannot create an empty ${t} delivery`);const e=[...new Set(s.map(r=>r.operation_id))].sort(),i=e.length-A;return{channel:t,entryIds:s.map(r=>r.entry_id),throughEntryId:s.at(-1).entry_id,sourceOperationIds:e.slice(0,A),...i>0?{sourceOperationIdsOmitted:i}:{}}}f(x,"deliveryFacts");function y(s){return{channel:s.channel,entry_ids:s.entryIds,through_entry_id:s.throughEntryId,source_operation_ids:s.sourceOperationIds,...s.sourceOperationIdsOmitted===void 0?{}:{source_operation_ids_omitted:s.sourceOperationIdsOmitted}}}f(y,"deliveryFields");function fe(s){C(s.operationId,{level:"info",event:"inbox.delivery.completed",outcome:"succeeded",fields:y(s)})}f(fe,"emitDeliveryCompleted");function T(s,t,e,i){C(s,{level:"error",event:"inbox.delivery.failed",outcome:"failed",error:i,error_class:k(i),fields:{stage:e,...y(t)}})}f(T,"emitDeliveryFailure");function M(s,t){C(s.operationId,{level:"error",event:"inbox.delivery.failed",outcome:"failed",error_class:{class:"unknown"},fields:{stage:"abandoned",reason:t,...y(s)}})}f(M,"emitDeliveryAbandoned");function he(s){return typeof s=="string"?s:Array.isArray(s)?s.filter(t=>typeof t=="object"&&t!==null&&typeof t.text=="string").map(t=>t.text).join(`
2
- `):""}f(he,"messageText");function H(s){return s==="quit"||s==="reload"||s==="new"||s==="resume"||s==="fork"}f(H,"isSessionShutdownReason");function ue(s){switch(s){case"quit":return"shutdown";case"reload":case"new":case"resume":case"fork":return"reload";default:return s}}f(ue,"disposalReasonForShutdown");function ce(s){return typeof s=="string"?s.slice(0,128):s===null?"null":s===void 0?"undefined":typeof s=="number"||typeof s=="boolean"||typeof s=="bigint"?String(s).slice(0,128):typeof s}f(ce,"boundedRuntimeReason");class me{static{f(this,"CanvasInboxWatcher")}pi;cadence;generation;timer;backstop;debounceTimer;signalUnsubscribers=[];rerun=!1;retryNeeded=!1;polledOk=!1;armed=!1;lastCtx;streaming=!1;runNode=q();nodeId;claims=new Map;reportBodies=new Map;tickInFlight;buffer=[];lastArrival=0;pendingHandoffs=[];pendingCommit;promptJoinInFlight=!1;replayUnseenInherited=!1;seeded=!1;disposed=!1;disposalReason;fatalError;fatalEscaped=!1;constructor(t,e,i){this.pi=t,this.cadence=e,this.generation=i}start(){const t=f((e,i)=>{this.canMutate()&&this.captureCtx(e,i)},"capture");this.pi.on("session_start",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.arm())}),this.pi.on("turn_end",t),this.pi.on("agent_start",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.streaming=!0)}),this.pi.on("agent_end",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.streaming=!1)}),this.pi.on("message_end",e=>{if(!this.canMutate())return;const i=e?.message;if(i?.role!=="user"&&i?.role!=="custom"||!this.pendingHandoffs.some(o=>o.awaitingDequeue!==void 0))return;const r=_(he(i.content)),n=this.pendingHandoffs.find(o=>o.awaitingDequeue===r);n!==void 0&&delete n.awaitingDequeue}),this.pi.on("agent_settled",async(e,i)=>{if(!this.canMutate())return;this.captureCtx(e,i);const r=E();if(r!==void 0)try{this.assertNodeOwnership(r,"settle"),await this.tickInFlight,this.seeded||await this.claimMail(r);const n=await this.settle(r,i,!1,!0);n==="refresh_held"&&this.dispose("refresh",!1),this.runNode&&n==="settled"&&this.canMutate()&&this.buffer.length>0&&await this.flushOrdinary(r),this.syncTimers()}catch(n){this.dispose("shutdown",!1,void 0,{error:n,stage:"settle"})}}),this.pi.on("session_shutdown",async e=>{if(!this.canMutate())return;let i;try{i=e?.reason}catch{i=void 0}const r=H(i)?ue(i):"reload";H(i)||w({level:"warn",event:"inbox.lifecycle.unknown_shutdown_reason",outcome:"failed",fields:{reason:ce(i)}});try{await this.disposeAtSettlement(r,this.lastCtx)}catch(n){this.dispose(r,!1,void 0,{error:n,stage:"settle"})}})}arm(){if(this.armed)return;this.armed=!0;const t=S();this.signalUnsubscribers.push(t.subscribe("state",()=>this.syncTimers()),t.subscribe("mail",()=>this.runTick("schedule")),t.subscribe("resync",()=>this.runTick("schedule"))),this.syncTimers(),t.state!=="connected"&&this.runTick("schedule")}async disposeAtSettlement(t,e){const i=E();if(i===void 0){this.dispose(t,!1);return}try{this.assertNodeOwnership(i,"settle");const r=await this.settle(i,e,t!=="refresh");this.dispose(r==="refresh_held"?"refresh":t,!1)}catch(r){this.dispose(t,!1,void 0,{error:r,stage:"settle"})}}dispose(t,e,i,r){if(this.disposed)return;this.disposed=!0,this.disposalReason=t,this.clearTimer();for(const o of this.signalUnsubscribers.splice(0))o();const n=r;{const o=[...new Set([...this.pendingCommit?.handoffs??[],...this.pendingHandoffs.splice(0)])];this.buffer.length=0,this.claims.clear(),this.reportBodies.clear(),this.pendingCommit=void 0,this.promptJoinInFlight=!1,this.replayUnseenInherited=!1,this.nodeId=void 0,this.lastCtx=void 0,this.lastArrival=0,this.streaming=!1,this.seeded=!1,this.ownsCurrentGeneration()&&(p=void 0);for(const a of o)M(a,t)}if(n!==void 0){this.scheduleHandlerFatal(n.error,n.stage);return}}ownsCurrentGeneration(){return p===this&&p.generation===this.generation}canMutate(){return this.ownsCurrentGeneration()&&!this.disposed&&this.fatalError===void 0}captureCtx(t,e){e!==void 0&&(this.lastCtx=e)}clearTimer(){this.timer!==void 0&&clearInterval(this.timer),this.backstop!==void 0&&clearInterval(this.backstop),this.debounceTimer!==void 0&&clearTimeout(this.debounceTimer),this.timer=this.backstop=this.debounceTimer=void 0}syncTimers(){if(!this.armed||!this.canMutate())return;const t=S().state,e=t==="connected",i=t!=="stopped"&&(t!=="connected"||this.buffer.length>0||this.pendingCommit!==void 0||this.retryNeeded);i&&this.timer===void 0?(this.timer=setInterval(()=>this.runTick("schedule"),this.cadence.tickMs),this.timer.unref?.()):!i&&this.timer!==void 0&&(clearInterval(this.timer),this.timer=void 0),e&&this.backstop===void 0?(this.backstop=setInterval(()=>this.runTick("schedule"),G()),this.backstop.unref?.()):!e&&this.backstop!==void 0&&(clearInterval(this.backstop),this.backstop=void 0),this.buffer.length===0&&this.debounceTimer!==void 0&&(clearTimeout(this.debounceTimer),this.debounceTimer=void 0)}armDebounce(){this.debounceTimer!==void 0&&clearTimeout(this.debounceTimer);const t=Math.max(0,this.lastArrival+this.cadence.debounceMs-Date.now());this.debounceTimer=setTimeout(()=>{this.debounceTimer=void 0,this.runTick("schedule")},t),this.debounceTimer.unref?.()}isIdle(){if(this.runNode&&se())return!1;try{if(typeof this.lastCtx?.isIdle=="function")return this.lastCtx.isIdle()===!0}catch{}return!this.streaming}assertNodeOwnership(t,e){!this.seeded||this.nodeId===void 0||this.nodeId===t||this.raiseReadFatal(e,new Error(`watcher node changed from ${this.nodeId} to ${t}`))}raiseFatal(t,e,i,r){throw this.fatalError===void 0&&(this.fatalError=new D(e,i),this.clearTimer(),w({level:"error",event:t,outcome:"failed",error:i,error_class:k(i),fields:r})),this.fatalError}raiseReadFatal(t,e){return this.raiseFatal("inbox.read.failed",`inbox ${t} state is corrupt`,e,{stage:t})}raiseSituationalFatal(t,e){return this.raiseFatal("inbox.situational_context.missing","situational context marker has no readable sidecar",e,{entry_id:t})}escapeFatal(t,e){if(!this.fatalEscaped){if(this.fatalEscaped=!0,e==="throw")throw t;queueMicrotask(()=>{throw t})}}scheduleHandlerFatal(t,e){let i;if(t instanceof D)i=t;else try{this.raiseReadFatal(e,t)}catch(r){i=r}this.escapeFatal(i,"schedule")}runTick(t="throw"){if(!this.canMutate())return;if(this.tickInFlight!==void 0){this.rerun=!0;return}const e=this.tick().then(()=>{this.retryNeeded=!1,this.polledOk=!0}).catch(i=>{if(this.canMutate()){if(R(i)){this.retryNeeded=!0,w({level:"warn",event:"inbox.read.failed",outcome:"failed",error:i,error_class:k(i),fields:{stage:"poll"}});return}if(this.polledOk&&K(i)){this.clearTimer(),w({level:"info",event:"inbox.poll.token_revoked",outcome:"succeeded",fields:{stage:"poll"}}),N()?.("token-revoked");return}this.scheduleHandlerFatal(i,"poll")}}).finally(()=>{this.tickInFlight===e&&(this.tickInFlight=void 0),this.syncTimers(),this.rerun&&this.canMutate()&&(this.rerun=!1,this.runTick("schedule"))});this.tickInFlight=e}async claimMail(t){this.assertNodeOwnership(t,"poll");let e;try{e=await L(t,"wake",this.reportBodies)}catch(a){throw R(a)&&(this.replayUnseenInherited=!0),a}if(!this.canMutate()||e.action==="hold")return!1;const i=this.replayUnseenInherited;this.replayUnseenInherited=!1,this.nodeId=t,this.seeded=!0;const r=new Set(e.deliveries.map(a=>a.entry.entry_id)),n=this.buffer.filter(a=>!r.has(a.entry_id));for(const a of n)this.claims.delete(a.entry_id);if(this.buffer.splice(0,this.buffer.length,...this.buffer.filter(a=>r.has(a.entry_id))),n.length>0){const a=globalThis[Symbol.for("@crouton-kit/crtr:watcher-cancel-probe")];typeof a=="function"&&a(n.map(l=>l.entry_id),process.hrtime.bigint())}let o=!1;for(const a of e.deliveries){const l=a.entry.entry_id;this.claims.has(l)||(a.entry=Q(a.entry),this.claims.set(l,a),a.handoff==="inherited"&&!i?m.fresh(d=>{this.pendingHandoffs.push({...x([a.entry],"visible"),operationId:d,settleAtAgentSettled:!0})}):(this.buffer.push(a.entry),this.lastArrival=Date.now(),o=!0))}return o&&this.armDebounce(),this.syncTimers(),!0}async tick(){const t=E();if(t!==void 0){if(this.pendingCommit!==void 0){await this.retryPendingCommit(t)==="refresh_held"&&this.canMutate()&&this.dispose("refresh",!1);return}this.promptJoinInFlight||P()||this.pendingHandoffs.length>=b||await this.claimMail(t)&&this.buffer.length>0&&(!this.runNode||this.isIdle()||this.buffer.some(e=>e.tier==="critical"))&&Date.now()-this.lastArrival>=this.cadence.debounceMs&&await this.flushOrdinary(t)}}missingChannelEntries(t){const e=t.filter(n=>this.claims.has(n.entry_id)),i=this.coveredEntryIds("hidden"),r=this.coveredEntryIds("visible");return{hiddenEntries:e.filter(n=>(n.data?.situational===!0||n.data?.situationalOnly===!0)&&!i.has(n.entry_id)),visibleEntries:e.filter(n=>n.data?.situationalOnly!==!0&&!r.has(n.entry_id))}}visibleRoute(t){return t.length===0?"none":this.isIdle()?"idle":this.runNode?t.some(e=>e.tier==="critical")?"critical":"none":t.some(e=>e.tier==="critical")?"critical":t.some(e=>e.tier==="urgent"||e.kind==="final")?"steer":"followUp"}buildPlan(t){const{hiddenEntries:e,visibleEntries:i}=this.missingChannelEntries(t),r=new Set(e.map(u=>u.entry_id)),n=new Set(i.map(u=>u.entry_id)),o=t.filter(u=>r.has(u.entry_id)||n.has(u.entry_id)),a=this.isIdle()&&o.length>0&&o.every(u=>u.tier==="deferred"),l=this.visibleRoute(i)==="critical",d=!a&&!l&&e.length>0,c=!a&&!l&&i.length>0;return{hiddenEntries:e,visibleEntries:i,deferredHold:a,sendCount:Number(d)+Number(c)}}ordinaryPlan(){return this.buildPlan(this.buffer)}async contextCards(t,e){const i=[];if(e.some(r=>r.data?.situational===!0)){const r=e.at(-1).entry_id;let n;try{n=(await F(t)).situational_card}catch(o){this.raiseSituationalFatal(r,o)}n===null&&this.raiseSituationalFatal(r,new Error(`missing situational context for entry ${r}`)),i.push(n)}for(const r of e)r.data?.situationalOnly===!0&&i.push(...j(r));return i}digest(t,e){try{return X(t)}catch(i){this.raiseReadFatal(e,i)}}sendDelivery(t,e,i,r,n){this.pendingHandoffs.length>=b&&this.raiseReadFatal(r,new Error(`pending handoff capacity exceeded before ${t} send`));let o;try{o=x(e,t)}catch(d){this.raiseReadFatal(r,d)}let a;try{m.fresh(d=>{a=d,v({level:"info",event:"inbox.delivery.started",operation_id:d,fields:y(o)}),i()})}catch(d){return a===void 0&&this.raiseReadFatal(r,d),T(a,o,"send",d),"failed"}a===void 0&&this.raiseReadFatal(r,new Error(`${t} delivery returned without an operation id`));const l={operationId:a,...o,...n===void 0?{}:{awaitingDequeue:n}};return this.canMutate()?(this.pendingHandoffs.push(l),"handed-off"):(this.disposed&&this.disposalReason!==void 0&&M(l,this.disposalReason),"inactive")}sendContext(t,e,i,r,n,o){const a=r==="steer"||r==="followUp"?r:this.isIdle()?void 0:"followUp",l=a===void 0||i.length===0?void 0:_(i.at(-1));return this.sendDelivery("hidden",e,()=>{this.runNode&&n&&this.pi.appendEntry("crtr-turn-input",{message_ids:e.map(d=>d.entry_id)}),i.forEach((d,c)=>{const u=n&&c===i.length-1;this.pi.sendMessage({customType:I,content:d,display:!0,details:{nodeId:t}},a!==void 0?{deliverAs:a}:u?{deliverAs:"followUp",triggerTurn:!0}:{})})},o,l)}sendVisible(t,e,i,r,n,o){return this.sendDelivery("visible",t,()=>{if(n!=="idle"&&n!=="steer"&&n!=="followUp")throw new Error(`visible send requested for non-send route ${n}`);for(const a of e)this.pi.sendMessage({customType:I,content:a,display:!0},n==="idle"?{}:{deliverAs:n});if(Z(i,r),n==="idle"){const a=re();if(a===void 0)throw new Error("broker idle-turn admission is unavailable");a(i,this.runNode?t.map(l=>l.entry_id):[])}else this.pi.sendUserMessage(i,{deliverAs:n})},o,n==="steer"||n==="followUp"?_(i):void 0)}abortCritical(){try{this.lastCtx?.abort?.()}catch{}}requeueFromBatch(t,e){if(e.size===0)return;const i=t.filter(o=>e.has(o.entry_id)),r=new Set(i.map(o=>o.entry_id)),n=this.buffer.filter(o=>!r.has(o.entry_id));this.buffer.splice(0,this.buffer.length,...i,...n)}async executePlan(t,e,i){const r=new Set,n=this.visibleRoute(e.visibleEntries);if(n==="critical"){if(this.abortCritical(),!this.canMutate())return{inactive:!0,requeueEntryIds:r};for(const d of e.visibleEntries)r.add(d.entry_id);for(const d of e.hiddenEntries)r.add(d.entry_id);return{inactive:!1,requeueEntryIds:r}}if(this.runNode&&n==="none")return{inactive:!1,requeueEntryIds:new Set([...e.hiddenEntries,...e.visibleEntries].map(d=>d.entry_id))};const o=e.visibleEntries.length>0?this.digest(e.visibleEntries,i):[],[a,...l]=o;for(const d of l)for(const c of d.entries)r.add(c.entry_id);if(e.hiddenEntries.length>0){const d=await this.contextCards(t,e.hiddenEntries),c=this.sendContext(t,e.hiddenEntries,d,n,a===void 0,i);if(c==="inactive")return{inactive:!0,requeueEntryIds:r};if(c==="failed")for(const u of e.hiddenEntries)r.add(u.entry_id)}if(!this.canMutate())return{inactive:!0,requeueEntryIds:r};if(a!==void 0){const d=this.sendVisible(a.entries,a.cards,a.text,le(a),n,i);if(d==="inactive")return{inactive:!0,requeueEntryIds:r};if(d==="failed")for(const c of a.entries)r.add(c.entry_id);else a.kind==="human"&&(await ee(t,a.text).catch(()=>{}),te(t,a.text))}return{inactive:!1,requeueEntryIds:r}}async flushOrdinary(t){if(this.buffer.length===0)return"empty";const e=this.ordinaryPlan();if(e.deferredHold)return"held";if(this.pendingHandoffs.length+e.sendCount>b)return"capacity-blocked";const i=[...this.buffer],r=await this.executePlan(t,e,"poll");return r.inactive||(this.buffer.splice(0,i.length),this.requeueFromBatch(i,r.requeueEntryIds)),"attempted"}async prependHeldDeferredInbox(t){if(!this.canMutate()||this.nodeId===void 0||P())return;const e=this.ordinaryPlan();if(!e.deferredHold)return;const i=e.visibleEntries.length===0?[]:this.digest(e.visibleEntries,"poll"),r=+(e.hiddenEntries.length>0)+i.length;if(this.pendingHandoffs.length+r>b)return;const n=[...this.buffer];this.promptJoinInFlight=!0;try{this.runNode&&this.pi.appendEntry("crtr-turn-input",{message_ids:[...e.hiddenEntries,...e.visibleEntries].map(o=>o.entry_id)}),e.hiddenEntries.length>0&&await this.sendPromptJoinDelivery(t,"hidden",e.hiddenEntries,await this.contextCards(this.nodeId,e.hiddenEntries));for(const o of i)await this.sendPromptJoinDelivery(t,"visible",o.entries,[...o.cards,o.text]);if(!this.canMutate())return;this.buffer.splice(0,n.length)}finally{this.promptJoinInFlight=!1}}async sendPromptJoinDelivery(t,e,i,r){let n;try{n=x(i,e)}catch(l){this.raiseReadFatal("poll",l)}let o;try{await m.fresh(async l=>{o=l,v({level:"info",event:"inbox.delivery.started",operation_id:l,fields:y(n)});for(const d of r)await t.sendCustomMessage({customType:I,content:d,display:!0})})}catch(l){throw o===void 0&&this.raiseReadFatal("poll",l),T(o,n,"send",l),l}o===void 0&&this.raiseReadFatal("poll",new Error(`${e} prompt-join delivery returned without an operation id`));const a={operationId:o,...n,settleAtAgentSettled:!0};if(!this.canMutate()){this.disposed&&this.disposalReason!==void 0&&M(a,this.disposalReason);return}this.pendingHandoffs.push(a)}coveredEntryIds(t){const e=new Set;for(const i of this.pendingHandoffs)if(i.channel===t)for(const r of i.entryIds)e.add(r);return e}retryPendingCommit(t){const e=this.pendingCommit;if(e===void 0)return Promise.resolve("settled");if(e.attempt!==void 0)return e.attempt;const i=this.attemptPendingCommit(t,e).finally(()=>{e.attempt===i&&(e.attempt=void 0)});return e.attempt=i,i}async attemptPendingCommit(t,e){let i=!1;try{const n=await z(t,e.receipts);if(n.action==="hold")return"refresh_held";for(const o of e.receipts){const a=n.results.find(l=>l.delivery_id===o.delivery_id);if(a?.outcome!==o.disposition&&a?.outcome!=="stale")throw new Error("daemon returned no terminal mail settlement outcome")}i=n.results.every(o=>o.outcome==="acknowledged")}catch(n){if(this.pendingCommit===e)for(const o of e.handoffs)T(o.operationId,o,"receipt_commit",n);return String(n?.message).startsWith("conversation_write_failed")&&(O({level:"error",event:"broker.exited",node_id:t,error:n,fields:{fault:"conversation_write_failed"}}),await V(t,n),N()?.("conversation_write_failed",1)),"held"}if(this.pendingCommit!==e)return"settled";for(const n of e.entryIds)this.claims.delete(n);if(e.receipts.some(n=>n.disposition==="released")){const n=new Set(e.entryIds);this.buffer.splice(0,this.buffer.length,...this.buffer.filter(o=>!n.has(o.entry_id)))}this.pendingCommit=void 0;const r=this.detachPending(n=>n.entryIds.every(o=>!this.claims.has(o)));for(const n of r)i&&fe(n);return"settled"}async settle(t,e,i,r=!1){if(i)try{if(e?.hasPendingMessages?.()===!0)return"held"}catch{}if(this.pendingCommit!==void 0)return this.retryPendingCommit(t);const n=Y(),o=!n&&this.pendingHandoffs.some(h=>h.awaitingDequeue!==void 0)&&await this.nodeRefreshing(t);if(this.pendingCommit!==void 0)return this.retryPendingCommit(t);const a=new Set(this.buffer.map(h=>h.entry_id)),l=new Set(o?this.pendingHandoffs.filter(h=>h.awaitingDequeue!==void 0).flatMap(h=>[...h.entryIds]):[]),d=this.pendingHandoffs.filter(h=>(r||h.settleAtAgentSettled!==!0)&&h.entryIds.every(g=>!l.has(g))),c=new Set(d.flatMap(h=>[...h.entryIds])),u=[...this.claims.keys()].filter(h=>n||c.has(h)&&!a.has(h));return u.length===0?"settled":(this.pendingCommit={handoffs:d,entryIds:u,receipts:u.map(h=>{const g=this.claims.get(h);return{delivery_id:g.delivery_id,attempts:g.attempts,disposition:n?"released":"acknowledged"}})},this.retryPendingCommit(t))}async nodeRefreshing(t){try{return(await F(t)).node.intent==="refresh"}catch{return!0}}detachPending(t){const e=[];for(let i=this.pendingHandoffs.length-1;i>=0;i-=1){const r=this.pendingHandoffs[i];t(r)&&e.push(...this.pendingHandoffs.splice(i,1))}return e.reverse(),e}}function pe(s,t){p?.dispose("reload",!1);const e=new me(s,t,++oe);p=e;const i=f(r=>e.prependHeldDeferredInbox(r),"promptJoin");globalThis[ae]=i;try{e.start()}catch(r){throw e.dispose("reload",!1),r}return e}f(pe,"installWatcher");function ye(s){const t=pe(s,de());return()=>t.dispose("shutdown",!1)}f(ye,"registerCanvasInboxWatcher");var Ne=ye;export{Ne as default,ye as registerCanvasInboxWatcher};
1
+ var J=Object.defineProperty;var l=(r,t)=>J(r,"name",{value:t,configurable:!0});import{envInboxBetweenTurns as $,envNodeId as G,envProfileId as X,envWatcherDebounceMs as j,envWatcherTickMs as z,envWatcherBackstopMs as L}from"../shared/env.js";import{coalesceBrokerInbox as V,entryCards as Q}from"../core/runtime/broker/inbox.js";import{acknowledgeBrokerMail as Y,claimBrokerMail as K,brokerExtensionState as O,recordConversationWriteFault as Z}from"../core/runtime/broker/daemon-ops.js";import{materializeSpilledBody as ee}from"../core/runtime/broker/spilled-body.js";import{brokerSignalClient as P}from"../core/runtime/broker/signal-stream.js";import{wasTurnRefused as te}from"../core/runtime/broker/extension-abort.js";import{isDaemonTransportApiError as A,isTokenRevokedApiError as ie}from"../api/errors.js";import{announceMessageOrigin as ne,messageSignature as C}from"../core/runtime/stamp/channel.js";import{captureGoalIfAbsent as se,generateAndCommitInitialName as re}from"./broker-local.js";import{emitEvent as D}from"../core/events/emit.js";import{errorClassFromError as ae}from"../core/events/errors.js";import{operationIdContext as g}from"../core/events/operation-id.js";import{SITUATIONAL_CONTEXT_CUSTOM_TYPE as x}from"../shared/generated-context.js";import{isProfilePaused as de}from"../core/profiles/manifest.js";import{brokerDisposeAndExit as H,brokerIdleTurnStart as oe,brokerRunTurnBusy as fe,brokerRunSteer as _,HELD_DEFERRED_INBOX_PROMPT_JOIN as le}from"../core/runtime/broker/held-deferred-inbox.js";const E=64,U=16;let v,he=0;class q extends Error{static{l(this,"WatcherFatalError")}constructor(t,e){super(t,{cause:e}),this.name="WatcherFatalError"}}function ue(){return{tickMs:z(),debounceMs:j()}}l(ue,"resolveCadence");function T(){const r=G();return r===void 0||r.trim()===""?void 0:r}l(T,"currentNodeId");function B(){const r=X();return r!==void 0&&r!==""&&de(r)}l(B,"profilePaused");function M(r){try{return ae(r)}catch{return{class:"unknown"}}}l(M,"safeErrorClass");function w(r){try{D(r)}catch{}}l(w,"emitBestEffort");function I(r){try{g.fresh(t=>{w({...r,operation_id:t})})}catch{}}l(I,"emitFreshBestEffort");function S(r,t){try{g.run(r,()=>{w({...t,operation_id:r})})}catch{}}l(S,"emitStoredBestEffort");function ce(r){return r.kind==="human"?{type:"human"}:{type:"runtime",kind:"inbox",entries:r.entries.map(t=>({...t.from===null?{}:{from:t.from},...t.from_name===void 0?{}:{fromName:t.from_name},kind:t.kind,tier:t.tier,...t.ref===void 0?{}:{ref:t.ref}}))}}l(ce,"deliveryOrigin");function k(r,t){if(r.length===0)throw new Error(`cannot create an empty ${t} delivery`);const e=[...new Set(r.map(n=>n.operation_id))].sort(),i=e.length-U;return{channel:t,entryIds:r.map(n=>n.entry_id),throughEntryId:r.at(-1).entry_id,sourceOperationIds:e.slice(0,U),...i>0?{sourceOperationIdsOmitted:i}:{}}}l(k,"deliveryFacts");function b(r){return{channel:r.channel,entry_ids:r.entryIds,through_entry_id:r.throughEntryId,source_operation_ids:r.sourceOperationIds,...r.sourceOperationIdsOmitted===void 0?{}:{source_operation_ids_omitted:r.sourceOperationIdsOmitted}}}l(b,"deliveryFields");function me(r){S(r.operationId,{level:"info",event:"inbox.delivery.completed",outcome:"succeeded",fields:b(r)})}l(me,"emitDeliveryCompleted");function F(r,t,e,i){S(r,{level:"error",event:"inbox.delivery.failed",outcome:"failed",error:i,error_class:M(i),fields:{stage:e,...b(t)}})}l(F,"emitDeliveryFailure");function R(r,t){S(r.operationId,{level:"error",event:"inbox.delivery.failed",outcome:"failed",error_class:{class:"unknown"},fields:{stage:"abandoned",reason:t,...b(r)}})}l(R,"emitDeliveryAbandoned");function pe(r){return typeof r=="string"?r:Array.isArray(r)?r.filter(t=>typeof t=="object"&&t!==null&&typeof t.text=="string").map(t=>t.text).join(`
2
+ `):""}l(pe,"messageText");function W(r){return r==="quit"||r==="reload"||r==="new"||r==="resume"||r==="fork"}l(W,"isSessionShutdownReason");function ye(r){switch(r){case"quit":return"shutdown";case"reload":case"new":case"resume":case"fork":return"reload";default:return r}}l(ye,"disposalReasonForShutdown");function ge(r){return typeof r=="string"?r.slice(0,128):r===null?"null":r===void 0?"undefined":typeof r=="number"||typeof r=="boolean"||typeof r=="bigint"?String(r).slice(0,128):typeof r}l(ge,"boundedRuntimeReason");class be{static{l(this,"CanvasInboxWatcher")}pi;cadence;generation;timer;backstop;debounceTimer;signalUnsubscribers=[];rerun=!1;retryNeeded=!1;polledOk=!1;armed=!1;lastCtx;streaming=!1;runNode=$();nodeId;claims=new Map;reportBodies=new Map;tickInFlight;buffer=[];lastArrival=0;pendingHandoffs=[];pendingCommit;promptJoinInFlight=!1;replayUnseenInherited=!1;seeded=!1;disposed=!1;disposalReason;fatalError;fatalEscaped=!1;constructor(t,e,i){this.pi=t,this.cadence=e,this.generation=i}start(){const t=l((e,i)=>{this.canMutate()&&this.captureCtx(e,i)},"capture");this.pi.on("session_start",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.arm())}),this.pi.on("turn_end",t),this.pi.on("agent_start",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.streaming=!0)}),this.pi.on("agent_end",(e,i)=>{this.canMutate()&&(this.captureCtx(e,i),this.streaming=!1)}),this.pi.on("message_end",e=>{if(!this.canMutate())return;const i=e?.message;if(i?.role!=="user"&&i?.role!=="custom"||!this.pendingHandoffs.some(d=>d.awaitingDequeue!==void 0))return;const n=C(pe(i.content)),s=this.pendingHandoffs.find(d=>d.awaitingDequeue===n);s!==void 0&&delete s.awaitingDequeue}),this.pi.on("agent_settled",async(e,i)=>{if(!this.canMutate())return;this.captureCtx(e,i);const n=T();if(n!==void 0)try{this.assertNodeOwnership(n,"settle"),await this.tickInFlight,this.seeded||await this.claimMail(n),this.returnUnconsumedSteers();const s=await this.settle(n,i,!1,!0);s==="refresh_held"&&this.dispose("refresh",!1),this.runNode&&s==="settled"&&this.canMutate()&&this.buffer.length>0&&await this.flushOrdinary(n),this.syncTimers()}catch(s){this.dispose("shutdown",!1,void 0,{error:s,stage:"settle"})}}),this.pi.on("session_shutdown",async e=>{if(!this.canMutate())return;let i;try{i=e?.reason}catch{i=void 0}const n=W(i)?ye(i):"reload";W(i)||I({level:"warn",event:"inbox.lifecycle.unknown_shutdown_reason",outcome:"failed",fields:{reason:ge(i)}});try{await this.disposeAtSettlement(n,this.lastCtx)}catch(s){this.dispose(n,!1,void 0,{error:s,stage:"settle"})}})}arm(){if(this.armed)return;this.armed=!0;const t=P();this.signalUnsubscribers.push(t.subscribe("state",()=>this.syncTimers()),t.subscribe("mail",()=>this.runTick("schedule")),t.subscribe("resync",()=>this.runTick("schedule"))),this.syncTimers(),t.state!=="connected"&&this.runTick("schedule")}async disposeAtSettlement(t,e){const i=T();if(i===void 0){this.dispose(t,!1);return}try{this.assertNodeOwnership(i,"settle"),this.returnUnconsumedSteers();const n=await this.settle(i,e,t!=="refresh");this.dispose(n==="refresh_held"?"refresh":t,!1)}catch(n){this.dispose(t,!1,void 0,{error:n,stage:"settle"})}}dispose(t,e,i,n){if(this.disposed)return;this.disposed=!0,this.disposalReason=t,this.clearTimer();for(const d of this.signalUnsubscribers.splice(0))d();const s=n;{const d=[...new Set([...this.pendingCommit?.handoffs??[],...this.pendingHandoffs.splice(0)])];this.buffer.length=0,this.claims.clear(),this.reportBodies.clear(),this.pendingCommit=void 0,this.promptJoinInFlight=!1,this.replayUnseenInherited=!1,this.nodeId=void 0,this.lastCtx=void 0,this.lastArrival=0,this.streaming=!1,this.seeded=!1,this.ownsCurrentGeneration()&&(v=void 0);for(const a of d)R(a,t)}if(s!==void 0){this.scheduleHandlerFatal(s.error,s.stage);return}}ownsCurrentGeneration(){return v===this&&v.generation===this.generation}canMutate(){return this.ownsCurrentGeneration()&&!this.disposed&&this.fatalError===void 0}captureCtx(t,e){e!==void 0&&(this.lastCtx=e)}clearTimer(){this.timer!==void 0&&clearInterval(this.timer),this.backstop!==void 0&&clearInterval(this.backstop),this.debounceTimer!==void 0&&clearTimeout(this.debounceTimer),this.timer=this.backstop=this.debounceTimer=void 0}syncTimers(){if(!this.armed||!this.canMutate())return;const t=P().state,e=t==="connected",i=t!=="stopped"&&(t!=="connected"||this.buffer.length>0||this.pendingCommit!==void 0||this.retryNeeded);i&&this.timer===void 0?(this.timer=setInterval(()=>this.runTick("schedule"),this.cadence.tickMs),this.timer.unref?.()):!i&&this.timer!==void 0&&(clearInterval(this.timer),this.timer=void 0),e&&this.backstop===void 0?(this.backstop=setInterval(()=>this.runTick("schedule"),L()),this.backstop.unref?.()):!e&&this.backstop!==void 0&&(clearInterval(this.backstop),this.backstop=void 0),this.buffer.length===0&&this.debounceTimer!==void 0&&(clearTimeout(this.debounceTimer),this.debounceTimer=void 0)}armDebounce(){this.debounceTimer!==void 0&&clearTimeout(this.debounceTimer);const t=Math.max(0,this.lastArrival+this.cadence.debounceMs-Date.now());this.debounceTimer=setTimeout(()=>{this.debounceTimer=void 0,this.runTick("schedule")},t),this.debounceTimer.unref?.()}isIdle(){if(this.runNode&&fe())return!1;try{if(typeof this.lastCtx?.isIdle=="function")return this.lastCtx.isIdle()===!0}catch{}return!this.streaming}assertNodeOwnership(t,e){!this.seeded||this.nodeId===void 0||this.nodeId===t||this.raiseReadFatal(e,new Error(`watcher node changed from ${this.nodeId} to ${t}`))}raiseFatal(t,e,i,n){throw this.fatalError===void 0&&(this.fatalError=new q(e,i),this.clearTimer(),I({level:"error",event:t,outcome:"failed",error:i,error_class:M(i),fields:n})),this.fatalError}raiseReadFatal(t,e){return this.raiseFatal("inbox.read.failed",`inbox ${t} state is corrupt`,e,{stage:t})}raiseSituationalFatal(t,e){return this.raiseFatal("inbox.situational_context.missing","situational context marker has no readable sidecar",e,{entry_id:t})}escapeFatal(t,e){if(!this.fatalEscaped){if(this.fatalEscaped=!0,e==="throw")throw t;queueMicrotask(()=>{throw t})}}scheduleHandlerFatal(t,e){let i;if(t instanceof q)i=t;else try{this.raiseReadFatal(e,t)}catch(n){i=n}this.escapeFatal(i,"schedule")}runTick(t="throw"){if(!this.canMutate())return;if(this.tickInFlight!==void 0){this.rerun=!0;return}const e=this.tick().then(()=>{this.retryNeeded=!1,this.polledOk=!0}).catch(i=>{if(this.canMutate()){if(A(i)){this.retryNeeded=!0,I({level:"warn",event:"inbox.read.failed",outcome:"failed",error:i,error_class:M(i),fields:{stage:"poll"}});return}if(this.polledOk&&ie(i)){this.clearTimer(),I({level:"info",event:"inbox.poll.token_revoked",outcome:"succeeded",fields:{stage:"poll"}}),H()?.("token-revoked");return}this.scheduleHandlerFatal(i,"poll")}}).finally(()=>{this.tickInFlight===e&&(this.tickInFlight=void 0),this.syncTimers(),this.rerun&&this.canMutate()&&(this.rerun=!1,this.runTick("schedule"))});this.tickInFlight=e}async claimMail(t){this.assertNodeOwnership(t,"poll");let e;try{e=await K(t,"wake",this.reportBodies)}catch(a){throw A(a)&&(this.replayUnseenInherited=!0),a}if(!this.canMutate()||e.action==="hold")return!1;const i=this.replayUnseenInherited;this.replayUnseenInherited=!1,this.nodeId=t,this.seeded=!0;const n=new Set(e.deliveries.map(a=>a.entry.entry_id)),s=this.buffer.filter(a=>!n.has(a.entry_id));for(const a of s)this.claims.delete(a.entry_id);if(this.buffer.splice(0,this.buffer.length,...this.buffer.filter(a=>n.has(a.entry_id))),s.length>0){const a=globalThis[Symbol.for("@crouton-kit/crtr:watcher-cancel-probe")];typeof a=="function"&&a(s.map(f=>f.entry_id),process.hrtime.bigint())}let d=!1;for(const a of e.deliveries){const f=a.entry.entry_id;this.claims.has(f)||(a.entry=ee(a.entry),this.claims.set(f,a),a.handoff==="inherited"&&!i?g.fresh(o=>{this.pendingHandoffs.push({...k([a.entry],"visible"),operationId:o,settleAtAgentSettled:!0})}):(this.buffer.push(a.entry),this.lastArrival=Date.now(),d=!0))}return d&&this.armDebounce(),this.syncTimers(),!0}async tick(){const t=T();if(t!==void 0){if(this.pendingCommit!==void 0){await this.retryPendingCommit(t)==="refresh_held"&&this.canMutate()&&this.dispose("refresh",!1);return}this.promptJoinInFlight||B()||this.pendingHandoffs.length>=E||await this.claimMail(t)&&this.buffer.length>0&&(!this.runNode||this.isIdle()||this.buffer.some(e=>e.tier==="critical")||this.buffer.some(e=>this.isRunSteer(e))&&_()?.steerable())&&Date.now()-this.lastArrival>=this.cadence.debounceMs&&await this.flushOrdinary(t)}}missingChannelEntries(t){const e=t.filter(s=>this.claims.has(s.entry_id)),i=this.coveredEntryIds("hidden"),n=this.coveredEntryIds("visible");return{hiddenEntries:e.filter(s=>(s.data?.situational===!0||s.data?.situationalOnly===!0)&&!i.has(s.entry_id)),visibleEntries:e.filter(s=>s.data?.situationalOnly!==!0&&!n.has(s.entry_id))}}isRunSteer(t){return this.runNode&&t.kind==="message"&&(t.from??"system")==="human"&&t.data?.steer===!0}returnUnconsumedSteers(){const t=_()?.takeUnconsumed()??[];if(t.length===0)return;const e=new Set(t.flatMap(n=>n.messageIds));this.detachPending(n=>n.settlesThroughBridge===!0&&n.entryIds.every(s=>e.has(s)));const i=[...e].map(n=>this.claims.get(n)?.entry).filter(n=>n!==void 0);this.requeueFromBatch(i,e)}visibleRoute(t){return t.length===0?"none":this.isIdle()?"idle":this.runNode?t.some(e=>e.tier==="critical")?"critical":t.some(e=>this.isRunSteer(e))&&_()?.steerable()?"steer":"none":t.some(e=>e.tier==="critical")?"critical":t.some(e=>e.tier==="urgent"||e.kind==="final")?"steer":"followUp"}buildPlan(t){const{hiddenEntries:e,visibleEntries:i}=this.missingChannelEntries(t),n=new Set(e.map(u=>u.entry_id)),s=new Set(i.map(u=>u.entry_id)),d=t.filter(u=>n.has(u.entry_id)||s.has(u.entry_id)),a=this.isIdle()&&d.length>0&&d.every(u=>u.tier==="deferred"),f=this.visibleRoute(i)==="critical",o=!a&&!f&&e.length>0,m=!a&&!f&&i.length>0;return{hiddenEntries:e,visibleEntries:i,deferredHold:a,sendCount:Number(o)+Number(m)}}ordinaryPlan(){return this.buildPlan(this.buffer)}async contextCards(t,e){const i=[];if(e.some(n=>n.data?.situational===!0)){const n=e.at(-1).entry_id;let s;try{s=(await O(t)).situational_card}catch(d){this.raiseSituationalFatal(n,d)}s===null&&this.raiseSituationalFatal(n,new Error(`missing situational context for entry ${n}`)),i.push(s)}for(const n of e)n.data?.situationalOnly===!0&&i.push(...Q(n));return i}digest(t,e){try{return V(t)}catch(i){this.raiseReadFatal(e,i)}}sendDelivery(t,e,i,n,s){this.pendingHandoffs.length>=E&&this.raiseReadFatal(n,new Error(`pending handoff capacity exceeded before ${t} send`));let d;try{d=k(e,t)}catch(o){this.raiseReadFatal(n,o)}let a;try{g.fresh(o=>{a=o,w({level:"info",event:"inbox.delivery.started",operation_id:o,fields:b(d)}),i()})}catch(o){return a===void 0&&this.raiseReadFatal(n,o),F(a,d,"send",o),"failed"}a===void 0&&this.raiseReadFatal(n,new Error(`${t} delivery returned without an operation id`));const f={operationId:a,...d,...s===void 0?{}:{awaitingDequeue:s}};return this.canMutate()?(this.pendingHandoffs.push(f),"handed-off"):(this.disposed&&this.disposalReason!==void 0&&R(f,this.disposalReason),"inactive")}sendContext(t,e,i,n,s,d){const a=n==="steer"||n==="followUp"?n:this.isIdle()?void 0:"followUp",f=a===void 0||i.length===0?void 0:C(i.at(-1));return this.sendDelivery("hidden",e,()=>{this.runNode&&s&&this.pi.appendEntry("crtr-turn-input",{message_ids:e.map(o=>o.entry_id)}),i.forEach((o,m)=>{const u=s&&m===i.length-1;this.pi.sendMessage({customType:x,content:o,display:!0,details:{nodeId:t}},a!==void 0?{deliverAs:a}:u?{deliverAs:"followUp",triggerTurn:!0}:{})})},d,f)}sendVisible(t,e,i,n,s,d){return this.sendDelivery("visible",t,()=>{if(s!=="idle"&&s!=="steer"&&s!=="followUp")throw new Error(`visible send requested for non-send route ${s}`);for(const a of e)this.pi.sendMessage({customType:x,content:a,display:!0},s==="idle"?{}:{deliverAs:s});if(ne(i,n),s==="idle"){const a=oe();if(a===void 0)throw new Error("broker idle-turn admission is unavailable");a(i,this.runNode?t.map(f=>f.entry_id):[])}else this.pi.sendUserMessage(i,{deliverAs:s})},d,s==="steer"||s==="followUp"?C(i):void 0)}abortCritical(){try{this.lastCtx?.abort?.()}catch{}}requeueFromBatch(t,e){if(e.size===0)return;const i=t.filter(d=>e.has(d.entry_id)),n=new Set(i.map(d=>d.entry_id)),s=this.buffer.filter(d=>!n.has(d.entry_id));this.buffer.splice(0,this.buffer.length,...i,...s)}async executePlan(t,e,i){const n=new Set,s=this.visibleRoute(e.visibleEntries);if(s==="critical"){if(this.abortCritical(),!this.canMutate())return{inactive:!0,requeueEntryIds:n};for(const o of e.visibleEntries)n.add(o.entry_id);for(const o of e.hiddenEntries)n.add(o.entry_id);return{inactive:!1,requeueEntryIds:n}}if(this.runNode&&s==="none")return{inactive:!1,requeueEntryIds:new Set([...e.hiddenEntries,...e.visibleEntries].map(o=>o.entry_id))};if(this.runNode&&s==="steer"){let o=-1;e.visibleEntries.forEach((h,y)=>{this.isRunSteer(h)&&(o=y)});const m=e.visibleEntries.slice(0,o+1);if(e.hiddenEntries.length>0||m.some(h=>h.kind!=="message"||h.tier==="deferred"||(h.from??"system")!=="human"))return{inactive:!1,requeueEntryIds:new Set([...e.hiddenEntries,...e.visibleEntries].map(h=>h.entry_id))};const u=this.digest(m,i),c=u.map(h=>({text:h.text,messageIds:h.entries.map(y=>y.entry_id),cards:h.cards}));if(_()?.accept(c)!=="accepted")return{inactive:!1,requeueEntryIds:new Set([...e.hiddenEntries,...e.visibleEntries].map(h=>h.entry_id))};for(const h of u){const y=k(h.entries,"visible");g.fresh(N=>{w({level:"info",event:"inbox.delivery.started",operation_id:N,fields:b(y)}),this.pendingHandoffs.push({...y,operationId:N,settlesThroughBridge:!0})})}const p=new Set(m.map(h=>h.entry_id));for(const h of e.visibleEntries)p.has(h.entry_id)||n.add(h.entry_id);for(const h of e.hiddenEntries)n.add(h.entry_id);return{inactive:!1,requeueEntryIds:n}}const d=e.visibleEntries.length>0?this.digest(e.visibleEntries,i):[],[a,...f]=d;for(const o of f)for(const m of o.entries)n.add(m.entry_id);if(e.hiddenEntries.length>0){const o=await this.contextCards(t,e.hiddenEntries),m=this.sendContext(t,e.hiddenEntries,o,s,a===void 0,i);if(m==="inactive")return{inactive:!0,requeueEntryIds:n};if(m==="failed")for(const u of e.hiddenEntries)n.add(u.entry_id)}if(!this.canMutate())return{inactive:!0,requeueEntryIds:n};if(a!==void 0){const o=this.sendVisible(a.entries,a.cards,a.text,ce(a),s,i);if(o==="inactive")return{inactive:!0,requeueEntryIds:n};if(o==="failed")for(const m of a.entries)n.add(m.entry_id);else a.kind==="human"&&(await se(t,a.text).catch(()=>{}),re(t,a.text))}return{inactive:!1,requeueEntryIds:n}}async flushOrdinary(t){if(this.buffer.length===0)return"empty";const e=this.ordinaryPlan();if(e.deferredHold)return"held";if(this.pendingHandoffs.length+e.sendCount>E)return"capacity-blocked";const i=[...this.buffer],n=await this.executePlan(t,e,"poll");return n.inactive||(this.buffer.splice(0,i.length),this.requeueFromBatch(i,n.requeueEntryIds)),"attempted"}async prependHeldDeferredInbox(t){if(!this.canMutate()||this.nodeId===void 0||B())return;const e=this.ordinaryPlan();if(!e.deferredHold)return;const i=e.visibleEntries.length===0?[]:this.digest(e.visibleEntries,"poll"),n=+(e.hiddenEntries.length>0)+i.length;if(this.pendingHandoffs.length+n>E)return;const s=[...this.buffer];this.promptJoinInFlight=!0;try{this.runNode&&this.pi.appendEntry("crtr-turn-input",{message_ids:[...e.hiddenEntries,...e.visibleEntries].map(d=>d.entry_id)}),e.hiddenEntries.length>0&&await this.sendPromptJoinDelivery(t,"hidden",e.hiddenEntries,await this.contextCards(this.nodeId,e.hiddenEntries));for(const d of i)await this.sendPromptJoinDelivery(t,"visible",d.entries,[...d.cards,d.text]);if(!this.canMutate())return;this.buffer.splice(0,s.length)}finally{this.promptJoinInFlight=!1}}async sendPromptJoinDelivery(t,e,i,n){let s;try{s=k(i,e)}catch(f){this.raiseReadFatal("poll",f)}let d;try{await g.fresh(async f=>{d=f,w({level:"info",event:"inbox.delivery.started",operation_id:f,fields:b(s)});for(const o of n)await t.sendCustomMessage({customType:x,content:o,display:!0})})}catch(f){throw d===void 0&&this.raiseReadFatal("poll",f),F(d,s,"send",f),f}d===void 0&&this.raiseReadFatal("poll",new Error(`${e} prompt-join delivery returned without an operation id`));const a={operationId:d,...s,settleAtAgentSettled:!0};if(!this.canMutate()){this.disposed&&this.disposalReason!==void 0&&R(a,this.disposalReason);return}this.pendingHandoffs.push(a)}coveredEntryIds(t){const e=new Set;for(const i of this.pendingHandoffs)if(i.channel===t)for(const n of i.entryIds)e.add(n);return e}retryPendingCommit(t){const e=this.pendingCommit;if(e===void 0)return Promise.resolve("settled");if(e.attempt!==void 0)return e.attempt;const i=this.attemptPendingCommit(t,e).finally(()=>{e.attempt===i&&(e.attempt=void 0)});return e.attempt=i,i}async attemptPendingCommit(t,e){let i=!1;try{const s=await Y(t,e.receipts);if(s.action==="hold")return"refresh_held";for(const d of e.receipts){const a=s.results.find(f=>f.delivery_id===d.delivery_id);if(a?.outcome!==d.disposition&&a?.outcome!=="stale")throw new Error("daemon returned no terminal mail settlement outcome")}i=s.results.every(d=>d.outcome==="acknowledged")}catch(s){if(this.pendingCommit===e)for(const d of e.handoffs)F(d.operationId,d,"receipt_commit",s);return String(s?.message).startsWith("conversation_write_failed")&&(D({level:"error",event:"broker.exited",node_id:t,error:s,fields:{fault:"conversation_write_failed"}}),await Z(t,s),H()?.("conversation_write_failed",1)),"held"}if(this.pendingCommit!==e)return"settled";for(const s of e.entryIds)this.claims.delete(s);if(e.receipts.some(s=>s.disposition==="released")){const s=new Set(e.entryIds);this.buffer.splice(0,this.buffer.length,...this.buffer.filter(d=>!s.has(d.entry_id)))}this.pendingCommit=void 0;const n=this.detachPending(s=>s.entryIds.every(d=>!this.claims.has(d)));for(const s of n)i&&me(s);return"settled"}async settle(t,e,i,n=!1){if(i)try{if(e?.hasPendingMessages?.()===!0)return"held"}catch{}if(this.pendingCommit!==void 0)return this.retryPendingCommit(t);const s=te(),d=!s&&this.pendingHandoffs.some(c=>c.awaitingDequeue!==void 0)&&await this.nodeRefreshing(t);if(this.pendingCommit!==void 0)return this.retryPendingCommit(t);const a=new Set(this.buffer.map(c=>c.entry_id)),f=new Set(d?this.pendingHandoffs.filter(c=>c.awaitingDequeue!==void 0).flatMap(c=>[...c.entryIds]):[]),o=this.pendingHandoffs.filter(c=>(n||c.settleAtAgentSettled!==!0)&&c.entryIds.every(p=>!f.has(p))),m=new Set(o.flatMap(c=>[...c.entryIds])),u=[...this.claims.keys()].filter(c=>s||m.has(c)&&!a.has(c));return u.length===0?"settled":(this.pendingCommit={handoffs:o,entryIds:u,receipts:u.map(c=>{const p=this.claims.get(c);return{delivery_id:p.delivery_id,attempts:p.attempts,disposition:s?"released":"acknowledged"}})},this.retryPendingCommit(t))}async nodeRefreshing(t){try{return(await O(t)).node.intent==="refresh"}catch{return!0}}detachPending(t){const e=[];for(let i=this.pendingHandoffs.length-1;i>=0;i-=1){const n=this.pendingHandoffs[i];t(n)&&e.push(...this.pendingHandoffs.splice(i,1))}return e.reverse(),e}}function ve(r,t){v?.dispose("reload",!1);const e=new be(r,t,++he);v=e;const i=l(n=>e.prependHeldDeferredInbox(n),"promptJoin");globalThis[le]=i;try{e.start()}catch(n){throw e.dispose("reload",!1),n}return e}l(ve,"installWatcher");function we(r){const t=ve(r,ue());return()=>t.dispose("shutdown",!1)}l(we,"registerCanvasInboxWatcher");var He=we;export{He as default,we as registerCanvasInboxWatcher};
@@ -24,6 +24,8 @@ A run is an agent that works on a person's Northlight runtime, on your behalf an
24
24
 
25
25
  `runs.wait` returns on `waiting_on_user`, `idle` and `settled`, so check the status before you read a result.
26
26
 
27
+ A turn that fails on a model error leaves the run `idle`, not `settled`, with `last_turn_error` set (`{code, message}`); the same error is on that turn's `turn.completed` event. The run stays usable, and a retry is a `runs.message`. When `code` is `rate_limited`, every model route is rate-limited and `retry_at` (an ISO time) says when the limit is expected to lift: send the retry after `retry_at`, or `runs.cancel` the run, rather than wait for its deadline. A message sent before `retry_at` fails the same way.
28
+
27
29
  ## Outcome and result
28
30
 
29
31
  A `settled` run has an `outcome`:
@@ -42,7 +42,7 @@ A chat app sends a message and shows the answer to it. `runs.reply(runId, sent,
42
42
  const sent = await runtime.runs.message(runId, 'What did the report say?');
43
43
  for await (const event of runtime.runs.reply(runId, sent)) {
44
44
  if (event.type === 'text.delta') append(event.delta);
45
- if (event.type === 'turn.completed' && event.error) showError(event.error); // {code: 'content_refused' | 'model_error', message}
45
+ if (event.type === 'turn.completed' && event.error) showError(event.error); // {code: 'content_refused' | 'model_error' | 'rate_limited', message, retry_at?}
46
46
  }
47
47
  // or collect it: text as it streams, then the whole text and how the turn ended
48
48
  const {text, ended, error, outcome} = await runtime.runs.reply(runId, sent).collect({onDelta: (delta) => append(delta)});
@@ -60,7 +60,7 @@ A responder gets `{run, kind: 'start' | 'message', messages}`. It is called afte
60
60
  | Method | Effect |
61
61
  |---|---|
62
62
  | `reply(text, {chunks = 2, messageIds?})` | One turn: `node.turn.started`, text deltas, `node.output_text.done`, `node.turn.completed`. Delivers `messageIds` (default: every pending message). The run goes `idle` (`waiting_on_user` with an open question). |
63
- | `failTurn({code: 'model_error' \| 'content_refused', message})` | The same turn ending `stop_reason: 'error'`; `runs.get` shows `last_turn_error`; `runs.reply(...).collect()` ends `error`. |
63
+ | `failTurn({code: 'model_error' \| 'content_refused' \| 'rate_limited', message, retry_at?})` | The same turn ending `stop_reason: 'error'`; `runs.get` shows `last_turn_error`; `runs.reply(...).collect()` ends `error`. `retry_at` (ISO) is required with `rate_limited` and refused with any other code. |
64
64
  | `setActivity(activity, {at?})` | Fake-only: status `running` with this `activity` until the next event. The only way to reach `writing` or a streaming tool call. |
65
65
  | `settle(ending)` | `{result}`, `{declined: {reason, code, retryable}}`, `{failure: 'canceled' \| 'deadline_exceeded'}`, or `{runtimeError: reason}`. Emits `node.settled` with the outcome the runtime writes. |
66
66
  | `idle()` | Goes idle without a new turn (stopped without submitting). |
@@ -7,6 +7,10 @@ description: Behavior changes in @north-light/sdk that an app must act on, newes
7
7
 
8
8
  Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
9
9
 
10
+ ## 0.3.578
11
+
12
+ `runs.message` now steers a running root turn by default: the agent reads it at its next step. Pass `{ steer: false }` to wait for the current turn to end and start a separate turn. `start_turn: false` remains deferred and cannot use `steer: true`. When a later message joins a turn, `runs.reply` stops yielding text for the earlier message at that boundary, but still waits for the turn's completion. An app on an older SDK gets steering as soon as the runtime upgrades, because it cannot send `steer: false`; its `runs.reply` then streams the answer to a joined later message as part of the earlier reply. Upgrade the SDK to get the `RunReply` boundary and the opt-out.
13
+
10
14
  ## 0.3.577
11
15
 
12
16
  `POST /v1/nodes` with a pinned `cwd` that is not an existing directory on the runtime's host now answers `cwd_missing` instead of creating a node whose broker then fails to launch. A sandbox, isolated or worktree node is not checked. `POST /v1/nodes/{id}/messages` with `reopen` to a dormant node whose recorded cwd is gone now answers `cwd_missing` before the reopen is committed or any mail is queued, instead of accepting the message and reporting `wake_failed`.
@@ -15,6 +19,10 @@ Changes to `@north-light/sdk` that can change what an existing app sees. Newest
15
19
 
16
20
  On a cloud role, `provider.tools` now reports a granted provider as `available` once its bundle is installed in the deployment's shared plugin directory. It no longer answers `500` when a person has no materialized bundle; a granted provider still being installed reports `installing` or `install_failed`. Providers without that person's grant remain hidden. The Mac's plugin settings and provider tools behavior are unchanged.
17
21
 
22
+ ## Next release
23
+
24
+ A turn that ends because every model route is rate-limited for longer than a turn waits now reports `error.code: 'rate_limited'` with `error.retry_at` (ISO time the limit is expected to lift), on `turn.completed` and `runs.get().last_turn_error`. It was `model_error` with the time only inside `message`. The run stays `idle` and usable, as before. An app that matches `error.code` exhaustively should handle the new value; one that treats any other code as a model error needs no change.
25
+
18
26
  ## 0.3.572
19
27
 
20
28
  `POST /v1/runs` works on a cloud role (a runtime started with `--role api` or `--role reconcile`). It answered `500 server_failure` because the role has no root launcher and the run start tried to prepare an app OS user and folder. Behavior on a person's Mac and on a hosted guest with a launcher is unchanged.
@@ -68,6 +68,8 @@ Every event except `error` carries `node_id` and `sequence_number` in addition t
68
68
  | `node.settled` | `{ node_id, sequence_number, outcome }` — terminal; the daemon ends the response after it |
69
69
  | `error` | `{ error: { code: 'stream_gap' \| 'stream_dropped' \| 'stream_error', message, details?: { earliest_sequence?: number } } }` — `stream_gap` is recoverable; the other codes terminate the response |
70
70
 
71
+ A turn can emit more than one `node.turn.started`. A `run.message` sent while the turn is running steers by default, so the agent reads it at its next step and the stream emits another `node.turn.started` whose `input.message_ids` holds the joined message; the turn ends with one `node.turn.completed` whose `message_ids` lists every message that joined it. A reply to one message therefore ends at the turn's completion, not at the next `node.turn.started`; the SDK's `RunReply` stops yielding text once a later message joins. Send `{ steer: false }` to start a separate turn after the current one ends.
72
+
71
73
  `summary` on a tool-call event states whether arguments are null, an array and its item count, an object and its field count, or a primitive type. It never includes argument values or tool output.
72
74
 
73
75
  **Deliberately not carried:** thinking deltas, the model's tool-call construction deltas, raw tool output, and the system prompt. Those belong to the owner's viewer, not to an application's run stream.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.577",
3
+ "version": "0.3.578",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -145,7 +145,7 @@
145
145
  },
146
146
  "license": "UNLICENSED",
147
147
  "dependencies": {
148
- "@north-light/identity": "0.3.577",
148
+ "@north-light/identity": "0.3.578",
149
149
  "@earendil-works/chord": "1.1.0",
150
150
  "@earendil-works/pi-agent-core": "1.1.0",
151
151
  "@earendil-works/pi-ai": "1.1.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.577",
3
+ "version": "0.3.578",
4
4
  "description": "Northlight access-token verification and scope rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",