@north-light/cli 0.3.577 → 0.3.579
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/run-events.d.ts +10 -4
- package/dist/build-root.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +8 -0
- package/dist/builtin-memory/crouter-sdk/streaming.md +2 -0
- package/dist/builtin-memory/northlight-apps/concepts/runs.md +2 -0
- package/dist/builtin-memory/northlight-apps/guides/stream-a-run.md +1 -1
- package/dist/builtin-memory/northlight-apps/guides/test-your-app.md +1 -1
- package/dist/clients/attach/viewer.js +338 -338
- package/dist/core/command.js +5 -5
- package/dist/core/human/feedback-companion.js +1 -1
- package/dist/core/human/page.d.ts +2 -4
- package/dist/core/human/page.js +6 -6
- package/dist/core/human/request-files.d.ts +14 -0
- package/dist/core/human/request-files.js +1 -0
- package/dist/core/human/requests.d.ts +3 -0
- package/dist/core/human/requests.js +18 -17
- package/dist/core/human/tickets.js +1 -1
- package/dist/core/human/visible-paths.d.ts +7 -0
- package/dist/core/human/visible-paths.js +1 -1
- package/dist/core/llm/refusal.d.ts +2 -1
- package/dist/core/llm/refusal.js +1 -1
- package/dist/core/review/document.d.ts +4 -4
- package/dist/core/review/document.js +2 -2
- package/dist/core/review/project.d.ts +7 -5
- package/dist/core/review/project.js +2 -1
- package/dist/core/review/remap.js +1 -1
- package/dist/core/review/stage.d.ts +2 -0
- package/dist/core/review/stage.js +1 -1
- package/dist/core/review/store.js +1 -1
- package/dist/core/runs/operations.d.ts +1 -1
- package/dist/core/runs/operations.js +5 -5
- package/dist/core/runtime/broker/held-deferred-inbox.d.ts +4 -0
- package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
- package/dist/core/runtime/broker/rebind.d.ts +3 -0
- package/dist/core/runtime/broker/rebind.js +1 -1
- package/dist/core/runtime/broker/run-steer.d.ts +15 -0
- package/dist/core/runtime/broker/run-steer.js +1 -0
- package/dist/core/runtime/broker/run-turn-queue.d.ts +2 -0
- package/dist/core/runtime/broker/run-turn-queue.js +1 -1
- package/dist/core/runtime/broker.js +1 -1
- package/dist/core/runtime/runner/blaxel/client.d.ts +2 -2
- package/dist/core/runtime/runner/blaxel/client.js +1 -1
- package/dist/core/runtime/runner/blaxel/runner.js +2 -2
- package/dist/daemon/api/bridge.d.ts +1 -1
- package/dist/daemon/api/bridge.js +5 -5
- package/dist/daemon/api/handlers/feedback-comments.js +2 -2
- package/dist/daemon/api/handlers/inbox-page.js +1 -1
- package/dist/daemon/api/handlers/inbox.js +1 -1
- package/dist/daemon/api/handlers/reviews.js +1 -1
- package/dist/daemon/api/handlers/runs.js +1 -1
- package/dist/daemon/profile-delete.js +1 -1
- package/dist/daemon/review/finish.js +1 -1
- package/dist/daemon/review/sweep.js +1 -1
- package/dist/daemon/runs/append-section.js +2 -2
- package/dist/daemon/runs/observer.js +3 -3
- package/dist/pi-extensions/canvas-bash-valve.js +6 -6
- package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
- package/dist/shared/env.d.ts +5 -0
- package/dist/shared/env.js +2 -2
- package/docs/apps/concepts/runs.md +2 -0
- package/docs/apps/guides/stream-a-run.md +1 -1
- package/docs/apps/guides/test-your-app.md +1 -1
- package/docs/sdk/release-notes.md +8 -0
- package/docs/sdk/streaming.md +2 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +162 -665
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)}}o(
|
|
3
|
-
`}}o(
|
|
4
|
-
[crtr could not record job ${t.jobId} (${
|
|
5
|
-
[backgrounded as job ${t.jobId} after ${
|
|
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(
|
|
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
|
|
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};
|
package/dist/shared/env.d.ts
CHANGED
|
@@ -11,6 +11,11 @@ export declare function launchSnapshotFile(dir: string): string | undefined;
|
|
|
11
11
|
export declare function sandboxCliNodeId(): string | undefined;
|
|
12
12
|
/** A Linux app command uses its mounted launch snapshot, not host user stores. */
|
|
13
13
|
export declare function isSandboxedNode(): boolean;
|
|
14
|
+
/** True when this CLI reaches `/v1` through its broker's CLI relay to a remote control plane: the broker exports `CRTR_API_URL` (an
|
|
15
|
+
* http(s) URL, the fleet launcher's) and `CRTR_API_SOCKET` (the relay it serves). It chooses only where this process gets provider command
|
|
16
|
+
* descriptions (`GET /v1/providers/tools` through that relay); the relay's node token stays the authority. It is not `isSandboxedNode()`,
|
|
17
|
+
* which gates owner-only commands and launch-snapshot reads on a Linux launcher mount. */
|
|
18
|
+
export declare function providerProjectionOverBrokerRelay(): boolean;
|
|
14
19
|
/** The app that owns the current node, supplied in its broker launch identity. */
|
|
15
20
|
export declare function envGrantee(): string | undefined;
|
|
16
21
|
/** Set to '1' by `nodeEnv` for an app-started run's brokers, which admit inbox
|
package/dist/shared/env.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var p=Object.defineProperty;var
|
|
2
|
-
`).map(o=>o.split(" ")[4])),t=
|
|
1
|
+
var p=Object.defineProperty;var e=(n,r)=>p(n,"name",{value:r,configurable:!0});import{lstatSync as T,readFileSync as c,readdirSync as f}from"node:fs";import{join as i}from"node:path";import{covers as _}from"@north-light/identity";function W(){return process.env.CRTR_NODE_ID}e(W,"envNodeId");function d(n){for(const r of["broker-launch.json","cron-launch.json"]){const t=i(n,r);if(T(t,{throwIfNoEntry:!1})?.isFile())return t}}e(d,"launchSnapshotFile");function E(){if(process.platform==="linux")try{const n="/run/crtr/nodes",r=new Set(c("/proc/self/mountinfo","utf8").split(`
|
|
2
|
+
`).map(o=>o.split(" ")[4])),t=f(n,{withFileTypes:!0}).filter(o=>o.isDirectory()&&/^[a-zA-Z0-9][a-zA-Z0-9-]{0,127}$/.test(o.name)&&r.has(i(n,o.name))&&d(i(n,o.name))!==void 0);return t.length===1?t[0].name:void 0}catch{return}}e(E,"sandboxCliNodeId");function F(){return E()!==void 0}e(F,"isSandboxedNode");function h(){return/^https?:\/\//.test(process.env.CRTR_API_URL??"")&&(process.env.CRTR_API_SOCKET??"")!==""}e(h,"providerProjectionOverBrokerRelay");function H(){return process.env.CRTR_GRANTEE}e(H,"envGrantee");const v="CRTR_INBOX_BETWEEN_TURNS";function K(){return process.env[v]==="1"}e(K,"envInboxBetweenTurns");const C="CRTR_SCOPES_FILE";function k(){const n=process.env[C];if(n!==void 0&&n!==""){const t=JSON.parse(c(n,"utf8"));if(!Array.isArray(t)||t.some(o=>typeof o!="string"))throw new Error(`invalid scopes file ${n}`);return t}const r=process.env.CRTR_SCOPES;return r===void 0?null:r===""?[]:r.split(",")}e(k,"envScopes");function y(n,r){return n==null?!0:a(n,r)}e(y,"scopeAllowed");function a(n,r){return n.includes(r)?(_(r,r),!0):n.some(t=>_(t,r))}e(a,"coveredByAny");function G(){return process.env.CRTR_EXECUTION_ID}e(G,"envExecutionId");function b(){return process.env.CRTR_PROFILE_ID}e(b,"envProfileId");function w(){return process.env.CRTR_NODE_CWD}e(w,"envNodeCwd");function g(){return process.env.CRTR_MODEL_INTENT}e(g,"envModelIntent");function V(){return process.env.CRTR_MODEL_EXACT==="1"}e(V,"envModelExact");function X(){return process.env.CRTR_HOME}e(X,"envHomeOverride");function j(){return process.env.CRTR_RUNTIME_HOME}e(j,"envRuntimeHomeOverride");function s(n,r){if(n===void 0)return r;const t=Number(n);return Number.isFinite(t)&&t>0?t:r}e(s,"parsePositiveMs");const A=5*6e4;function Y(){return s(process.env.CRTR_STREAM_WATCHDOG_MS,A)}e(Y,"envStreamWatchdogMs");const S=800;function $(){return s(process.env.CRTR_WATCHER_TICK_MS,S)}e($,"envWatcherTickMs");const M=6e4;function z(){return s(process.env.CRTR_WATCHER_BACKSTOP_MS,M)}e(z,"envWatcherBackstopMs");const N=1e3;function Z(){return s(process.env.CRTR_WATCHER_DEBOUNCE_MS,N)}e(Z,"envWatcherDebounceMs");const x=6e4;function J(){return s(process.env.CRTR_RECAP_IDLE_MS,x)}e(J,"envRecapIdleMs");function q(){const n=process.env.CRTR_RECAP_MODEL;return n!==void 0&&n.trim()!==""?n.trim():void 0}e(q,"envRecapModelOverride");const D=300*1e3;function Q(){return s(process.env.CRTR_BASH_VALVE_MS,D)}e(Q,"envBashValveMs");const O=60*1e3;function ee(){return s(process.env.CRTR_BASH_AUTO_BG_DEADLINE_MS,O)}e(ee,"envBashAutoBgDeadlineMs");function ne(){const n=process.env.CRTR_PIDFILE;return n!==void 0&&n!==""?n:void 0}e(ne,"envPidfileOverride");const I="@earendil-works/pi-coding-agent";function re(){return process.env.CRTR_BROKER_ENGINE??I}e(re,"envBrokerEngine");function te(){return process.env.CRTR_PI_BINARY}e(te,"envPiBinaryOverride");function oe(){return process.env.CRTR_NO_DAEMON_AUTOSTART==="1"}e(oe,"envNoDaemonAutostart");function se(){const n=process.env.CRTR_DOCUMENT_PORT;if(n===void 0||!/^\d+$/.test(n))return;const r=Number(n);return r<=65535?r:void 0}e(se,"envDocumentPort");function ue(){return process.env.CRTRD_TCP}e(ue,"envCrtrdTcp");function ie(){const n=process.env.CRTRD_TOKEN;return n!==void 0&&n!==""?n:void 0}e(ie,"envCrtrdToken");function ce(){return process.env.CRTR_NO_EXPORTS==="1"}e(ce,"envNoExports");function _e(){return process.env.CRTR_DEBUG==="1"}e(_e,"envDebug");function R(n){if(n===void 0||n==="")return;const r=Number(n);return Number.isSafeInteger(r)&&r>=1?r:void 0}e(R,"parsePositiveInteger");function Re(){return R(process.env.CRTR_MAX_LIVE_BROKERS)}e(Re,"envMaxLiveBrokers");function pe(){return R(process.env.CRTR_WARN_LIVE_BROKERS)}e(pe,"envWarnLiveBrokers");function u(n){if(n===void 0)return;const r=Number(n);return Number.isFinite(r)&&r>0?r:void 0}e(u,"parsePositiveMsNoDefault");function Te(){return u(process.env.CRTR_TEST_NODE_SOCKET_WINDOW_MS)}e(Te,"envTestNodeSocketWindowMs");function fe(){return u(process.env.CRTR_TEST_UNATTENDED_PARK_MS)}e(fe,"envTestUnattendedParkMs");function de(){return u(process.env.CRTR_UNATTENDED_PARK_MS)}e(de,"envUnattendedParkMs");function Ee(){return u(process.env.CRTR_BASH_SILENCE_MS)??15*6e4}e(Ee,"envBashSilenceMs");function l(){return u(process.env.CRTR_TEST_PARK_SUMMARY_GRACE_MS)}e(l,"envTestParkSummaryGraceMs");const m=5*6e4;function ve(){return l()??m}e(ve,"parkSummaryGraceMs");export{v as INBOX_BETWEEN_TURNS_ENV,C as SCOPES_FILE_ENV,a as coveredByAny,ee as envBashAutoBgDeadlineMs,Ee as envBashSilenceMs,Q as envBashValveMs,re as envBrokerEngine,ue as envCrtrdTcp,ie as envCrtrdToken,_e as envDebug,se as envDocumentPort,G as envExecutionId,H as envGrantee,X as envHomeOverride,K as envInboxBetweenTurns,Re as envMaxLiveBrokers,V as envModelExact,g as envModelIntent,oe as envNoDaemonAutostart,ce as envNoExports,w as envNodeCwd,W as envNodeId,te as envPiBinaryOverride,ne as envPidfileOverride,b as envProfileId,J as envRecapIdleMs,q as envRecapModelOverride,j as envRuntimeHomeOverride,k as envScopes,Y as envStreamWatchdogMs,Te as envTestNodeSocketWindowMs,l as envTestParkSummaryGraceMs,fe as envTestUnattendedParkMs,de as envUnattendedParkMs,pe as envWarnLiveBrokers,z as envWatcherBackstopMs,Z as envWatcherDebounceMs,$ as envWatcherTickMs,F as isSandboxedNode,d as launchSnapshotFile,ve as parkSummaryGraceMs,h as providerProjectionOverBrokerRelay,E as sandboxCliNodeId,y as scopeAllowed};
|
|
@@ -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
|
+
## 0.3.579
|
|
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.
|
package/docs/sdk/streaming.md
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "0.3.579",
|
|
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.
|
|
148
|
+
"@north-light/identity": "0.3.579",
|
|
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",
|