@north-light/cli 0.3.571 → 0.3.572

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 (47) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/api/dto/broker-ops.d.ts +3 -0
  4. package/dist/api/dto/recovery.d.ts +7 -0
  5. package/dist/api/errors.d.ts +3 -0
  6. package/dist/api/errors.js +1 -1
  7. package/dist/builtin-memory/crouter-sdk/release-notes.md +7 -0
  8. package/dist/commands/dashboard.js +3 -1
  9. package/dist/core/auth-file.d.ts +2 -0
  10. package/dist/core/auth-file.js +1 -1
  11. package/dist/core/canvas/recovery.d.ts +3 -0
  12. package/dist/core/canvas/recovery.js +2 -2
  13. package/dist/core/command.d.ts +2 -3
  14. package/dist/core/command.js +6 -6
  15. package/dist/core/llm/run.js +2 -2
  16. package/dist/core/model-auth/readiness.js +1 -1
  17. package/dist/core/providers/files.js +1 -1
  18. package/dist/core/runs/operations.js +6 -6
  19. package/dist/core/runtime/blaxel-environment.d.ts +2 -0
  20. package/dist/core/runtime/blaxel-environment.js +1 -0
  21. package/dist/core/runtime/broker/fault-retry.d.ts +4 -0
  22. package/dist/core/runtime/broker/fault-retry.js +1 -1
  23. package/dist/core/runtime/broker/rebind.d.ts +1 -0
  24. package/dist/core/runtime/broker/rebind.js +1 -1
  25. package/dist/core/runtime/nodes.js +1 -1
  26. package/dist/core/runtime/revive.js +2 -2
  27. package/dist/core/runtime/spawn.js +2 -2
  28. package/dist/core/runtime/warm-pool.js +1 -1
  29. package/dist/core/spaces/process.js +1 -1
  30. package/dist/core/spaces/space.d.ts +2 -1
  31. package/dist/core/spaces/space.js +2 -2
  32. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  33. package/dist/daemon/api/handlers/modelauth.js +1 -1
  34. package/dist/daemon/reconcilers/action-lane.js +1 -1
  35. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
  36. package/dist/daemon/reconcilers/node-lifecycle/launch-failure.d.ts +1 -0
  37. package/dist/daemon/reconcilers/node-lifecycle/launch-failure.js +1 -0
  38. package/dist/daemon/reconcilers/node-lifecycle/node-person.d.ts +9 -0
  39. package/dist/daemon/reconcilers/node-lifecycle/node-person.js +1 -0
  40. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +0 -2
  41. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -1
  42. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  43. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
  44. package/docs/sdk/release-notes.md +7 -0
  45. package/package.json +2 -2
  46. package/packages/crouter-identity/package.json +1 -1
  47. package/runtime.lock.json +13 -13
@@ -1 +1 @@
1
- var te=Object.defineProperty;var d=(e,t)=>te(e,"name",{value:t,configurable:!0});import{createHash as oe}from"node:crypto";import{closeSync as D,constants as ne,createWriteStream as ie}from"node:fs";import{basename as re,extname as ae,isAbsolute as se,join as F}from"node:path";import{Transform as de}from"node:stream";import{pipeline as ue}from"node:stream/promises";import{appSpace as I}from"../layout.js";import{RunError as T}from"../runs/operations.js";import{createFileBeneath as ce,existsBeneath as M,openBeneath as le,removeTreeBeneath as N,setModeBeneath as fe,unlinkBeneath as pe,withDirBeneath as he}from"../spaces/open-beneath.js";import{ensureAppSpace as we}from"../spaces/space.js";import{invalidStorageName as H,storageClient as B,StorageRefused as U,UPLOAD_MAX_BYTES as C}from"../storage/client.js";import{contentTypeFor as _e,openCallerFile as me}from"../storage/download.js";import{withCanvasWrite as ge}from"../canvas/db.js";import{deleteUploadUse as ye,getUpload as be,insertUpload as $e,insertUploadUses as Se,setUploadSize as ve,uploadUseAt as Ue}from"../storage/tables.js";import{ProviderError as c}from"./errors.js";const ze=11e4,Ee="app:terminal";function P(e){if(e.class==="person")return e.grantee??Ee;if(!e.grantee)throw new c("server_error","provider caller has no grantee");return e.grantee}d(P,"grantee");function Ae(e){return e.class==="person"?null:{app:P(e),resolved:e.resolved??[],launched:e.class==="broker"?e.nodeScopes:null}}d(Ae,"fileCaller");function y(e){return e instanceof c?e:e instanceof U?new c("storage_unavailable",`storage refused: ${e.message}`):e instanceof T&&e.code==="storage_unavailable"?new c("storage_unavailable",e.message):e}d(y,"storageFailure");const b=d(e=>new c("provider_error",e,{origin:"provider"}),"providerError");function Fe(e,t,n){if(typeof n!="string"||!n||!se(n)||n.includes("\0"))throw new c("invalid_request",`${t} must be an absolute runtime path`,{param:t});let r;try{r=me(Ae(e),n)}catch(i){throw i instanceof T?i.code==="scope_missing"?new c("scope_missing",i.message,{param:t,...i.scope?{scope:i.scope}:{}}):i.code==="not_found"||i.code==="invalid_request"?new c("not_found",`no such file: ${n}`,{param:t}):i:i}try{if(r.size>C)throw new c("invalid_request",`${t} exceeds 100 MiB`,{param:t});const i=re(r.path),o=H(i);if(o)throw new c("invalid_request",`${t}: file name cannot be sent: ${o}`,{param:t});return{param:t,file:r,name:i,mediaType:_e(r.path)}}catch(i){throw D(r.fd),i}}d(Fe,"checkFileArgument");async function Te(e,t){try{const n=await e.createOutbound({name:t.name,size:t.file.size,media_type:t.mediaType});await e.putFileBytes(n.upload_link,t.file.fd,t.file.size,t.mediaType);const r=await e.signOutbound(n.object_id);return{param:t.param,path:t.file.path,name:t.name,object_id:n.object_id,url:r.url}}catch(n){throw y(n)}}d(Te,"copyOutbound");async function k(e,t,n,r){const i=[];try{for(const a of t)n[a]===void 0||n[a]===null||i.push(Fe(e,a,n[a]));if(!i.length)return[];let o;try{o=r()}catch(a){throw y(a)}const l=[];for(const a of i)l.push(await Te(o,a));return l}finally{for(const o of i)D(o.file.fd)}}d(k,"copyArguments");function q(e){const t=H(e);if(t)throw new c("server_error",`file output ${e} cannot name an upload: ${t}`)}d(q,"checkOutputField");async function W(e,t,n){q(t);const r=P(e),i="application/octet-stream";let o;try{o=await n().createResultUpload({name:t,media_type:i,max_size:C})}catch(l){throw y(l)}return await $e({upload_id:o.upload_id,grantee:r,name:t,media_type:i,size:C,created_at:new Date().toISOString()}),{field:t,upload_id:o.upload_id,upload_link:o.upload_link,grantee:r}}d(W,"createResultUpload");function X(e){return e?{"Crtr-Upload-Id":e.upload_id,"Crtr-Upload-Link":e.upload_link}:{}}d(X,"uploadHeaders");function Y(e,t){const n={...e};for(const r of t)n[r.param]=r.url;return n}d(Y,"withUrls");async function Ye(e,t,n,r={}){const i=r.storage??B;t.output&&q(t.output);const o=await k(e,t.params,n,i),l=t.output?await W(e,t.output,i):null;return{arguments:Y(n,o),copies:o,upload:l,headers:X(l)}}d(Ye,"prepareFileCall");async function Ge(e,t,n,r,i={}){const o=i.storage??B,l=[];for(const s of r.copies)try{const p=await o().signOutbound(s.object_id);l.push({...s,url:p.url})}catch(p){if(!(p instanceof U))throw y(p);l.push(...await k(e,[s.param],n,o))}let a=r.upload;if(a)try{const s=await o().signUploadLink(a.upload_id);a={...a,upload_link:s.upload_link}}catch(s){if(!(s instanceof U))throw y(s);s.status===404&&(a=await W(e,t.output??a.field,o))}return{arguments:Y(n,l),copies:l,upload:a,headers:X(a)}}d(Ge,"resignForRetry");const Be=/^[0-9a-f]{64}$/i;function Ce(e,t){if(t===1)return e;const n=ae(e);return n&&n!==e?`${e.slice(0,-n.length)}-${t}${n}`:`${e}-${t}`}d(Ce,"candidate");async function Je(e,t,n,r={}){const i={result:n,file:null,cleanup:d(async()=>{},"cleanup")},o=t.upload;if(!o||!n||typeof n!="object"||Array.isArray(n))return i;const l=n,a=l[o.field];if(a==null)return i;if(typeof a!="object"||Array.isArray(a))throw b(`${o.field} must be {upload_id, size, sha256}`);const s=a;if(typeof s.upload_id!="string"||typeof s.size!="number"||!Number.isSafeInteger(s.size)||s.size<0||typeof s.sha256!="string"||!Be.test(s.sha256))throw b(`${o.field} must be {upload_id, size, sha256}`);if(s.upload_id!==o.upload_id)throw b(`${o.field} names an upload other than Crtr-Upload-Id`);const p=s.size,G=s.sha256.toLowerCase(),z=AbortSignal.timeout(r.budgetMs??ze),O=r.signal?AbortSignal.any([z,r.signal]):z;let h;try{h=await(r.storage??B)().fetchUpload(o.upload_id,O)}catch(_){throw _ instanceof U?b(`${o.field} names an upload with nothing stored`):y(_)}if(h.size===null)throw h.body.destroy(),new c("storage_unavailable",`storage did not say how many bytes ${o.field} holds`);if(h.size!==p)throw h.body.destroy(),b(`${o.field} claims ${p} bytes; storage holds ${h.size}`);const E=o.grantee,$=e.node_id!==null,J=await we(E),f=$?I(E).nodes:I(E).files,A=$?F(f,e.node_id):f,S=$?F(A,"attachments"):f,K={uid:process.getuid?.()??0,gid:J.gid},R=$&&!M(f,A),Q=$&&!R&&!M(f,S);let w=null,v=null,x=!1;const L=d(async()=>{if(!x){if(x=!0,v)try{await ye(v)}catch{}if(w)try{pe(f,w)}catch{}try{R?N(f,A):Q&&N(f,S)}catch{}}},"cleanup");try{he(f,S,()=>{},!0);for(let u=1;;u++){const m=F(S,Ce(o.field,u));if(!await Ue(m))try{ce(f,m,Buffer.alloc(0),432),w=m;break}catch(g){if(g.code!=="EEXIST")throw g}}let _=0;const j=oe("sha256"),V=new de({transform(u,m,g){_+=u.length,j.update(u),g(null,u)}}),Z=le(f,w,ne.O_WRONLY);try{await ue(h.body,V,ie("",{fd:Z,autoClose:!0}),{signal:O})}catch(u){throw z.aborted?new c("storage_unavailable","the file result could not be fetched within 110 s"):u instanceof T?y(u):new c("storage_unavailable",`storage stopped sending ${o.field}: ${u instanceof Error?u.message:String(u)}`)}if(_!==p)throw new c("storage_unavailable",`storage sent ${_} of ${p} bytes of ${o.field}`);if(j.digest("hex")!==G)throw b(`${o.field}: the stored bytes do not match sha256`);fe(f,w,432,K);const ee=w;await ge(async u=>{const m=await be(o.upload_id,u);if(!m||m.delete_pending_since)throw new c("not_found",`${o.field}: the upload was deleted while it was fetched`);if(await ve(o.upload_id,p,u),!e.run_id)return;const g=await u.prepare("SELECT deleting_at FROM nodes WHERE node_id = ?").get(e.run_id);if(!g||g.deleting_at)throw new c("not_found",`run ${e.run_id} was deleted while its file result was fetched`);v={upload_id:o.upload_id,run_id:e.run_id,path:ee},await Se([v],u)})}catch(_){throw h.body.destroy(),await L(),_}return{result:{...l,[o.field]:w},file:{path:w,upload_id:o.upload_id},cleanup:L}}d(Je,"resolveFileResult");export{ze as FILE_RESULT_FETCH_BUDGET_MS,Ye as prepareFileCall,Ge as resignForRetry,Je as resolveFileResult};
1
+ var te=Object.defineProperty;var d=(e,t)=>te(e,"name",{value:t,configurable:!0});import{createHash as oe}from"node:crypto";import{closeSync as D,constants as ne,createWriteStream as ie}from"node:fs";import{basename as re,extname as ae,isAbsolute as se,join as C}from"node:path";import{Transform as de}from"node:stream";import{pipeline as ue}from"node:stream/promises";import{appSpace as I}from"../layout.js";import{RunError as F}from"../runs/operations.js";import{createFileBeneath as le,existsBeneath as M,openBeneath as ce,removeTreeBeneath as N,setModeBeneath as fe,unlinkBeneath as pe,withDirBeneath as he}from"../spaces/open-beneath.js";import{ensureAppSpace as we}from"../spaces/space.js";import{isCloudRole as _e}from"../canvas/person-state.js";import{invalidStorageName as H,storageClient as T,StorageRefused as U,UPLOAD_MAX_BYTES as B}from"../storage/client.js";import{contentTypeFor as me,openCallerFile as ge}from"../storage/download.js";import{withCanvasWrite as ye}from"../canvas/db.js";import{deleteUploadUse as be,getUpload as $e,insertUpload as ve,insertUploadUses as Se,setUploadSize as Ue,uploadUseAt as ze}from"../storage/tables.js";import{ProviderError as u}from"./errors.js";const Ee=11e4,Ae="app:terminal";function P(e){if(e.class==="person")return e.grantee??Ae;if(!e.grantee)throw new u("server_error","provider caller has no grantee");return e.grantee}d(P,"grantee");function Ce(e){return e.class==="person"?null:{app:P(e),resolved:e.resolved??[],launched:e.class==="broker"?e.nodeScopes:null}}d(Ce,"fileCaller");function y(e){return e instanceof u?e:e instanceof U?new u("storage_unavailable",`storage refused: ${e.message}`):e instanceof F&&e.code==="storage_unavailable"?new u("storage_unavailable",e.message):e}d(y,"storageFailure");const b=d(e=>new u("provider_error",e,{origin:"provider"}),"providerError");function Fe(e,t,n){if(typeof n!="string"||!n||!se(n)||n.includes("\0"))throw new u("invalid_request",`${t} must be an absolute runtime path`,{param:t});let r;try{r=ge(Ce(e),n)}catch(i){throw i instanceof F?i.code==="scope_missing"?new u("scope_missing",i.message,{param:t,...i.scope?{scope:i.scope}:{}}):i.code==="not_found"||i.code==="invalid_request"?new u("not_found",`no such file: ${n}`,{param:t}):i:i}try{if(r.size>B)throw new u("invalid_request",`${t} exceeds 100 MiB`,{param:t});const i=re(r.path),o=H(i);if(o)throw new u("invalid_request",`${t}: file name cannot be sent: ${o}`,{param:t});return{param:t,file:r,name:i,mediaType:me(r.path)}}catch(i){throw D(r.fd),i}}d(Fe,"checkFileArgument");async function Te(e,t){try{const n=await e.createOutbound({name:t.name,size:t.file.size,media_type:t.mediaType});await e.putFileBytes(n.upload_link,t.file.fd,t.file.size,t.mediaType);const r=await e.signOutbound(n.object_id);return{param:t.param,path:t.file.path,name:t.name,object_id:n.object_id,url:r.url}}catch(n){throw y(n)}}d(Te,"copyOutbound");async function k(e,t,n,r){const i=[];try{for(const a of t)n[a]===void 0||n[a]===null||i.push(Fe(e,a,n[a]));if(!i.length)return[];let o;try{o=r()}catch(a){throw y(a)}const c=[];for(const a of i)c.push(await Te(o,a));return c}finally{for(const o of i)D(o.file.fd)}}d(k,"copyArguments");function q(e){const t=H(e);if(t)throw new u("server_error",`file output ${e} cannot name an upload: ${t}`)}d(q,"checkOutputField");async function W(e,t,n){q(t);const r=P(e),i="application/octet-stream";let o;try{o=await n().createResultUpload({name:t,media_type:i,max_size:B})}catch(c){throw y(c)}return await ve({upload_id:o.upload_id,grantee:r,name:t,media_type:i,size:B,created_at:new Date().toISOString()}),{field:t,upload_id:o.upload_id,upload_link:o.upload_link,grantee:r}}d(W,"createResultUpload");function X(e){return e?{"Crtr-Upload-Id":e.upload_id,"Crtr-Upload-Link":e.upload_link}:{}}d(X,"uploadHeaders");function Y(e,t){const n={...e};for(const r of t)n[r.param]=r.url;return n}d(Y,"withUrls");async function Je(e,t,n,r={}){const i=r.storage??T;t.output&&q(t.output);const o=await k(e,t.params,n,i),c=t.output?await W(e,t.output,i):null;return{arguments:Y(n,o),copies:o,upload:c,headers:X(c)}}d(Je,"prepareFileCall");async function Ke(e,t,n,r,i={}){const o=i.storage??T,c=[];for(const s of r.copies)try{const p=await o().signOutbound(s.object_id);c.push({...s,url:p.url})}catch(p){if(!(p instanceof U))throw y(p);c.push(...await k(e,[s.param],n,o))}let a=r.upload;if(a)try{const s=await o().signUploadLink(a.upload_id);a={...a,upload_link:s.upload_link}}catch(s){if(!(s instanceof U))throw y(s);s.status===404&&(a=await W(e,t.output??a.field,o))}return{arguments:Y(n,c),copies:c,upload:a,headers:X(a)}}d(Ke,"resignForRetry");const Be=/^[0-9a-f]{64}$/i;function Re(e,t){if(t===1)return e;const n=ae(e);return n&&n!==e?`${e.slice(0,-n.length)}-${t}${n}`:`${e}-${t}`}d(Re,"candidate");async function Qe(e,t,n,r={}){const i={result:n,file:null,cleanup:d(async()=>{},"cleanup")},o=t.upload;if(!o||!n||typeof n!="object"||Array.isArray(n))return i;const c=n,a=c[o.field];if(a==null)return i;if(typeof a!="object"||Array.isArray(a))throw b(`${o.field} must be {upload_id, size, sha256}`);const s=a;if(typeof s.upload_id!="string"||typeof s.size!="number"||!Number.isSafeInteger(s.size)||s.size<0||typeof s.sha256!="string"||!Be.test(s.sha256))throw b(`${o.field} must be {upload_id, size, sha256}`);if(s.upload_id!==o.upload_id)throw b(`${o.field} names an upload other than Crtr-Upload-Id`);const p=s.size,G=s.sha256.toLowerCase(),z=AbortSignal.timeout(r.budgetMs??Ee),R=r.signal?AbortSignal.any([z,r.signal]):z;let h;try{h=await(r.storage??T)().fetchUpload(o.upload_id,R)}catch(_){throw _ instanceof U?b(`${o.field} names an upload with nothing stored`):y(_)}if(h.size===null)throw h.body.destroy(),new u("storage_unavailable",`storage did not say how many bytes ${o.field} holds`);if(h.size!==p)throw h.body.destroy(),b(`${o.field} claims ${p} bytes; storage holds ${h.size}`);if(_e())throw new u("storage_unavailable","a cloud role has no folder to land a file result in");const E=o.grantee,$=e.node_id!==null,J=await we(E),f=$?I(E).nodes:I(E).files,A=$?C(f,e.node_id):f,v=$?C(A,"attachments"):f,K={uid:process.getuid?.()??0,gid:J.gid},O=$&&!M(f,A),Q=$&&!O&&!M(f,v);let w=null,S=null,x=!1;const L=d(async()=>{if(!x){if(x=!0,S)try{await be(S)}catch{}if(w)try{pe(f,w)}catch{}try{O?N(f,A):Q&&N(f,v)}catch{}}},"cleanup");try{he(f,v,()=>{},!0);for(let l=1;;l++){const m=C(v,Re(o.field,l));if(!await ze(m))try{le(f,m,Buffer.alloc(0),432),w=m;break}catch(g){if(g.code!=="EEXIST")throw g}}let _=0;const j=oe("sha256"),V=new de({transform(l,m,g){_+=l.length,j.update(l),g(null,l)}}),Z=ce(f,w,ne.O_WRONLY);try{await ue(h.body,V,ie("",{fd:Z,autoClose:!0}),{signal:R})}catch(l){throw z.aborted?new u("storage_unavailable","the file result could not be fetched within 110 s"):l instanceof F?y(l):new u("storage_unavailable",`storage stopped sending ${o.field}: ${l instanceof Error?l.message:String(l)}`)}if(_!==p)throw new u("storage_unavailable",`storage sent ${_} of ${p} bytes of ${o.field}`);if(j.digest("hex")!==G)throw b(`${o.field}: the stored bytes do not match sha256`);fe(f,w,432,K);const ee=w;await ye(async l=>{const m=await $e(o.upload_id,l);if(!m||m.delete_pending_since)throw new u("not_found",`${o.field}: the upload was deleted while it was fetched`);if(await Ue(o.upload_id,p,l),!e.run_id)return;const g=await l.prepare("SELECT deleting_at FROM nodes WHERE node_id = ?").get(e.run_id);if(!g||g.deleting_at)throw new u("not_found",`run ${e.run_id} was deleted while its file result was fetched`);S={upload_id:o.upload_id,run_id:e.run_id,path:ee},await Se([S],l)})}catch(_){throw h.body.destroy(),await L(),_}return{result:{...c,[o.field]:w},file:{path:w,upload_id:o.upload_id},cleanup:L}}d(Qe,"resolveFileResult");export{Ee as FILE_RESULT_FETCH_BUDGET_MS,Je as prepareFileCall,Ke as resignForRetry,Qe as resolveFileResult};
@@ -1,14 +1,14 @@
1
- var Oe=Object.defineProperty;var c=(e,t)=>Oe(e,"name",{value:t,configurable:!0});import{coveredByAny as De}from"../../shared/env.js";import{createHash as Q}from"node:crypto";import{ApiError as Me}from"../../api/errors.js";import{CrtrError as Ae}from"../errors.js";import{isCloudRole as Ie}from"../canvas/person.js";import{isAbsolute as V,relative as Te,resolve as Le,sep as Ce,join as ke}from"node:path";import{SessionManager as xe}from"@earendil-works/pi-coding-agent";import{covers as P}from"@north-light/identity";import{openDb as B,withCanvasWrite as F}from"../canvas/db.js";import{deleteNodeWithOwned as We}from"../graph/deletion.js";import{getById as Fe}from"../graph/objects.js";import{repairRunConversations as Z}from"../canvas/sessions.js";import{getNode as O,nodeFiles as qe,removeNodeFiles as He,updateNode as Ue}from"../canvas/canvas.js";import{fullName as $e}from"../canvas/labels.js";import{renameWindow as Pe}from"../runtime/placement.js";import{liveExecutionForNode as K}from"../canvas/executions.js";import{conversationStore as ee,isConversationNotFound as Be}from"../conversation-store/index.js";import{PARKED_RESIDENT_SQL as je}from"../canvas/canvas.js";import{WAKE_MAIL_SQL as Je}from"../canvas/deliveries.js";import{readNodeRecap as Ye}from"../canvas/node-recap.js";import{enqueueNodeMail as Ge}from"../feed/messages.js";import{newestReportBody as Xe}from"../feed/reports.js";import{ensureAppProfile as ze,loadExactProfileManifest as Qe}from"../profiles/manifest.js";import{stopNodeSandboxes as te}from"../spaces/stop.js";import{BrokerUnreachableError as Ve}from"../runtime/broker-request.js";import{interruptLive as Ze}from"../runtime/interactive-deliver.js";import{isBrokerLive as ne}from"../runtime/model-swap.js";import{newNodeId as Ke}from"../runtime/nodes.js";import{reviveNode as j}from"../runtime/revive.js";import{spawnChildPrepared as et}from"../runtime/spawn.js";import{isPortableModelToken as tt}from"../runtime/launch.js";import{parseDurationMs as nt}from"../wake.js";import{expandModelCandidates as st,parseModelSpec as at}from"../model-routes.js";import{payerOfGrant as J}from"../grants/mirror.js";import{isolatedBrokerLauncherAvailable as it}from"../runtime/host.js";import{resolveRunKind as rt,resolveRunModel as ot}from"../runtime/launch-target.js";import{cycleAwareMessages as dt}from"../runtime/session-cycles.js";import{originFor as se}from"../runtime/stamp/protocol.js";import{readOutputRequest as ct,readOutputResult as ae}from"../runtime/output-result.js";import{admitLegacyStructuredOutput as ie}from"../../daemon/structured-requests/sidecar.js";import{runSchemaRepromptPending as ut}from"../runtime/stop-guard.js";import{toRunOutcomeDTO as mt}from"../../daemon/api/map.js";import{openAssistantMessageStartSequence as lt}from"../../daemon/runs/observer.js";import{closeNode as re}from"../runtime/close.js";import{parseScope as pt}from"@north-light/identity";import{appendRunEvent as oe,lockRunRoot as ft,subscribeRunEvents as _t}from"./events.js";import{runActivity as wt}from"./activity.js";import{cancelOpenQuestions as de,openNoticeIds as gt,openQuestionIds as ce}from"./questions.js";import{appSpace as Y}from"../layout.js";import{ensureAppSpace as Et}from"../spaces/space.js";import{assertUploadsUsable as ue,resolveStorageUrls as yt}from"../storage/attachments.js";import{storageClient as ht,storageConfig as Rt}from"../storage/client.js";import{deleteUploadRow as St,insertUploadUses as me,releaseRunUploads as Nt,runUsesPath as vt}from"../storage/tables.js";import{abortProviderCalls as le}from"../providers/inflight.js";import{filterAsync as pe,mapAsync as x}from"../async-array.js";const fe=c((e,t,s,n)=>yt(t,e,s,n?{taken:n}:{}),"defaultResolver"),bt=3e3;class R extends Error{static{c(this,"RunError")}status;code;param;details;scope;retryable;constructor(t,s,n,o,m,i,r=t===503){super(n),this.status=t,this.code=s,this.param=o,this.details=m,this.scope=i,this.retryable=r}}const N=c((e,t)=>new R(400,"invalid_request",e,t),"invalid"),G=c(()=>new R(404,"not_found","run not found"),"missing"),X=c(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"usage"),A=c(async()=>await B(),"db");async function q(e,t,s=!1){const n=await O(e);if(!n||n.parent!==null||n.run_id!==e||n.grantee!==t||!s&&n.deleting_at)throw G();return n}c(q,"ownedRun");const _e=["waiting_on_user","idle","settled"];async function C(e){if(e.status==="canceled"||e.status==="dead"||e.status==="done"&&e.intent!=="parked")return"settled";const t=await(await A()).prepare(`SELECT 1 FROM node_rows n WHERE n.run_id = ? AND n.status = 'active' AND
1
+ var Me=Object.defineProperty;var c=(e,t)=>Me(e,"name",{value:t,configurable:!0});import{coveredByAny as Ae}from"../../shared/env.js";import{createHash as Q}from"node:crypto";import{ApiError as Ie}from"../../api/errors.js";import{CrtrError as Te}from"../errors.js";import{isCloudRole as Le}from"../canvas/person.js";import{isAbsolute as V,relative as Ce,resolve as ke,sep as xe,join as We}from"node:path";import{SessionManager as Fe}from"@earendil-works/pi-coding-agent";import{covers as P}from"@north-light/identity";import{openDb as B,withCanvasWrite as F}from"../canvas/db.js";import{deleteNodeWithOwned as qe}from"../graph/deletion.js";import{getById as He}from"../graph/objects.js";import{repairRunConversations as Z}from"../canvas/sessions.js";import{getNode as O,nodeFiles as Ue,removeNodeFiles as $e,updateNode as Pe}from"../canvas/canvas.js";import{fullName as Be}from"../canvas/labels.js";import{renameWindow as je}from"../runtime/placement.js";import{liveExecutionForNode as K}from"../canvas/executions.js";import{conversationStore as ee,isConversationNotFound as Je}from"../conversation-store/index.js";import{PARKED_RESIDENT_SQL as Ye}from"../canvas/canvas.js";import{WAKE_MAIL_SQL as Ge}from"../canvas/deliveries.js";import{readNodeRecap as Xe}from"../canvas/node-recap.js";import{enqueueNodeMail as ze}from"../feed/messages.js";import{newestReportBody as Qe}from"../feed/reports.js";import{ensureAppProfile as te,loadExactProfileManifest as ne}from"../profiles/manifest.js";import{stopNodeSandboxes as se}from"../spaces/stop.js";import{BrokerUnreachableError as Ve}from"../runtime/broker-request.js";import{interruptLive as Ze}from"../runtime/interactive-deliver.js";import{isBrokerLive as ae}from"../runtime/model-swap.js";import{newNodeId as Ke}from"../runtime/nodes.js";import{reviveNode as j}from"../runtime/revive.js";import{spawnChildPrepared as et}from"../runtime/spawn.js";import{isPortableModelToken as tt}from"../runtime/launch.js";import{parseDurationMs as nt}from"../wake.js";import{expandModelCandidates as st,parseModelSpec as at}from"../model-routes.js";import{payerOfGrant as J}from"../grants/mirror.js";import{isolatedBrokerLauncherAvailable as it}from"../runtime/host.js";import{blaxelEnvironmentOf as rt}from"../runtime/blaxel-environment.js";import{resolveRunKind as ot,resolveRunModel as dt}from"../runtime/launch-target.js";import{cycleAwareMessages as ct}from"../runtime/session-cycles.js";import{originFor as ie}from"../runtime/stamp/protocol.js";import{readOutputRequest as ut,readOutputResult as re}from"../runtime/output-result.js";import{admitLegacyStructuredOutput as oe}from"../../daemon/structured-requests/sidecar.js";import{runSchemaRepromptPending as mt}from"../runtime/stop-guard.js";import{toRunOutcomeDTO as lt}from"../../daemon/api/map.js";import{openAssistantMessageStartSequence as pt}from"../../daemon/runs/observer.js";import{closeNode as de}from"../runtime/close.js";import{parseScope as ft}from"@north-light/identity";import{appendRunEvent as ce,lockRunRoot as _t,subscribeRunEvents as wt}from"./events.js";import{runActivity as gt}from"./activity.js";import{cancelOpenQuestions as ue,openNoticeIds as Et,openQuestionIds as me}from"./questions.js";import{appSpace as Y}from"../layout.js";import{ensureAppSpace as yt}from"../spaces/space.js";import{assertUploadsUsable as le,resolveStorageUrls as ht}from"../storage/attachments.js";import{storageClient as Rt,storageConfig as St}from"../storage/client.js";import{deleteUploadRow as Nt,insertUploadUses as pe,releaseRunUploads as vt,runUsesPath as bt}from"../storage/tables.js";import{abortProviderCalls as fe}from"../providers/inflight.js";import{filterAsync as _e,mapAsync as x}from"../async-array.js";const we=c((e,t,s,n)=>ht(t,e,s,n?{taken:n}:{}),"defaultResolver"),Ot=3e3;class R extends Error{static{c(this,"RunError")}status;code;param;details;scope;retryable;constructor(t,s,n,o,m,i,r=t===503){super(n),this.status=t,this.code=s,this.param=o,this.details=m,this.scope=i,this.retryable=r}}const N=c((e,t)=>new R(400,"invalid_request",e,t),"invalid"),G=c(()=>new R(404,"not_found","run not found"),"missing"),X=c(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"usage"),A=c(async()=>await B(),"db");async function q(e,t,s=!1){const n=await O(e);if(!n||n.parent!==null||n.run_id!==e||n.grantee!==t||!s&&n.deleting_at)throw G();return n}c(q,"ownedRun");const ge=["waiting_on_user","idle","settled"];async function C(e){if(e.status==="canceled"||e.status==="dead"||e.status==="done"&&e.intent!=="parked")return"settled";const t=await(await A()).prepare(`SELECT 1 FROM node_rows n WHERE n.run_id = ? AND n.status = 'active' AND
2
2
  (n.last_settled_at IS NULL OR EXISTS (
3
3
  SELECT 1 FROM run_events e WHERE e.run_id = ? AND e.node_id = n.node_id AND e.type = 'node.turn.started'
4
4
  AND e.sequence_number > COALESCE((SELECT MAX(c.sequence_number) FROM run_events c
5
5
  WHERE c.run_id = e.run_id AND c.node_id = e.node_id AND c.type = 'node.turn.completed'), 0)
6
- )) LIMIT 1`).get(e.node_id,e.node_id),s=!e.last_settled_at&&e.status!=="done";return t||s||await ut(e)?"running":(await ce(e.node_id)).length?"waiting_on_user":await(await A()).prepare(`SELECT 1 FROM deliveries d JOIN nodes n ON n.node_id = d.destination_id
7
- WHERE n.run_id = ? AND ${Je("d","'armed', 'pending'")}
8
- AND (n.status IN ('active', 'idle') OR ${je("n")}) LIMIT 1`).get(e.node_id)?"running":"idle"}c(C,"runStatus");async function kn(e){const t=await O(e);return t!==null&&await C(t)==="running"}c(kn,"runIsRunning");async function W(e){await Z(e.node_id),e=await O(e.node_id)??e;const t=await C(e),s=t==="settled"?[]:await ce(e.node_id),n=await Xe(e.node_id)||void 0,o=t==="settled"?await mt(e):null,m=o?.kind==="result"?await ae(e.node_id):void 0,i=Ye(e.node_id);return{run_id:e.node_id,name:e.name,status:t,...o?{outcome:o}:{},...m===void 0?{}:{result:m},...n!==void 0?{last_report:n}:{},...i?{recap:i}:{},pending_question_ids:s,open_notice_ids:t==="settled"?[]:await gt(e.node_id),...e.last_turn_error_json?{last_turn_error:JSON.parse(e.last_turn_error_json)}:{},usage:JSON.parse(e.usage_json??JSON.stringify(X())),payer:await J(e.grant_id),scopes:e.scopes,metadata:e.metadata_json?JSON.parse(e.metadata_json):{},created_at:e.created,...t==="settled"&&e.outcome_at?{settled_at:e.outcome_at}:{},latest_sequence:(e.event_cursor??1)-1,last_activity_at:e.last_activity_at,activity:await wt(e.node_id,t)}}c(W,"runObject");const z=864e5,we=255;function xn(e){if(e!==void 0){if(typeof e!="string"||!e.length||e.length>we)throw N(`Idempotency-Key must be 1-${we} characters`);return e}}c(xn,"idempotencyKeyOf");function Ot(e){if(Ie()){if(!e)throw new R(403,"permission_error","a cloud role serves this call only for a token that names a person");return e}}c(Ot,"cloudPersonScope");function k(e,t){const s=Ot(e);return s===void 0?t:`${s}:${t}`}c(k,"personKey");async function H(e,t,s,n){n&&await n.prepare("SELECT pg_advisory_xact_lock(hashtext(?))").get(`idempotency:${e}:${t}`),n??=await B();const o=await n.prepare("SELECT body_hash, run_id, response_json, created_at FROM idempotency_keys WHERE grantee = ? AND key = ?").get(e,t);if(!(!o||Date.now()-Date.parse(o.created_at)>=z)){if(o.body_hash!==s)throw new R(409,"idempotency_conflict","key used with a different request");return{run_id:o.run_id,response:JSON.parse(o.response_json)}}}c(H,"findIdempotent");async function ge(e,t,s,n,o,m,i){await e.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key = ? AND created_at <= ?").run(t,s,new Date(Date.now()-z).toISOString()),await e.prepare("INSERT INTO idempotency_keys (grantee, key, body_hash, run_id, response_json, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(t,s,n,o,JSON.stringify(m),i)}c(ge,"recordIdempotent");async function Wn(e=Date.now()){return Number((await(await B()).prepare("DELETE FROM idempotency_keys WHERE created_at <= ?").run(new Date(e-z).toISOString())).changes)}c(Wn,"pruneExpiredIdempotencyKeys");async function Ee(e,t,s,n,o){const m=n.text;let i=0;return{messageId:(await Ge(t,{kind:"message",label:m.slice(0,120),senderNodeId:"human",senderName:s,bodyJson:o?{body:m}:{body:m,start_turn:!1},urgency:o?"normal":"deferred",wake:o?1:0,withinEnqueue:c(async(u,l)=>{n.uses.length&&(await ue(s,n.uses,u),await me(n.uses.map(({upload_id:g,path:E,blob_key:y})=>({upload_id:g,path:E,blob_key:y,run_id:t})),u)),i=(await oe(u,t,t,"node.message.accepted",{message_id:l,start_turn:o})).sequence_number},"withinEnqueue")})).messageId,sequence:i}}c(Ee,"acceptRunMessage");class ye extends Error{static{c(this,"IdempotentReplay")}winner;constructor(t){super("idempotent replay"),this.winner=t}}const Dt=1440*60*1e3;function he(e){const t=typeof e=="string"?nt(e):null;if(t===null||!Number.isSafeInteger(t))throw N(`deadline '${String(e)}' is not a duration; use one or more <int>[smhd] parts such as 10m or 1h30m`,"deadline");if(t<=0)throw N("deadline must be greater than zero","deadline");if(t>Dt)throw N("deadline must be at most 24h","deadline");return t}c(he,"parseRunDeadlineMs");async function Fn(e,t,s,n=fe){if(t.isolated&&!it())throw new R(503,"isolated_unavailable","isolated runs require the Linux root launcher","isolated");t.deadline!==void 0&&he(t.deadline);const{causedBy:o,...m}=t,i=Q("sha256").update(JSON.stringify(m)).digest("hex");if(s){const a=await H(e.grantee,k(e.sub,s),i);if(a){const p=await q(a.run_id,e.grantee);return!p.pi_pid&&await C(p)==="running"&&(t.output_schema!==void 0&&!p.outcome_at&&await ct(p.node_id)===null&&await ae(p.node_id)===void 0&&await ie(p.node_id,"terminal",t.output_schema),await j(p.node_id,{resume:!0,capacity:"freeze"})),a.response}}await Et(e.grantee);const r=t.profile===void 0?await ze(e.grantee):Qe(t.profile),u=r.manifest.grantee??"app:terminal";if(u!==e.grantee){const a=`crtr:memory:rw:${u}:profile:${r.profileId}`;if(!e.resolved.some(p=>P(p,a))||t.scopes!==null&&!t.scopes.some(p=>P(p,a)))throw new R(403,"scope_missing",`missing scope ${a}`,"profile",void 0,a)}if(t.model!==void 0){const a=t.model;if(tt(a)){if(a.includes("/")&&await J(e.grant)!=="app"){const p=a.lastIndexOf("/"),D=a.slice(0,p),v=a.slice(p+1);if(["ultra","strong","medium","light"].includes(v)&&(await st({kind:"logical",family:D,strength:v,spec:a},Y(e.grantee).files,r.profileId)).every(S=>S.family!==D))throw N(`model '${a}' names a model family that is not configured in this daemon`,"model")}}else if(at(a)===void 0)throw N(`model '${a}' is not a strength (ultra, strong, medium, light), a family/strength, or a provider/model id`,"model")}const l=await Ke(),g=ke(Y(e.grantee).nodes,l);if(t.isolated&&t.cwd!==void 0){const a=Te(g,Le(t.cwd));if(!V(t.cwd)||a===".."||a.startsWith(`..${Ce}`)||V(a))throw N("cwd must be inside the isolated root node folder","cwd")}const E=await n(l,e.grantee,t.prompt),y=E.text,d=t.message===void 0?void 0:await n(l,e.grantee,t.message,E.uses.map(a=>a.path)),_=await J(e.grant);let h;try{const a=ot(r,t.model);await et({kind:rt(r),mode:"base",root:!0,rootLifecycle:"resident",readiness:"launch",cwd:t.isolated?t.cwd??g:Y(e.grantee).files,profile:r.profileId,prompt:y,nodeId:l,isolated:t.isolated??!1,...t.causedBy===void 0?{}:{causedBy:t.causedBy},...a===void 0?{}:{model:a},...t.deadline===void 0?{}:{deadlineAt:new Date(Date.now()+he(t.deadline)).toISOString()},...t.scopes===null?{}:{scopes:t.scopes},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant,client_id:e.client_id,metadata_json:JSON.stringify(t.metadata),run_id:l,event_cursor:1,usage_json:JSON.stringify(X())},withinCreate:c(async(p,D)=>{if(s){const S=await H(e.grantee,k(e.sub,s),i,p);if(S)throw new ye(S)}const v=E.uses;if(v.length&&(await ue(e.grantee,v,p),await me(v.map(({upload_id:S,path:I,blob_key:U})=>({upload_id:S,path:I,blob_key:U,run_id:l})),p)),h={run_id:l,node_id:l,grantee:e.grantee,grant:e.grant,rev:e.rev,scopes:t.scopes,payer:_,status:"running",created_at:D.created,metadata:t.metadata,sequence_number:0},await oe(p,l,l,"run.started",{scopes:t.scopes,payer:_,usage:X()}),d){const S=await Ee(p,l,e.grantee,d,!1);h.message={run_id:l,message_id:S.messageId,sequence_number:S.sequence}}s&&await ge(p,e.grantee,k(e.sub,s),i,l,h,D.created)},"withinCreate")},t.output_schema===void 0?void 0:async p=>{await ie(p,"terminal",t.output_schema)})}catch(a){if(a instanceof ye)return a.winner.response;if(t.model!==void 0&&a instanceof Ae&&a.code==="usage")throw N(a.message,"model");if(a instanceof Me&&(a.code==="isolated_run"||a.code==="invalid_request")){const p=a.details&&typeof a.details=="object"&&"param"in a.details&&typeof a.details.param=="string"?a.details.param:void 0;throw new R(a.status,a.code,a.message,p)}throw a}if(!h)throw new Error("run creation did not commit");return h}c(Fn,"startRun");async function qn(e,t){let s;if(t.after)try{const r=JSON.parse(Buffer.from(t.after,"base64url").toString());if(!r||typeof r!="object"||!("activity"in r)||typeof r.activity!="string"||!("id"in r)||typeof r.id!="string")throw new Error("invalid cursor");s={activity:r.activity,id:r.id}}catch{throw N("invalid list cursor","after")}const n=await(await A()).prepareUncached(`SELECT node_id, COALESCE(last_activity_at, created) AS activity FROM node_rows
6
+ )) LIMIT 1`).get(e.node_id,e.node_id),s=!e.last_settled_at&&e.status!=="done";return t||s||await mt(e)?"running":(await me(e.node_id)).length?"waiting_on_user":await(await A()).prepare(`SELECT 1 FROM deliveries d JOIN nodes n ON n.node_id = d.destination_id
7
+ WHERE n.run_id = ? AND ${Ge("d","'armed', 'pending'")}
8
+ AND (n.status IN ('active', 'idle') OR ${Ye("n")}) LIMIT 1`).get(e.node_id)?"running":"idle"}c(C,"runStatus");async function Wn(e){const t=await O(e);return t!==null&&await C(t)==="running"}c(Wn,"runIsRunning");async function W(e){await Z(e.node_id),e=await O(e.node_id)??e;const t=await C(e),s=t==="settled"?[]:await me(e.node_id),n=await Qe(e.node_id)||void 0,o=t==="settled"?await lt(e):null,m=o?.kind==="result"?await re(e.node_id):void 0,i=Xe(e.node_id);return{run_id:e.node_id,name:e.name,status:t,...o?{outcome:o}:{},...m===void 0?{}:{result:m},...n!==void 0?{last_report:n}:{},...i?{recap:i}:{},pending_question_ids:s,open_notice_ids:t==="settled"?[]:await Et(e.node_id),...e.last_turn_error_json?{last_turn_error:JSON.parse(e.last_turn_error_json)}:{},usage:JSON.parse(e.usage_json??JSON.stringify(X())),payer:await J(e.grant_id),scopes:e.scopes,metadata:e.metadata_json?JSON.parse(e.metadata_json):{},created_at:e.created,...t==="settled"&&e.outcome_at?{settled_at:e.outcome_at}:{},latest_sequence:(e.event_cursor??1)-1,last_activity_at:e.last_activity_at,activity:await gt(e.node_id,t)}}c(W,"runObject");const z=864e5,Ee=255;function Fn(e){if(e!==void 0){if(typeof e!="string"||!e.length||e.length>Ee)throw N(`Idempotency-Key must be 1-${Ee} characters`);return e}}c(Fn,"idempotencyKeyOf");function Dt(e){if(Le()){if(!e)throw new R(403,"permission_error","a cloud role serves this call only for a token that names a person");return e}}c(Dt,"cloudPersonScope");function k(e,t){const s=Dt(e);return s===void 0?t:`${s}:${t}`}c(k,"personKey");async function H(e,t,s,n){n&&await n.prepare("SELECT pg_advisory_xact_lock(hashtext(?))").get(`idempotency:${e}:${t}`),n??=await B();const o=await n.prepare("SELECT body_hash, run_id, response_json, created_at FROM idempotency_keys WHERE grantee = ? AND key = ?").get(e,t);if(!(!o||Date.now()-Date.parse(o.created_at)>=z)){if(o.body_hash!==s)throw new R(409,"idempotency_conflict","key used with a different request");return{run_id:o.run_id,response:JSON.parse(o.response_json)}}}c(H,"findIdempotent");async function ye(e,t,s,n,o,m,i){await e.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key = ? AND created_at <= ?").run(t,s,new Date(Date.now()-z).toISOString()),await e.prepare("INSERT INTO idempotency_keys (grantee, key, body_hash, run_id, response_json, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(t,s,n,o,JSON.stringify(m),i)}c(ye,"recordIdempotent");async function qn(e=Date.now()){return Number((await(await B()).prepare("DELETE FROM idempotency_keys WHERE created_at <= ?").run(new Date(e-z).toISOString())).changes)}c(qn,"pruneExpiredIdempotencyKeys");async function he(e,t,s,n,o){const m=n.text;let i=0;return{messageId:(await ze(t,{kind:"message",label:m.slice(0,120),senderNodeId:"human",senderName:s,bodyJson:o?{body:m}:{body:m,start_turn:!1},urgency:o?"normal":"deferred",wake:o?1:0,withinEnqueue:c(async(u,l)=>{n.uses.length&&(await le(s,n.uses,u),await pe(n.uses.map(({upload_id:g,path:E,blob_key:y})=>({upload_id:g,path:E,blob_key:y,run_id:t})),u)),i=(await ce(u,t,t,"node.message.accepted",{message_id:l,start_turn:o})).sequence_number},"withinEnqueue")})).messageId,sequence:i}}c(he,"acceptRunMessage");class Re extends Error{static{c(this,"IdempotentReplay")}winner;constructor(t){super("idempotent replay"),this.winner=t}}const Mt=1440*60*1e3;function Se(e){const t=typeof e=="string"?nt(e):null;if(t===null||!Number.isSafeInteger(t))throw N(`deadline '${String(e)}' is not a duration; use one or more <int>[smhd] parts such as 10m or 1h30m`,"deadline");if(t<=0)throw N("deadline must be greater than zero","deadline");if(t>Mt)throw N("deadline must be at most 24h","deadline");return t}c(Se,"parseRunDeadlineMs");async function Hn(e,t,s,n=we){if(t.isolated&&!it()&&await rt((t.profile===void 0?await te(e.grantee):ne(t.profile)).profileId)===null)throw new R(503,"isolated_unavailable","isolated runs require the Linux root launcher","isolated");t.deadline!==void 0&&Se(t.deadline);const{causedBy:o,...m}=t,i=Q("sha256").update(JSON.stringify(m)).digest("hex");if(s){const a=await H(e.grantee,k(e.sub,s),i);if(a){const p=await q(a.run_id,e.grantee);return!p.pi_pid&&await C(p)==="running"&&(t.output_schema!==void 0&&!p.outcome_at&&await ut(p.node_id)===null&&await re(p.node_id)===void 0&&await oe(p.node_id,"terminal",t.output_schema),await j(p.node_id,{resume:!0,capacity:"freeze"})),a.response}}await yt(e.grantee);const r=t.profile===void 0?await te(e.grantee):ne(t.profile),u=r.manifest.grantee??"app:terminal";if(u!==e.grantee){const a=`crtr:memory:rw:${u}:profile:${r.profileId}`;if(!e.resolved.some(p=>P(p,a))||t.scopes!==null&&!t.scopes.some(p=>P(p,a)))throw new R(403,"scope_missing",`missing scope ${a}`,"profile",void 0,a)}if(t.model!==void 0){const a=t.model;if(tt(a)){if(a.includes("/")&&await J(e.grant)!=="app"){const p=a.lastIndexOf("/"),D=a.slice(0,p),v=a.slice(p+1);if(["ultra","strong","medium","light"].includes(v)&&(await st({kind:"logical",family:D,strength:v,spec:a},Y(e.grantee).files,r.profileId)).every(S=>S.family!==D))throw N(`model '${a}' names a model family that is not configured in this daemon`,"model")}}else if(at(a)===void 0)throw N(`model '${a}' is not a strength (ultra, strong, medium, light), a family/strength, or a provider/model id`,"model")}const l=await Ke(),g=We(Y(e.grantee).nodes,l);if(t.isolated&&t.cwd!==void 0){const a=Ce(g,ke(t.cwd));if(!V(t.cwd)||a===".."||a.startsWith(`..${xe}`)||V(a))throw N("cwd must be inside the isolated root node folder","cwd")}const E=await n(l,e.grantee,t.prompt),y=E.text,d=t.message===void 0?void 0:await n(l,e.grantee,t.message,E.uses.map(a=>a.path)),_=await J(e.grant);let h;try{const a=dt(r,t.model);await et({kind:ot(r),mode:"base",root:!0,rootLifecycle:"resident",readiness:"launch",cwd:t.isolated?t.cwd??g:Y(e.grantee).files,profile:r.profileId,prompt:y,nodeId:l,isolated:t.isolated??!1,...t.causedBy===void 0?{}:{causedBy:t.causedBy},...a===void 0?{}:{model:a},...t.deadline===void 0?{}:{deadlineAt:new Date(Date.now()+Se(t.deadline)).toISOString()},...t.scopes===null?{}:{scopes:t.scopes},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant,client_id:e.client_id,metadata_json:JSON.stringify(t.metadata),run_id:l,event_cursor:1,usage_json:JSON.stringify(X())},withinCreate:c(async(p,D)=>{if(s){const S=await H(e.grantee,k(e.sub,s),i,p);if(S)throw new Re(S)}const v=E.uses;if(v.length&&(await le(e.grantee,v,p),await pe(v.map(({upload_id:S,path:I,blob_key:U})=>({upload_id:S,path:I,blob_key:U,run_id:l})),p)),h={run_id:l,node_id:l,grantee:e.grantee,grant:e.grant,rev:e.rev,scopes:t.scopes,payer:_,status:"running",created_at:D.created,metadata:t.metadata,sequence_number:0},await ce(p,l,l,"run.started",{scopes:t.scopes,payer:_,usage:X()}),d){const S=await he(p,l,e.grantee,d,!1);h.message={run_id:l,message_id:S.messageId,sequence_number:S.sequence}}s&&await ye(p,e.grantee,k(e.sub,s),i,l,h,D.created)},"withinCreate")},t.output_schema===void 0?void 0:async p=>{await oe(p,"terminal",t.output_schema)})}catch(a){if(a instanceof Re)return a.winner.response;if(t.model!==void 0&&a instanceof Te&&a.code==="usage")throw N(a.message,"model");if(a instanceof Ie&&(a.code==="isolated_run"||a.code==="invalid_request")){const p=a.details&&typeof a.details=="object"&&"param"in a.details&&typeof a.details.param=="string"?a.details.param:void 0;throw new R(a.status,a.code,a.message,p)}throw a}if(!h)throw new Error("run creation did not commit");return h}c(Hn,"startRun");async function Un(e,t){let s;if(t.after)try{const r=JSON.parse(Buffer.from(t.after,"base64url").toString());if(!r||typeof r!="object"||!("activity"in r)||typeof r.activity!="string"||!("id"in r)||typeof r.id!="string")throw new Error("invalid cursor");s={activity:r.activity,id:r.id}}catch{throw N("invalid list cursor","after")}const n=await(await A()).prepareUncached(`SELECT node_id, COALESCE(last_activity_at, created) AS activity FROM node_rows
9
9
  WHERE run_id = node_id AND deleting_at IS NULL ${e===null?"":"AND grantee = ?"}${t.sub===void 0?"":" AND sub = ?"}
10
- ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e],...t.sub===void 0?[]:[t.sub]),o=await pe(await x((await pe(n,async({node_id:r})=>!t.visible||await t.visible(r))).filter(({activity:r,node_id:u})=>!s||r<s.activity||r===s.activity&&u<s.id),async r=>({root:await O(r.node_id),activity:r.activity})),async({root:r})=>!t.status||await C(r)===t.status),m=o.slice(0,t.limit),i=m.at(-1);return{runs:await x(m,async({root:r})=>await W(r)),next:o.length>t.limit&&i?Buffer.from(JSON.stringify({activity:i.activity,id:i.root.node_id})).toString("base64url"):null}}c(qn,"listRuns");async function Hn(e,t,s){const n=await q(e,t),o=c(async()=>await W(await q(e,t)),"read");return!s||_e.includes(await C(n))?await o():new Promise((m,i)=>{let r=c(()=>{},"unsubscribe");const u=c(()=>{clearTimeout(d),clearInterval(y),r()},"settle"),l=c(async()=>{u();try{m(await o())}catch(_){i(_)}},"finish"),g=c(async()=>{_e.includes(String((await o()).status))&&await l()},"check"),E=c(()=>{g().catch(_=>{u(),i(_)})},"run"),y=setInterval(E,250),d=setTimeout(()=>{l()},s*1e3);_t(e,E).then(_=>{r=_},_=>{u(),i(_)})})}c(Hn,"waitRun");const Mt=5;async function Un(e,t,s=fe,n=!0,o){const m=e.status==="canceled"||e.outcome_reason==="closed",i=e.grantee??"app:terminal",r=Q("sha256").update(JSON.stringify(["run.message",e.node_id,t,n])).digest("hex"),u=c(async g=>{const E=await O(e.node_id),y=await(await A()).prepare("SELECT 1 FROM deliveries WHERE message_id = ? AND state = 'pending' AND canceled_at IS NULL").get(String(g.message_id));return E&&!m&&n&&!E.pi_pid&&y&&await j(e.node_id,{resume:!0,capacity:"freeze"}),g},"replay");if(o){const g=await H(i,k(e.sub,o),r);if(g)return u(g.response)}if(m)throw new R(409,"run_settled","canceled run cannot receive a message");const l=n&&e.final_report&&!e.pi_pid?(await import("../runtime/reopen.js")).commitReopenResident:void 0;for(let g=1;;g++){const E=await s(e.node_id,i,t),y=await F(async d=>{if(o){const a=await H(i,k(e.sub,o),r,d);if(a)return{replayed:a.response}}if(E.uses.length){await ft(d,e.node_id);for(const a of E.uses)if(await vt(e.node_id,a.path,d))return"path_taken"}await l?.(e.node_id);const _=await Ee(d,e.node_id,i,E,n),h={run_id:e.node_id,message_id:_.messageId,sequence_number:_.sequence};return o&&await ge(d,i,k(e.sub,o),r,e.node_id,h,new Date().toISOString()),{result:h}});if(y==="path_taken"){if(g>=Mt)throw new R(503,"service_unavailable","attachments of concurrent messages kept taking each other's file names; retry");continue}return"replayed"in y?u(y.replayed):(n&&!e.pi_pid&&await j(e.node_id,{resume:!0,capacity:"freeze"}),y.result)}}c(Un,"messageRun");async function At(e){const t=c(()=>new R(503,"service_unavailable",`node ${e} broker was unavailable before the interrupt reached it`),"unavailable");for(let s=0;s<2;s++){const n=await O(e);if(!n)throw G();if(!ne(n)){if(n.streaming)throw t();return}const o=(await K(e))?.executionId;try{await Ze(e)==="turn"&&le({nodes:[e]});return}catch(m){if(!(m instanceof Ve))throw m;const i=await O(e);if(!i)throw G();if(!i.streaming)return;if(s===0&&ne(i)&&(await K(e))?.executionId!==o)continue;throw t()}}throw t()}c(At,"interruptNode");async function $n(e,t="stop"){const s=t==="stop"?(await(await A()).prepare("SELECT node_id FROM nodes WHERE run_id = ?").all(e.node_id)).map(({node_id:n})=>n):[e.node_id];return await Promise.all(s.map(n=>At(n))),await W(await O(e.node_id)??e)}c($n,"interruptRun");async function Pn(e,t={}){const s=(await(await A()).prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(i=>i.node_id);le({run:e.node_id});const n=[];if(await C(e)!=="settled")n.push(...(await re(e.node_id,{preserveEmpty:!0,stopReason:"run_canceled"})).closed);else for(const i of s){if(i===e.node_id)continue;const r=await O(i);r&&r.status!=="done"&&r.status!=="canceled"&&r.status!=="dead"&&n.push(...(await re(i,{preserveEmpty:!0,stopReason:"run_canceled"})).closed)}if(await de({run_id:e.node_id},"the run was canceled"),(await Promise.all([...new Set([e.node_id,...n])].map(i=>te(i,"run_canceled")))).some(i=>!i.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const m=await O(e.node_id);return await W(m??e)}c(Pn,"cancelRun");const Re=200;async function Bn(e,t){if(typeof t!="string"||!t.trim())throw N("name is required","name");const s=t.trim();if(s.length>Re)throw N(`name exceeds ${Re} characters`,"name");const n=await Ue(e.node_id,{name:s});return n.window&&Pe(n.window,$e(n)),await W(n)}c(Bn,"renameRun");async function jn(e,t={config:Rt,client:ht}){await de({run_id:e.node_id},"the run was deleted");const s=await F(async u=>(await u.prepare("UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id = ?").run(new Date().toISOString(),e.node_id),(await u.prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(l=>l.node_id)));if((await Promise.all(s.map(u=>te(u,"run_deleted",{deleting:!0})))).some(u=>!u.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const o=await x(s,async u=>{const l=await qe(u);if(l.app===null)throw new Error(`run node missing during deletion: ${u}`);return l}),m=await(await A()).prepare("SELECT DISTINCT session_id FROM sessions WHERE session_id IS NOT NULL AND node_id IN (SELECT node_id FROM nodes WHERE run_id = ?)").all(e.node_id);for(const{session_id:u}of m)try{await ee().delete(u)}catch(l){if(!Be(l))throw l}const i=await F(async u=>{for(const y of s){const d=await Fe(u,y);d!==null&&d.deleted_at===null&&await We(u,d)}const l=await Nt([e.node_id],u);await u.prepare("DELETE FROM run_events WHERE run_id = ?").run(e.node_id),await u.prepare("DELETE FROM idempotency_keys WHERE run_id = ?").run(e.node_id);const g=s.map(()=>"?").join(","),E=await u.prepareUncached(`SELECT message_id FROM messages WHERE sender_node_id IN (${g}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${g})`).all(...s,...s);await u.prepareUncached(`DELETE FROM deliveries WHERE destination_id IN (${g})`).run(...s);for(const{message_id:y}of E)await u.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(y,y);return l});for(const u of o)await He(u);const{notifyRunDeleted:r}=await import("./events.js");return r(e.node_id),{uploads_pending:await It(i,t)}}c(jn,"deleteRun");async function It(e,t){if(!e.length||!t.config())return e.length;let s;try{s=t.client()}catch{return e.length}const n=new Set,o=e.map(i=>s.deleteUpload(i).then(async()=>{await St(i),n.add(i)},()=>{}));let m;return await Promise.race([Promise.all(o),new Promise(i=>{m=setTimeout(i,t.budgetMs??bt),m.unref?.()})]),clearTimeout(m),e.length-n.size}c(It,"deleteReleasedUploads");async function Jn(e,t,s){await q(e,t),await Z(e);const n=await F(async d=>{const _=await d.prepare("SELECT node_id, parent FROM nodes WHERE run_id = ? ORDER BY CASE WHEN node_id = ? THEN 0 ELSE 1 END, created, node_id").all(e,e),h=new Map;for(const S of _)h.set(S.node_id,(await d.prepare("SELECT session_id FROM sessions WHERE node_id = ? GROUP BY session_id ORDER BY min(created_at)").all(S.node_id)).map(I=>I.session_id));const p=(await d.prepare("SELECT event_cursor FROM nodes WHERE node_id = ?").get(e)).event_cursor-1,D=await d.prepare(`SELECT node_id, sequence_number FROM run_events
10
+ ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e],...t.sub===void 0?[]:[t.sub]),o=await _e(await x((await _e(n,async({node_id:r})=>!t.visible||await t.visible(r))).filter(({activity:r,node_id:u})=>!s||r<s.activity||r===s.activity&&u<s.id),async r=>({root:await O(r.node_id),activity:r.activity})),async({root:r})=>!t.status||await C(r)===t.status),m=o.slice(0,t.limit),i=m.at(-1);return{runs:await x(m,async({root:r})=>await W(r)),next:o.length>t.limit&&i?Buffer.from(JSON.stringify({activity:i.activity,id:i.root.node_id})).toString("base64url"):null}}c(Un,"listRuns");async function $n(e,t,s){const n=await q(e,t),o=c(async()=>await W(await q(e,t)),"read");return!s||ge.includes(await C(n))?await o():new Promise((m,i)=>{let r=c(()=>{},"unsubscribe");const u=c(()=>{clearTimeout(d),clearInterval(y),r()},"settle"),l=c(async()=>{u();try{m(await o())}catch(_){i(_)}},"finish"),g=c(async()=>{ge.includes(String((await o()).status))&&await l()},"check"),E=c(()=>{g().catch(_=>{u(),i(_)})},"run"),y=setInterval(E,250),d=setTimeout(()=>{l()},s*1e3);wt(e,E).then(_=>{r=_},_=>{u(),i(_)})})}c($n,"waitRun");const At=5;async function Pn(e,t,s=we,n=!0,o){const m=e.status==="canceled"||e.outcome_reason==="closed",i=e.grantee??"app:terminal",r=Q("sha256").update(JSON.stringify(["run.message",e.node_id,t,n])).digest("hex"),u=c(async g=>{const E=await O(e.node_id),y=await(await A()).prepare("SELECT 1 FROM deliveries WHERE message_id = ? AND state = 'pending' AND canceled_at IS NULL").get(String(g.message_id));return E&&!m&&n&&!E.pi_pid&&y&&await j(e.node_id,{resume:!0,capacity:"freeze"}),g},"replay");if(o){const g=await H(i,k(e.sub,o),r);if(g)return u(g.response)}if(m)throw new R(409,"run_settled","canceled run cannot receive a message");const l=n&&e.final_report&&!e.pi_pid?(await import("../runtime/reopen.js")).commitReopenResident:void 0;for(let g=1;;g++){const E=await s(e.node_id,i,t),y=await F(async d=>{if(o){const a=await H(i,k(e.sub,o),r,d);if(a)return{replayed:a.response}}if(E.uses.length){await _t(d,e.node_id);for(const a of E.uses)if(await bt(e.node_id,a.path,d))return"path_taken"}await l?.(e.node_id);const _=await he(d,e.node_id,i,E,n),h={run_id:e.node_id,message_id:_.messageId,sequence_number:_.sequence};return o&&await ye(d,i,k(e.sub,o),r,e.node_id,h,new Date().toISOString()),{result:h}});if(y==="path_taken"){if(g>=At)throw new R(503,"service_unavailable","attachments of concurrent messages kept taking each other's file names; retry");continue}return"replayed"in y?u(y.replayed):(n&&!e.pi_pid&&await j(e.node_id,{resume:!0,capacity:"freeze"}),y.result)}}c(Pn,"messageRun");async function It(e){const t=c(()=>new R(503,"service_unavailable",`node ${e} broker was unavailable before the interrupt reached it`),"unavailable");for(let s=0;s<2;s++){const n=await O(e);if(!n)throw G();if(!ae(n)){if(n.streaming)throw t();return}const o=(await K(e))?.executionId;try{await Ze(e)==="turn"&&fe({nodes:[e]});return}catch(m){if(!(m instanceof Ve))throw m;const i=await O(e);if(!i)throw G();if(!i.streaming)return;if(s===0&&ae(i)&&(await K(e))?.executionId!==o)continue;throw t()}}throw t()}c(It,"interruptNode");async function Bn(e,t="stop"){const s=t==="stop"?(await(await A()).prepare("SELECT node_id FROM nodes WHERE run_id = ?").all(e.node_id)).map(({node_id:n})=>n):[e.node_id];return await Promise.all(s.map(n=>It(n))),await W(await O(e.node_id)??e)}c(Bn,"interruptRun");async function jn(e,t={}){const s=(await(await A()).prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(i=>i.node_id);fe({run:e.node_id});const n=[];if(await C(e)!=="settled")n.push(...(await de(e.node_id,{preserveEmpty:!0,stopReason:"run_canceled"})).closed);else for(const i of s){if(i===e.node_id)continue;const r=await O(i);r&&r.status!=="done"&&r.status!=="canceled"&&r.status!=="dead"&&n.push(...(await de(i,{preserveEmpty:!0,stopReason:"run_canceled"})).closed)}if(await ue({run_id:e.node_id},"the run was canceled"),(await Promise.all([...new Set([e.node_id,...n])].map(i=>se(i,"run_canceled")))).some(i=>!i.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const m=await O(e.node_id);return await W(m??e)}c(jn,"cancelRun");const Ne=200;async function Jn(e,t){if(typeof t!="string"||!t.trim())throw N("name is required","name");const s=t.trim();if(s.length>Ne)throw N(`name exceeds ${Ne} characters`,"name");const n=await Pe(e.node_id,{name:s});return n.window&&je(n.window,Be(n)),await W(n)}c(Jn,"renameRun");async function Yn(e,t={config:St,client:Rt}){await ue({run_id:e.node_id},"the run was deleted");const s=await F(async u=>(await u.prepare("UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id = ?").run(new Date().toISOString(),e.node_id),(await u.prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(l=>l.node_id)));if((await Promise.all(s.map(u=>se(u,"run_deleted",{deleting:!0})))).some(u=>!u.confirmed))throw new R(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const o=await x(s,async u=>{const l=await Ue(u);if(l.app===null)throw new Error(`run node missing during deletion: ${u}`);return l}),m=await(await A()).prepare("SELECT DISTINCT session_id FROM sessions WHERE session_id IS NOT NULL AND node_id IN (SELECT node_id FROM nodes WHERE run_id = ?)").all(e.node_id);for(const{session_id:u}of m)try{await ee().delete(u)}catch(l){if(!Je(l))throw l}const i=await F(async u=>{for(const y of s){const d=await He(u,y);d!==null&&d.deleted_at===null&&await qe(u,d)}const l=await vt([e.node_id],u);await u.prepare("DELETE FROM run_events WHERE run_id = ?").run(e.node_id),await u.prepare("DELETE FROM idempotency_keys WHERE run_id = ?").run(e.node_id);const g=s.map(()=>"?").join(","),E=await u.prepareUncached(`SELECT message_id FROM messages WHERE sender_node_id IN (${g}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${g})`).all(...s,...s);await u.prepareUncached(`DELETE FROM deliveries WHERE destination_id IN (${g})`).run(...s);for(const{message_id:y}of E)await u.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(y,y);return l});for(const u of o)await $e(u);const{notifyRunDeleted:r}=await import("./events.js");return r(e.node_id),{uploads_pending:await Tt(i,t)}}c(Yn,"deleteRun");async function Tt(e,t){if(!e.length||!t.config())return e.length;let s;try{s=t.client()}catch{return e.length}const n=new Set,o=e.map(i=>s.deleteUpload(i).then(async()=>{await Nt(i),n.add(i)},()=>{}));let m;return await Promise.race([Promise.all(o),new Promise(i=>{m=setTimeout(i,t.budgetMs??Ot),m.unref?.()})]),clearTimeout(m),e.length-n.size}c(Tt,"deleteReleasedUploads");async function Gn(e,t,s){await q(e,t),await Z(e);const n=await F(async d=>{const _=await d.prepare("SELECT node_id, parent FROM nodes WHERE run_id = ? ORDER BY CASE WHEN node_id = ? THEN 0 ELSE 1 END, created, node_id").all(e,e),h=new Map;for(const S of _)h.set(S.node_id,(await d.prepare("SELECT session_id FROM sessions WHERE node_id = ? GROUP BY session_id ORDER BY min(created_at)").all(S.node_id)).map(I=>I.session_id));const p=(await d.prepare("SELECT event_cursor FROM nodes WHERE node_id = ?").get(e)).event_cursor-1,D=await d.prepare(`SELECT node_id, sequence_number FROM run_events
11
11
  WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.done', 'node.output_text.done', 'usage')
12
12
  ORDER BY sequence_number`).all(e,p),v=await d.prepare(`SELECT node_id, sequence_number, type, body_json FROM run_events
13
13
  WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.delta', 'node.output_text.delta')
14
- ORDER BY sequence_number`).all(e,p);return{nodes:_,sessionIds:h,latestSequence:p,completed:D,deltas:v}}),{nodes:o,sessionIds:m,latestSequence:i,completed:r,deltas:u}=n,l=await x(o,async d=>({node_id:d.node_id,parent_node_id:d.parent,conversations:await x(m.get(d.node_id)??[],async _=>{const{header:h,entries:a}=await ee().read(_);if(s==="entries")return{conversation_id:_,header:h,entries:a};const p=xe.inMemory(h.cwd,void 0,[h,...a]),D=dt(p)??[],v=new Map(a.map((w,b)=>[w.id,b])),S=a.filter(w=>w.type==="custom"&&w.customType==="crtr-system-prompt").map(w=>({id:w.id,message:{role:"custom",customType:"crtr-system-prompt",data:"data"in w?w.data:void 0,timestamp:w.timestamp}})),I=a.filter(w=>w.type==="custom_message"&&w.customType==="app_message").map(w=>({id:w.id,message:{role:"custom",customType:"app_message",content:"content"in w?w.content:"",timestamp:w.timestamp}})),U=d.node_id===e?a.find(w=>{if(w.type!=="message"||w.message.role!=="user")return!1;const b=se(a,w.message.timestamp);return b?.type==="runtime"&&b.kind==="kickoff"})?.id:void 0,Se=[...D.filter(({id:w})=>!I.some(b=>b.id===w)),...S,...I].sort((w,b)=>(v.get(w.id)??Number.MAX_SAFE_INTEGER)-(v.get(b.id)??Number.MAX_SAFE_INTEGER)).map(({id:w,message:b})=>{const f=b,T=f.usage,$=f.role==="user"?se(a,Number(f.timestamp)):null,Ne=$?.type==="human"||w===U,L=f.role==="toolResult"?"tool_result":f.role==="bashExecution"?"bash_execution":f.role==="compactionSummary"?"compaction_summary":f.role==="branchSummary"?"branch_summary":f.role==="custom"&&f.customType==="crtr-system-prompt"?"system":f.role==="user"?Ne?"user":"custom":f.role,ve=L==="custom"?f.role==="custom"?f.customType:$?.type==="runtime"?$.kind:"unknown":void 0,be=L==="system"&&f.customType==="crtr-system-prompt"?[{type:"text",text:f.data?.text??""}]:typeof f.content=="string"?[{type:"text",text:f.content}]:Array.isArray(f.content)?f.content.map(M=>M.type==="toolCall"?{type:"tool_call",tool_call_id:M.id,name:M.name,arguments:M.arguments}:M.type==="image"?{type:"image",mime_type:M.mimeType}:M.type==="thinking"?{type:"thinking",text:M.thinking}:M):[];return{entry_id:w,role:L,...L==="custom"?{custom_type:ve}:{},time:typeof f.timestamp=="string"?f.timestamp:new Date(Number(f.timestamp??Date.now())).toISOString(),content:be,...L==="assistant"?{model:f.model,provider:f.provider,stop_reason:f.stopReason,...f.errorMessage?{error_message:f.errorMessage}:{},...T?{usage:{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}}:{}}:{},...L==="tool_result"?{tool_call_id:f.toolCallId,tool_name:f.toolName,is_error:f.isError}:{}}})??[];return{conversation_id:_,messages:Se}})})),g=new Map(await Promise.all(l.map(async d=>[d.node_id,await lt(d.node_id)])));for(const d of r){const _=g.get(d.node_id);_!=null&&d.sequence_number>=_&&g.set(d.node_id,null)}const E=new Map;for(const d of u){const _=g.get(d.node_id);if(_==null||d.sequence_number<_)continue;const h=JSON.parse(d.body_json),a=E.get(d.node_id)??{thinking:"",text:""};d.type==="node.thinking.delta"?a.thinking+=h.delta:a.text+=h.delta,E.set(d.node_id,a)}const y=l.map(d=>({...d,in_progress_message:g.get(d.node_id)==null?null:E.get(d.node_id)??{thinking:"",text:""}}));return{run_id:e,latest_sequence:i,nodes:y}}c(Jn,"traceRun");function Yn(e,t,s){if(t===void 0){if(!e.some(n=>P(n,"crtr:llm")))throw new R(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return null}if(!Array.isArray(t)||t.some(n=>typeof n!="string"))throw N("scopes must be a list of scope strings","scopes");for(const n of t){try{pt(n)}catch{throw N(`invalid scope ${n}`,"scopes")}if(/^crtr:files:(?:read|rw):app:/.test(n)&&n.split(":")[4]===s.slice(4))throw N(`own-app files cannot narrow a run: ${n}`,"scopes");if(!(/^crtr:memory:(?:read|rw):app:/.test(n)&&n.split(":")[4]===s.slice(4))&&!De(e,n))throw new R(403,"scope_missing",`missing scope ${n}`,void 0,void 0,n)}if(!t.includes("crtr:llm"))throw new R(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return t}c(Yn,"requestedScopes");export{z as IDEMPOTENCY_WINDOW_MS,Dt as RUN_DEADLINE_MAX_MS,bt as RUN_DELETE_STORAGE_BUDGET_MS,Re as RUN_NAME_MAX,R as RunError,Pn as cancelRun,Ot as cloudPersonScope,jn as deleteRun,xn as idempotencyKeyOf,$n as interruptRun,qn as listRuns,Un as messageRun,q as ownedRun,he as parseRunDeadlineMs,Wn as pruneExpiredIdempotencyKeys,Bn as renameRun,Yn as requestedScopes,kn as runIsRunning,W as runObject,Fn as startRun,Jn as traceRun,Hn as waitRun};
14
+ ORDER BY sequence_number`).all(e,p);return{nodes:_,sessionIds:h,latestSequence:p,completed:D,deltas:v}}),{nodes:o,sessionIds:m,latestSequence:i,completed:r,deltas:u}=n,l=await x(o,async d=>({node_id:d.node_id,parent_node_id:d.parent,conversations:await x(m.get(d.node_id)??[],async _=>{const{header:h,entries:a}=await ee().read(_);if(s==="entries")return{conversation_id:_,header:h,entries:a};const p=Fe.inMemory(h.cwd,void 0,[h,...a]),D=ct(p)??[],v=new Map(a.map((w,b)=>[w.id,b])),S=a.filter(w=>w.type==="custom"&&w.customType==="crtr-system-prompt").map(w=>({id:w.id,message:{role:"custom",customType:"crtr-system-prompt",data:"data"in w?w.data:void 0,timestamp:w.timestamp}})),I=a.filter(w=>w.type==="custom_message"&&w.customType==="app_message").map(w=>({id:w.id,message:{role:"custom",customType:"app_message",content:"content"in w?w.content:"",timestamp:w.timestamp}})),U=d.node_id===e?a.find(w=>{if(w.type!=="message"||w.message.role!=="user")return!1;const b=ie(a,w.message.timestamp);return b?.type==="runtime"&&b.kind==="kickoff"})?.id:void 0,ve=[...D.filter(({id:w})=>!I.some(b=>b.id===w)),...S,...I].sort((w,b)=>(v.get(w.id)??Number.MAX_SAFE_INTEGER)-(v.get(b.id)??Number.MAX_SAFE_INTEGER)).map(({id:w,message:b})=>{const f=b,T=f.usage,$=f.role==="user"?ie(a,Number(f.timestamp)):null,be=$?.type==="human"||w===U,L=f.role==="toolResult"?"tool_result":f.role==="bashExecution"?"bash_execution":f.role==="compactionSummary"?"compaction_summary":f.role==="branchSummary"?"branch_summary":f.role==="custom"&&f.customType==="crtr-system-prompt"?"system":f.role==="user"?be?"user":"custom":f.role,Oe=L==="custom"?f.role==="custom"?f.customType:$?.type==="runtime"?$.kind:"unknown":void 0,De=L==="system"&&f.customType==="crtr-system-prompt"?[{type:"text",text:f.data?.text??""}]:typeof f.content=="string"?[{type:"text",text:f.content}]:Array.isArray(f.content)?f.content.map(M=>M.type==="toolCall"?{type:"tool_call",tool_call_id:M.id,name:M.name,arguments:M.arguments}:M.type==="image"?{type:"image",mime_type:M.mimeType}:M.type==="thinking"?{type:"thinking",text:M.thinking}:M):[];return{entry_id:w,role:L,...L==="custom"?{custom_type:Oe}:{},time:typeof f.timestamp=="string"?f.timestamp:new Date(Number(f.timestamp??Date.now())).toISOString(),content:De,...L==="assistant"?{model:f.model,provider:f.provider,stop_reason:f.stopReason,...f.errorMessage?{error_message:f.errorMessage}:{},...T?{usage:{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}}:{}}:{},...L==="tool_result"?{tool_call_id:f.toolCallId,tool_name:f.toolName,is_error:f.isError}:{}}})??[];return{conversation_id:_,messages:ve}})})),g=new Map(await Promise.all(l.map(async d=>[d.node_id,await pt(d.node_id)])));for(const d of r){const _=g.get(d.node_id);_!=null&&d.sequence_number>=_&&g.set(d.node_id,null)}const E=new Map;for(const d of u){const _=g.get(d.node_id);if(_==null||d.sequence_number<_)continue;const h=JSON.parse(d.body_json),a=E.get(d.node_id)??{thinking:"",text:""};d.type==="node.thinking.delta"?a.thinking+=h.delta:a.text+=h.delta,E.set(d.node_id,a)}const y=l.map(d=>({...d,in_progress_message:g.get(d.node_id)==null?null:E.get(d.node_id)??{thinking:"",text:""}}));return{run_id:e,latest_sequence:i,nodes:y}}c(Gn,"traceRun");function Xn(e,t,s){if(t===void 0){if(!e.some(n=>P(n,"crtr:llm")))throw new R(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return null}if(!Array.isArray(t)||t.some(n=>typeof n!="string"))throw N("scopes must be a list of scope strings","scopes");for(const n of t){try{ft(n)}catch{throw N(`invalid scope ${n}`,"scopes")}if(/^crtr:files:(?:read|rw):app:/.test(n)&&n.split(":")[4]===s.slice(4))throw N(`own-app files cannot narrow a run: ${n}`,"scopes");if(!(/^crtr:memory:(?:read|rw):app:/.test(n)&&n.split(":")[4]===s.slice(4))&&!Ae(e,n))throw new R(403,"scope_missing",`missing scope ${n}`,void 0,void 0,n)}if(!t.includes("crtr:llm"))throw new R(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return t}c(Xn,"requestedScopes");export{z as IDEMPOTENCY_WINDOW_MS,Mt as RUN_DEADLINE_MAX_MS,Ot as RUN_DELETE_STORAGE_BUDGET_MS,Ne as RUN_NAME_MAX,R as RunError,jn as cancelRun,Dt as cloudPersonScope,Yn as deleteRun,Fn as idempotencyKeyOf,Bn as interruptRun,Un as listRuns,Pn as messageRun,q as ownedRun,Se as parseRunDeadlineMs,qn as pruneExpiredIdempotencyKeys,Jn as renameRun,Xn as requestedScopes,Wn as runIsRunning,W as runObject,Hn as startRun,Gn as traceRun,$n as waitRun};
@@ -0,0 +1,2 @@
1
+ /** The Blaxel environment the node's profile points at, or null when it runs on a machine. Reads rows only, so it is safe before any environment is registered. A leaf module: the launch and run-start paths import it without pulling in the ledger. */
2
+ export declare function blaxelEnvironmentOf(profileId: string | null): Promise<string | null>;
@@ -0,0 +1 @@
1
+ var o=Object.defineProperty;var t=(e,n)=>o(e,"name",{value:n,configurable:!0});import{openDb as r}from"../canvas/db.js";async function a(e){return e===null?null:(await(await r()).prepare("SELECT e.object AS object FROM profiles p JOIN environments e ON e.object = p.environment_id WHERE p.id = ? AND e.kind = 'blaxel'").get(e))?.object??null}t(a,"blaxelEnvironmentOf");export{a as blaxelEnvironmentOf};
@@ -65,6 +65,10 @@ export declare class FaultRetry {
65
65
  constructor(deps: FaultRetryDeps);
66
66
  clearTimer(): void;
67
67
  reset(): void;
68
+ /** Whether a model fallback has been started and may still prompt a recovery turn. */
69
+ fallbackInFlight(): boolean;
70
+ /** The recorded provider fault is fatal: nothing is armed to re-drive the node. Only meaningful right after `schedule()` armed nothing. */
71
+ faultIsFatal(): Promise<boolean>;
68
72
  /** No recovery turn will follow the settled turn: a run node writes its held-back turn end now. */
69
73
  abandonTurnEnd(): void;
70
74
  private state;
@@ -1 +1 @@
1
- var ce=Object.defineProperty;var m=(r,e)=>ce(r,"name",{value:e,configurable:!0});import{envModelIntent as B,envNodeCwd as j,envProfileId as Y}from"../../../shared/env.js";import{isContextOverflow as q}from"@earendil-works/pi-ai";import{classify as T}from"../../fault-classifier.js";import{isDaemonTransportApiError as V}from"../../../api/errors.js";import{isModelNotFoundError as fe,isProviderUnconfiguredError as pe}from"../fault.js";import{brokerRecovery as he,daemonClient as me,mutateBrokerFault as X,mutateBrokerProviderRetry as I}from"./daemon-ops.js";import{extractCoolingDeadline as ge}from"../managed-provider-cooling.js";import{extractProviderFailureDetails as ye}from"../../provider-failure.js";import{expandModelCandidates as J,modelRequestFromConfig as z}from"../../model-routes.js";import{operationIdContext as H}from"../../events/operation-id.js";import{emitEvent as F}from"../../events/emit.js";import{CONNECTION_FAULT_RECOVERY_BODY as ve,PROVIDER_FAULT_RECOVERY_BODY as we,RUN_CONNECTION_FAULT_RETRY_BODY as Fe,formatCard as K,formatModelFallbackRecovery as be}from"../../../shared/generated-context.js";import{markFatalOnExhaust as Q,nextFaultRetry as C,policyFor as Z}from"../fault-recovery.js";import{parseModelSpec as ee}from"../model-selection.js";import{endedByAbort as te}from"../session-cycles.js";import{isManagedProvider as ke,resolveProviderCandidates as Ie}from"../../subscription-state.js";import{announceMessageOrigin as ne}from"../stamp/channel.js";import{LLM_REFUSAL_DIAGNOSTIC as _e,refusalRetryAt as Ee}from"./model-adapter.js";import{isSafetyRefusal as ie}from"../../llm/refusal.js";const Re=2147483647,re=3e4;function Se(r,e,t){const n=Date.parse(r);return!Number.isFinite(n)||t+e-n>re}m(Se,"runTurnRetryExpired");class _ extends Error{static{m(this,"StaleOutcome")}}const Ae=1e3,Me=3e4;function xe(r,e){return r!=="connection"?we:e?Fe:ve}m(xe,"retryCardBody");function R(r){return{link:r.link,op:r.op,kind:r.kind,retry:r.retry,message:r.message,since:r.since,operation_id:r.operation_id,...r.anchor_entry_id===void 0?{}:{anchorEntryId:r.anchor_entry_id},...r.provider_error===void 0?{}:{providerError:r.provider_error}}}m(R,"policyFault");function se(r){return r instanceof Error?r.message:String(r)}m(se,"errorText");function oe(r){return V(r)||T("pi\u2192provider",r).disposition==="auto"}m(oe,"isTransientRejection");function O(r){return{link:r.link,op:r.op,kind:r.kind,retry:r.retry,message:r.message,...r.operation_id===void 0?{}:{operation_id:r.operation_id},...r.since===void 0?{}:{since:r.since},...r.anchorEntryId===void 0?{}:{anchor_entry_id:r.anchorEntryId},...r.providerError===void 0?{}:{provider_error:r.providerError}}}m(O,"faultInput");function ae(r,e){const t=m(n=>`${n.providerId}/${n.modelId}`===e.modelSpec,"onLiveModel");return(r.find(n=>t(n)&&n.thinkingLevel===e.thinkingLevel)??r.find(t))?.rank}m(ae,"liveRouteRank");function de(r){const e=T("pi\u2192provider",r),t=r.diagnostics?.some(n=>n.type===_e)??!1;return{classified:e,disposition:t?"fatal":e.disposition}}m(de,"settledProviderFault");function Ze(r,e){if(r.stopReason!=="error"||te(r)||ie(r)||q(r,e))return!1;const{classified:t,disposition:n}=de(r);return n==="auto"&&(t.kind==="connection"||t.kind==="rate-limit"||t.kind==="overloaded")}m(Ze,"willDaemonRetry");const Te=/does not support assistant message prefill|conversation must end with a user message/i;function Ce(r,e){return e&&Te.test(r)}m(Ce,"isPrefillRejectionDuringEpisode");class et{static{m(this,"FaultRetry")}deps;timer=null;failedRetryRoutes=new Set;notFoundModels=new Set;providerFallbackInFlight=null;notFoundFallbackInFlight=null;stagedCapacityFailure=null;agentStarts=0;constructor(e){this.deps=e}clearTimer(){this.timer!==null&&(clearTimeout(this.timer),this.timer=null)}reset(){this.clearTimer(),this.deps.turnEnds?.abandon(),this.providerFallbackInFlight=null,this.notFoundFallbackInFlight=null,this.stagedCapacityFailure=null}abandonTurnEnd(){this.deps.turnEnds?.abandon()}state(){return he(this.deps.nodeId)}async recordFault(e){return(await X(this.deps.nodeId,{op:"record",input:e})).fault}async clearFault(e,t=!1){await X(this.deps.nodeId,{op:"clear",...e===void 0?{}:{link:e},...t?{preserve_episode:!0}:{}})}sessionId(e){const t=e.sessionManager.getSessionId?.();return typeof t=="string"&&t!==""?t:null}async clearEpisode(e){const t=this.sessionId(e);t!==null&&await I(this.deps.nodeId,{op:"clear",session_id:t})}onEvent(e){e.type==="agent_start"&&this.agentStarts++,this.maybeSwitchProviderForRetry(e),this.maybeFallbackOnUnusableModel(e)}isActiveAutoFault(e){return e===null||e.retry.disposition!=="auto"||e.retry.by!=="daemon"?!1:e.kind==="connection"||e.kind==="rate-limit"||e.kind==="overloaded"}async publishSettledProviderFault(e){const t={agentEnd:e.stagedProviderAgentEnd,watchdogAbort:e.stagedWatchdogAbort,refreshAbort:e.stagedRefreshAbort,overflowFailure:e.stagedOverflowFailure,capacityFailure:this.stagedCapacityFailure?.generation===e?this.stagedCapacityFailure:null};this.stagedCapacityFailure=null,e.stagedProviderAgentEnd=null,e.stagedWatchdogAbort=!1,e.stagedRefreshAbort=!1,e.stagedOverflowFailure=null;const n=this.agentStarts,i=m(()=>{if(this.deps.installedGeneration()!==e||this.agentStarts!==n)throw new _},"assertCurrent");try{await this.retryWhileDaemonUnreachable("settled turn fault write",()=>this.recordStagedOutcome(e,t,i),i)}catch(s){if(!(s instanceof _))throw s}}async retryWhileDaemonUnreachable(e,t,n){let i=Ae;for(;;){try{await t();return}catch(s){if(!V(s))throw s;F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`${e} waiting for the daemon: ${s.message}`}})}await new Promise(s=>setTimeout(s,i)),i=Math.min(i*2,Me),n()}}async recordStagedOutcome(e,t,n){const{agentEnd:i,watchdogAbort:s,refreshAbort:d,overflowFailure:c,capacityFailure:f}=t,o=e.session,a=m(async b=>{n(),await this.clearEpisode(b)},"clearEpisode"),g=m(async b=>{n(),await this.recordFault(b)},"recordFault"),y=m(async(b,ue)=>{n(),await this.recordPendingProviderFault(b,ue)},"recordPendingProviderFault"),u=await this.state(),p=u.fault,v=u.episode,k=this.sessionId(o),w=this.isActiveAutoFault(p)&&p.link==="pi\u2192provider"?p:v?.state==="admitted"&&v.session_id===k&&this.isActiveAutoFault(v.fault)?v.fault:null,h=w===null?null:{since:w.since,anchorEntryId:w.anchor_entry_id,attempt:w.retry.attempt},l=[...Array.isArray(i?.messages)?i.messages:[]].reverse().find(b=>typeof b=="object"&&b!==null&&b.role==="assistant");if(f!==null||c!==null){await a(o),await g({link:"pi\u2192provider",op:"context overflow recovery",kind:"context-overflow",retry:{disposition:"fatal"},message:f?.errorMessage??c.errorMessage,anchor_entry_id:o.sessionManager.getLeafId?.()??void 0});return}if(s&&l?.stopReason==="aborted"){await y(o,{link:"pi\u2192provider",op:"generation turn",kind:"connection",retry:{disposition:"auto",by:"daemon",...h?.attempt!==void 0?{attempt:h.attempt}:{}},message:typeof l.errorMessage=="string"?l.errorMessage:"engine error (no errorMessage recorded)",...h!==null?{since:h.since}:{},anchor_entry_id:this.retryAnchor(o,h?.anchorEntryId)});return}if(l?.stopReason!=="error"){l!==void 0&&await a(o);return}if(d){await a(o);return}if(ie(l)){await a(o);return}const A=typeof l.errorMessage=="string"?l.errorMessage:"engine error (no errorMessage recorded)";if(te({stopReason:"error",errorMessage:A})){await a(o);return}const E=q(l,o.model?.contextWindow),M=de(l),$=h!==null&&M.disposition!=="auto"&&Ce(A,!0),le=$?{kind:"connection",disposition:"auto"}:M.classified,S=$?"auto":M.disposition,D=ge(l),L=Ee(l),N=ye(l),G=typeof l.provider=="string"&&l.provider!==""?l.provider:o.model?.provider,U=typeof l.model=="string"&&l.model!==""?l.model:o.model?.id,W=N===void 0||G===void 0||U===void 0?void 0:{provider:G,model:U,...N};E&&await a(o);const x={link:"pi\u2192provider",op:"generation turn",kind:E?"context-overflow":le.kind,retry:E?{disposition:"fatal"}:S==="auto"?{disposition:"auto",by:"daemon",...D!==void 0?{nextAt:new Date(D).toISOString()}:{},...h?.attempt!==void 0?{attempt:h.attempt}:{}}:{disposition:S,...L!==void 0?{nextAt:new Date(L).toISOString()}:{}},message:A,...!E&&S==="auto"&&h!==null?{since:h.since}:{},anchor_entry_id:!E&&S==="auto"?this.retryAnchor(o,h?.anchorEntryId):o.sessionManager.getLeafId?.()??void 0,...W===void 0?{}:{provider_error:W}};x.retry.disposition==="auto"?await y(o,x):(await a(o),await g(x))}retryAnchor(e,t){const n=e.sessionManager.getLeafId?.()??void 0;return t!==void 0&&!this.hasProgressPastAnchor(e,t)?t:n}async recoverOnStartup(){const e=this.deps.installedGeneration();if(e===void 0)return"none";const t=(await this.state()).episode,n=this.sessionId(e.session);if(this.deps.cfg.newCycle===!0)return t!==null&&n!==null&&t.session_id===n&&await I(this.deps.nodeId,{op:"invalidate",session_id:n}),"none";if(t===null||n===null||t.session_id!==n)return"none";if(t.state==="admitted")if(t.fault.anchor_entry_id!==void 0&&!this.hasProgressPastAnchor(e.session,t.fault.anchor_entry_id)){if(!(await I(this.deps.nodeId,{op:"reopen",session_id:n})).applied)return"none"}else return await this.clearEpisode(e.session),"none";else if(t.state!=="pending")return"none";await this.recordFault(O(R(t.fault)));const i=await this.schedule();return i==="armed"?"retry-armed":i==="exhausted"?"fatal":"none"}hasProgressPastAnchor(e,t){const n=e.sessionManager.getBranch(),i=n.findIndex(d=>d.id===t);return(i===-1?n:n.slice(i+1)).some(d=>{if(d.type!=="message")return!1;const{message:c}=d;return c.role==="toolResult"?!0:c.role==="assistant"&&Array.isArray(c.content)&&c.content.length>0})}async schedule(){const e=this.deps.installedGeneration(),t=this.deps.currentSession();if(t.isStreaming||this.timer!==null)return this.timer!==null?"armed":"none";if(e===void 0)return this.abandonTurnEnd(),"none";const n=await this.state(),i=n.fault;if(!this.isActiveAutoFault(i)||this.sessionId(t)!==null&&!this.pendingEpisodeMatches(t,i,n))return this.clearTimer(),this.abandonTurnEnd(),"none";const s=Date.now(),d=this.faultRetryDelayMs(i,s);return d!==null&&this.deps.turnEnds?.holding()===!0&&Se(i.since,d,s)?(await this.endRunTurnOnFault(t,i,d),"none"):d!==null?(this.clearAndArmFaultRetry(d,e),this.timer!==null?"armed":"none"):(this.abandonTurnEnd(),i.retry.nextAt!==void 0||this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming?"none":(await this.settleExhausted(t,i),"exhausted"))}async settleExhausted(e,t){await this.recordFault(O(Q(R(t)))),await this.clearEpisode(e)}async endRunTurnOnFault(e,t,n){this.clearTimer(),this.abandonTurnEnd(),F({level:"info",event:"broker.runtime.diagnostic",fields:{message:`run turn completed on provider fault: ${t.kind} retry in ${n}ms is past the ${re}ms turn window (since ${t.since}, attempt ${t.retry.attempt??0})`}}),await this.clearEpisode(e),await this.clearFault("pi\u2192provider")}async rearmIfStranded(){if(this.timer!==null||this.deps.installedGeneration()===void 0)return;const e=this.deps.currentSession();if(e.isStreaming)return;const t=await this.state();!this.isActiveAutoFault(t.fault)||!this.pendingEpisodeMatches(e,t.fault,t)||await this.schedule()}maybeSwitchProviderForRetry(e){if(e.type!=="auto_retry_start")return;const t=T("pi\u2192provider",e.errorMessage??"").kind;if(t!=="rate-limit"&&t!=="connection"||this.providerFallbackInFlight!==null||this.deps.cfg.modelExact===!0)return;const n=this.deps.installedGeneration();if(n===void 0)return;const i=this.deps.currentSession(),s=this.deps.currentServices(),d=this.deps.formatModelSpec(i.model,i.thinkingLevel);if(d===null)return;const c=z(this.deps.cfg.model,B(),!1);if(c===void 0)return;const f=ee(d),o=(async()=>{const a=await J(c,j()??this.deps.cfg.cwd,Y()),g=ae(a,f),y=g===void 0?{route:void 0,capacityExhausted:!1}:await this.nextEligibleFallbackRoute(a.slice(g+1),this.deps.registryOf(s),i.getContextUsage()?.tokens,k=>!this.failedRetryRoutes.has(k.routeId));if(y.route===void 0){y.capacityExhausted&&(this.stageCapacityFailure(n,i),queueMicrotask(()=>{this.deps.installedGeneration()===n&&this.deps.currentSession()===i&&i.abortRetry()}));return}const u=y.route,p=this.deps.registryOf(s).find(u.providerId,u.modelId);if(!p||(await i.setModel(p),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i))return;const v=`${u.providerId}/${u.modelId}`===f.modelSpec?f.thinkingLevel:u.thinkingLevel;i.setThinkingLevel(v??"off"),this.failedRetryRoutes.add(u.routeId),await this.deps.broadcastModelChanged(!1)})().catch(a=>{F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`provider route fallback failed: ${a instanceof Error?a.message:String(a)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.providerFallbackInFlight=null)});this.providerFallbackInFlight=o}maybeFallbackOnUnusableModel(e){if(e.type!=="message_end"||e.message.role!=="assistant"||e.message.stopReason!=="error")return;const t=pe(e.message.errorMessage??"");if(!t&&!fe(e.message.errorMessage??"")||this.deps.cfg.modelExact===!0||this.notFoundFallbackInFlight!==null)return;const n=this.deps.installedGeneration();if(n===void 0)return;const i=this.deps.currentSession(),s=this.deps.currentServices(),d=this.deps.formatModelSpec(i.model,i.thinkingLevel);if(d===null)return;const c=ee(d);this.notFoundModels.add(c.modelSpec);const f=z(this.deps.cfg.model,B(),!1),o=(async()=>{const a=f?await J(f,j()??this.deps.cfg.cwd,Y()):[],g=ae(a,c),y=g===void 0?{route:void 0,capacityExhausted:!1}:await this.nextEligibleFallbackRoute(a.slice(g+1),this.deps.registryOf(s),i.getContextUsage()?.tokens,h=>!this.notFoundModels.has(`${h.providerId}/${h.modelId}`));if(y.route===void 0){y.capacityExhausted&&this.stageCapacityFailure(n,i);return}const u=y.route,p=`${u.providerId}/${u.modelId}${u.thinkingLevel?`:${u.thinkingLevel}`:""}`,v=this.deps.registryOf(s).find(u.providerId,u.modelId);if(v===void 0){F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback skipped: ${p} is not registered`}});return}if(await i.setModel(v),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i)return;i.setThinkingLevel(u.thinkingLevel??"off"),await this.deps.broadcastModelChanged();const k=this.deps.formatModelSpec(i.model,i.thinkingLevel)??p;if(F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback: ${d} unavailable \u2192 ${k}; re-driving turn`}}),await i.agent.waitForIdle(),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i)return;t&&await this.clearFault("pi\u2192provider");const w=K("recovery",{},be(d,k,t?"credential":"not-found"));ne(w,{type:"runtime",kind:"recovery"}),await this.deps.turnAdmission.admit(h=>H.fresh(()=>i.prompt(w,{preflightResult:h})))})().catch(a=>{F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback failed: ${a instanceof Error?a.message:String(a)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.notFoundFallbackInFlight=null)});this.notFoundFallbackInFlight=o}async nextEligibleFallbackRoute(e,t,n,i){let s=0,d=0;for(const c of e){if(!i(c)||Ie([{...c,...await this.routeAvailability(c,t),automaticFallback:!0}]).candidates.length===0)continue;const f=t.find(c.providerId,c.modelId);if(f!==void 0){if(s++,this.targetCannotFitContext(f.contextWindow,n)){d++;continue}return{route:c,capacityExhausted:!1}}}return{route:void 0,capacityExhausted:s>0&&s===d}}async routeAvailability(e,t){const n=t.find(e.providerId,e.modelId);if(n===void 0)return{registered:!1,authenticated:!1};if(!ke(e.providerId))return{registered:!0,authenticated:t.hasConfiguredAuth(n)};const i=await me().getModelAuthReadiness({model:`${e.providerId}/${e.modelId}`}),s=i.rate_limited_until==null?void 0:Date.parse(i.rate_limited_until);return{registered:!0,authenticated:i.credential==="ready",...s!==void 0&&s>Date.now()?{coolingUntil:s}:{}}}targetCannotFitContext(e,t){return Number.isFinite(e)&&Number.isFinite(t)&&e<t}stageCapacityFailure(e,t){const n=t.getContextUsage()?.tokens;this.stagedCapacityFailure={generation:e,errorMessage:Number.isFinite(n)?`All available fallback models have context windows smaller than the current conversation (${n} tokens).`:"All available fallback models have context windows smaller than the current conversation."}}async recordPendingProviderFault(e,t){const n=this.sessionId(e);if(n===null){await this.recordFault(t);return}await I(this.deps.nodeId,{op:"record-pending",session_id:n,input:t})}pendingEpisodeMatches(e,t,n){const i=this.sessionId(e),s=n.episode;return i!==null&&s!==null&&s.state==="pending"&&s.session_id===i&&s.fault.fault_id===t.fault_id}faultRetryDelayMs(e,t){if(e.retry.nextAt!==void 0){const n=Date.parse(e.retry.nextAt)-t;return Number.isFinite(n)?Math.max(0,n):null}return C(R(e),t)?.delayMs??null}clearAndArmFaultRetry(e,t){if(this.deps.installedGeneration()!==t)return;this.clearTimer();const n=Math.min(Math.max(0,e),Re),i=Math.max(0,e-n);this.timer=setTimeout(()=>{if(this.timer=null,this.deps.installedGeneration()===t){if(i>0){this.clearAndArmFaultRetry(i,t);return}this.runFaultRetry().catch(s=>{this.abandonTurnEnd(),F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry fire failed: ${s instanceof Error?s.message:String(s)}`}})})}},n),typeof this.timer.unref=="function"&&this.timer.unref()}async runFaultRetry(){await this.attemptFaultRetry()===void 0&&this.abandonTurnEnd()}async attemptFaultRetry(){const e=this.deps.installedGeneration();if(e===void 0)return;const t=e.session;if(t.isStreaming)return;const n=(await this.state()).fault;if(!this.isActiveAutoFault(n))return;let i=n;if(n.kind==="connection"&&this.deps.turnEnds===void 0){const u=await this.deps.retryProbe();if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming)return;const p=(await this.state()).fault;if(!this.isActiveAutoFault(p)||p.since!==n.since)return;if(!u)return this.clearAndArmFaultRetry(Z(p.kind).baseDelayMs,e),"rearmed";i=p}const s=this.deps.turnEnds!==void 0,d=i.retry.attempt??0,c=Z(i.kind),f=d>=c.max?null:C(R(i),Date.now());if(f===null){await this.settleExhausted(t,i);return}if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t)return;let o,a;const g=await this.deps.turnAdmission.admit(async u=>{const p=(await this.state()).fault;if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||!this.isActiveAutoFault(p)||p.since!==i.since){u();return}const v=this.sessionId(t);if(v===null){u();return}if(!(await I(this.deps.nodeId,{op:"admit",session_id:v,fault_id:p.fault_id,retry:f.fault.retry})).applied){u();return}const w=i.anchor_entry_id;if(w!==void 0&&(t.sessionManager.getLeafId?.()??null)!==w&&!this.hasProgressPastAnchor(t,w))try{const l=await t.navigateTree(w);if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t){u();return}l.cancelled||this.deps.reWelcomeAll()}catch(l){F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`fault retry rewind skipped: ${l instanceof Error?l.message:String(l)}`}})}const h=K("recovery",s?{attempt:f.attempt}:{attempt:f.attempt,max:f.max},xe(i.kind,s));ne(h,{type:"runtime",kind:"recovery"}),this.deps.turnEnds?.beginRecovery();const P=this.agentStarts;try{await H.fresh(()=>t.prompt(h,{preflightResult:u}))}catch(l){o={error:l,faultId:p.fault_id,startsBefore:P},oe(l)||(a={code:"model_error",message:`recovery prompt rejected: ${se(l)}`});return}finally{this.deps.turnEnds?.recoveryReturned(a)}return"prompted"});if(o===void 0)return g;const y=await this.settleRejectedRecoveryPrompt(e,t,o);if(y==="stale")throw o.error;return y==="rearmed"?"rearmed":void 0}async settleRejectedRecoveryPrompt(e,t,n){const i=this.sessionId(t);if(i===null)return"stale";const s=m(()=>{if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||this.agentStarts!==n.startsBefore)throw new _},"assertCurrent"),d=se(n.error),c=oe(n.error);try{let f;return await this.retryWhileDaemonUnreachable("rejected recovery prompt settlement",async()=>{s();const{episode:o}=await this.state();if(s(),o===null||o.state!=="admitted"||o.session_id!==i||o.fault.fault_id!==n.faultId)throw new _;const a=R(o.fault),g=c&&this.deps.turnEnds===void 0?C(a,Date.now()):null,y=g===null?Q(c?a:{...a,kind:"other",message:`recovery prompt rejected: ${d}`}):{...a,retry:{...a.retry,nextAt:g.nextAt}};if(s(),!(await I(this.deps.nodeId,{op:"settle-rejected",session_id:i,fault_id:n.faultId,reopen:g!==null,input:O(y)})).applied)throw new _;f=g?.nextAt},s),f===void 0?(F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`recovery prompt rejected, ending the retry episode: ${d}`}}),"ended"):(F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`recovery prompt rejected before any turn started, retrying at ${f}: ${d}`}}),await this.schedule(),"rearmed")}catch(f){if(!(f instanceof _))throw f;return"stale"}}}export{et as FaultRetry,re as RUN_TURN_RETRY_WINDOW_MS,Ce as isPrefillRejectionDuringEpisode,xe as retryCardBody,Se as runTurnRetryExpired,de as settledProviderFault,Ze as willDaemonRetry};
1
+ var ce=Object.defineProperty;var m=(r,e)=>ce(r,"name",{value:e,configurable:!0});import{envModelIntent as B,envNodeCwd as j,envProfileId as Y}from"../../../shared/env.js";import{isContextOverflow as q}from"@earendil-works/pi-ai";import{classify as T}from"../../fault-classifier.js";import{isDaemonTransportApiError as V}from"../../../api/errors.js";import{isModelNotFoundError as fe,isProviderUnconfiguredError as pe}from"../fault.js";import{brokerRecovery as he,daemonClient as me,mutateBrokerFault as X,mutateBrokerProviderRetry as I}from"./daemon-ops.js";import{extractCoolingDeadline as ge}from"../managed-provider-cooling.js";import{extractProviderFailureDetails as ye}from"../../provider-failure.js";import{expandModelCandidates as J,modelRequestFromConfig as z}from"../../model-routes.js";import{operationIdContext as H}from"../../events/operation-id.js";import{emitEvent as F}from"../../events/emit.js";import{CONNECTION_FAULT_RECOVERY_BODY as ve,PROVIDER_FAULT_RECOVERY_BODY as we,RUN_CONNECTION_FAULT_RETRY_BODY as Fe,formatCard as K,formatModelFallbackRecovery as be}from"../../../shared/generated-context.js";import{markFatalOnExhaust as Q,nextFaultRetry as C,policyFor as Z}from"../fault-recovery.js";import{parseModelSpec as ee}from"../model-selection.js";import{endedByAbort as te}from"../session-cycles.js";import{isManagedProvider as ke,resolveProviderCandidates as Ie}from"../../subscription-state.js";import{announceMessageOrigin as ne}from"../stamp/channel.js";import{LLM_REFUSAL_DIAGNOSTIC as _e,refusalRetryAt as Ee}from"./model-adapter.js";import{isSafetyRefusal as ie}from"../../llm/refusal.js";const Re=2147483647,re=3e4;function Se(r,e,t){const n=Date.parse(r);return!Number.isFinite(n)||t+e-n>re}m(Se,"runTurnRetryExpired");class _ extends Error{static{m(this,"StaleOutcome")}}const Ae=1e3,Me=3e4;function xe(r,e){return r!=="connection"?we:e?Fe:ve}m(xe,"retryCardBody");function R(r){return{link:r.link,op:r.op,kind:r.kind,retry:r.retry,message:r.message,since:r.since,operation_id:r.operation_id,...r.anchor_entry_id===void 0?{}:{anchorEntryId:r.anchor_entry_id},...r.provider_error===void 0?{}:{providerError:r.provider_error}}}m(R,"policyFault");function se(r){return r instanceof Error?r.message:String(r)}m(se,"errorText");function oe(r){return V(r)||T("pi\u2192provider",r).disposition==="auto"}m(oe,"isTransientRejection");function O(r){return{link:r.link,op:r.op,kind:r.kind,retry:r.retry,message:r.message,...r.operation_id===void 0?{}:{operation_id:r.operation_id},...r.since===void 0?{}:{since:r.since},...r.anchorEntryId===void 0?{}:{anchor_entry_id:r.anchorEntryId},...r.providerError===void 0?{}:{provider_error:r.providerError}}}m(O,"faultInput");function ae(r,e){const t=m(n=>`${n.providerId}/${n.modelId}`===e.modelSpec,"onLiveModel");return(r.find(n=>t(n)&&n.thinkingLevel===e.thinkingLevel)??r.find(t))?.rank}m(ae,"liveRouteRank");function de(r){const e=T("pi\u2192provider",r),t=r.diagnostics?.some(n=>n.type===_e)??!1;return{classified:e,disposition:t?"fatal":e.disposition}}m(de,"settledProviderFault");function Ze(r,e){if(r.stopReason!=="error"||te(r)||ie(r)||q(r,e))return!1;const{classified:t,disposition:n}=de(r);return n==="auto"&&(t.kind==="connection"||t.kind==="rate-limit"||t.kind==="overloaded")}m(Ze,"willDaemonRetry");const Te=/does not support assistant message prefill|conversation must end with a user message/i;function Ce(r,e){return e&&Te.test(r)}m(Ce,"isPrefillRejectionDuringEpisode");class et{static{m(this,"FaultRetry")}deps;timer=null;failedRetryRoutes=new Set;notFoundModels=new Set;providerFallbackInFlight=null;notFoundFallbackInFlight=null;stagedCapacityFailure=null;agentStarts=0;constructor(e){this.deps=e}clearTimer(){this.timer!==null&&(clearTimeout(this.timer),this.timer=null)}reset(){this.clearTimer(),this.deps.turnEnds?.abandon(),this.providerFallbackInFlight=null,this.notFoundFallbackInFlight=null,this.stagedCapacityFailure=null}fallbackInFlight(){return this.providerFallbackInFlight!==null||this.notFoundFallbackInFlight!==null}async faultIsFatal(){const e=(await this.state()).fault;return e!==null&&e.link==="pi\u2192provider"&&e.retry.disposition==="fatal"}abandonTurnEnd(){this.deps.turnEnds?.abandon()}state(){return he(this.deps.nodeId)}async recordFault(e){return(await X(this.deps.nodeId,{op:"record",input:e})).fault}async clearFault(e,t=!1){await X(this.deps.nodeId,{op:"clear",...e===void 0?{}:{link:e},...t?{preserve_episode:!0}:{}})}sessionId(e){const t=e.sessionManager.getSessionId?.();return typeof t=="string"&&t!==""?t:null}async clearEpisode(e){const t=this.sessionId(e);t!==null&&await I(this.deps.nodeId,{op:"clear",session_id:t})}onEvent(e){e.type==="agent_start"&&this.agentStarts++,this.maybeSwitchProviderForRetry(e),this.maybeFallbackOnUnusableModel(e)}isActiveAutoFault(e){return e===null||e.retry.disposition!=="auto"||e.retry.by!=="daemon"?!1:e.kind==="connection"||e.kind==="rate-limit"||e.kind==="overloaded"}async publishSettledProviderFault(e){const t={agentEnd:e.stagedProviderAgentEnd,watchdogAbort:e.stagedWatchdogAbort,refreshAbort:e.stagedRefreshAbort,overflowFailure:e.stagedOverflowFailure,capacityFailure:this.stagedCapacityFailure?.generation===e?this.stagedCapacityFailure:null};this.stagedCapacityFailure=null,e.stagedProviderAgentEnd=null,e.stagedWatchdogAbort=!1,e.stagedRefreshAbort=!1,e.stagedOverflowFailure=null;const n=this.agentStarts,i=m(()=>{if(this.deps.installedGeneration()!==e||this.agentStarts!==n)throw new _},"assertCurrent");try{await this.retryWhileDaemonUnreachable("settled turn fault write",()=>this.recordStagedOutcome(e,t,i),i)}catch(s){if(!(s instanceof _))throw s}}async retryWhileDaemonUnreachable(e,t,n){let i=Ae;for(;;){try{await t();return}catch(s){if(!V(s))throw s;F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`${e} waiting for the daemon: ${s.message}`}})}await new Promise(s=>setTimeout(s,i)),i=Math.min(i*2,Me),n()}}async recordStagedOutcome(e,t,n){const{agentEnd:i,watchdogAbort:s,refreshAbort:d,overflowFailure:c,capacityFailure:f}=t,o=e.session,a=m(async b=>{n(),await this.clearEpisode(b)},"clearEpisode"),g=m(async b=>{n(),await this.recordFault(b)},"recordFault"),y=m(async(b,ue)=>{n(),await this.recordPendingProviderFault(b,ue)},"recordPendingProviderFault"),u=await this.state(),p=u.fault,v=u.episode,k=this.sessionId(o),w=this.isActiveAutoFault(p)&&p.link==="pi\u2192provider"?p:v?.state==="admitted"&&v.session_id===k&&this.isActiveAutoFault(v.fault)?v.fault:null,h=w===null?null:{since:w.since,anchorEntryId:w.anchor_entry_id,attempt:w.retry.attempt},l=[...Array.isArray(i?.messages)?i.messages:[]].reverse().find(b=>typeof b=="object"&&b!==null&&b.role==="assistant");if(f!==null||c!==null){await a(o),await g({link:"pi\u2192provider",op:"context overflow recovery",kind:"context-overflow",retry:{disposition:"fatal"},message:f?.errorMessage??c.errorMessage,anchor_entry_id:o.sessionManager.getLeafId?.()??void 0});return}if(s&&l?.stopReason==="aborted"){await y(o,{link:"pi\u2192provider",op:"generation turn",kind:"connection",retry:{disposition:"auto",by:"daemon",...h?.attempt!==void 0?{attempt:h.attempt}:{}},message:typeof l.errorMessage=="string"?l.errorMessage:"engine error (no errorMessage recorded)",...h!==null?{since:h.since}:{},anchor_entry_id:this.retryAnchor(o,h?.anchorEntryId)});return}if(l?.stopReason!=="error"){l!==void 0&&await a(o);return}if(d){await a(o);return}if(ie(l)){await a(o);return}const A=typeof l.errorMessage=="string"?l.errorMessage:"engine error (no errorMessage recorded)";if(te({stopReason:"error",errorMessage:A})){await a(o);return}const E=q(l,o.model?.contextWindow),M=de(l),$=h!==null&&M.disposition!=="auto"&&Ce(A,!0),le=$?{kind:"connection",disposition:"auto"}:M.classified,S=$?"auto":M.disposition,D=ge(l),L=Ee(l),N=ye(l),G=typeof l.provider=="string"&&l.provider!==""?l.provider:o.model?.provider,U=typeof l.model=="string"&&l.model!==""?l.model:o.model?.id,W=N===void 0||G===void 0||U===void 0?void 0:{provider:G,model:U,...N};E&&await a(o);const x={link:"pi\u2192provider",op:"generation turn",kind:E?"context-overflow":le.kind,retry:E?{disposition:"fatal"}:S==="auto"?{disposition:"auto",by:"daemon",...D!==void 0?{nextAt:new Date(D).toISOString()}:{},...h?.attempt!==void 0?{attempt:h.attempt}:{}}:{disposition:S,...L!==void 0?{nextAt:new Date(L).toISOString()}:{}},message:A,...!E&&S==="auto"&&h!==null?{since:h.since}:{},anchor_entry_id:!E&&S==="auto"?this.retryAnchor(o,h?.anchorEntryId):o.sessionManager.getLeafId?.()??void 0,...W===void 0?{}:{provider_error:W}};x.retry.disposition==="auto"?await y(o,x):(await a(o),await g(x))}retryAnchor(e,t){const n=e.sessionManager.getLeafId?.()??void 0;return t!==void 0&&!this.hasProgressPastAnchor(e,t)?t:n}async recoverOnStartup(){const e=this.deps.installedGeneration();if(e===void 0)return"none";const t=(await this.state()).episode,n=this.sessionId(e.session);if(this.deps.cfg.newCycle===!0)return t!==null&&n!==null&&t.session_id===n&&await I(this.deps.nodeId,{op:"invalidate",session_id:n}),"none";if(t===null||n===null||t.session_id!==n)return"none";if(t.state==="admitted")if(t.fault.anchor_entry_id!==void 0&&!this.hasProgressPastAnchor(e.session,t.fault.anchor_entry_id)){if(!(await I(this.deps.nodeId,{op:"reopen",session_id:n})).applied)return"none"}else return await this.clearEpisode(e.session),"none";else if(t.state!=="pending")return"none";await this.recordFault(O(R(t.fault)));const i=await this.schedule();return i==="armed"?"retry-armed":i==="exhausted"?"fatal":"none"}hasProgressPastAnchor(e,t){const n=e.sessionManager.getBranch(),i=n.findIndex(d=>d.id===t);return(i===-1?n:n.slice(i+1)).some(d=>{if(d.type!=="message")return!1;const{message:c}=d;return c.role==="toolResult"?!0:c.role==="assistant"&&Array.isArray(c.content)&&c.content.length>0})}async schedule(){const e=this.deps.installedGeneration(),t=this.deps.currentSession();if(t.isStreaming||this.timer!==null)return this.timer!==null?"armed":"none";if(e===void 0)return this.abandonTurnEnd(),"none";const n=await this.state(),i=n.fault;if(!this.isActiveAutoFault(i)||this.sessionId(t)!==null&&!this.pendingEpisodeMatches(t,i,n))return this.clearTimer(),this.abandonTurnEnd(),"none";const s=Date.now(),d=this.faultRetryDelayMs(i,s);return d!==null&&this.deps.turnEnds?.holding()===!0&&Se(i.since,d,s)?(await this.endRunTurnOnFault(t,i,d),"none"):d!==null?(this.clearAndArmFaultRetry(d,e),this.timer!==null?"armed":"none"):(this.abandonTurnEnd(),i.retry.nextAt!==void 0||this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming?"none":(await this.settleExhausted(t,i),"exhausted"))}async settleExhausted(e,t){await this.recordFault(O(Q(R(t)))),await this.clearEpisode(e)}async endRunTurnOnFault(e,t,n){this.clearTimer(),this.abandonTurnEnd(),F({level:"info",event:"broker.runtime.diagnostic",fields:{message:`run turn completed on provider fault: ${t.kind} retry in ${n}ms is past the ${re}ms turn window (since ${t.since}, attempt ${t.retry.attempt??0})`}}),await this.clearEpisode(e),await this.clearFault("pi\u2192provider")}async rearmIfStranded(){if(this.timer!==null||this.deps.installedGeneration()===void 0)return;const e=this.deps.currentSession();if(e.isStreaming)return;const t=await this.state();!this.isActiveAutoFault(t.fault)||!this.pendingEpisodeMatches(e,t.fault,t)||await this.schedule()}maybeSwitchProviderForRetry(e){if(e.type!=="auto_retry_start")return;const t=T("pi\u2192provider",e.errorMessage??"").kind;if(t!=="rate-limit"&&t!=="connection"||this.providerFallbackInFlight!==null||this.deps.cfg.modelExact===!0)return;const n=this.deps.installedGeneration();if(n===void 0)return;const i=this.deps.currentSession(),s=this.deps.currentServices(),d=this.deps.formatModelSpec(i.model,i.thinkingLevel);if(d===null)return;const c=z(this.deps.cfg.model,B(),!1);if(c===void 0)return;const f=ee(d),o=(async()=>{const a=await J(c,j()??this.deps.cfg.cwd,Y()),g=ae(a,f),y=g===void 0?{route:void 0,capacityExhausted:!1}:await this.nextEligibleFallbackRoute(a.slice(g+1),this.deps.registryOf(s),i.getContextUsage()?.tokens,k=>!this.failedRetryRoutes.has(k.routeId));if(y.route===void 0){y.capacityExhausted&&(this.stageCapacityFailure(n,i),queueMicrotask(()=>{this.deps.installedGeneration()===n&&this.deps.currentSession()===i&&i.abortRetry()}));return}const u=y.route,p=this.deps.registryOf(s).find(u.providerId,u.modelId);if(!p||(await i.setModel(p),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i))return;const v=`${u.providerId}/${u.modelId}`===f.modelSpec?f.thinkingLevel:u.thinkingLevel;i.setThinkingLevel(v??"off"),this.failedRetryRoutes.add(u.routeId),await this.deps.broadcastModelChanged(!1)})().catch(a=>{F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`provider route fallback failed: ${a instanceof Error?a.message:String(a)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.providerFallbackInFlight=null)});this.providerFallbackInFlight=o}maybeFallbackOnUnusableModel(e){if(e.type!=="message_end"||e.message.role!=="assistant"||e.message.stopReason!=="error")return;const t=pe(e.message.errorMessage??"");if(!t&&!fe(e.message.errorMessage??"")||this.deps.cfg.modelExact===!0||this.notFoundFallbackInFlight!==null)return;const n=this.deps.installedGeneration();if(n===void 0)return;const i=this.deps.currentSession(),s=this.deps.currentServices(),d=this.deps.formatModelSpec(i.model,i.thinkingLevel);if(d===null)return;const c=ee(d);this.notFoundModels.add(c.modelSpec);const f=z(this.deps.cfg.model,B(),!1),o=(async()=>{const a=f?await J(f,j()??this.deps.cfg.cwd,Y()):[],g=ae(a,c),y=g===void 0?{route:void 0,capacityExhausted:!1}:await this.nextEligibleFallbackRoute(a.slice(g+1),this.deps.registryOf(s),i.getContextUsage()?.tokens,h=>!this.notFoundModels.has(`${h.providerId}/${h.modelId}`));if(y.route===void 0){y.capacityExhausted&&this.stageCapacityFailure(n,i);return}const u=y.route,p=`${u.providerId}/${u.modelId}${u.thinkingLevel?`:${u.thinkingLevel}`:""}`,v=this.deps.registryOf(s).find(u.providerId,u.modelId);if(v===void 0){F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback skipped: ${p} is not registered`}});return}if(await i.setModel(v),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i)return;i.setThinkingLevel(u.thinkingLevel??"off"),await this.deps.broadcastModelChanged();const k=this.deps.formatModelSpec(i.model,i.thinkingLevel)??p;if(F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback: ${d} unavailable \u2192 ${k}; re-driving turn`}}),await i.agent.waitForIdle(),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i)return;t&&await this.clearFault("pi\u2192provider");const w=K("recovery",{},be(d,k,t?"credential":"not-found"));ne(w,{type:"runtime",kind:"recovery"}),await this.deps.turnAdmission.admit(h=>H.fresh(()=>i.prompt(w,{preflightResult:h})))})().catch(a=>{F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback failed: ${a instanceof Error?a.message:String(a)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.notFoundFallbackInFlight=null)});this.notFoundFallbackInFlight=o}async nextEligibleFallbackRoute(e,t,n,i){let s=0,d=0;for(const c of e){if(!i(c)||Ie([{...c,...await this.routeAvailability(c,t),automaticFallback:!0}]).candidates.length===0)continue;const f=t.find(c.providerId,c.modelId);if(f!==void 0){if(s++,this.targetCannotFitContext(f.contextWindow,n)){d++;continue}return{route:c,capacityExhausted:!1}}}return{route:void 0,capacityExhausted:s>0&&s===d}}async routeAvailability(e,t){const n=t.find(e.providerId,e.modelId);if(n===void 0)return{registered:!1,authenticated:!1};if(!ke(e.providerId))return{registered:!0,authenticated:t.hasConfiguredAuth(n)};const i=await me().getModelAuthReadiness({model:`${e.providerId}/${e.modelId}`}),s=i.rate_limited_until==null?void 0:Date.parse(i.rate_limited_until);return{registered:!0,authenticated:i.credential==="ready",...s!==void 0&&s>Date.now()?{coolingUntil:s}:{}}}targetCannotFitContext(e,t){return Number.isFinite(e)&&Number.isFinite(t)&&e<t}stageCapacityFailure(e,t){const n=t.getContextUsage()?.tokens;this.stagedCapacityFailure={generation:e,errorMessage:Number.isFinite(n)?`All available fallback models have context windows smaller than the current conversation (${n} tokens).`:"All available fallback models have context windows smaller than the current conversation."}}async recordPendingProviderFault(e,t){const n=this.sessionId(e);if(n===null){await this.recordFault(t);return}await I(this.deps.nodeId,{op:"record-pending",session_id:n,input:t})}pendingEpisodeMatches(e,t,n){const i=this.sessionId(e),s=n.episode;return i!==null&&s!==null&&s.state==="pending"&&s.session_id===i&&s.fault.fault_id===t.fault_id}faultRetryDelayMs(e,t){if(e.retry.nextAt!==void 0){const n=Date.parse(e.retry.nextAt)-t;return Number.isFinite(n)?Math.max(0,n):null}return C(R(e),t)?.delayMs??null}clearAndArmFaultRetry(e,t){if(this.deps.installedGeneration()!==t)return;this.clearTimer();const n=Math.min(Math.max(0,e),Re),i=Math.max(0,e-n);this.timer=setTimeout(()=>{if(this.timer=null,this.deps.installedGeneration()===t){if(i>0){this.clearAndArmFaultRetry(i,t);return}this.runFaultRetry().catch(s=>{this.abandonTurnEnd(),F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry fire failed: ${s instanceof Error?s.message:String(s)}`}})})}},n),typeof this.timer.unref=="function"&&this.timer.unref()}async runFaultRetry(){await this.attemptFaultRetry()===void 0&&this.abandonTurnEnd()}async attemptFaultRetry(){const e=this.deps.installedGeneration();if(e===void 0)return;const t=e.session;if(t.isStreaming)return;const n=(await this.state()).fault;if(!this.isActiveAutoFault(n))return;let i=n;if(n.kind==="connection"&&this.deps.turnEnds===void 0){const u=await this.deps.retryProbe();if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming)return;const p=(await this.state()).fault;if(!this.isActiveAutoFault(p)||p.since!==n.since)return;if(!u)return this.clearAndArmFaultRetry(Z(p.kind).baseDelayMs,e),"rearmed";i=p}const s=this.deps.turnEnds!==void 0,d=i.retry.attempt??0,c=Z(i.kind),f=d>=c.max?null:C(R(i),Date.now());if(f===null){await this.settleExhausted(t,i);return}if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t)return;let o,a;const g=await this.deps.turnAdmission.admit(async u=>{const p=(await this.state()).fault;if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||!this.isActiveAutoFault(p)||p.since!==i.since){u();return}const v=this.sessionId(t);if(v===null){u();return}if(!(await I(this.deps.nodeId,{op:"admit",session_id:v,fault_id:p.fault_id,retry:f.fault.retry})).applied){u();return}const w=i.anchor_entry_id;if(w!==void 0&&(t.sessionManager.getLeafId?.()??null)!==w&&!this.hasProgressPastAnchor(t,w))try{const l=await t.navigateTree(w);if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t){u();return}l.cancelled||this.deps.reWelcomeAll()}catch(l){F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`fault retry rewind skipped: ${l instanceof Error?l.message:String(l)}`}})}const h=K("recovery",s?{attempt:f.attempt}:{attempt:f.attempt,max:f.max},xe(i.kind,s));ne(h,{type:"runtime",kind:"recovery"}),this.deps.turnEnds?.beginRecovery();const P=this.agentStarts;try{await H.fresh(()=>t.prompt(h,{preflightResult:u}))}catch(l){o={error:l,faultId:p.fault_id,startsBefore:P},oe(l)||(a={code:"model_error",message:`recovery prompt rejected: ${se(l)}`});return}finally{this.deps.turnEnds?.recoveryReturned(a)}return"prompted"});if(o===void 0)return g;const y=await this.settleRejectedRecoveryPrompt(e,t,o);if(y==="stale")throw o.error;return y==="rearmed"?"rearmed":void 0}async settleRejectedRecoveryPrompt(e,t,n){const i=this.sessionId(t);if(i===null)return"stale";const s=m(()=>{if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||this.agentStarts!==n.startsBefore)throw new _},"assertCurrent"),d=se(n.error),c=oe(n.error);try{let f;return await this.retryWhileDaemonUnreachable("rejected recovery prompt settlement",async()=>{s();const{episode:o}=await this.state();if(s(),o===null||o.state!=="admitted"||o.session_id!==i||o.fault.fault_id!==n.faultId)throw new _;const a=R(o.fault),g=c&&this.deps.turnEnds===void 0?C(a,Date.now()):null,y=g===null?Q(c?a:{...a,kind:"other",message:`recovery prompt rejected: ${d}`}):{...a,retry:{...a.retry,nextAt:g.nextAt}};if(s(),!(await I(this.deps.nodeId,{op:"settle-rejected",session_id:i,fault_id:n.faultId,reopen:g!==null,input:O(y)})).applied)throw new _;f=g?.nextAt},s),f===void 0?(F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`recovery prompt rejected, ending the retry episode: ${d}`}}),"ended"):(F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`recovery prompt rejected before any turn started, retrying at ${f}: ${d}`}}),await this.schedule(),"rearmed")}catch(f){if(!(f instanceof _))throw f;return"stale"}}}export{et as FaultRetry,re as RUN_TURN_RETRY_WINDOW_MS,Ce as isPrefillRejectionDuringEpisode,xe as retryCardBody,Se as runTurnRetryExpired,de as settledProviderFault,Ze as willDaemonRetry};
@@ -82,6 +82,7 @@ export declare class RebindDriver {
82
82
  private captureSystemPrompt;
83
83
  private executeSubscriptionCallback;
84
84
  private publishAndScheduleProviderRecovery;
85
+ private idleAfterFatalFault;
85
86
  private ackTo;
86
87
  }
87
88
  export {};
@@ -1 +1 @@
1
- var k=Object.defineProperty;var l=(a,e)=>k(a,"name",{value:e,configurable:!0});import{mkdirSync as v,writeFileSync as _}from"node:fs";import{join as I}from"node:path";import{nodeRunJobDir as A}from"../../canvas/paths.js";import{emitEvent as u}from"../../events/emit.js";import{operationIdContext as c}from"../../events/operation-id.js";import{installPersonaTransitionGate as E}from"../broker-persona-guidance.js";import{installRetryCardElision as T}from"./retry-card-elision.js";import{abortSessionFromExtension as w}from"./extension-abort.js";import{brokerRecovery as S,mutateBrokerFault as x,mutateBrokerProviderRetry as P}from"./daemon-ops.js";const f=2,m=3e4,b=Symbol("replacement-deadline-exceeded"),V="(interrupted)",g=Symbol.for("crtr.trailing-thinking-guard");function C(a){for(let e=a.length-1;e>=0;e--){const t=a[e];if(t!==void 0){if(t.type==="text"){if(t.text.trim().length===0)continue;return t}if(t.type==="thinking"){const d=t.thinkingSignature,r=typeof d=="string"&&d.trim().length>0;if(t.redacted!==!0&&t.thinking.trim().length===0&&!r)continue;return t}return t}}}l(C,"lastSerializedBlock");function y(a){return a.map(e=>e.role!=="assistant"||e.stopReason==="error"||e.stopReason==="aborted"||C(e.content)?.type!=="thinking"?e:{...e,content:[...e.content,{type:"text",text:V}]})}l(y,"guardTrailingThinking");function N(a){const e=a.convertToLlm;if(e[g]===!0)return;const t=l(d=>{const r=e(d);return r instanceof Promise?r.then(y):y(r)},"wrapped");t[g]=!0,a.convertToLlm=t}l(N,"installTrailingThinkingGuard");class ${static{l(this,"RebindDriver")}deps;sessionValue;servicesValue;unsubscribe;installed;nextSubscriptionGeneration=0;rebindTail=Promise.resolve();latestRebindRequest=0;replacementTail=Promise.resolve();admittedReplacements=0;rebindsInFlight=0;rebinding;agentRunActiveValue=!1;shutdownRequested=!1;constructor(e){this.deps=e,this.sessionValue=e.session,this.servicesValue=e.services}session(){return this.sessionValue}services(){return this.servicesValue}installedGeneration(){return this.installed}isAgentRunActive(){return this.agentRunActiveValue}isReplacing(){return this.admittedReplacements>0}pendingRebind(){return this.rebinding?.done}requestShutdown(){if(!this.agentRunActiveValue){this.deps.disposeAndExit("shutdown-hook");return}this.shutdownRequested=!0}setRebindSession(){this.deps.runtime?.setRebindSession(e=>this.enqueueRebind(e,this.deps.runtime.services))}enqueueRebind(e,t){const d=++this.latestRebindRequest;if(this.rebindsInFlight++===0){let n;this.rebinding={done:new Promise(o=>{n=o}),resolve:n}}const r=this.rebindTail.then(async()=>{if(d!==this.latestRebindRequest)return;if(this.installed!==void 0){typeof this.sessionValue.sessionFile=="string"&&this.sessionValue.sessionFile!==""&&await P(this.deps.nodeId,{op:"invalidate",session_id:this.sessionValue.sessionManager.getSessionId()});const i=(await S(this.deps.nodeId)).fault;i?.link==="pi\u2192provider"&&i.retry.disposition==="auto"&&i.retry.by==="daemon"&&await x(this.deps.nodeId,{op:"clear",link:"pi\u2192provider",preserve_episode:!0})}this.installed?.executingTools.clear(),this.removeInstalledSubscription(),this.deps.setActiveSession(e),this.deps.projection().reset(),this.deps.faultRetry().reset(),this.agentRunActiveValue=!1,this.deps.onSessionRebind(),this.deps.toolGroups.resetForSession(),this.sessionValue=e,this.servicesValue=t;const n=e.agent;n&&(E(this.deps.nodeId,{agent:n,sessionManager:e.sessionManager}),T(n),n.steeringMode="all",n.followUpMode="all",N(n));const o={id:++this.nextSubscriptionGeneration,session:e,fallback:{blocked:!1},stagedProviderAgentEnd:null,stagedWatchdogAbort:!1,stagedRefreshAbort:!1,stagedOverflowFailure:null,executingTools:new Set};if(this.unsubscribe=this.sessionValue.subscribe(i=>{if(!this.deps.projection().isCurrent(o))return;if(c.current()!==void 0){this.executeSubscriptionCallback(o,i);return}const s=o.fallback;if(i.type==="turn_start"&&s.storedId!==void 0){s.storedId=void 0,s.blocked=!0,c.fresh(()=>{u({level:"warn",event:"broker.turn.fallback_overlap",fields:{generation:o.id,engine_event:i.type}}),this.executeSubscriptionCallback(o,i)});return}if(i.type==="turn_start"&&!s.blocked){c.fresh(R=>{s.storedId=R,this.executeSubscriptionCallback(o,i)});return}const h=s.storedId;if(h!==void 0&&!s.blocked){c.run(h,()=>{this.executeSubscriptionCallback(o,i),i.type==="agent_settled"&&s.storedId===h&&(s.storedId=void 0)});return}this.executeSubscriptionCallback(o,i)}),this.installed=o,await e.bindExtensions({uiContext:this.deps.uiContext(),mode:"rpc",abortHandler:l(()=>w(e,()=>{const i=this.installed;i?.session===e&&(i.stagedRefreshAbort=!0)}),"abortHandler"),shutdownHandler:l(()=>this.requestShutdown(),"shutdownHandler")}),d===this.latestRebindRequest){if(this.deps.cfg.editorName!==void 0&&this.deps.cfg.editorName!=="")try{e.setSessionName(this.deps.cfg.editorName)}catch{}try{this.deps.turnReplies.seed(this.deps.transcriptMessages(e))}catch(i){u({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`turn reply seed failed: ${String(i)}`}})}this.deps.projection().rearmIfStreaming(o),this.captureSystemPrompt(e)}}).finally(()=>{--this.rebindsInFlight>0||(this.rebinding?.resolve(),this.rebinding=void 0)});return this.rebindTail=r.catch(()=>{}),r}runReplacement(e,t,d){const r=this.deps.runtime;if(r===void 0){this.deps.registry.sendFrame(e,{type:"error",code:"engine_error",message:"session replacement unsupported at this engine pin"});return}if(this.admittedReplacements>=f){c.fresh(()=>u({level:"warn",event:"broker.session_replacement.rejected",outcome:"skipped",fields:{op:t,reason:"capacity",admitted:f,cap:f}})),this.ackTo(e,t,!1,`session replacement busy (${f} active or queued)`);return}this.admittedReplacements++;const n=this.replacementTail.then(async()=>{try{await c.fresh(async()=>{const o=process.hrtime.bigint();let i;try{const p=new Promise(h=>{i=setTimeout(()=>h(b),m)}),s=await Promise.race([d(r),p]);if(s===b){u({level:"error",event:"broker.session_replacement.timed_out",outcome:"failed",duration_ms:Number(process.hrtime.bigint()-o)/1e6,fields:{op:t,deadline_ms:m,mutation_outcome:"indeterminate"}}),this.ackTo(e,t,!1,"session replacement timed out; broker restarting because session state is indeterminate"),this.deps.disposeAndExit("session-replacement-timeout",1);return}s.cancelled||this.deps.reWelcomeAll(),this.ackTo(e,t,!s.cancelled,s.cancelled?s.detail??"cancelled by extension":void 0)}catch(p){u({level:"error",event:"broker.session_replacement.failed",outcome:"failed",error:p,fields:{op:t,mutation_outcome:"indeterminate"}}),this.ackTo(e,t,!1,"session replacement failed; broker restarting because session state is indeterminate"),this.deps.disposeAndExit("session-replacement-failed",1)}finally{clearTimeout(i)}})}finally{this.admittedReplacements--,this.admittedReplacements===0&&this.deps.onReplacementIdle?.()}});this.replacementTail=n.catch(()=>{})}removeInstalledSubscription(){this.unsubscribe?.(),this.unsubscribe=void 0,this.installed=void 0}captureSystemPrompt(e){try{const t=A(this.deps.nodeId);v(t,{recursive:!0}),_(I(t,"system-prompt.md"),e.systemPrompt,"utf8")}catch{}}executeSubscriptionCallback(e,t){try{t.type==="agent_start"&&(this.agentRunActiveValue=!0),t.type==="agent_end"&&(e.stagedProviderAgentEnd=t,e.stagedOverflowFailure=null),t.type==="compaction_end"&&t.reason==="overflow"&&t.willRetry===!1&&typeof t.errorMessage=="string"&&(e.stagedOverflowFailure={errorMessage:t.errorMessage}),t.type==="agent_settled"&&(this.agentRunActiveValue=!1,this.publishAndScheduleProviderRecovery(e)),this.deps.toolGroups.observeToolGroupEvent(t),this.deps.turnReplies.observe(t),this.deps.projection().relayEvent(t,e)}catch(d){u({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`event relay threw: ${String(d)}`}})}}async publishAndScheduleProviderRecovery(e){try{if(await this.deps.faultRetry().publishSettledProviderFault(e),this.shutdownRequested){this.deps.disposeAndExit("shutdown-after-settle");return}await this.deps.faultRetry().schedule()}catch(t){this.deps.faultRetry().abandonTurnEnd(),u({level:"error",event:"broker.daemon.operation.failed",node_id:this.deps.nodeId,error:t,fields:{operation:"provider_fault_settlement"}}),this.shutdownRequested&&this.deps.disposeAndExit("shutdown-after-settle")}}ackTo(e,t,d=!0,r,n){this.deps.registry.sendFrame(e,{type:"ack",for:t,ok:d,...r!==void 0?{detail:r}:{},...n!==void 0?{id:n}:{}})}}export{$ as RebindDriver};
1
+ var k=Object.defineProperty;var l=(d,e)=>k(d,"name",{value:e,configurable:!0});import{mkdirSync as v,writeFileSync as I}from"node:fs";import{join as A}from"node:path";import{nodeRunJobDir as _}from"../../canvas/paths.js";import{emitEvent as u}from"../../events/emit.js";import{operationIdContext as c}from"../../events/operation-id.js";import{installPersonaTransitionGate as w}from"../broker-persona-guidance.js";import{installRetryCardElision as E}from"./retry-card-elision.js";import{abortSessionFromExtension as T}from"./extension-abort.js";import{brokerRecovery as S,mutateBrokerFault as x,mutateBrokerProviderRetry as F,postBrokerIdle as P,settleBroker as V}from"./daemon-ops.js";import{brokerSignalClient as C}from"./signal-stream.js";const f=2,m=3e4,b=Symbol("replacement-deadline-exceeded"),N="(interrupted)",g=Symbol.for("crtr.trailing-thinking-guard");function M(d){for(let e=d.length-1;e>=0;e--){const t=d[e];if(t!==void 0){if(t.type==="text"){if(t.text.trim().length===0)continue;return t}if(t.type==="thinking"){const i=t.thinkingSignature,n=typeof i=="string"&&i.trim().length>0;if(t.redacted!==!0&&t.thinking.trim().length===0&&!n)continue;return t}return t}}}l(M,"lastSerializedBlock");function y(d){return d.map(e=>e.role!=="assistant"||e.stopReason==="error"||e.stopReason==="aborted"||M(e.content)?.type!=="thinking"?e:{...e,content:[...e.content,{type:"text",text:N}]})}l(y,"guardTrailingThinking");function q(d){const e=d.convertToLlm;if(e[g]===!0)return;const t=l(i=>{const n=e(i);return n instanceof Promise?n.then(y):y(n)},"wrapped");t[g]=!0,d.convertToLlm=t}l(q,"installTrailingThinkingGuard");class X{static{l(this,"RebindDriver")}deps;sessionValue;servicesValue;unsubscribe;installed;nextSubscriptionGeneration=0;rebindTail=Promise.resolve();latestRebindRequest=0;replacementTail=Promise.resolve();admittedReplacements=0;rebindsInFlight=0;rebinding;agentRunActiveValue=!1;shutdownRequested=!1;constructor(e){this.deps=e,this.sessionValue=e.session,this.servicesValue=e.services}session(){return this.sessionValue}services(){return this.servicesValue}installedGeneration(){return this.installed}isAgentRunActive(){return this.agentRunActiveValue}isReplacing(){return this.admittedReplacements>0}pendingRebind(){return this.rebinding?.done}requestShutdown(){if(!this.agentRunActiveValue){this.deps.disposeAndExit("shutdown-hook");return}this.shutdownRequested=!0}setRebindSession(){this.deps.runtime?.setRebindSession(e=>this.enqueueRebind(e,this.deps.runtime.services))}enqueueRebind(e,t){const i=++this.latestRebindRequest;if(this.rebindsInFlight++===0){let o;this.rebinding={done:new Promise(a=>{o=a}),resolve:o}}const n=this.rebindTail.then(async()=>{if(i!==this.latestRebindRequest)return;if(this.installed!==void 0){typeof this.sessionValue.sessionFile=="string"&&this.sessionValue.sessionFile!==""&&await F(this.deps.nodeId,{op:"invalidate",session_id:this.sessionValue.sessionManager.getSessionId()});const s=(await S(this.deps.nodeId)).fault;s?.link==="pi\u2192provider"&&s.retry.disposition==="auto"&&s.retry.by==="daemon"&&await x(this.deps.nodeId,{op:"clear",link:"pi\u2192provider",preserve_episode:!0})}this.installed?.executingTools.clear(),this.removeInstalledSubscription(),this.deps.setActiveSession(e),this.deps.projection().reset(),this.deps.faultRetry().reset(),this.agentRunActiveValue=!1,this.deps.onSessionRebind(),this.deps.toolGroups.resetForSession(),this.sessionValue=e,this.servicesValue=t;const o=e.agent;o&&(w(this.deps.nodeId,{agent:o,sessionManager:e.sessionManager}),E(o),o.steeringMode="all",o.followUpMode="all",q(o));const a={id:++this.nextSubscriptionGeneration,session:e,fallback:{blocked:!1},stagedProviderAgentEnd:null,stagedWatchdogAbort:!1,stagedRefreshAbort:!1,stagedOverflowFailure:null,executingTools:new Set};if(this.unsubscribe=this.sessionValue.subscribe(s=>{if(!this.deps.projection().isCurrent(a))return;if(c.current()!==void 0){this.executeSubscriptionCallback(a,s);return}const r=a.fallback;if(s.type==="turn_start"&&r.storedId!==void 0){r.storedId=void 0,r.blocked=!0,c.fresh(()=>{u({level:"warn",event:"broker.turn.fallback_overlap",fields:{generation:a.id,engine_event:s.type}}),this.executeSubscriptionCallback(a,s)});return}if(s.type==="turn_start"&&!r.blocked){c.fresh(R=>{r.storedId=R,this.executeSubscriptionCallback(a,s)});return}const h=r.storedId;if(h!==void 0&&!r.blocked){c.run(h,()=>{this.executeSubscriptionCallback(a,s),s.type==="agent_settled"&&r.storedId===h&&(r.storedId=void 0)});return}this.executeSubscriptionCallback(a,s)}),this.installed=a,await e.bindExtensions({uiContext:this.deps.uiContext(),mode:"rpc",abortHandler:l(()=>T(e,()=>{const s=this.installed;s?.session===e&&(s.stagedRefreshAbort=!0)}),"abortHandler"),shutdownHandler:l(()=>this.requestShutdown(),"shutdownHandler")}),i===this.latestRebindRequest){if(this.deps.cfg.editorName!==void 0&&this.deps.cfg.editorName!=="")try{e.setSessionName(this.deps.cfg.editorName)}catch{}try{this.deps.turnReplies.seed(this.deps.transcriptMessages(e))}catch(s){u({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`turn reply seed failed: ${String(s)}`}})}this.deps.projection().rearmIfStreaming(a),this.captureSystemPrompt(e)}}).finally(()=>{--this.rebindsInFlight>0||(this.rebinding?.resolve(),this.rebinding=void 0)});return this.rebindTail=n.catch(()=>{}),n}runReplacement(e,t,i){const n=this.deps.runtime;if(n===void 0){this.deps.registry.sendFrame(e,{type:"error",code:"engine_error",message:"session replacement unsupported at this engine pin"});return}if(this.admittedReplacements>=f){c.fresh(()=>u({level:"warn",event:"broker.session_replacement.rejected",outcome:"skipped",fields:{op:t,reason:"capacity",admitted:f,cap:f}})),this.ackTo(e,t,!1,`session replacement busy (${f} active or queued)`);return}this.admittedReplacements++;const o=this.replacementTail.then(async()=>{try{await c.fresh(async()=>{const a=process.hrtime.bigint();let s;try{const p=new Promise(h=>{s=setTimeout(()=>h(b),m)}),r=await Promise.race([i(n),p]);if(r===b){u({level:"error",event:"broker.session_replacement.timed_out",outcome:"failed",duration_ms:Number(process.hrtime.bigint()-a)/1e6,fields:{op:t,deadline_ms:m,mutation_outcome:"indeterminate"}}),this.ackTo(e,t,!1,"session replacement timed out; broker restarting because session state is indeterminate"),this.deps.disposeAndExit("session-replacement-timeout",1);return}r.cancelled||this.deps.reWelcomeAll(),this.ackTo(e,t,!r.cancelled,r.cancelled?r.detail??"cancelled by extension":void 0)}catch(p){u({level:"error",event:"broker.session_replacement.failed",outcome:"failed",error:p,fields:{op:t,mutation_outcome:"indeterminate"}}),this.ackTo(e,t,!1,"session replacement failed; broker restarting because session state is indeterminate"),this.deps.disposeAndExit("session-replacement-failed",1)}finally{clearTimeout(s)}})}finally{this.admittedReplacements--,this.admittedReplacements===0&&this.deps.onReplacementIdle?.()}});this.replacementTail=o.catch(()=>{})}removeInstalledSubscription(){this.unsubscribe?.(),this.unsubscribe=void 0,this.installed=void 0}captureSystemPrompt(e){try{const t=_(this.deps.nodeId);v(t,{recursive:!0}),I(A(t,"system-prompt.md"),e.systemPrompt,"utf8")}catch{}}executeSubscriptionCallback(e,t){try{t.type==="agent_start"&&(this.agentRunActiveValue=!0),t.type==="agent_end"&&(e.stagedProviderAgentEnd=t,e.stagedOverflowFailure=null),t.type==="compaction_end"&&t.reason==="overflow"&&t.willRetry===!1&&typeof t.errorMessage=="string"&&(e.stagedOverflowFailure={errorMessage:t.errorMessage}),t.type==="agent_settled"&&(this.agentRunActiveValue=!1,this.publishAndScheduleProviderRecovery(e)),this.deps.toolGroups.observeToolGroupEvent(t),this.deps.turnReplies.observe(t),this.deps.projection().relayEvent(t,e)}catch(i){u({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`event relay threw: ${String(i)}`}})}}async publishAndScheduleProviderRecovery(e){try{if(await this.deps.faultRetry().publishSettledProviderFault(e),this.shutdownRequested){this.deps.disposeAndExit("shutdown-after-settle");return}const t=this.deps.faultRetry();await t.schedule()!=="armed"&&!t.fallbackInFlight()&&await t.faultIsFatal()&&await this.idleAfterFatalFault(e)}catch(t){this.deps.faultRetry().abandonTurnEnd(),u({level:"error",event:"broker.daemon.operation.failed",node_id:this.deps.nodeId,error:t,fields:{operation:"provider_fault_settlement"}}),this.shutdownRequested&&this.deps.disposeAndExit("shutdown-after-settle")}}async idleAfterFatalFault(e){const t=l(()=>this.installed===e&&!this.agentRunActiveValue&&!e.session.isStreaming,"stillSettled");!t()||(await V(this.deps.nodeId,{stopReason:"error",pushedFinal:!1,faultSettled:!0})).action!=="stay_resident"||!t()||(await P(this.deps.nodeId),C().stop())}ackTo(e,t,i=!0,n,o){this.deps.registry.sendFrame(e,{type:"ack",for:t,ok:i,...n!==void 0?{detail:n}:{},...o!==void 0?{id:o}:{}})}}export{X as RebindDriver};
@@ -1 +1 @@
1
- var J=Object.defineProperty;var l=(e,r)=>J(e,"name",{value:r,configurable:!0});import{envHomeOverride as K,envRuntimeHomeOverride as X,INBOX_BETWEEN_TURNS_ENV as z,SCOPES_FILE_ENV as V,scopeAllowed as I}from"../../shared/env.js";import{isOwnMemoryScope as Y,validateScopes as Q}from"../scopes.js";import{activeGrant as F,terminalGrant as k}from"../grants/mirror.js";import{grantSnapshotMissing as Z}from"../../daemon/readiness.js";import{appendRunEvent as ee}from"../runs/events.js";import{personId as ne}from"../canvas/person.js";import{InputError as S}from"../io.js";import{ApiError as g}from"../../api/errors.js";import{assertGranteeRunsHere as re}from"./unsandboxed.js";import{randomBytes as te}from"node:crypto";import{existsSync as B}from"node:fs";import{resolve as ie,relative as oe,isAbsolute as L,join as ae,sep as le}from"node:path";import{appSpace as se}from"../layout.js";import{scopesFilePath as de}from"../canvas/paths.js";import{createNode as ce,assertRunNotDeleting as ue,conversationOwner as fe,getNode as u,getRow as pe}from"../canvas/index.js";import{ensureAppSpace as _e}from"../spaces/space.js";import{LAUNCHER_SOCKET as P}from"../../launcher/spec.js";import{resolveInstallId as me}from"../canvas/install-id.js";import{isAppRun as we}from"../canvas/app-run.js";import{contextDir as ge}from"../canvas/paths.js";import{conversationIdFromPath as he}from"../conversation-store/index.js";import{usage as U}from"../errors.js";import{assertProfileAvailableForBirth as Re}from"../profiles/deletion-reservation.js";import{loadExactProfileManifest as ve}from"../profiles/manifest.js";import{admitNodeCreate as ye}from"../command-hooks/lifecycle-create.js";import{getById as Ee,nodeObject as N,ownerForGrantee as Ie}from"../graph/objects.js";import{recordActedOn as Ne}from"../graph/edges.js";import{recordWatch as Ce,copyWatchesForFork as ke}from"../graph/watches.js";import{publish as Se}from"../graph/events.js";import{canvasDatabaseUrl as Te}from"../canvas/db.js";import{someAsync as De}from"../async-array.js";import{admitIntoManagedCheckout as Oe}from"../worktree-mutation-async.js";let T;const H=new Map;function hn(e){T=e}l(hn,"setInstallId");async function xe(){if(T!==void 0)return T;const e=await Te(),r=H.get(e);if(r!==void 0)return r;const o=await me();return H.set(e,o),o}l(xe,"installId");function be(){return`${Date.now().toString(36)}-${te(4).toString("hex")}`}l(be,"newJobId");async function Ae(){return`${await xe()}-${be()}`}l(Ae,"newNodeId");const D=128,Me=/^[a-z0-9-]+$/;class j extends Error{static{l(this,"NodeIdConflictError")}nodeId;constructor(r){super(`node already exists: ${r}`),this.nodeId=r,this.name="NodeIdConflictError"}}function O(e){if(e.length===0||e.length>D)throw U(`invalid --node-id '${e}': must be 1-${D} characters`,{next:`Pass an id-safe string (lowercase letters, digits, hyphens) of at most ${D} characters.`});if(!Me.test(e))throw U(`invalid --node-id '${e}': must contain only lowercase letters, digits, and hyphens (e.g. 'remote42', 'my-node-1')`,{next:"Use only lowercase letters, digits, and hyphens."})}l(O,"validateNodeIdFormat");async function Rn(e){if(O(e),await u(e)!==null)throw new j(e)}l(Rn,"preflightNodeId");async function $e(e){if(O(e),await u(e)!==null)throw new j(e);return e}l($e,"validatedExplicitNodeId");async function vn(e){let r=e;const o=new Set;for(;;){if(o.has(r))return r;o.add(r);const s=await pe(r);if(s===null||s.parent==null)return r;r=s.parent}}l(vn,"rootOfSpine");function yn(){const e=process.env;return{nodeId:e.CRTR_NODE_ID??null,kind:e.CRTR_KIND??null,mode:e.CRTR_MODE??null}}l(yn,"currentNodeContext");function Fe(e){return we(e)}l(Fe,"inboxBetweenTurns");function En(e){const r={CRTR_NODE_ID:e.node_id,CRTR_KIND:e.kind,CRTR_MODE:e.mode,CRTR_LIFECYCLE:e.lifecycle,CRTR_NODE_CWD:e.cwd,CRTR_CONTEXT_DIR:ge(e.node_id,e.grantee),...e.deadline_at?{CRTR_NODE_DEADLINE_AT:e.deadline_at}:{},CRTR_CYCLES:(e.cycles??0).toString(),CRTR_PROFILE_ID:e.profile_id??"",PI_CLAUDE_OAUTH_REINJECT_SCOPE:"never"};e.parent&&(r.CRTR_PARENT_NODE_ID=e.parent),e.run_id&&(r.CRTR_RUN_ID=e.run_id),Fe(e)&&(r[z]="1"),e.scopes!==null&&e.scopes!==void 0&&(r[V]=de(e.node_id)),r.CRTR_GRANTEE=e.grantee??"app:terminal";const o=K();o!==void 0&&o!==""&&(r.CRTR_HOME=o);const s=X();s!==void 0&&s!==""&&(r.CRTR_RUNTIME_HOME=s);const f=process.env.CRTR_SUBTREE;return f!==void 0&&f!==""&&(r.CRTR_SUBTREE=f),{...r,...e.launch?.env??{}}}l(En,"nodeEnv");async function Be(e){const r=he(e),o=r===null?null:await fe(r);return o===null?null:await u(o)}l(Be,"forkSourceNode");async function In(e){const r=e.nodeId!==void 0?(O(e.nodeId),e.nodeId):await Ae(),o=e.parent??null,s=o===null,f=e.spawnedBy??o,_=e.creator??null,x=e.mode??"base",C=e.lifecycle??(s?"resident":"terminal"),h=e.run===void 0?await k():null,n={node_id:r,name:e.name??e.kind,description:e.description,cycles:0,created:new Date().toISOString(),cwd:e.cwd,host_kind:"broker",profile_id:e.profile_id??null,kind:e.kind,mode:x,lifecycle:C,persona_ack:{kind:e.kind,mode:x,lifecycle:C},status:"active",parent:o,spawned_by:f,creator:_,fork_from:e.forkFrom,fork_source_file:e.forkSourceFile,review_binding:e.reviewBinding,model_override:e.modelOverride??null,scopes:e.scopes??null,isolated:e.isolated===!0,grantee:e.run?.grantee??"app:terminal",sub:e.run?.sub??h?.sub??null,grant_id:e.run?.grant_id??h?.id??null,client_id:e.run?.client_id??null,metadata_json:s?e.run?.metadata_json??null:null,run_id:s?r:e.run?.run_id??null,event_cursor:s?e.run?.event_cursor??1:null,usage_json:s?e.run?.usage_json??JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}):null,passive_default:e.passiveDefault??!1,caused_by:e.causedBy??null,...e.managedWorktree!==void 0?{managed_worktree:e.managedWorktree}:{},intent:null,deadline_at:e.deadlineAt??null,pi_session_id:null,pi_session_file:null,launch:e.launch},y=e.scopes===void 0?void 0:Q(e.scopes),R=o===null?null:await u(o);if(o!==null&&R===null)throw new Error(`cannot spawn under unknown parent node: ${o}`);if(R?.run_id&&(await u(R.run_id))?.deleting_at)throw new Error("run is deleting");const c=_===null?null:await u(_);if(_!==null&&c===null)throw new Error(`cannot spawn from unknown creator node: ${_}`);if(c?.run_id&&(await u(c.run_id))?.deleting_at)throw new Error("run is deleting");const b=e.forkSourceFile==null?null:await Be(e.forkSourceFile),A=e.replaces==null?null:await u(e.replaces),m=R??b??A??c??(f===null?null:await u(f)),q=[R,c,b,A,f===null?null:await u(f)],M=await De(q,async t=>t?.isolated===!0||t?.run_id!=null&&(await u(t.run_id))?.isolated===!0);if(M&&e.isolated===!1)throw new g(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});if(n.isolated=M||e.isolated===!0,m!==null){const t=c??m;if(c&&m.grantee&&c.grantee&&c.grantee!==m.grantee)throw new g(403,"other_app_run","cannot create under another app's node");const i=c&&c!==m?[m,c]:[m],d=[],w=new Set;for(const a of i){a.scopes!==null&&a.scopes!==void 0&&d.push(a.scopes);const p=a.grantee==="app:terminal"?await k():a.grant_id?await F(a.grant_id):null;if(p){const v=[...p.resolved];w.add(v),d.push(v)}else{const v=[];w.add(v),d.push(v)}}const E=l(a=>Y(a,t.grantee??"app:terminal"),"ownMemory"),$=l((a,p)=>E(p)&&w.has(a)||I(a,p),"allowed");if(y!==void 0&&!y.every(a=>d.every(p=>$(p,a))))throw new S({error:"unknown_scope",message:"child scopes must be covered by the creating node, its source and their current grants",received:y,field:"scopes",next:"Request only scopes the creating node currently holds."});if(n.scopes=y??(i.every(a=>a.scopes==null)&&i.every(a=>a.grant_id===t.grant_id&&a.sub===t.sub)?null:i.length===1?m.scopes??null:[...new Set(d.flat().filter(a=>d.every(p=>$(p,a))))]),n.scopes?.includes("crtr:act")&&!I(n.scopes,"crtr:llm"))throw new S({error:"invalid_scope",message:"crtr:act requires crtr:llm",received:n.scopes,field:"scopes",next:"Include crtr:llm or omit crtr:act."});n.grantee=t.grantee,n.sub=t.sub??(n.grantee==="app:terminal"?h?.sub:null)??null,n.grant_id=t.grant_id??(n.grantee==="app:terminal"?h?.id:null)??null,n.client_id=t.client_id,n.run_id=s?r:t.run_id??null}if(re(n.grantee),e.run===void 0&&n.grantee==="app:terminal"&&h===null)throw await Z()?new g(503,"runtime_starting","the first grant snapshot from the directory has not committed"):new g(403,"scope_missing","terminal grant is not active");if(n.isolated){if(process.platform!=="linux"||!B(P))throw new g(400,"isolated_unavailable","isolated nodes require the Linux root launcher");if(e.managedWorktree!==void 0)throw new g(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const t=ae(se(n.grantee??"app:terminal").nodes,r),i=oe(t,ie(n.cwd));if(!L(n.cwd)||i===".."||i.startsWith(`..${le}`)||L(i))throw new g(400,"invalid_request","isolated node cwd must be inside its own folder",{param:"cwd"})}if(n.profile_id){const t=ve(n.profile_id).manifest.grantee??"app:terminal";if(t!==n.grantee){const i=`crtr:memory:rw:${t}:profile:${n.profile_id}`,d=n.grantee==="app:terminal"?await k():n.grant_id?await F(n.grant_id):null;if(!d||d.grantee!==n.grantee||!I(d.resolved,i)||!I(n.scopes,i))throw new S({error:"scope_missing",message:`missing scope ${i}`,received:n.profile_id,field:"profile",next:"Select a profile covered by this run and its current grant."})}}if(Re(n.profile_id??null),!n.grantee)throw new Error("node creation requires an app");const W=await ye({kind:n.kind,mode:n.mode,cwd:n.cwd,profile:n.profile_id??null,root:s,parentId:o,requestingNodeId:_,lifecycle:C,replaces:e.replaces??null},n.grantee),G=l(async(t,i)=>{const d=await N(t,i.node_id),w=_??o??e.actor??f??null,E=w!==null&&(await Ee(t,w))?.type==="node"?await N(t,w):await Ie(t,e.run===void 0?ne():i.grantee);await Ne(t,E,d),o!==null&&e.subscribe!==!1&&await Ce(t,await N(t,o),d,"spawn"),e.forkSourceNode&&await ke(t,await N(t,e.forkSourceNode),d),await Se(t,{event:"created",object:d,actor:E,attrs:{name:i.name,kind:i.kind,creator:_??null}})},"recordBirth");try{e.nodeId!==void 0&&await $e(e.nodeId),await e.beforeCreate?.(),(process.platform!=="linux"||B(P))&&await _e(n.grantee??"app:terminal"),await Oe(n.cwd,r,()=>ce(n,{outcomeDelivery:e.outcomeDelivery,withinCreate:l(async(t,i)=>{i.run_id&&i.run_id!==i.node_id&&await ue(t,i.run_id),s&&e.run===void 0&&await ee(t,r,r,"run.started",{scopes:i.scopes??null,payer:"user",usage:JSON.parse(i.usage_json)}),await e.withinCreate?.(t,i),await G(t,i)},"withinCreate")}))}finally{W()}return n}l(In,"spawnNode");export{j as NodeIdConflictError,yn as currentNodeContext,Be as forkSourceNode,Fe as inboxBetweenTurns,be as newJobId,Ae as newNodeId,En as nodeEnv,Rn as preflightNodeId,vn as rootOfSpine,hn as setInstallId,In as spawnNode};
1
+ var J=Object.defineProperty;var l=(e,r)=>J(e,"name",{value:r,configurable:!0});import{envHomeOverride as K,envRuntimeHomeOverride as X,INBOX_BETWEEN_TURNS_ENV as z,SCOPES_FILE_ENV as V,scopeAllowed as I}from"../../shared/env.js";import{isOwnMemoryScope as Y,validateScopes as Q}from"../scopes.js";import{activeGrant as F,terminalGrant as k}from"../grants/mirror.js";import{grantSnapshotMissing as Z}from"../../daemon/readiness.js";import{appendRunEvent as ee}from"../runs/events.js";import{personId as ne}from"../canvas/person.js";import{InputError as S}from"../io.js";import{ApiError as g}from"../../api/errors.js";import{assertGranteeRunsHere as re}from"./unsandboxed.js";import{randomBytes as te}from"node:crypto";import{existsSync as B}from"node:fs";import{resolve as ie,relative as oe,isAbsolute as L,join as ae,sep as le}from"node:path";import{appSpace as se}from"../layout.js";import{scopesFilePath as de}from"../canvas/paths.js";import{createNode as ce,assertRunNotDeleting as ue,conversationOwner as fe,getNode as u,getRow as pe}from"../canvas/index.js";import{ensureAppSpace as _e}from"../spaces/space.js";import{LAUNCHER_SOCKET as P}from"../../launcher/spec.js";import{blaxelEnvironmentOf as me}from"./blaxel-environment.js";import{resolveInstallId as we}from"../canvas/install-id.js";import{isAppRun as ge}from"../canvas/app-run.js";import{contextDir as he}from"../canvas/paths.js";import{conversationIdFromPath as ve}from"../conversation-store/index.js";import{usage as U}from"../errors.js";import{assertProfileAvailableForBirth as Re}from"../profiles/deletion-reservation.js";import{loadExactProfileManifest as ye}from"../profiles/manifest.js";import{admitNodeCreate as Ee}from"../command-hooks/lifecycle-create.js";import{getById as Ie,nodeObject as N,ownerForGrantee as Ne}from"../graph/objects.js";import{recordActedOn as Ce}from"../graph/edges.js";import{recordWatch as ke,copyWatchesForFork as Se}from"../graph/watches.js";import{publish as Te}from"../graph/events.js";import{canvasDatabaseUrl as De}from"../canvas/db.js";import{someAsync as Oe}from"../async-array.js";import{admitIntoManagedCheckout as xe}from"../worktree-mutation-async.js";let T;const H=new Map;function Rn(e){T=e}l(Rn,"setInstallId");async function be(){if(T!==void 0)return T;const e=await De(),r=H.get(e);if(r!==void 0)return r;const o=await we();return H.set(e,o),o}l(be,"installId");function Ae(){return`${Date.now().toString(36)}-${te(4).toString("hex")}`}l(Ae,"newJobId");async function Me(){return`${await be()}-${Ae()}`}l(Me,"newNodeId");const D=128,$e=/^[a-z0-9-]+$/;class j extends Error{static{l(this,"NodeIdConflictError")}nodeId;constructor(r){super(`node already exists: ${r}`),this.nodeId=r,this.name="NodeIdConflictError"}}function O(e){if(e.length===0||e.length>D)throw U(`invalid --node-id '${e}': must be 1-${D} characters`,{next:`Pass an id-safe string (lowercase letters, digits, hyphens) of at most ${D} characters.`});if(!$e.test(e))throw U(`invalid --node-id '${e}': must contain only lowercase letters, digits, and hyphens (e.g. 'remote42', 'my-node-1')`,{next:"Use only lowercase letters, digits, and hyphens."})}l(O,"validateNodeIdFormat");async function yn(e){if(O(e),await u(e)!==null)throw new j(e)}l(yn,"preflightNodeId");async function Fe(e){if(O(e),await u(e)!==null)throw new j(e);return e}l(Fe,"validatedExplicitNodeId");async function En(e){let r=e;const o=new Set;for(;;){if(o.has(r))return r;o.add(r);const s=await pe(r);if(s===null||s.parent==null)return r;r=s.parent}}l(En,"rootOfSpine");function In(){const e=process.env;return{nodeId:e.CRTR_NODE_ID??null,kind:e.CRTR_KIND??null,mode:e.CRTR_MODE??null}}l(In,"currentNodeContext");function Be(e){return ge(e)}l(Be,"inboxBetweenTurns");function Nn(e){const r={CRTR_NODE_ID:e.node_id,CRTR_KIND:e.kind,CRTR_MODE:e.mode,CRTR_LIFECYCLE:e.lifecycle,CRTR_NODE_CWD:e.cwd,CRTR_CONTEXT_DIR:he(e.node_id,e.grantee),...e.deadline_at?{CRTR_NODE_DEADLINE_AT:e.deadline_at}:{},CRTR_CYCLES:(e.cycles??0).toString(),CRTR_PROFILE_ID:e.profile_id??"",PI_CLAUDE_OAUTH_REINJECT_SCOPE:"never"};e.parent&&(r.CRTR_PARENT_NODE_ID=e.parent),e.run_id&&(r.CRTR_RUN_ID=e.run_id),Be(e)&&(r[z]="1"),e.scopes!==null&&e.scopes!==void 0&&(r[V]=de(e.node_id)),r.CRTR_GRANTEE=e.grantee??"app:terminal";const o=K();o!==void 0&&o!==""&&(r.CRTR_HOME=o);const s=X();s!==void 0&&s!==""&&(r.CRTR_RUNTIME_HOME=s);const f=process.env.CRTR_SUBTREE;return f!==void 0&&f!==""&&(r.CRTR_SUBTREE=f),{...r,...e.launch?.env??{}}}l(Nn,"nodeEnv");async function Le(e){const r=ve(e),o=r===null?null:await fe(r);return o===null?null:await u(o)}l(Le,"forkSourceNode");async function Cn(e){const r=e.nodeId!==void 0?(O(e.nodeId),e.nodeId):await Me(),o=e.parent??null,s=o===null,f=e.spawnedBy??o,_=e.creator??null,x=e.mode??"base",C=e.lifecycle??(s?"resident":"terminal"),h=e.run===void 0?await k():null,n={node_id:r,name:e.name??e.kind,description:e.description,cycles:0,created:new Date().toISOString(),cwd:e.cwd,host_kind:"broker",profile_id:e.profile_id??null,kind:e.kind,mode:x,lifecycle:C,persona_ack:{kind:e.kind,mode:x,lifecycle:C},status:"active",parent:o,spawned_by:f,creator:_,fork_from:e.forkFrom,fork_source_file:e.forkSourceFile,review_binding:e.reviewBinding,model_override:e.modelOverride??null,scopes:e.scopes??null,isolated:e.isolated===!0,grantee:e.run?.grantee??"app:terminal",sub:e.run?.sub??h?.sub??null,grant_id:e.run?.grant_id??h?.id??null,client_id:e.run?.client_id??null,metadata_json:s?e.run?.metadata_json??null:null,run_id:s?r:e.run?.run_id??null,event_cursor:s?e.run?.event_cursor??1:null,usage_json:s?e.run?.usage_json??JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}):null,passive_default:e.passiveDefault??!1,caused_by:e.causedBy??null,...e.managedWorktree!==void 0?{managed_worktree:e.managedWorktree}:{},intent:null,deadline_at:e.deadlineAt??null,pi_session_id:null,pi_session_file:null,launch:e.launch},y=e.scopes===void 0?void 0:Q(e.scopes),v=o===null?null:await u(o);if(o!==null&&v===null)throw new Error(`cannot spawn under unknown parent node: ${o}`);if(v?.run_id&&(await u(v.run_id))?.deleting_at)throw new Error("run is deleting");const c=_===null?null:await u(_);if(_!==null&&c===null)throw new Error(`cannot spawn from unknown creator node: ${_}`);if(c?.run_id&&(await u(c.run_id))?.deleting_at)throw new Error("run is deleting");const b=e.forkSourceFile==null?null:await Le(e.forkSourceFile),A=e.replaces==null?null:await u(e.replaces),m=v??b??A??c??(f===null?null:await u(f)),q=[v,c,b,A,f===null?null:await u(f)],M=await Oe(q,async t=>t?.isolated===!0||t?.run_id!=null&&(await u(t.run_id))?.isolated===!0);if(M&&e.isolated===!1)throw new g(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});if(n.isolated=M||e.isolated===!0,m!==null){const t=c??m;if(c&&m.grantee&&c.grantee&&c.grantee!==m.grantee)throw new g(403,"other_app_run","cannot create under another app's node");const i=c&&c!==m?[m,c]:[m],d=[],w=new Set;for(const a of i){a.scopes!==null&&a.scopes!==void 0&&d.push(a.scopes);const p=a.grantee==="app:terminal"?await k():a.grant_id?await F(a.grant_id):null;if(p){const R=[...p.resolved];w.add(R),d.push(R)}else{const R=[];w.add(R),d.push(R)}}const E=l(a=>Y(a,t.grantee??"app:terminal"),"ownMemory"),$=l((a,p)=>E(p)&&w.has(a)||I(a,p),"allowed");if(y!==void 0&&!y.every(a=>d.every(p=>$(p,a))))throw new S({error:"unknown_scope",message:"child scopes must be covered by the creating node, its source and their current grants",received:y,field:"scopes",next:"Request only scopes the creating node currently holds."});if(n.scopes=y??(i.every(a=>a.scopes==null)&&i.every(a=>a.grant_id===t.grant_id&&a.sub===t.sub)?null:i.length===1?m.scopes??null:[...new Set(d.flat().filter(a=>d.every(p=>$(p,a))))]),n.scopes?.includes("crtr:act")&&!I(n.scopes,"crtr:llm"))throw new S({error:"invalid_scope",message:"crtr:act requires crtr:llm",received:n.scopes,field:"scopes",next:"Include crtr:llm or omit crtr:act."});n.grantee=t.grantee,n.sub=t.sub??(n.grantee==="app:terminal"?h?.sub:null)??null,n.grant_id=t.grant_id??(n.grantee==="app:terminal"?h?.id:null)??null,n.client_id=t.client_id,n.run_id=s?r:t.run_id??null}if(re(n.grantee),e.run===void 0&&n.grantee==="app:terminal"&&h===null)throw await Z()?new g(503,"runtime_starting","the first grant snapshot from the directory has not committed"):new g(403,"scope_missing","terminal grant is not active");if(n.isolated){if((process.platform!=="linux"||!B(P))&&await me(n.profile_id??null)===null)throw new g(400,"isolated_unavailable","isolated nodes require the Linux root launcher");if(e.managedWorktree!==void 0)throw new g(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const t=ae(se(n.grantee??"app:terminal").nodes,r),i=oe(t,ie(n.cwd));if(!L(n.cwd)||i===".."||i.startsWith(`..${le}`)||L(i))throw new g(400,"invalid_request","isolated node cwd must be inside its own folder",{param:"cwd"})}if(n.profile_id){const t=ye(n.profile_id).manifest.grantee??"app:terminal";if(t!==n.grantee){const i=`crtr:memory:rw:${t}:profile:${n.profile_id}`,d=n.grantee==="app:terminal"?await k():n.grant_id?await F(n.grant_id):null;if(!d||d.grantee!==n.grantee||!I(d.resolved,i)||!I(n.scopes,i))throw new S({error:"scope_missing",message:`missing scope ${i}`,received:n.profile_id,field:"profile",next:"Select a profile covered by this run and its current grant."})}}if(Re(n.profile_id??null),!n.grantee)throw new Error("node creation requires an app");const W=await Ee({kind:n.kind,mode:n.mode,cwd:n.cwd,profile:n.profile_id??null,root:s,parentId:o,requestingNodeId:_,lifecycle:C,replaces:e.replaces??null},n.grantee),G=l(async(t,i)=>{const d=await N(t,i.node_id),w=_??o??e.actor??f??null,E=w!==null&&(await Ie(t,w))?.type==="node"?await N(t,w):await Ne(t,e.run===void 0?ne():i.grantee);await Ce(t,E,d),o!==null&&e.subscribe!==!1&&await ke(t,await N(t,o),d,"spawn"),e.forkSourceNode&&await Se(t,await N(t,e.forkSourceNode),d),await Te(t,{event:"created",object:d,actor:E,attrs:{name:i.name,kind:i.kind,creator:_??null}})},"recordBirth");try{e.nodeId!==void 0&&await Fe(e.nodeId),await e.beforeCreate?.(),(process.platform!=="linux"||B(P))&&await _e(n.grantee??"app:terminal"),await xe(n.cwd,r,()=>ce(n,{outcomeDelivery:e.outcomeDelivery,withinCreate:l(async(t,i)=>{i.run_id&&i.run_id!==i.node_id&&await ue(t,i.run_id),s&&e.run===void 0&&await ee(t,r,r,"run.started",{scopes:i.scopes??null,payer:"user",usage:JSON.parse(i.usage_json)}),await e.withinCreate?.(t,i),await G(t,i)},"withinCreate")}))}finally{W()}return n}l(Cn,"spawnNode");export{j as NodeIdConflictError,In as currentNodeContext,Le as forkSourceNode,Be as inboxBetweenTurns,Ae as newJobId,Me as newNodeId,Nn as nodeEnv,yn as preflightNodeId,En as rootOfSpine,Rn as setInstallId,Cn as spawnNode};
@@ -1,3 +1,3 @@
1
- var re=Object.defineProperty;var l=(e,r)=>re(e,"name",{value:r,configurable:!0});import{assertGranteeRunsHere as ie}from"./unsandboxed.js";import{existsSync as L}from"node:fs";import{conversationIdFromPath as v,conversationStore as p,ConversationNotFoundError as H}from"../conversation-store/index.js";import{isAbsolute as te}from"node:path";import{findNodeBySessionFile as ne,getNode as E,updateNode as S,beginExecution as ae,ExecutionAlreadyLive as oe,recordExecutionPid as se,abortExecution as $,endLiveExecution as le,unbindSession as M,recordLaunch as ce,setFrozen as ue,clearFrozen as U,fullName as fe}from"../canvas/index.js";import{emitEvent as we}from"../events/emit.js";import{consumeDeadlineOnWake as de}from"../feed/messages.js";import{isParked as z,transition as N}from"./lifecycle.js";import{publishNotice as he}from"./close.js";import{InputError as C}from"../io.js";import{buildPiArgv as _}from"./launch.js";import{buildReviveKickoff as pe,drainBearings as me,hasRetainedReports as ve,readGoal as g,readYieldMessage as _e,RUNTIME_RESTART_CONTINUATION as ye}from"./kickoff.js";import{readRoadmap as K}from"./roadmap.js";import{encodeKickoffOrigin as ke,KICKOFF_ORIGIN_ENV as ge}from"./stamp/protocol.js";import{headlessBrokerHost as be,brokerLaunchJson as Re}from"./host.js";import{processRunner as xe}from"./runner/process.js";import{runnerForEnvironment as Ee}from"./runner/index.js";import{acceptedRelease as Se,blaxelEnvironmentOf as G,checkBilledWake as $e,EnvironmentImagePending as Ne,imageForWake as Ce}from"../../daemon/reconcilers/node-lifecycle/remote-wake.js";import{ExecutionCapReached as Fe,liveExecutionForNode as Ae,setExecutionLaunchJson as Pe}from"../canvas/executions.js";import{appMemoryFull as Be,appMemoryTooSmall as Oe,boundFleet as J,brokerCapReached as W,brokerThresholdsForDaemon as F}from"./fleet.js";import{admitBrokerMemory as De}from"./memory-admission.js";import{beginBootFaultAttempt as Te,clearFault as Le,hasAdmittedTurn as He}from"../canvas/recovery.js";import{clearNodeLedger as j,forkNodeLedger as Me,noticeChangedWhileDone as Ue}from"./ledger.js";import{forkSourceNode as ze,rootOfSpine as Ke}from"./nodes.js";import{checkStart as Ge}from"./start-check.js";import{isReviewCompanionBound as Je}from"../review/companion.js";import{assertProfileActive as We}from"../profiles/manifest.js";import{isPidAlive as je}from"../canvas/pid.js";import{crtrHome as Ve,jobDir as Xe}from"../canvas/paths.js";import{contextDir as Ye,ensureNodeDirs as qe,nodeAgentDir as Qe}from"../canvas/node-agent-paths.js";import{discoverLifecycleHookRegistry as Ze}from"../command-hooks/discovery.js";import{invokeLifecycleHooks as Ie}from"../command-hooks/transport/exec-lifecycle.js";async function er(e,r){const i=e.pi_session_file,t=i?v(i):null;if(t===null)return{};try{const a=await p().header(t);return r?{resumeSessionPath:a.path}:{resumeSessionPath:a.path,newCycle:!0}}catch(a){if(a instanceof H)return{};throw a}}l(er,"resumeArgs");async function V(e){const r=v(e);if(r===null)return!1;try{return await p().header(r),!0}catch(i){if(i instanceof H)return!1;throw i}}l(V,"conversationExists");async function rr(e){const r=v(e);return r===null?!0:(await p().read(r)).entries.some(i=>i.type==="message")}l(rr,"sessionHasConversation");async function ir(e){const r=v(e);if(r===null)return!0;const i=(await p().header(r)).header.cwd;return typeof i!="string"||L(i)}l(ir,"sessionCwdExists");async function tr(e,r,i,t){const a=r.pi_session_file;if(!a||!await V(a))return{};const u=await ne(a);return u!==null&&u.node_id!==e&&je(u.pi_pid)?{}:t&&!await ir(a)?{}:await er(r,i)}l(tr,"resumableSessionArgs");async function nr(e){return await K(e)===null&&await _e(e)===null&&!await ve(e)}l(nr,"isUnstartedBirth");async function Ur(e){const r=await g(e);if(r!==null&&r.trim()!=="")return!0;const i=await K(e);return i!==null&&i.trim()!==""}l(Ur,"hasFreshGroundState");async function zr(e,r){const i=await X(e,r,!0);if(i.parked)throw new Error(`reviveNode: internal parked decision for activating revive ${e}`);return i.result}l(zr,"reviveNode");async function Kr(e,r,i){return X(e,{...r,viewerAttach:!0},i)}l(Kr,"decideAttachRevive");async function X(e,r,i){let t=await E(e);if(t===null)throw new Error(`reviveNode: unknown node ${e}`);if(t=await E(e)??t,t.run_id&&(await E(t.run_id))?.deleting_at)throw new Error("run is deleting");if(t.final_report!==null)throw new Error(`reviveNode: refusing to revive ${e} \u2014 its finalization latch is set (final_report=${t.final_report}). A final landed between the caller's reopen-gate check and this revive; re-run with --reopen if this retask is still wanted.`);We(t.profile_id),ie(t.grantee);const a=J();if(!await a.has(e)&&!await a.isClaimed(e)&&r.wakeReason!=="runtime-restart-abort"&&r.viewerAttach!==!0&&!(!i&&t.lifecycle==="resident"&&z(t))&&(await Ge(t),await G(t.profile_id??null)!==null&&await $e(t)),await a.has(e))return await N(e,t.intent==="refresh"?"refresh-launch":"revive"),{parked:!1,result:{window:null,session:t.tmux_session??null,resumed:!1,outcome:"already-live"}};if(await a.isClaimed(e))return{parked:!1,result:{window:null,session:t.tmux_session??null,resumed:!1,outcome:"already-live"}};if(!i&&t.lifecycle==="resident"&&z(t))return{parked:!0};const u=await a.reserveSlot(e);let o=null;if(u){try{o=await De(e)}catch(w){throw a.releaseReservation(e),w}o.verdict!=="admitted"&&a.releaseReservation(e)}if(o?.verdict==="too_small")throw Oe(o);if(!u||o?.verdict==="denied"){if(r.capacity==="refuse")throw o?.verdict==="denied"?Be(o):W(F().automaticReviveCap,await a.occupancy());return t.frozen_at==null&&(await ue(e,new Date().toISOString()),await le(e,{reason:"frozen"}),we({level:"info",event:"broker.frozen",node_id:e,fields:o?.verdict==="denied"?{reason:"app_memory",free_bytes:o.free_bytes,needed_bytes:o.needed_bytes}:{reason:"broker_cap",live_count:await a.occupancy(),hard_cap:F().automaticReviveCap}})),{parked:!1,result:{window:null,session:t.tmux_session??null,resumed:!1,outcome:"frozen"}}}try{return{parked:!1,result:await ar(e,t,r)}}finally{a.releaseReservation(e)}}l(X,"decideRevive");async function ar(e,r,i){const t=await G(r.profile_id??null);if(t===null&&!L(r.cwd)){const s=r.fork_from!=null?`Restore ${r.cwd}, then revive the fork normally with \`crtr node lifecycle revive ${e}\`.`:r.managed_worktree?.state==="open"?"Restore or reconcile the recorded checkout, then use the managed-worktree close or abandon recovery.":`Recreate ${r.cwd}, or repair it with \`crtr node config --cwd <path> --node ${e}\`; durable node work resumes in a fresh conversation. Then retry \`crtr node lifecycle revive ${e}\`.`;throw new C({error:"cwd_missing",message:`${e} cannot be revived \u2014 its recorded cwd no longer exists: ${r.cwd}`,next:s})}r.host_kind!=="broker"&&(await S(e,{host_kind:"broker"}),r.host_kind="broker"),r.review_binding!=null&&!await Je(e)&&(r.pi_session_id!=null||r.pi_session_file!=null)&&(await M(e),r.pi_session_id=null,r.pi_session_file=null);const u=r.fork_from!=null&&r.pi_session_id==null,o=r.fork_source_file;if(u&&(typeof o!="string"||!te(o)))throw new C({error:"fork_source_invalid",message:`${e} cannot be revived \u2014 its resolved fork source file is missing or invalid.`,next:`Restore the node's fork source metadata, then retry \`crtr node lifecycle revive ${e}\`.`});const w=u&&typeof o=="string";if(w&&!await V(o))throw new C({error:"fork_from_missing",message:`${e} cannot be revived \u2014 its fork source no longer exists: ${o}`,next:`Restore ${o}, or update the node's fork source, then retry \`crtr node lifecycle revive ${e}\`.`});const{automaticReviveCap:Y}=F();if(t!==null){const s=await Ce(t);if(s.state==="pending")throw new Ne(t,s.error)}const q=t===null?xe:Ee("blaxel");let f;try{f=await ae(e,{daemonEpoch:J().epoch(),cap:Y,...t===null?{}:{runner:"blaxel",release:Se()}})}catch(s){if(s instanceof Fe)throw W(s.cap,s.live);if(s instanceof oe)return{window:null,session:r.tmux_session??null,resumed:!1,outcome:"already-live"};throw s}try{r.cycles=(r.cycles??0)+1,await S(e,{cycles:r.cycles});const Q=r.intent==="refresh"||r.cycle_pending===!0?!1:i.resume,h=w?{}:await tr(e,r,Q,t===null),y=h.resumeSessionPath!==void 0,A=y&&h.newCycle===!0,m=y&&!A,P=!y&&(i.wakeReason===void 0||i.wakeReason==="runtime-restart-abort")&&await nr(e),b=m&&(i.wakeReason===void 0||i.wakeReason==="runtime-restart-abort")&&!await He(e)&&!await rr(h.resumeSessionPath)?(await g(e))?.trim():void 0,B=b!==void 0&&b!=="",Z=w||P||B,O=r.cycle_pending===!0||A;await S(e,{cycle_pending:O}),r.cycle_pending=O,!w&&!y&&(r.pi_session_id!=null||r.pi_session_file!=null)&&(await M(e),r.pi_session_id=null,r.pi_session_file=null);let c,d=null;const R=l(()=>({type:"runtime",kind:"kickoff",...r.spawned_by!=null&&r.spawned_by!==""?{spawnedBy:r.spawned_by}:{}}),"birthOrigin");if(w){const n=await g(e),x=v(o);if(x===null)throw new Error(`invalid fork source ${o}`);const ee=(await p().header(await p().fork(x,e))).path;c=_(r,{resumeSessionPath:ee,...n!=null&&n!==""?{prompt:n}:{}}),n!=null&&n!==""&&(d=R()),await Me(e,(await ze(o))?.node_id??null)}else if(B)c=_(r,{...h,prompt:b}),d=R();else if(m)c=_(r,{...h,...i.wakeReason==="runtime-restart-abort"?{prompt:ye}:{}}),i.wakeReason==="runtime-restart-abort"&&(d={type:"runtime",kind:"restart-continuation"});else if(P){const n=(await g(e))?.trim();c=_(r,n!==void 0&&n!==""?{prompt:n}:{}),n!==void 0&&n!==""&&(d=R()),await j(e)}else{const n=await me(r,f);c=_(r,{...h,prompt:await pe(r,n,i.wakeReason==="runtime-restart-abort"?void 0:i.wakeReason)}),r.run_id&&n.unreadMessageIds?.length&&(c.env.CRTR_KICKOFF_MESSAGE_IDS=JSON.stringify(n.unreadMessageIds)),d=i.wakeReason!==void 0&&i.wakeReason!=="runtime-restart-abort"?{type:"runtime",kind:"wake",wake:i.wakeReason}:{type:"runtime",kind:"revive",cycle:r.cycles??0},await j(e)}c.env={...c.env,CRTR_SUBTREE:await Ke(e)},d!==null&&(c.env[ge]=ke(d)),r.status==="done"&&r.last_settled_at!=null&&await Ue(e,r.last_settled_at),await N(e,r.intent==="refresh"?"refresh-launch":"revive"),await Le(e,{preserveEpisode:!0});const D=await Te(e);i.recovery!==!0&&i.viewerAttach!==!0&&await de(e),await qe(e,r.grantee);const I=Ze(r.cwd,r.profile_id).plans.get("node:start")??[];await Ie(I,{node:{id:e,name:r.name,kind:r.kind,mode:r.mode,lifecycle:r.lifecycle,cwd:r.cwd,nodeDir:await Qe(e),contextDir:await Ye(e),jobDir:Xe(e)},runtime:{isBirth:Z,canvasHome:Ve(),profile:r.profile_id??null}}),c.env={...c.env,CRTR_EXECUTION_ID:f};const T=await Re(e,c,r.cwd);await Pe(f,T);let k;try{const n=await Ae(e);if(k=await q.start({executionId:f,nodeId:e,attempt:n?.attempt??1,sandboxName:n?.sandboxName??null,release:n?.release??"",launchJson:T,cwd:r.cwd,resuming:m}),t===null&&k.pid===null)throw new Error(`broker host returned no pid for ${e}`)}catch(n){try{await N(e,"crash",{reason:"launch_failed",outcome:{detail:{schema:"crtr.node-outcome-detail/v1",message:n instanceof Error?n.message:String(n)}}}),await he(e,"notice.crash",`Child crashed \u2014 ${fe(r)} (${e}) failed to relaunch and is now dead: ${n.message}
1
+ var ie=Object.defineProperty;var l=(e,r)=>ie(e,"name",{value:r,configurable:!0});import{assertGranteeRunsHere as te}from"./unsandboxed.js";import{existsSync as H}from"node:fs";import{conversationIdFromPath as v,conversationStore as p,ConversationNotFoundError as M}from"../conversation-store/index.js";import{isAbsolute as ne}from"node:path";import{findNodeBySessionFile as ae,getNode as S,updateNode as $,beginExecution as oe,ExecutionAlreadyLive as se,recordExecutionPid as le,abortExecution as N,endLiveExecution as ce,unbindSession as U,recordLaunch as ue,setFrozen as fe,clearFrozen as z,fullName as we}from"../canvas/index.js";import{emitEvent as K}from"../events/emit.js";import{consumeDeadlineOnWake as de}from"../feed/messages.js";import{isParked as G,transition as C}from"./lifecycle.js";import{publishNotice as he}from"./close.js";import{InputError as F}from"../io.js";import{buildPiArgv as _}from"./launch.js";import{buildReviveKickoff as pe,drainBearings as me,hasRetainedReports as ve,readGoal as g,readYieldMessage as _e,RUNTIME_RESTART_CONTINUATION as ye}from"./kickoff.js";import{readRoadmap as J}from"./roadmap.js";import{encodeKickoffOrigin as ke,KICKOFF_ORIGIN_ENV as ge}from"./stamp/protocol.js";import{headlessBrokerHost as be,brokerLaunchJson as Re}from"./host.js";import{processRunner as xe}from"./runner/process.js";import{runnerForEnvironment as Ee}from"./runner/index.js";import{blaxelEnvironmentOf as W}from"./blaxel-environment.js";import{recordLaunchFailure as Se}from"../../daemon/reconcilers/node-lifecycle/launch-failure.js";import{acceptedRelease as $e,checkBilledWake as Ne,EnvironmentImagePending as Ce,imageForWake as Fe}from"../../daemon/reconcilers/node-lifecycle/remote-wake.js";import{ExecutionCapReached as Ae,liveExecutionForNode as Pe,setExecutionLaunchJson as Be}from"../canvas/executions.js";import{appMemoryFull as Oe,appMemoryTooSmall as De,boundFleet as j,brokerCapReached as V,brokerThresholdsForDaemon as A}from"./fleet.js";import{admitBrokerMemory as Te}from"./memory-admission.js";import{beginBootFaultAttempt as Le,clearFault as He,hasAdmittedTurn as Me}from"../canvas/recovery.js";import{clearNodeLedger as X,forkNodeLedger as Ue,noticeChangedWhileDone as ze}from"./ledger.js";import{forkSourceNode as Ke,rootOfSpine as Ge}from"./nodes.js";import{checkStart as Je}from"./start-check.js";import{isReviewCompanionBound as We}from"../review/companion.js";import{assertProfileActive as je}from"../profiles/manifest.js";import{isPidAlive as Ve}from"../canvas/pid.js";import{crtrHome as Xe,jobDir as Ye}from"../canvas/paths.js";import{contextDir as qe,ensureNodeDirs as Qe,nodeAgentDir as Ze}from"../canvas/node-agent-paths.js";import{discoverLifecycleHookRegistry as Ie}from"../command-hooks/discovery.js";import{invokeLifecycleHooks as er}from"../command-hooks/transport/exec-lifecycle.js";async function rr(e,r){const i=e.pi_session_file,t=i?v(i):null;if(t===null)return{};try{const a=await p().header(t);return r?{resumeSessionPath:a.path}:{resumeSessionPath:a.path,newCycle:!0}}catch(a){if(a instanceof M)return{};throw a}}l(rr,"resumeArgs");async function Y(e){const r=v(e);if(r===null)return!1;try{return await p().header(r),!0}catch(i){if(i instanceof M)return!1;throw i}}l(Y,"conversationExists");async function ir(e){const r=v(e);return r===null?!0:(await p().read(r)).entries.some(i=>i.type==="message")}l(ir,"sessionHasConversation");async function tr(e){const r=v(e);if(r===null)return!0;const i=(await p().header(r)).header.cwd;return typeof i!="string"||H(i)}l(tr,"sessionCwdExists");async function nr(e,r,i,t){const a=r.pi_session_file;if(!a||!await Y(a))return{};const f=await ae(a);return f!==null&&f.node_id!==e&&Ve(f.pi_pid)?{}:t&&!await tr(a)?{}:await rr(r,i)}l(nr,"resumableSessionArgs");async function ar(e){return await J(e)===null&&await _e(e)===null&&!await ve(e)}l(ar,"isUnstartedBirth");async function Gr(e){const r=await g(e);if(r!==null&&r.trim()!=="")return!0;const i=await J(e);return i!==null&&i.trim()!==""}l(Gr,"hasFreshGroundState");async function Jr(e,r){const i=await q(e,r,!0);if(i.parked)throw new Error(`reviveNode: internal parked decision for activating revive ${e}`);return i.result}l(Jr,"reviveNode");async function Wr(e,r,i){return q(e,{...r,viewerAttach:!0},i)}l(Wr,"decideAttachRevive");async function q(e,r,i){let t=await S(e);if(t===null)throw new Error(`reviveNode: unknown node ${e}`);if(t=await S(e)??t,t.run_id&&(await S(t.run_id))?.deleting_at)throw new Error("run is deleting");if(t.final_report!==null)throw new Error(`reviveNode: refusing to revive ${e} \u2014 its finalization latch is set (final_report=${t.final_report}). A final landed between the caller's reopen-gate check and this revive; re-run with --reopen if this retask is still wanted.`);je(t.profile_id),te(t.grantee);const a=j();if(!await a.has(e)&&!await a.isClaimed(e)&&r.wakeReason!=="runtime-restart-abort"&&r.viewerAttach!==!0&&!(!i&&t.lifecycle==="resident"&&G(t))&&(await Je(t),await W(t.profile_id??null)!==null&&await Ne(t)),await a.has(e))return await C(e,t.intent==="refresh"?"refresh-launch":"revive"),{parked:!1,result:{window:null,session:t.tmux_session??null,resumed:!1,outcome:"already-live"}};if(await a.isClaimed(e))return{parked:!1,result:{window:null,session:t.tmux_session??null,resumed:!1,outcome:"already-live"}};if(!i&&t.lifecycle==="resident"&&G(t))return{parked:!0};const f=await a.reserveSlot(e);let o=null;if(f){try{o=await Te(e)}catch(w){throw a.releaseReservation(e),w}o.verdict!=="admitted"&&a.releaseReservation(e)}if(o?.verdict==="too_small")throw De(o);if(!f||o?.verdict==="denied"){if(r.capacity==="refuse")throw o?.verdict==="denied"?Oe(o):V(A().automaticReviveCap,await a.occupancy());return t.frozen_at==null&&(await fe(e,new Date().toISOString()),await ce(e,{reason:"frozen"}),K({level:"info",event:"broker.frozen",node_id:e,fields:o?.verdict==="denied"?{reason:"app_memory",free_bytes:o.free_bytes,needed_bytes:o.needed_bytes}:{reason:"broker_cap",live_count:await a.occupancy(),hard_cap:A().automaticReviveCap}})),{parked:!1,result:{window:null,session:t.tmux_session??null,resumed:!1,outcome:"frozen"}}}try{return{parked:!1,result:await or(e,t,r)}}finally{a.releaseReservation(e)}}l(q,"decideRevive");async function or(e,r,i){const t=await W(r.profile_id??null);if(t===null&&!H(r.cwd)){const s=r.fork_from!=null?`Restore ${r.cwd}, then revive the fork normally with \`crtr node lifecycle revive ${e}\`.`:r.managed_worktree?.state==="open"?"Restore or reconcile the recorded checkout, then use the managed-worktree close or abandon recovery.":`Recreate ${r.cwd}, or repair it with \`crtr node config --cwd <path> --node ${e}\`; durable node work resumes in a fresh conversation. Then retry \`crtr node lifecycle revive ${e}\`.`;throw new F({error:"cwd_missing",message:`${e} cannot be revived \u2014 its recorded cwd no longer exists: ${r.cwd}`,next:s})}r.host_kind!=="broker"&&(await $(e,{host_kind:"broker"}),r.host_kind="broker"),r.review_binding!=null&&!await We(e)&&(r.pi_session_id!=null||r.pi_session_file!=null)&&(await U(e),r.pi_session_id=null,r.pi_session_file=null);const f=r.fork_from!=null&&r.pi_session_id==null,o=r.fork_source_file;if(f&&(typeof o!="string"||!ne(o)))throw new F({error:"fork_source_invalid",message:`${e} cannot be revived \u2014 its resolved fork source file is missing or invalid.`,next:`Restore the node's fork source metadata, then retry \`crtr node lifecycle revive ${e}\`.`});const w=f&&typeof o=="string";if(w&&!await Y(o))throw new F({error:"fork_from_missing",message:`${e} cannot be revived \u2014 its fork source no longer exists: ${o}`,next:`Restore ${o}, or update the node's fork source, then retry \`crtr node lifecycle revive ${e}\`.`});const{automaticReviveCap:Q}=A();if(t!==null){const s=await Fe(t);if(s.state==="pending")throw new Ce(t,s.error)}const Z=t===null?xe:Ee("blaxel");let u;try{u=await oe(e,{daemonEpoch:j().epoch(),cap:Q,...t===null?{}:{runner:"blaxel",release:$e()}})}catch(s){if(s instanceof Ae)throw V(s.cap,s.live);if(s instanceof se)return{window:null,session:r.tmux_session??null,resumed:!1,outcome:"already-live"};throw s}try{r.cycles=(r.cycles??0)+1,await $(e,{cycles:r.cycles});const b=r.intent==="refresh"||r.cycle_pending===!0?!1:i.resume,h=w?{}:await nr(e,r,b,t===null),y=h.resumeSessionPath!==void 0,P=y&&h.newCycle===!0,m=y&&!P,B=!y&&(i.wakeReason===void 0||i.wakeReason==="runtime-restart-abort")&&await ar(e),R=m&&(i.wakeReason===void 0||i.wakeReason==="runtime-restart-abort")&&!await Me(e)&&!await ir(h.resumeSessionPath)?(await g(e))?.trim():void 0,O=R!==void 0&&R!=="",I=w||B||O,D=r.cycle_pending===!0||P;await $(e,{cycle_pending:D}),r.cycle_pending=D,!w&&!y&&(r.pi_session_id!=null||r.pi_session_file!=null)&&(await U(e),r.pi_session_id=null,r.pi_session_file=null);let c,d=null;const x=l(()=>({type:"runtime",kind:"kickoff",...r.spawned_by!=null&&r.spawned_by!==""?{spawnedBy:r.spawned_by}:{}}),"birthOrigin");if(w){const n=await g(e),E=v(o);if(E===null)throw new Error(`invalid fork source ${o}`);const re=(await p().header(await p().fork(E,e))).path;c=_(r,{resumeSessionPath:re,...n!=null&&n!==""?{prompt:n}:{}}),n!=null&&n!==""&&(d=x()),await Ue(e,(await Ke(o))?.node_id??null)}else if(O)c=_(r,{...h,prompt:R}),d=x();else if(m)c=_(r,{...h,...i.wakeReason==="runtime-restart-abort"?{prompt:ye}:{}}),i.wakeReason==="runtime-restart-abort"&&(d={type:"runtime",kind:"restart-continuation"});else if(B){const n=(await g(e))?.trim();c=_(r,n!==void 0&&n!==""?{prompt:n}:{}),n!==void 0&&n!==""&&(d=x()),await X(e)}else{const n=await me(r,u);c=_(r,{...h,prompt:await pe(r,n,i.wakeReason==="runtime-restart-abort"?void 0:i.wakeReason)}),r.run_id&&n.unreadMessageIds?.length&&(c.env.CRTR_KICKOFF_MESSAGE_IDS=JSON.stringify(n.unreadMessageIds)),d=i.wakeReason!==void 0&&i.wakeReason!=="runtime-restart-abort"?{type:"runtime",kind:"wake",wake:i.wakeReason}:{type:"runtime",kind:"revive",cycle:r.cycles??0},await X(e)}c.env={...c.env,CRTR_SUBTREE:await Ge(e)},d!==null&&(c.env[ge]=ke(d)),r.status==="done"&&r.last_settled_at!=null&&await ze(e,r.last_settled_at),await C(e,r.intent==="refresh"?"refresh-launch":"revive"),await He(e,{preserveEpisode:!0});const T=await Le(e);i.recovery!==!0&&i.viewerAttach!==!0&&await de(e),await Qe(e,r.grantee);const ee=Ie(r.cwd,r.profile_id).plans.get("node:start")??[];await er(ee,{node:{id:e,name:r.name,kind:r.kind,mode:r.mode,lifecycle:r.lifecycle,cwd:r.cwd,nodeDir:await Ze(e),contextDir:await qe(e),jobDir:Ye(e)},runtime:{isBirth:I,canvasHome:Xe(),profile:r.profile_id??null}}),c.env={...c.env,CRTR_EXECUTION_ID:u};const L=await Re(e,c,r.cwd);await Be(u,L);let k;try{const n=await Pe(e);if(k=await Z.start({executionId:u,nodeId:e,attempt:n?.attempt??1,sandboxName:n?.sandboxName??null,release:n?.release??"",launchJson:L,cwd:r.cwd,resuming:m}),t===null&&k.pid===null)throw new Error(`broker host returned no pid for ${e}`)}catch(n){try{await C(e,"crash",{reason:"launch_failed",outcome:{detail:{schema:"crtr.node-outcome-detail/v1",message:n instanceof Error?n.message:String(n)}}}),await he(e,"notice.crash",`Child crashed \u2014 ${we(r)} (${e}) failed to relaunch and is now dead: ${n.message}
2
2
 
3
- It stays dead until you revive it \u2014 \`crtr node lifecycle revive ${e}\`.`,{reason:"child-crashed",child:e})}catch{}throw await $(f,{reason:"launch_failed"}),new Error(`[broker] refusing to revive ${e}: ${n.message}`)}if(await ce(e,new Date().toISOString()),t!==null)return await U(e),{window:null,session:r.tmux_session??null,resumed:m,faultExplanation:D.explanation,outcome:"launched"};if(await se(f,e,k.pid)==="shutdown")throw await $(f,{reason:"superseded"}),be.teardown(e),new Error(`[broker] refusing to revive ${e}: execution was superseded during launch`);return await U(e),{window:null,session:r.tmux_session??null,resumed:m,launch:k,faultExplanation:D.explanation,outcome:"launched"}}catch(s){throw await $(f,{reason:"launch_failed"}),s}}l(ar,"launchRevive");export{Kr as decideAttachRevive,Ur as hasFreshGroundState,er as resumeArgs,zr as reviveNode};
3
+ It stays dead until you revive it \u2014 \`crtr node lifecycle revive ${e}\`.`,{reason:"child-crashed",child:e})}catch{}throw await N(u,{reason:"launch_failed"}),new Error(`[broker] refusing to revive ${e}: ${n.message}`)}if(await ue(e,new Date().toISOString()),t!==null)return await z(e),{window:null,session:r.tmux_session??null,resumed:m,faultExplanation:T.explanation,outcome:"launched"};if(await le(u,e,k.pid)==="shutdown")throw await N(u,{reason:"superseded"}),be.teardown(e),new Error(`[broker] refusing to revive ${e}: execution was superseded during launch`);return await z(e),{window:null,session:r.tmux_session??null,resumed:m,launch:k,faultExplanation:T.explanation,outcome:"launched"}}catch(s){throw await N(u,{reason:"launch_failed"}),await Se(u,s).catch(b=>{K({level:"warn",event:"broker.launch_failure.unrecorded",node_id:e,error:b})}),s}}l(or,"launchRevive");export{Wr as decideAttachRevive,Gr as hasFreshGroundState,rr as resumeArgs,Jr as reviveNode};