@north-light/cli 0.3.533 → 0.3.535

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 (52) hide show
  1. package/dist/api/dto/crons.d.ts +1 -1
  2. package/dist/api/dto/nodes.d.ts +2 -0
  3. package/dist/builtin-memory/crouter-sdk/release-notes.md +6 -1
  4. package/dist/builtin-memory/northlight-apps/providers/README.md +1 -1
  5. package/dist/builtin-memory/northlight-apps/providers/sms.md +1 -1
  6. package/dist/clients/attach/viewer.js +2 -2
  7. package/dist/commands/api-client.d.ts +1 -0
  8. package/dist/commands/api-client.js +1 -1
  9. package/dist/commands/canvas.js +1 -1
  10. package/dist/commands/node/inspect.js +9 -9
  11. package/dist/core/bash-jobs.d.ts +4 -0
  12. package/dist/core/bash-jobs.js +8 -8
  13. package/dist/core/canvas/canvas.d.ts +16 -2
  14. package/dist/core/canvas/canvas.js +20 -20
  15. package/dist/core/canvas/recovery.js +1 -1
  16. package/dist/core/canvas/remote-canvas-source.d.ts +1 -0
  17. package/dist/core/canvas/remote-canvas-source.js +1 -1
  18. package/dist/core/canvas/source.d.ts +4 -0
  19. package/dist/core/canvas/source.js +1 -1
  20. package/dist/core/canvas/tree.d.ts +2 -0
  21. package/dist/core/canvas/tree.js +12 -5
  22. package/dist/core/graph/documents.js +7 -7
  23. package/dist/core/graph/watches.d.ts +0 -2
  24. package/dist/core/graph/watches.js +7 -12
  25. package/dist/core/runs/questions.d.ts +2 -0
  26. package/dist/core/runs/questions.js +1 -1
  27. package/dist/core/runtime/broker/fault-retry.d.ts +6 -0
  28. package/dist/core/runtime/broker/fault-retry.js +1 -1
  29. package/dist/core/runtime/launch.js +2 -2
  30. package/dist/core/worktree.js +8 -8
  31. package/dist/daemon/api/handlers/crons.js +1 -1
  32. package/dist/daemon/api/handlers/docs.js +1 -1
  33. package/dist/daemon/api/handlers/nodes.js +1 -1
  34. package/dist/daemon/api/handlers/objects.js +3 -3
  35. package/dist/daemon/api/handlers/questions.js +1 -1
  36. package/dist/daemon/cron/ended.d.ts +3 -0
  37. package/dist/daemon/cron/ended.js +2 -2
  38. package/dist/daemon/cron/narrowed.d.ts +5 -0
  39. package/dist/daemon/cron/narrowed.js +2 -0
  40. package/dist/daemon/cron/settle.js +1 -1
  41. package/dist/daemon/reconcilers/bash-deadline.d.ts +6 -0
  42. package/dist/daemon/reconcilers/bash-deadline.js +3 -2
  43. package/dist/daemon/runs/observer.d.ts +2 -0
  44. package/dist/daemon/runs/observer.js +2 -2
  45. package/docs/apps/providers/README.md +1 -1
  46. package/docs/apps/providers/sms.md +1 -1
  47. package/docs/sdk/release-notes.md +6 -1
  48. package/package.json +2 -2
  49. package/packages/crouter-identity/package.json +1 -1
  50. package/runtime.lock.json +12 -12
  51. package/dist/core/canvas/nav-model.d.ts +0 -44
  52. package/dist/core/canvas/nav-model.js +0 -1
@@ -1,10 +1,10 @@
1
- var oe=Object.defineProperty;var s=(e,n)=>oe(e,"name",{value:n,configurable:!0});import{randomUUID as se}from"node:crypto";import{CrtrError as O,notFound as v,usage as p}from"../errors.js";import{ExitCode as D}from"../../types.js";import{canChange as L,canRead as P,missingMemoryScope as ue,missingMemoryScopeUnder as ce,ownerChain as we}from"./access.js";import{bodyOf as pe,get as le,put as B}from"./bodies.js";import{dropUnresolved as me,linksOf as de,recordActedOn as N,setLinks as G,setUnresolved as fe,unresolvedLinksCouldName as ye}from"./edges.js";import{publish as k}from"./events.js";import{resolveName as he}from"./names.js";import{createObject as Ee,getById as m}from"./objects.js";import{liveLookups as _e,readerForNode as ve,readerForObject as Ne}from"./reader.js";import{replaceRules as K,setRulesOwner as Re}from"./rules.js";import{recordWatch as I}from"./watches.js";import{withCanvasWrite as y}from"../canvas/db.js";import{filterAsync as q,forEachAsync as ge,mapAsync as R}from"../async-array.js";function E(e){return e.class==="node"?e.node:e.class==="app"?e.app:e.class==="object"?e.object:e.person}s(E,"actorOf");function Se(e,n){return new O("conflict",e,D.GENERAL,n)}s(Se,"conflict");function h(e){if(e.type!=="document")throw p(`${e.type} ${e.id} is not a document`)}s(h,"assertDocument");async function Y(e,n){const t=await we(e,n);return(t.length?t[t.length-1]:n).type}s(Y,"spaceType");async function $(e,n,t){if(t!=="public"&&await Y(e,n)==="repo")throw p(`a repo owns only public documents; ${n.name} cannot own a ${t} one`,{rule:"repo-public-only",next:"Write it as public, or give it another owner (your own documents can carry `if: {repo: <handle>}`)."})}s($,"assertPublicInRepo");async function H(e,n,t,a){if(t.trim()===""||t!==t.trim())throw p(`invalid document name ${JSON.stringify(t)}`);const i=await e.prepare("SELECT id FROM objects WHERE (type = 'document' OR strpos(type, '.') > 0) AND owner = ? AND name = ? AND deleted_at IS NULL AND home_machine IS NULL").get(n.id,t);if(i&&i.id!==a?.id)throw Se(`${n.name} already has a document named ${t}`,{name:t,holder:i.id})}s(H,"assertNameFree");async function U(e,n,t){if(!await P(e,n,t))throw v(`no document ${t.id}`)}s(U,"readable");function V(e){return new O("scope_missing",`missing scope ${e}`,D.GENERAL,{scope:e})}s(V,"scopeMissing");async function x(e,n,t){await U(e,n,t);const a=await ue(e,n,t,"rw");if(a!==null)throw V(a);if(!await L(e,n,t))throw new O("forbidden",`you cannot change ${t.name}`,D.GENERAL,{id:t.id})}s(x,"changeable");async function X(e,n){const t=await e.prepare(`SELECT p.rev_id FROM revision_parents rp JOIN revisions p ON p.rev_id = rp.parent
2
- WHERE rp.child = ? ORDER BY p.rev_id`).all(n.rev_id);return{rev_id:n.rev_id,seq:n.seq,number:n.number,body_hash:n.body_hash,author:n.author,rationale:n.rationale,verbatim:n.verbatim===1,created_at:n.created_at,parents:t.map(a=>a.rev_id)}}s(X,"toRevision");async function C(e,n){const t=await e.prepare(`SELECT * FROM revisions r WHERE r.document = ?
3
- AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id) ORDER BY r.number DESC, r.rev_id`).all(n.id);return await R(t,async a=>await X(e,a))}s(C,"heads");function Oe(e){return e.map(n=>n.rev_id).sort().join(",")}s(Oe,"headsKeyOf");async function T(e,n){const t=await e.prepare("SELECT * FROM documents WHERE object = ?").get(n.id);if(!t)throw v(`no document record for ${n.id}`);return{summary:t.summary,heads_key:t.heads_key,revision:t.revision,why_it_exists:t.why_it_exists,unlisted:t.unlisted===1,lint_ignore:t.lint_ignore_json===null?null:JSON.parse(t.lint_ignore_json),extensions:t.extensions_json===null?null:JSON.parse(t.extensions_json),source_path:t.source_path}}s(T,"documentRecord");async function j(e,n,t){const a=await e.prepare(`INSERT INTO revisions (document, rev_id, number, body_hash, author, rationale, verbatim, created_at)
4
- VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(n.id,t.rev_id??se(),t.number,t.body_hash,t.author?.id??null,t.rationale,t.verbatim?1:0,t.created_at??new Date().toISOString()),i=e.prepare("INSERT INTO revision_parents (child, parent) VALUES (?, ?)");for(const r of t.parents)await i.run(a.rev_id,r);return a}s(j,"insertRevision");function De(e){const n=new Set;for(const t of e.matchAll(/\[\[([^[\]\n]+?)\]\]/g))n.add(t[1].trim());return[...n]}s(De,"linksIn");async function z(e,n,t){const a=[],i=[];for(const r of t){const o=await he(e,n,r);"object"in o?a.push({written_as:r,dst:o.object}):"deleted"in o?a.push({written_as:r,dst:o.deleted}):"remote"in o||i.push(r)}return{links:a,unresolved:i}}s(z,"resolveLinks");async function Q(e,n,t,a){const{links:i,unresolved:r}=await z(e,n,De(a));return await ae(e,await G(e,t,i)),await fe(e,t,r),r}s(Q,"setRevisionLinks");async function W(e,n,t){const a=new Map;for(const{src:i,name:r}of await ye(e,n.name,n.id))a.set(i,[...a.get(i)??[],r]);for(const[i,r]of a){const o=await m(e,i),u=o?.owner==null?null:await m(e,o.owner);if(!o||o.deleted_at||o.home_machine!==null||!u||u.deleted_at)continue;const c=u.type==="node"?await ve(e,u.id,t??(async()=>[]),_e(e),!0):await Ne(e,o,t),w=(await z(e,c,r)).links.filter(l=>l.dst.id===n.id);w.length!==0&&(await G(e,o,[...await de(e,o),...w]),await me(e,o,w.map(l=>l.written_as)))}}s(W,"linkNewlyNamed");async function ke(e,n,t){return n.type==="node"?n.id:await e.prepare(`SELECT 1 FROM objects o JOIN objects d ON d.owner = o.id
1
+ var oe=Object.defineProperty;var s=(t,n)=>oe(t,"name",{value:n,configurable:!0});import{randomUUID as se}from"node:crypto";import{CrtrError as v,notFound as N,usage as p}from"../errors.js";import{ExitCode as R}from"../../types.js";import{canChange as j,canRead as P,missingMemoryScope as ce,missingMemoryScopeUnder as ue,ownerChain as we}from"./access.js";import{bodyOf as pe,get as le,put as B}from"./bodies.js";import{dropUnresolved as me,linksOf as de,recordActedOn as g,setLinks as G,setUnresolved as fe,unresolvedLinksCouldName as he}from"./edges.js";import{publish as x}from"./events.js";import{resolveName as ye}from"./names.js";import{createObject as Ee,getById as m}from"./objects.js";import{liveLookups as _e,readerForNode as ve,readerForObject as Ne}from"./reader.js";import{replaceRules as K,setRulesOwner as Re}from"./rules.js";import{recordWatch as I}from"./watches.js";import{withCanvasWrite as h}from"../canvas/db.js";import{filterAsync as q,forEachAsync as ge,mapAsync as S}from"../async-array.js";function E(t){return t.class==="node"?t.node:t.class==="app"?t.app:t.class==="object"?t.object:t.person}s(E,"actorOf");function Se(t,n){return new v("conflict",t,R.GENERAL,n)}s(Se,"conflict");function y(t){if(t.type!=="document")throw p(`${t.type} ${t.id} is not a document`)}s(y,"assertDocument");async function Y(t,n){const e=await we(t,n);return(e.length?e[e.length-1]:n).type}s(Y,"spaceType");async function $(t,n,e){if(e!=="public"&&await Y(t,n)==="repo")throw p(`a repo owns only public documents; ${n.name} cannot own a ${e} one`,{rule:"repo-public-only",next:"Write it as public, or give it another owner (your own documents can carry `if: {repo: <handle>}`)."})}s($,"assertPublicInRepo");async function H(t,n,e,i){if(e.trim()===""||e!==e.trim())throw p(`invalid document name ${JSON.stringify(e)}`);const a=await t.prepare("SELECT id FROM objects WHERE (type = 'document' OR strpos(type, '.') > 0) AND owner = ? AND name = ? AND deleted_at IS NULL AND home_machine IS NULL").get(n.id,e);if(a&&a.id!==i?.id)throw Se(`${n.name} already has a document named ${e}`,{name:e,holder:a.id})}s(H,"assertNameFree");async function U(t,n,e){if(!await P(t,n,e))throw N(`no document ${e.id}`)}s(U,"readable");function V(t){return new v("scope_missing",`missing scope ${t}`,R.GENERAL,{scope:t})}s(V,"scopeMissing");async function T(t,n,e){await U(t,n,e);const{source_path:i}=await O(t,e);if(i!==null)throw new v("forbidden",`${e.name} is a built-in document shipped by a package (served from ${i}); \`crtr doc\` cannot change it`,R.GENERAL,{id:e.id,next:"Edit it in the source repository of the package that ships it (for crtr's own documents, the crouter repo), not with `crtr doc`."});const a=await ce(t,n,e,"rw");if(a!==null)throw V(a);if(!await j(t,n,e))throw new v("forbidden",`you cannot change ${e.name}`,R.GENERAL,{id:e.id})}s(T,"changeable");async function X(t,n){const e=await t.prepare(`SELECT p.rev_id FROM revision_parents rp JOIN revisions p ON p.rev_id = rp.parent
2
+ WHERE rp.child = ? ORDER BY p.rev_id`).all(n.rev_id);return{rev_id:n.rev_id,seq:n.seq,number:n.number,body_hash:n.body_hash,author:n.author,rationale:n.rationale,verbatim:n.verbatim===1,created_at:n.created_at,parents:e.map(i=>i.rev_id)}}s(X,"toRevision");async function C(t,n){const e=await t.prepare(`SELECT * FROM revisions r WHERE r.document = ?
3
+ AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rev_id) ORDER BY r.number DESC, r.rev_id`).all(n.id);return await S(e,async i=>await X(t,i))}s(C,"heads");function Oe(t){return t.map(n=>n.rev_id).sort().join(",")}s(Oe,"headsKeyOf");async function O(t,n){const e=await t.prepare("SELECT * FROM documents WHERE object = ?").get(n.id);if(!e)throw N(`no document record for ${n.id}`);return{summary:e.summary,heads_key:e.heads_key,revision:e.revision,why_it_exists:e.why_it_exists,unlisted:e.unlisted===1,lint_ignore:e.lint_ignore_json===null?null:JSON.parse(e.lint_ignore_json),extensions:e.extensions_json===null?null:JSON.parse(e.extensions_json),source_path:e.source_path}}s(O,"documentRecord");async function A(t,n,e){const i=await t.prepare(`INSERT INTO revisions (document, rev_id, number, body_hash, author, rationale, verbatim, created_at)
4
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(n.id,e.rev_id??se(),e.number,e.body_hash,e.author?.id??null,e.rationale,e.verbatim?1:0,e.created_at??new Date().toISOString()),a=t.prepare("INSERT INTO revision_parents (child, parent) VALUES (?, ?)");for(const r of e.parents)await a.run(i.rev_id,r);return i}s(A,"insertRevision");function De(t){const n=new Set;for(const e of t.matchAll(/\[\[([^[\]\n]+?)\]\]/g))n.add(e[1].trim());return[...n]}s(De,"linksIn");async function z(t,n,e){const i=[],a=[];for(const r of e){const o=await ye(t,n,r);"object"in o?i.push({written_as:r,dst:o.object}):"deleted"in o?i.push({written_as:r,dst:o.deleted}):"remote"in o||a.push(r)}return{links:i,unresolved:a}}s(z,"resolveLinks");async function Q(t,n,e,i){const{links:a,unresolved:r}=await z(t,n,De(i));return await ae(t,await G(t,e,a)),await fe(t,e,r),r}s(Q,"setRevisionLinks");async function W(t,n,e){const i=new Map;for(const{src:a,name:r}of await he(t,n.name,n.id))i.set(a,[...i.get(a)??[],r]);for(const[a,r]of i){const o=await m(t,a),c=o?.owner==null?null:await m(t,o.owner);if(!o||o.deleted_at||o.home_machine!==null||!c||c.deleted_at)continue;const u=c.type==="node"?await ve(t,c.id,e??(async()=>[]),_e(t),!0):await Ne(t,o,e),w=(await z(t,u,r)).links.filter(l=>l.dst.id===n.id);w.length!==0&&(await G(t,o,[...await de(t,o),...w]),await me(t,o,w.map(l=>l.written_as)))}}s(W,"linkNewlyNamed");async function ke(t,n,e){return n.type==="node"?n.id:await t.prepare(`SELECT 1 FROM objects o JOIN objects d ON d.owner = o.id
5
5
  WHERE o.name = ? AND o.type <> ? AND o.type IN ('person','app','repo','profile')
6
- AND d.type = 'document' AND d.name = ? AND d.deleted_at IS NULL LIMIT 1`).get(n.name,n.type,t)?`${n.type}:${n.name}`:n.name}s(ke,"ownerPrefix");async function Z(e,n,t){const a=await e.prepare(`SELECT e.written_as, o.* FROM edges e JOIN objects o ON o.id = e.dst
7
- WHERE e.type = 'links-to' AND e.src = ? AND e.written_as IS NOT NULL`).all(n.id);if(a.length===0)return t;const i=new Map;for(const r of a){if(r.deleted_at||r.written_as===r.id)continue;const o=!r.written_as.includes("/");if(o&&r.name===r.written_as)continue;const u=r.owner===null?null:await m(e,r.owner),c=u?`${await ke(e,u,r.name)}/${r.name}`:r.name,w=o&&r.owner===n.owner?r.name:c;w!==r.written_as&&i.set(r.written_as,w)}return i.size===0?t:t.replace(/\[\[([^[\]\n]+?)\]\]/g,(r,o)=>{const u=i.get(o.trim());return u===void 0?r:`[[${u}]]`})}s(Z,"withCurrentNames");async function M(e,n,t){if(t.preview!==void 0){if(t.preview.trim()==="")throw p("a document needs a preview",{field:"preview"});await e.prepare("UPDATE objects SET preview = ? WHERE id = ?").run(t.preview,n.id)}t.privacy!==void 0&&(await $(e,await m(e,n.owner),t.privacy),await e.prepare("UPDATE objects SET privacy = ? WHERE id = ?").run(t.privacy,n.id));const a=s(async(r,o)=>await e.prepareUncached(`UPDATE documents SET ${r} = ? WHERE object = ?`).run(o,n.id),"set");t.summary!==void 0&&await a("summary",t.summary),t.whyItExists!==void 0&&await a("why_it_exists",t.whyItExists),t.unlisted!==void 0&&await a("unlisted",t.unlisted?1:0),t.lintIgnore!==void 0&&await a("lint_ignore_json",t.lintIgnore===null?null:JSON.stringify(t.lintIgnore)),t.extensions!==void 0&&await a("extensions_json",t.extensions===null?null:JSON.stringify(t.extensions));const i=await m(e,n.id);return t.rules!==void 0&&await K(e,i,t.rules),i}s(M,"applyDocumentFields");async function b(e,n,t){if(n.owner.deleted_at)throw p(`${n.owner.name} is deleted`);if(n.preview.trim()==="")throw p("a document needs a preview",{field:"preview"});await $(e,n.owner,n.privacy),await H(e,n.owner,n.name);const a=await Ee(e,{...n.id?{id:n.id}:{},type:"document",kind:n.kind,owner:n.owner.id,privacy:n.privacy,name:n.name,preview:n.preview});await e.prepare("INSERT INTO documents (object, heads_key, revision) VALUES (?, '', 0)").run(a.id);const i=await M(e,a,{...n,preview:void 0,privacy:void 0,rules:n.rules??[]});return await W(e,i,t),i}s(b,"createDocument");async function F(e,n){const t=await C(e,n),a=Oe(t),i=Math.max(0,...t.map(r=>r.number));return await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(a,i,n.id),{heads_key:a,revision:i,headCount:t.length}}s(F,"setHeads");async function Ke(e,n,t,a,i){const r=B(a);return await y(async()=>{const o=E(n),u=await ce(e,n,t.owner,"rw");if(u!==null)throw V(u);const c=await b(e,t,i),w=await j(e,c,{number:1,body_hash:r,author:o,rationale:null,parents:[]}),{heads_key:l}=await F(e,c);if(!await L(e,n,c))throw v(`no owner ${t.owner.name} you can write to`);const d=await Q(e,n,c,a);return await N(e,o,c),await I(e,o,c,"write",{readHeadsKey:l}),await k(e,{event:"created",object:c,actor:o,attrs:{revision:1},pointer:w.rev_id},i),{object:c,revision:1,unresolved:d}},{savepoint:!0})}s(Ke,"writeDocument");async function qe(e,n,t){return(await e.prepare("SELECT read_heads_key FROM watches WHERE watcher = ? AND object = ?").get(E(n).id,t.id))?.read_heads_key??null}s(qe,"readBase");function xe(e,n){let t=e;for(const[a,{old:i,new:r}]of n.entries()){if(i==="")throw p(`block ${a+1}: a replacement needs non-empty old text`,{field:"replace"});const o=t.indexOf(i);if(o<0)throw p(`block ${a+1}: old text not found: ${JSON.stringify(i.slice(0,120))}`,{field:"replace",old:i});if(t.indexOf(i,o+1)>=0)throw p(`block ${a+1}: old text occurs more than once: ${JSON.stringify(i.slice(0,120))}`,{field:"replace",old:i});t=t.slice(0,o)+r+t.slice(o+i.length)}return t}s(xe,"applyReplacements");async function Ye(e,n,t,a,i,r,o={},u){if(h(t),await x(e,n,t),t.deleted_at)throw p(`${t.name} is deleted`);if(r===null)throw new O("read_first",`read ${t.name} before editing it`,D.GENERAL,{id:t.id});if(i.trim()==="")throw p("an edit needs a rationale",{field:"rationale"});const c=await T(e,t);if(c.heads_key!==r)return{stale:{heads_key:c.heads_key,revision:c.revision}};const w=await C(e,t);let l;if("body"in a)l=a.body;else{if(w.length!==1)throw p(`${t.name} has ${w.length} heads and needs a merge; pass the whole merged body`,{heads:w.length});l=xe(await Z(e,t,le(w[0].body_hash)),a.replace)}const d=B(l);try{return await y(async()=>{const f=E(n),g=1+Math.max(0,...w.map(_=>_.number)),J=await j(e,t,{number:g,body_hash:d,author:f,rationale:i,verbatim:a.verbatim===!0,parents:w.map(_=>_.rev_id)}),A=J.rev_id,ie=await e.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ? AND heads_key = ?").run(A,g,t.id,r);if(Number(ie.changes)===0){const _=await T(e,t);throw new ee(_.heads_key,_.revision)}const S=await M(e,t,o),re=await Q(e,n,S,l);return await N(e,f,S),await I(e,f,S,"write",{readHeadsKey:A}),await k(e,{event:"edited",object:S,actor:f,pointer:J.rev_id,attrs:{revision:g,from_revision:c.revision,rationale:i}},u),{revision:g,heads_key:A,unresolved:re}},{savepoint:!0})}catch(f){if(f instanceof ee)return{stale:{heads_key:f.heads_key,revision:f.revision}};throw f}}s(Ye,"editDocument");class ee extends Error{static{s(this,"StaleEdit")}heads_key;revision;constructor(n,t){super("stale"),this.heads_key=n,this.revision=t}}async function Ve(e,n,t,a){return h(t),n&&await x(e,n,t),await y(async()=>{const i=await M(e,t,a);return n&&await N(e,E(n),i),i},{savepoint:!0})}s(Ve,"setDocumentRecord");async function Te(e,n,t,a){const i=E(n),{heads_key:r}=await T(e,t);await I(e,i,t,"read",{readHeadsKey:r}),await k(e,{event:"read",object:t,actor:i,attrs:{}},a)}s(Te,"recordRead");async function Xe(e,n,t,a){return h(t),await U(e,n,t),await y(async()=>{const i=await T(e,t),r=await R(await C(e,t),async u=>({...u,body:await Z(e,t,pe(u.body_hash,i.source_path))})),o=(await q(await R(await e.prepare("SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ? ORDER BY written_as").all(t.id),async u=>({written_as:u.written_as??"",object:await m(e,u.dst)})),async u=>u.object&&await P(e,n,u.object))).map(u=>({...u,deleted:u.object.deleted_at!==null}));return t.deleted_at||await Te(e,n,t,a),{object:t,record:i,heads:r,needsMerge:r.length>1,links:o}},{savepoint:!0})}s(Xe,"readDocument");async function ze(e,n,t){h(t),await U(e,n,t);const a=await e.prepare("SELECT * FROM revisions WHERE document = ? ORDER BY number DESC, created_at DESC, seq DESC").all(t.id);return await R(a,async i=>await X(e,i))}s(ze,"history");async function Qe(e,n,t,a,i){if(h(t),t.deleted_at)throw p(`${t.name} is deleted`);return n&&await x(e,n,t),await y(async()=>{const r=a.owner??await m(e,t.owner),o=a.name??t.name;await H(e,r,o,t),await $(e,r,t.privacy),await e.prepare("UPDATE objects SET name = ?, owner = ? WHERE id = ?").run(o,r.id,t.id);const u=await m(e,t.id);if(r.id!==t.owner&&(await Re(e,u),n&&!await L(e,n,u)))throw v(`no owner ${r.name} you can move documents to`);return n&&await N(e,E(n),u),(o!==t.name||r.id!==t.owner)&&await W(e,u,i),u},{savepoint:!0})}s(Qe,"moveDocument");async function Ze(e,n,t,a,i){return h(t),t.deleted_at?t:(n&&await x(e,n,t),await y(async()=>{const r=await te(e,n,t,a,i);return await ae(e,await ne(e,t.id),a),r},{savepoint:!0}))}s(Ze,"deleteDocument");async function be(e,n,t,a){return h(n),n.deleted_at?await y(async()=>{await H(e,await m(e,n.owner),t,n),await e.prepare("UPDATE objects SET deleted_at = NULL, name = ? WHERE id = ?").run(t,n.id);const i=await m(e,n.id);return await W(e,i,a),i},{savepoint:!0}):n}s(be,"restoreDocument");async function te(e,n,t,a,i){await e.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(i??new Date().toISOString(),t.id);const r=await m(e,t.id),o=n?E(n):null;return o&&await N(e,o,r),await k(e,{event:"deleted",object:r,actor:o,attrs:{}},a),await e.prepare("DELETE FROM watches WHERE object = ?").run(t.id),await K(e,r,[]),r}s(te,"markDeleted");async function ne(e,n){return(await e.prepare("SELECT dst FROM edges WHERE type = 'links-to' AND src = ?").all(n)).map(({dst:t})=>t)}s(ne,"linkTargets");async function ae(e,n,t){const a=e.prepare(`SELECT 1 FROM objects d JOIN objects w ON w.id = d.owner
6
+ AND d.type = 'document' AND d.name = ? AND d.deleted_at IS NULL LIMIT 1`).get(n.name,n.type,e)?`${n.type}:${n.name}`:n.name}s(ke,"ownerPrefix");async function Z(t,n,e){const i=await t.prepare(`SELECT e.written_as, o.* FROM edges e JOIN objects o ON o.id = e.dst
7
+ WHERE e.type = 'links-to' AND e.src = ? AND e.written_as IS NOT NULL`).all(n.id);if(i.length===0)return e;const a=new Map;for(const r of i){if(r.deleted_at||r.written_as===r.id)continue;const o=!r.written_as.includes("/");if(o&&r.name===r.written_as)continue;const c=r.owner===null?null:await m(t,r.owner),u=c?`${await ke(t,c,r.name)}/${r.name}`:r.name,w=o&&r.owner===n.owner?r.name:u;w!==r.written_as&&a.set(r.written_as,w)}return a.size===0?e:e.replace(/\[\[([^[\]\n]+?)\]\]/g,(r,o)=>{const c=a.get(o.trim());return c===void 0?r:`[[${c}]]`})}s(Z,"withCurrentNames");async function M(t,n,e){if(e.preview!==void 0){if(e.preview.trim()==="")throw p("a document needs a preview",{field:"preview"});await t.prepare("UPDATE objects SET preview = ? WHERE id = ?").run(e.preview,n.id)}e.privacy!==void 0&&(await $(t,await m(t,n.owner),e.privacy),await t.prepare("UPDATE objects SET privacy = ? WHERE id = ?").run(e.privacy,n.id));const i=s(async(r,o)=>await t.prepareUncached(`UPDATE documents SET ${r} = ? WHERE object = ?`).run(o,n.id),"set");e.summary!==void 0&&await i("summary",e.summary),e.whyItExists!==void 0&&await i("why_it_exists",e.whyItExists),e.unlisted!==void 0&&await i("unlisted",e.unlisted?1:0),e.lintIgnore!==void 0&&await i("lint_ignore_json",e.lintIgnore===null?null:JSON.stringify(e.lintIgnore)),e.extensions!==void 0&&await i("extensions_json",e.extensions===null?null:JSON.stringify(e.extensions));const a=await m(t,n.id);return e.rules!==void 0&&await K(t,a,e.rules),a}s(M,"applyDocumentFields");async function b(t,n,e){if(n.owner.deleted_at)throw p(`${n.owner.name} is deleted`);if(n.preview.trim()==="")throw p("a document needs a preview",{field:"preview"});await $(t,n.owner,n.privacy),await H(t,n.owner,n.name);const i=await Ee(t,{...n.id?{id:n.id}:{},type:"document",kind:n.kind,owner:n.owner.id,privacy:n.privacy,name:n.name,preview:n.preview});await t.prepare("INSERT INTO documents (object, heads_key, revision) VALUES (?, '', 0)").run(i.id);const a=await M(t,i,{...n,preview:void 0,privacy:void 0,rules:n.rules??[]});return await W(t,a,e),a}s(b,"createDocument");async function F(t,n){const e=await C(t,n),i=Oe(e),a=Math.max(0,...e.map(r=>r.number));return await t.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ?").run(i,a,n.id),{heads_key:i,revision:a,headCount:e.length}}s(F,"setHeads");async function Ke(t,n,e,i,a){const r=B(i);return await h(async()=>{const o=E(n),c=await ue(t,n,e.owner,"rw");if(c!==null)throw V(c);const u=await b(t,e,a),w=await A(t,u,{number:1,body_hash:r,author:o,rationale:null,parents:[]}),{heads_key:l}=await F(t,u);if(!await j(t,n,u))throw N(`no owner ${e.owner.name} you can write to`);const d=await Q(t,n,u,i);return await g(t,o,u),await I(t,o,u,"write",{readHeadsKey:l}),await x(t,{event:"created",object:u,actor:o,attrs:{revision:1},pointer:w.rev_id},a),{object:u,revision:1,unresolved:d}},{savepoint:!0})}s(Ke,"writeDocument");async function qe(t,n,e){return(await t.prepare("SELECT read_heads_key FROM watches WHERE watcher = ? AND object = ?").get(E(n).id,e.id))?.read_heads_key??null}s(qe,"readBase");function xe(t,n){let e=t;for(const[i,{old:a,new:r}]of n.entries()){if(a==="")throw p(`block ${i+1}: a replacement needs non-empty old text`,{field:"replace"});const o=e.indexOf(a);if(o<0)throw p(`block ${i+1}: old text not found: ${JSON.stringify(a.slice(0,120))}`,{field:"replace",old:a});if(e.indexOf(a,o+1)>=0)throw p(`block ${i+1}: old text occurs more than once: ${JSON.stringify(a.slice(0,120))}`,{field:"replace",old:a});e=e.slice(0,o)+r+e.slice(o+a.length)}return e}s(xe,"applyReplacements");async function Ye(t,n,e,i,a,r,o={},c){if(y(e),await T(t,n,e),e.deleted_at)throw p(`${e.name} is deleted`);if(r===null)throw new v("read_first",`read ${e.name} before editing it`,R.GENERAL,{id:e.id});if(a.trim()==="")throw p("an edit needs a rationale",{field:"rationale"});const u=await O(t,e);if(u.heads_key!==r)return{stale:{heads_key:u.heads_key,revision:u.revision}};const w=await C(t,e);let l;if("body"in i)l=i.body;else{if(w.length!==1)throw p(`${e.name} has ${w.length} heads and needs a merge; pass the whole merged body`,{heads:w.length});l=xe(await Z(t,e,le(w[0].body_hash)),i.replace)}const d=B(l);try{return await h(async()=>{const f=E(n),D=1+Math.max(0,...w.map(_=>_.number)),J=await A(t,e,{number:D,body_hash:d,author:f,rationale:a,verbatim:i.verbatim===!0,parents:w.map(_=>_.rev_id)}),L=J.rev_id,ie=await t.prepare("UPDATE documents SET heads_key = ?, revision = ? WHERE object = ? AND heads_key = ?").run(L,D,e.id,r);if(Number(ie.changes)===0){const _=await O(t,e);throw new ee(_.heads_key,_.revision)}const k=await M(t,e,o),re=await Q(t,n,k,l);return await g(t,f,k),await I(t,f,k,"write",{readHeadsKey:L}),await x(t,{event:"edited",object:k,actor:f,pointer:J.rev_id,attrs:{revision:D,from_revision:u.revision,rationale:a}},c),{revision:D,heads_key:L,unresolved:re}},{savepoint:!0})}catch(f){if(f instanceof ee)return{stale:{heads_key:f.heads_key,revision:f.revision}};throw f}}s(Ye,"editDocument");class ee extends Error{static{s(this,"StaleEdit")}heads_key;revision;constructor(n,e){super("stale"),this.heads_key=n,this.revision=e}}async function Ve(t,n,e,i){return y(e),n&&await T(t,n,e),await h(async()=>{const a=await M(t,e,i);return n&&await g(t,E(n),a),a},{savepoint:!0})}s(Ve,"setDocumentRecord");async function Te(t,n,e,i){const a=E(n),{heads_key:r}=await O(t,e);await I(t,a,e,"read",{readHeadsKey:r}),await x(t,{event:"read",object:e,actor:a,attrs:{}},i)}s(Te,"recordRead");async function Xe(t,n,e,i){return y(e),await U(t,n,e),await h(async()=>{const a=await O(t,e),r=await S(await C(t,e),async c=>({...c,body:await Z(t,e,pe(c.body_hash,a.source_path))})),o=(await q(await S(await t.prepare("SELECT dst, written_as FROM edges WHERE type = 'links-to' AND src = ? ORDER BY written_as").all(e.id),async c=>({written_as:c.written_as??"",object:await m(t,c.dst)})),async c=>c.object&&await P(t,n,c.object))).map(c=>({...c,deleted:c.object.deleted_at!==null}));return e.deleted_at||await Te(t,n,e,i),{object:e,record:a,heads:r,needsMerge:r.length>1,links:o}},{savepoint:!0})}s(Xe,"readDocument");async function ze(t,n,e){y(e),await U(t,n,e);const i=await t.prepare("SELECT * FROM revisions WHERE document = ? ORDER BY number DESC, created_at DESC, seq DESC").all(e.id);return await S(i,async a=>await X(t,a))}s(ze,"history");async function Qe(t,n,e,i,a){if(y(e),e.deleted_at)throw p(`${e.name} is deleted`);return n&&await T(t,n,e),await h(async()=>{const r=i.owner??await m(t,e.owner),o=i.name??e.name;await H(t,r,o,e),await $(t,r,e.privacy),await t.prepare("UPDATE objects SET name = ?, owner = ? WHERE id = ?").run(o,r.id,e.id);const c=await m(t,e.id);if(r.id!==e.owner&&(await Re(t,c),n&&!await j(t,n,c)))throw N(`no owner ${r.name} you can move documents to`);return n&&await g(t,E(n),c),(o!==e.name||r.id!==e.owner)&&await W(t,c,a),c},{savepoint:!0})}s(Qe,"moveDocument");async function Ze(t,n,e,i,a){return y(e),e.deleted_at?e:(n&&await T(t,n,e),await h(async()=>{const r=await te(t,n,e,i,a);return await ae(t,await ne(t,e.id),i),r},{savepoint:!0}))}s(Ze,"deleteDocument");async function be(t,n,e,i){return y(n),n.deleted_at?await h(async()=>{await H(t,await m(t,n.owner),e,n),await t.prepare("UPDATE objects SET deleted_at = NULL, name = ? WHERE id = ?").run(e,n.id);const a=await m(t,n.id);return await W(t,a,i),a},{savepoint:!0}):n}s(be,"restoreDocument");async function te(t,n,e,i,a){await t.prepare("UPDATE objects SET deleted_at = ? WHERE id = ?").run(a??new Date().toISOString(),e.id);const r=await m(t,e.id),o=n?E(n):null;return o&&await g(t,o,r),await x(t,{event:"deleted",object:r,actor:o,attrs:{}},i),await t.prepare("DELETE FROM watches WHERE object = ?").run(e.id),await K(t,r,[]),r}s(te,"markDeleted");async function ne(t,n){return(await t.prepare("SELECT dst FROM edges WHERE type = 'links-to' AND src = ?").all(n)).map(({dst:e})=>e)}s(ne,"linkTargets");async function ae(t,n,e){const i=t.prepare(`SELECT 1 FROM objects d JOIN objects w ON w.id = d.owner
8
8
  WHERE d.id = ? AND d.type = 'document' AND d.deleted_at IS NULL AND w.deleted_at IS NOT NULL
9
9
  AND NOT EXISTS (SELECT 1 FROM edges e JOIN objects s ON s.id = e.src
10
- WHERE e.type = 'links-to' AND e.dst = d.id AND e.src != d.id AND s.deleted_at IS NULL)`),i=[...n];for(;i.length>0;){const r=i.pop();await a.get(r)!==void 0&&(await te(e,null,await m(e,r),t),i.push(...await ne(e,r)))}}s(ae,"collectUnlinked");async function et(e,n,t){if(n.revisions.length===0)throw p(`import of ${n.name}: no revisions`);return await y(async()=>{const a=await b(e,n,t);await e.prepare("UPDATE objects SET created_at = ? WHERE id = ?").run(n.revisions[0].created_at,a.id);let i=null;return await ge(n.revisions,async(r,o)=>{i=(await j(e,a,{...r,number:o+1,parents:i===null?[]:[i]})).rev_id}),await F(e,a),await m(e,a.id)},{savepoint:!0})}s(et,"importDocument");async function tt(e,n,t){if(h(n),n.privacy!=="public"&&await Y(e,n)==="repo")throw p(`a repo owns only public documents; refusing revisions of ${n.name}`);return await y(async()=>{const a=e.prepare("SELECT rev_id, number, document FROM revisions WHERE rev_id = ?");let i=await q(t,async u=>!await a.get(u.rev_id));for(;i.length>0;){const u=[];for(const c of i){const w=await R(c.parents,async d=>await a.get(d));if(w.some(d=>d===void 0)){u.push(c);continue}const l=w;if(l.some(d=>d.document!==n.id))throw p(`revision ${c.rev_id} names a parent of another document`);await j(e,n,{...c,author:null,number:1+Math.max(0,...l.map(d=>d.number)),parents:l.map(d=>d.rev_id)})}if(u.length===i.length)throw p(`revisions of ${n.name} name unknown parents: ${u.map(c=>c.rev_id).join(", ")}`);i=u}const{heads_key:r,headCount:o}=await F(e,n);return{heads_key:r,headCount:o}},{savepoint:!0})}s(tt,"insertImportedRevisions");async function nt(e,n){const t=await m(e,n);if(!t||t.type!=="document")throw v(`no document ${n}`);return t}s(nt,"documentById");export{E as actorOf,M as applyDocumentFields,xe as applyReplacements,ae as collectUnlinked,Ze as deleteDocument,nt as documentById,T as documentRecord,Ye as editDocument,C as heads,ze as history,et as importDocument,tt as insertImportedRevisions,De as linksIn,Qe as moveDocument,qe as readBase,Xe as readDocument,Te as recordRead,be as restoreDocument,Ve as setDocumentRecord,Q as setRevisionLinks,Z as withCurrentNames,Ke as writeDocument};
10
+ WHERE e.type = 'links-to' AND e.dst = d.id AND e.src != d.id AND s.deleted_at IS NULL)`),a=[...n];for(;a.length>0;){const r=a.pop();await i.get(r)!==void 0&&(await te(t,null,await m(t,r),e),a.push(...await ne(t,r)))}}s(ae,"collectUnlinked");async function et(t,n,e){if(n.revisions.length===0)throw p(`import of ${n.name}: no revisions`);return await h(async()=>{const i=await b(t,n,e);await t.prepare("UPDATE objects SET created_at = ? WHERE id = ?").run(n.revisions[0].created_at,i.id);let a=null;return await ge(n.revisions,async(r,o)=>{a=(await A(t,i,{...r,number:o+1,parents:a===null?[]:[a]})).rev_id}),await F(t,i),await m(t,i.id)},{savepoint:!0})}s(et,"importDocument");async function tt(t,n,e){if(y(n),n.privacy!=="public"&&await Y(t,n)==="repo")throw p(`a repo owns only public documents; refusing revisions of ${n.name}`);return await h(async()=>{const i=t.prepare("SELECT rev_id, number, document FROM revisions WHERE rev_id = ?");let a=await q(e,async c=>!await i.get(c.rev_id));for(;a.length>0;){const c=[];for(const u of a){const w=await S(u.parents,async d=>await i.get(d));if(w.some(d=>d===void 0)){c.push(u);continue}const l=w;if(l.some(d=>d.document!==n.id))throw p(`revision ${u.rev_id} names a parent of another document`);await A(t,n,{...u,author:null,number:1+Math.max(0,...l.map(d=>d.number)),parents:l.map(d=>d.rev_id)})}if(c.length===a.length)throw p(`revisions of ${n.name} name unknown parents: ${c.map(u=>u.rev_id).join(", ")}`);a=c}const{heads_key:r,headCount:o}=await F(t,n);return{heads_key:r,headCount:o}},{savepoint:!0})}s(tt,"insertImportedRevisions");async function nt(t,n){const e=await m(t,n);if(!e||e.type!=="document")throw N(`no document ${n}`);return e}s(nt,"documentById");export{E as actorOf,M as applyDocumentFields,xe as applyReplacements,ae as collectUnlinked,Ze as deleteDocument,nt as documentById,O as documentRecord,Ye as editDocument,C as heads,ze as history,et as importDocument,tt as insertImportedRevisions,De as linksIn,Qe as moveDocument,qe as readBase,Xe as readDocument,Te as recordRead,be as restoreDocument,Ve as setDocumentRecord,Q as setRevisionLinks,Z as withCurrentNames,Ke as writeDocument};
@@ -57,8 +57,6 @@ export declare function watchesOf(db: CanvasDb, reader: Reader, watcher: ObjectR
57
57
  export declare function watchersOf(db: CanvasDb, reader: Reader, object: ObjectRow): Promise<Watch[]>;
58
58
  /** The ids of every object that watches `object`, whatever started the watch. */
59
59
  export declare function watcherIdsOf(db: CanvasDb, object: ObjectRow): Promise<string[]>;
60
- /** Every node below `root` in the tree (in-tree watches, watcher → object), each once. */
61
- export declare function descendants(db: CanvasDb, root: ObjectRow): Promise<ObjectRow[]>;
62
60
  /** Every node above `leaf` in the tree (in-tree watches, object → watcher), each once. */
63
61
  export declare function ancestors(db: CanvasDb, leaf: ObjectRow): Promise<ObjectRow[]>;
64
62
  /** The objects whose in-tree watch covers `object` (its spawner, job starter, explicit watchers). */
@@ -1,19 +1,14 @@
1
- var T=Object.defineProperty;var i=(t,e)=>T(t,"name",{value:e,configurable:!0});import{canRead as R}from"./access.js";import{receiverForObject as j}from"./receivers.js";import{getById as N}from"./objects.js";import{randomUUID as h}from"node:crypto";import{filterAsync as A,mapAsync as D}from"../async-array.js";const f=["spawn","job-start","ask"],b=["report.final","report.result","notice.>"];function S(t){return JSON.stringify(Object.fromEntries(t.map(e=>[String(e.threshold),e.delivery])))}i(S,"configToJson");function I(t){const e=JSON.parse(t);return Object.entries(e).map(([n,r])=>({threshold:Number(n),delivery:r})).sort((n,r)=>r.threshold-n.threshold)}i(I,"configFromJson");async function _(t,e,n){return await t.prepare("SELECT * FROM watches WHERE watcher = ? AND object = ?").get(e.id,n.id)}i(_,"watchRow");async function E(t,e){return await t.prepare("SELECT * FROM watch_subscriptions WHERE watch = ? ORDER BY seq").all(e)}i(E,"subsOf");async function u(t,e){const n=await E(t,e);if(n.length===0){await t.prepare("DELETE FROM watches WHERE watch_id = ?").run(e);return}const r=n.some(a=>f.includes(a.started_by))?1:0;await t.prepare("UPDATE watches SET in_tree = ? WHERE watch_id = ? AND in_tree != ?").run(r,e,r)}i(u,"settle");function m(t,e){return t==="message"?b:e.patterns?.length?e.patterns:[">"]}i(m,"defaultPatterns");async function x(t,e,n,r,a={}){if(r==="fork")throw new Error("recordWatch: fork watches are made by copyWatchesForFork");if(e.type.includes(".")&&!await j(t,e))throw new Error(`custom object ${e.id} has no receiver`);const o=new Date().toISOString();await t.prepare(`INSERT INTO watches (watch_id, watcher, object, in_tree, read_heads_key, created_at, link_app) VALUES (?, ?, ?, 0, ?, ?, ?)
1
+ var S=Object.defineProperty;var i=(t,e)=>S(t,"name",{value:e,configurable:!0});import{canRead as O}from"./access.js";import{receiverForObject as j}from"./receivers.js";import{getById as y}from"./objects.js";import{randomUUID as h}from"node:crypto";import{filterAsync as D,mapAsync as m}from"../async-array.js";const f=["spawn","job-start","ask"],A=["report.final","report.result","notice.>"];function N(t){return JSON.stringify(Object.fromEntries(t.map(e=>[String(e.threshold),e.delivery])))}i(N,"configToJson");function b(t){const e=JSON.parse(t);return Object.entries(e).map(([n,r])=>({threshold:Number(n),delivery:r})).sort((n,r)=>r.threshold-n.threshold)}i(b,"configFromJson");async function _(t,e,n){return await t.prepare("SELECT * FROM watches WHERE watcher = ? AND object = ?").get(e.id,n.id)}i(_,"watchRow");async function E(t,e){return await t.prepare("SELECT * FROM watch_subscriptions WHERE watch = ? ORDER BY seq").all(e)}i(E,"subsOf");async function u(t,e){const n=await E(t,e);if(n.length===0){await t.prepare("DELETE FROM watches WHERE watch_id = ?").run(e);return}const r=n.some(a=>f.includes(a.started_by))?1:0;await t.prepare("UPDATE watches SET in_tree = ? WHERE watch_id = ? AND in_tree != ?").run(r,e,r)}i(u,"settle");function g(t,e){return t==="message"?A:e.patterns?.length?e.patterns:[">"]}i(g,"defaultPatterns");async function x(t,e,n,r,a={}){if(r==="fork")throw new Error("recordWatch: fork watches are made by copyWatchesForFork");if(e.type.includes(".")&&!await j(t,e))throw new Error(`custom object ${e.id} has no receiver`);const s=new Date().toISOString();await t.prepare(`INSERT INTO watches (watch_id, watcher, object, in_tree, read_heads_key, created_at, link_app) VALUES (?, ?, ?, 0, ?, ?, ?)
2
2
  ON CONFLICT (watcher, object) DO UPDATE SET read_heads_key = COALESCE(excluded.read_heads_key, watches.read_heads_key),
3
- link_app = COALESCE(excluded.link_app, watches.link_app)`).run(h(),e.id,n.id,a.readHeadsKey??null,o,a.linkApp?.id??null);const c=await _(t,e,n),w=r==="message"?a.endsOn??"final":null,l=f.includes(r),s=await E(t,c.watch_id),d=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
4
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);for(const O of m(r,a))s.some(p=>p.pattern===O&&(p.ends_on===null||p.ends_on===w)&&(!l||f.includes(p.started_by)))||await d.run(h(),c.watch_id,O,a.filter===void 0?null:JSON.stringify(a.filter),a.config?S(a.config):null,r,w,o);await u(t,c.watch_id)}i(x,"recordWatch");async function U(t,e,n,r){await t.prepare("UPDATE watches SET link_app = ? WHERE watcher = ? AND object = ?").run(r.id,e.id,n.id)}i(U,"markLinkWatch");async function J(t,e,n,r){const a=await _(t,e,n);a&&(r===void 0?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ?").run(a.watch_id):typeof r=="string"?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND pattern = ?").run(a.watch_id,r):await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND started_by = ?").run(a.watch_id,r.startedBy),await u(t,a.watch_id))}i(J,"unwatch");async function v(t,e,n,r,a){const o=await _(t,e,n),c=o?(await t.prepare("UPDATE watch_subscriptions SET config_json = ? WHERE watch = ? AND pattern = ?").run(a?S(a):null,o.watch_id,r)).changes:0;if(Number(c)===0)throw new Error(`${e.id} has no subscription ${r} on ${n.id}`)}i(v,"setSubscriptionConfig");async function B(t,e,n){const r=typeof n=="string"?await t.prepare(`DELETE FROM watch_subscriptions WHERE started_by = 'message'
3
+ link_app = COALESCE(excluded.link_app, watches.link_app)`).run(h(),e.id,n.id,a.readHeadsKey??null,s,a.linkApp?.id??null);const c=await _(t,e,n),w=r==="message"?a.endsOn??"final":null,l=f.includes(r),o=await E(t,c.watch_id),d=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
4
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);for(const R of g(r,a))o.some(p=>p.pattern===R&&(p.ends_on===null||p.ends_on===w)&&(!l||f.includes(p.started_by)))||await d.run(h(),c.watch_id,R,a.filter===void 0?null:JSON.stringify(a.filter),a.config?N(a.config):null,r,w,s);await u(t,c.watch_id)}i(x,"recordWatch");async function U(t,e,n,r){await t.prepare("UPDATE watches SET link_app = ? WHERE watcher = ? AND object = ?").run(r.id,e.id,n.id)}i(U,"markLinkWatch");async function J(t,e,n,r){const a=await _(t,e,n);a&&(r===void 0?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ?").run(a.watch_id):typeof r=="string"?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND pattern = ?").run(a.watch_id,r):await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND started_by = ?").run(a.watch_id,r.startedBy),await u(t,a.watch_id))}i(J,"unwatch");async function v(t,e,n,r,a){const s=await _(t,e,n),c=s?(await t.prepare("UPDATE watch_subscriptions SET config_json = ? WHERE watch = ? AND pattern = ?").run(a?N(a):null,s.watch_id,r)).changes:0;if(Number(c)===0)throw new Error(`${e.id} has no subscription ${r} on ${n.id}`)}i(v,"setSubscriptionConfig");async function B(t,e,n){const r=typeof n=="string"?await t.prepare(`DELETE FROM watch_subscriptions WHERE started_by = 'message'
5
5
  AND watch IN (SELECT watch_id FROM watches WHERE object = ?) RETURNING watch`).all(e.id):await t.prepare(`DELETE FROM watch_subscriptions WHERE started_by = 'message' AND ends_on = ?
6
- AND watch IN (SELECT watch_id FROM watches WHERE object = ?) RETURNING watch`).all(`result:${n.result}`,e.id);for(const a of new Set(r.map(o=>o.watch)))await u(t,a)}i(B,"endMessageSubscriptions");async function g(t,e){const n=await N(t,e.watcher),r=await N(t,e.object);return!n||!r?null:{watch_id:e.watch_id,watcher:n,object:r,in_tree:e.in_tree===1,read_heads_key:e.read_heads_key,created_at:e.created_at,subscriptions:(await E(t,e.watch_id)).map(a=>({subscription_id:a.subscription_id,pattern:a.pattern,started_by:a.started_by,ends_on:a.ends_on,created_at:a.created_at,filter:a.filter_json===null?null:JSON.parse(a.filter_json),config:a.config_json===null?null:I(a.config_json)}))}}i(g,"hydrate");async function y(t,e,n){const r=(await D(n,async a=>await g(t,a))).filter(a=>a!==null);return await A(r,async a=>await R(t,e,a.watcher)&&await R(t,e,a.object))}i(y,"visible");async function P(t,e,n){return await y(t,e,await t.prepare("SELECT * FROM watches WHERE watcher = ? ORDER BY seq").all(n.id))}i(P,"watchesOf");async function V(t,e,n){return await y(t,e,await t.prepare("SELECT * FROM watches WHERE object = ? ORDER BY seq").all(n.id))}i(V,"watchersOf");async function q(t,e){return(await t.prepare("SELECT o.id FROM watches w JOIN objects o ON o.id = w.watcher WHERE w.object = ? ORDER BY w.seq").all(e.id)).map(n=>n.id)}i(q,"watcherIdsOf");async function Y(t,e){return await t.prepare(`WITH RECURSIVE walk(id) AS (
7
- SELECT CAST(? AS text)
8
- UNION
9
- SELECT w.object FROM walk JOIN watches w ON w.watcher = walk.id AND w.in_tree = 1
10
- ) SELECT o.* FROM walk JOIN objects o ON o.id = walk.id
11
- WHERE o.type = 'node' AND o.id != ?`).all(e.id,e.id)}i(Y,"descendants");async function $(t,e){return await t.prepare(`WITH RECURSIVE walk(id) AS (
6
+ AND watch IN (SELECT watch_id FROM watches WHERE object = ?) RETURNING watch`).all(`result:${n.result}`,e.id);for(const a of new Set(r.map(s=>s.watch)))await u(t,a)}i(B,"endMessageSubscriptions");async function I(t,e){const n=await y(t,e.watcher),r=await y(t,e.object);return!n||!r?null:{watch_id:e.watch_id,watcher:n,object:r,in_tree:e.in_tree===1,read_heads_key:e.read_heads_key,created_at:e.created_at,subscriptions:(await E(t,e.watch_id)).map(a=>({subscription_id:a.subscription_id,pattern:a.pattern,started_by:a.started_by,ends_on:a.ends_on,created_at:a.created_at,filter:a.filter_json===null?null:JSON.parse(a.filter_json),config:a.config_json===null?null:b(a.config_json)}))}}i(I,"hydrate");async function T(t,e,n){const r=(await m(n,async a=>await I(t,a))).filter(a=>a!==null);return await D(r,async a=>await O(t,e,a.watcher)&&await O(t,e,a.object))}i(T,"visible");async function P(t,e,n){return await T(t,e,await t.prepare("SELECT * FROM watches WHERE watcher = ? ORDER BY seq").all(n.id))}i(P,"watchesOf");async function q(t,e,n){return await T(t,e,await t.prepare("SELECT * FROM watches WHERE object = ? ORDER BY seq").all(n.id))}i(q,"watchersOf");async function V(t,e){return(await t.prepare("SELECT o.id FROM watches w JOIN objects o ON o.id = w.watcher WHERE w.object = ? ORDER BY w.seq").all(e.id)).map(n=>n.id)}i(V,"watcherIdsOf");async function Y(t,e){return await t.prepare(`WITH RECURSIVE walk(id) AS (
12
7
  SELECT CAST(? AS text)
13
8
  UNION
14
9
  SELECT w.watcher FROM walk JOIN watches w ON w.object = walk.id AND w.in_tree = 1
15
10
  ) SELECT o.* FROM walk JOIN objects o ON o.id = walk.id
16
- WHERE o.type = 'node' AND o.id != ?`).all(e.id,e.id)}i($,"ancestors");async function G(t,e){return await t.prepare(`SELECT o.* FROM watches w JOIN objects o ON o.id = w.watcher
17
- WHERE w.object = ? AND w.in_tree = 1 ORDER BY w.seq`).all(e.id)}i(G,"treeWatchersOf");async function K(t,e,n){const r=new Date().toISOString(),a=await t.prepare("SELECT * FROM watches WHERE watcher = ?").all(e.id),o=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
11
+ WHERE o.type = 'node' AND o.id != ?`).all(e.id,e.id)}i(Y,"ancestors");async function $(t,e){return await t.prepare(`SELECT o.* FROM watches w JOIN objects o ON o.id = w.watcher
12
+ WHERE w.object = ? AND w.in_tree = 1 ORDER BY w.seq`).all(e.id)}i($,"treeWatchersOf");async function G(t,e,n){const r=new Date().toISOString(),a=await t.prepare("SELECT * FROM watches WHERE watcher = ?").all(e.id),s=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
18
13
  VALUES (?, ?, ?, ?, ?, 'fork', ?, ?)`);for(const c of a){if(c.object===n.id)continue;await t.prepare(`INSERT INTO watches (watch_id, watcher, object, in_tree, read_heads_key, created_at) VALUES (?, ?, ?, 0, ?, ?)
19
- ON CONFLICT (watcher, object) DO NOTHING`).run(h(),n.id,c.object,c.read_heads_key,r);const w=await _(t,n,{id:c.object}),l=await E(t,w.watch_id);for(const s of await E(t,c.watch_id))l.some(d=>d.pattern===s.pattern&&d.ends_on===s.ends_on)||await o.run(h(),w.watch_id,s.pattern,s.filter_json,s.config_json,s.ends_on,r);await u(t,w.watch_id)}}i(K,"copyWatchesForFork");export{$ as ancestors,I as configFromJson,S as configToJson,K as copyWatchesForFork,Y as descendants,B as endMessageSubscriptions,U as markLinkWatch,x as recordWatch,v as setSubscriptionConfig,G as treeWatchersOf,J as unwatch,q as watcherIdsOf,V as watchersOf,P as watchesOf};
14
+ ON CONFLICT (watcher, object) DO NOTHING`).run(h(),n.id,c.object,c.read_heads_key,r);const w=await _(t,n,{id:c.object}),l=await E(t,w.watch_id);for(const o of await E(t,c.watch_id))l.some(d=>d.pattern===o.pattern&&d.ends_on===o.ends_on)||await s.run(h(),w.watch_id,o.pattern,o.filter_json,o.config_json,o.ends_on,r);await u(t,w.watch_id)}}i(G,"copyWatchesForFork");export{Y as ancestors,b as configFromJson,N as configToJson,G as copyWatchesForFork,B as endMessageSubscriptions,U as markLinkWatch,x as recordWatch,v as setSubscriptionConfig,$ as treeWatchersOf,J as unwatch,V as watcherIdsOf,q as watchersOf,P as watchesOf};
@@ -1,6 +1,7 @@
1
1
  import { type CanvasDb } from '../canvas/db.js';
2
2
  import { type HumanRequestRecord } from '../human/requests.js';
3
3
  import { type PreparedPage } from '../human/page-authoring.js';
4
+ import { type PageManifest } from '../human/page-schema.js';
4
5
  /**
5
6
  * A question is a settleable human page request. Most belong to a run; a
6
7
  * provider approval can also belong to an app without a run. Its question id
@@ -29,6 +30,7 @@ export declare function providerApprovalPage(question: {
29
30
  label: string;
30
31
  }>;
31
32
  }): PreparedPage;
33
+ export declare function manifestOf(request: HumanRequestRecord): PageManifest;
32
34
  /** The question's run, asking node, and app, or null when the request is not a question. */
33
35
  export declare function questionRun(request: HumanRequestRecord, db?: CanvasDb): Promise<AskingNode | null>;
34
36
  export type QuestionKind = 'decision' | 'notice';
@@ -1,4 +1,4 @@
1
1
  var g=Object.defineProperty;var r=(n,e)=>g(n,"name",{value:e,configurable:!0});import{openDb as s}from"../canvas/db.js";import{askingNodeSql as c,getRequest as y}from"../human/requests.js";import{preparePageSource as h}from"../human/page-authoring.js";import{pageAwaitsResponse as d}from"../human/page-schema.js";import{appendRunEvent as f}from"./events.js";async function p(n,e){return await n.prepare("SELECT question_id, grantee, sub, provider, tool, status, option_id FROM provider_approvals WHERE question_id = ?").get(e)??null}r(p,"providerQuestion");function j(n){if(!n.title.trim()||!n.body.trim()||!Array.isArray(n.options)||n.options.length===0)throw new Error("provider approval needs a title, body, and options");const e=new Set(["deny"]);for(const o of n.options){if(!o.option_id||!o.label||e.has(o.option_id))throw new Error("provider approval option ids must be nonempty, unique, and different from deny");e.add(o.option_id)}const t=[...n.options.map(({option_id:o,label:a})=>({id:o,label:a})),{id:"deny",label:"Don't do this"}],i=`export default function Approval() { return <Page title={${JSON.stringify(n.title)}}><UserQuestion id="options" label="Choose an option" body={${JSON.stringify(n.body)}} mode="single" required={true} allowFreetext={false} options={${JSON.stringify(t)}} /></Page>; }`;return h({dialect:"jsx",document:i,delivery:{placement:"inline",inbox:!1,reply:!0}})}r(j,"providerApprovalPage");async function N(n,e){return e===null?null:await n.prepare("SELECT node_id, run_id, grantee FROM node_rows WHERE node_id = ? AND run_id IS NOT NULL").get(e)??null}r(N,"askingNode");function u(n){return JSON.parse(n.specification_json)}r(u,"manifestOf");async function l(n,e){if(e??=await s(),n.kind!=="page"||!n.settleable)return null;const t=u(n);return await N(e,n.requester_node_id??(d(t)?null:t.source?.nodeId??null))}r(l,"questionRun");function _(n){return d(u(n))?"decision":"notice"}r(_,"questionKind");async function w(n,e,t){t??=await s();const i=u(n),o=await p(t,n.request_id);return{question_id:n.request_id,initiating_app:e,title:n.title,...n.subtitle?{subtitle:n.subtitle}:{},...o?{provider:o.provider,tool:o.tool}:{},steps:i.steps,slots:i.slots.map(a=>({...a.id===void 0?{}:{id:a.id},kind:a.kind,step:a.step,config:a.config}))}}r(w,"questionFields");async function I(n,e){const t=await l(e,n);t===null||e.state!=="open"||_(e)==="notice"||await f(n,t.run_id,t.node_id,"waiting_on_user",await w(e,t.grantee,n))}r(I,"appendWaitingOnUser");async function J(n,e){const t=e.result_json===null?null:JSON.parse(e.result_json);if(await p(n,e.request_id)!==null){const a=t?.responses?.options?.selectedOptionIds?.[0];if(e.outcome==="answered"&&(typeof a!="string"||!a))throw new Error(`provider approval ${e.request_id} has no selected option`);await n.prepare("UPDATE provider_approvals SET status = ?, option_id = ? WHERE question_id = ? AND status = 'open'").run(e.outcome==="answered"?"answered":"canceled",e.outcome==="answered"?a:null,e.request_id)}if(e.outcome!=="answered"||t===null)return;const o=await l(e,n);o!==null&&await f(n,o.run_id,o.node_id,"question.answered",{question_id:e.request_id,answer:t.responses??{}})}r(J,"appendQuestionAnswered");async function m(n,e,t){return(await t.prepare(`SELECT h.request_id, h.specification_json FROM human_requests h JOIN node_rows n ON n.node_id = ${c("h")}
2
2
  WHERE n.run_id = ? AND h.kind = 'page' AND h.state = 'open' AND h.settleable = 1
3
3
  ORDER BY h.created_at, h.request_id`).all(n)).filter(i=>d(JSON.parse(i.specification_json))===(e==="decision")).map(i=>i.request_id)}r(m,"openIdsOfKind");async function T(n,e){return e??=await s(),await m(n,"decision",e)}r(T,"openQuestionIds");async function L(n,e){return e??=await s(),await m(n,"notice",e)}r(L,"openNoticeIds");function E(n){return n.state!=="settled"?"open":n.outcome==="answered"?"answered":"canceled"}r(E,"questionStatus");function v(n){if(n.result_json===null)return null;const e=JSON.parse(n.result_json);return n.outcome==="answered"?e.responses??{}:e}r(v,"questionResult");async function F(n,e,t){const i=E(n);return{...await w(n,e?.grantee??t.grantee),kind:_(n),...e===null?{}:{run_id:e.run_id,node_id:e.node_id},status:i,...i==="answered"?{answer:v(n)}:{}}}r(F,"questionObject");async function Q(n){const e=await y(n);if(e===null||e.kind!=="page"||!e.settleable)return null;const t=await s(),i=await l(e,t),o=await p(t,n);return i===null&&o===null?null:{request:e,run:i,approval:o}}r(Q,"getQuestion");async function O(n){const[e,t]="run_id"in n?["n.run_id = ?",n.run_id]:"node_id"in n?["n.node_id = ?",n.node_id]:["n.grantee = ?",n.grantee];return(await(await s()).prepareUncached(`SELECT h.request_id FROM human_requests h JOIN node_rows n ON n.node_id = ${c("h")}
4
- WHERE ${e} AND n.run_id IS NOT NULL AND h.kind = 'page' AND h.state = 'open' AND h.settleable = 1`).all(t)).map(i=>i.request_id)}r(O,"openQuestionIdsFor");async function $(n,e){const t=await O(n);if(!t.length)return 0;const{cancelHumanTicket:i}=await import("../../daemon/human/finish.js");let o=0;for(const a of t)(await i(a,{reason:e,actor:"crtrd"})).outcome==="settled"&&(o+=1);return o}r($,"cancelOpenQuestions");export{J as appendQuestionAnswered,I as appendWaitingOnUser,$ as cancelOpenQuestions,Q as getQuestion,L as openNoticeIds,T as openQuestionIds,O as openQuestionIdsFor,j as providerApprovalPage,_ as questionKind,F as questionObject,v as questionResult,l as questionRun,E as questionStatus};
4
+ WHERE ${e} AND n.run_id IS NOT NULL AND h.kind = 'page' AND h.state = 'open' AND h.settleable = 1`).all(t)).map(i=>i.request_id)}r(O,"openQuestionIdsFor");async function $(n,e){const t=await O(n);if(!t.length)return 0;const{cancelHumanTicket:i}=await import("../../daemon/human/finish.js");let o=0;for(const a of t)(await i(a,{reason:e,actor:"crtrd"})).outcome==="settled"&&(o+=1);return o}r($,"cancelOpenQuestions");export{J as appendQuestionAnswered,I as appendWaitingOnUser,$ as cancelOpenQuestions,Q as getQuestion,u as manifestOf,L as openNoticeIds,T as openQuestionIds,O as openQuestionIdsFor,j as providerApprovalPage,_ as questionKind,F as questionObject,v as questionResult,l as questionRun,E as questionStatus};
@@ -84,6 +84,12 @@ export declare class FaultRetry {
84
84
  * another turn starts, since a stale fault would re-drive from the old anchor. */
85
85
  publishSettledProviderFault(generation: SubscriptionGeneration): Promise<void>;
86
86
  private recordStagedOutcome;
87
+ /** Where the next retry of this episode rewinds to. The episode keeps its
88
+ * original anchor while every attempt since failed without doing anything, so
89
+ * empty failures do not pile up on the branch. Once an attempt carries
90
+ * content or tool results past that anchor, rewinding would hide work whose
91
+ * effects cannot be undone, so the anchor advances to the current leaf. */
92
+ private retryAnchor;
87
93
  /** Startup re-drive reads the durable episode surviving from the process this
88
94
  * replaces. Revive deliberately clears the ordinary fault marker before this
89
95
  * boundary, so the episode is the recovery authority. A `pending` episode
@@ -1 +1 @@
1
- var ae=Object.defineProperty;var h=(r,e)=>ae(r,"name",{value:e,configurable:!0});import{envModelIntent as N,envNodeCwd as G,envProfileId as U}from"../../../shared/env.js";import{isContextOverflow as Y}from"@earendil-works/pi-ai";import{classify as B}from"../../fault-classifier.js";import{isDaemonTransportApiError as de}from"../../../api/errors.js";import{isModelNotFoundError as le,isProviderUnconfiguredError as ce}from"../fault.js";import{brokerRecovery as ue,mutateBrokerFault as W,mutateBrokerProviderRetry as _}from"./daemon-ops.js";import{extractCoolingDeadline as pe}from"../managed-provider-cooling.js";import{extractProviderFailureDetails as fe}from"../../provider-failure.js";import{expandModelCandidates as q,modelRequestFromConfig as V,probeRouteAvailability as he}from"../../model-routes.js";import{operationIdContext as j}from"../../events/operation-id.js";import{emitEvent as w}from"../../events/emit.js";import{CONNECTION_FAULT_RECOVERY_BODY as me,PROVIDER_FAULT_RECOVERY_BODY as ge,RUN_CONNECTION_FAULT_RETRY_BODY as ye,formatCard as X,formatModelFallbackRecovery as ve}from"../../../shared/generated-context.js";import{markFatalOnExhaust as J,nextFaultRetry as z,policyFor as H}from"../fault-recovery.js";import{parseModelSpec as K}from"../model-selection.js";import{endedByAbort as Q}from"../session-cycles.js";import{resolveProviderCandidates as Fe}from"../../subscription-state.js";import{announceMessageOrigin as Z}from"../stamp/channel.js";import{LLM_REFUSAL_DIAGNOSTIC as we,refusalRetryAt as ke}from"./model-adapter.js";import{isSafetyRefusal as ee}from"../../llm/refusal.js";const be=2147483647,te=3e4;function _e(r,e,t){const n=Date.parse(r);return!Number.isFinite(n)||t+e-n>te}h(_e,"runTurnRetryExpired");class ne extends Error{static{h(this,"StaleOutcome")}}const Ee=1e3,Ie=3e4;function Re(r,e){return r!=="connection"?ge:e?ye:me}h(Re,"retryCardBody");function E(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}}}h(E,"policyFault");function x(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}}}h(x,"faultInput");function ie(r,e){const t=h(n=>`${n.providerId}/${n.modelId}`===e.modelSpec,"onLiveModel");return(r.find(n=>t(n)&&n.thinkingLevel===e.thinkingLevel)??r.find(t))?.rank}h(ie,"liveRouteRank");function re(r){const e=B("pi\u2192provider",r),t=r.diagnostics?.some(n=>n.type===we)??!1;return{classified:e,disposition:t?"fatal":e.disposition}}h(re,"settledProviderFault");function He(r,e){if(r.stopReason!=="error"||Q(r)||ee(r)||Y(r,e))return!1;const{classified:t,disposition:n}=re(r);return n==="auto"&&(t.kind==="connection"||t.kind==="rate-limit"||t.kind==="overloaded")}h(He,"willDaemonRetry");const Se=/does not support assistant message prefill|conversation must end with a user message/i;function Ae(r,e){return e&&Se.test(r)}h(Ae,"isPrefillRejectionDuringEpisode");class Ke{static{h(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 ue(this.deps.nodeId)}async recordFault(e){return(await W(this.deps.nodeId,{op:"record",input:e})).fault}async clearFault(e,t=!1){await W(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 _(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=h(()=>{if(this.deps.installedGeneration()!==e||this.agentStarts!==n)throw new ne},"assertCurrent");let a=Ee;try{for(;;){try{await this.recordStagedOutcome(e,t,i);return}catch(o){if(!de(o))throw o;w({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`settled turn fault write waiting for the daemon: ${o.message}`}})}await new Promise(o=>setTimeout(o,a)),a=Math.min(a*2,Ie),i()}}catch(o){if(!(o instanceof ne))throw o}}async recordStagedOutcome(e,t,n){const{agentEnd:i,watchdogAbort:a,refreshAbort:o,overflowFailure:d,capacityFailure:p}=t,s=e.session,l=h(async F=>{n(),await this.clearEpisode(F)},"clearEpisode"),y=h(async F=>{n(),await this.recordFault(F)},"recordFault"),u=h(async(F,oe)=>{n(),await this.recordPendingProviderFault(F,oe)},"recordPendingProviderFault"),v=await this.state(),m=v.fault,f=v.episode,I=this.sessionId(s),k=this.isActiveAutoFault(m)&&m.link==="pi\u2192provider"?m:f?.state==="admitted"&&f.session_id===I&&this.isActiveAutoFault(f.fault)?f.fault:null,g=k===null?null:{since:k.since,anchorEntryId:k.anchor_entry_id,attempt:k.retry.attempt},c=[...Array.isArray(i?.messages)?i.messages:[]].reverse().find(F=>typeof F=="object"&&F!==null&&F.role==="assistant");if(p!==null||d!==null){await l(s),await y({link:"pi\u2192provider",op:"context overflow recovery",kind:"context-overflow",retry:{disposition:"fatal"},message:p?.errorMessage??d.errorMessage,anchor_entry_id:s.sessionManager.getLeafId?.()??void 0});return}if(a&&c?.stopReason==="aborted"){await u(s,{link:"pi\u2192provider",op:"generation turn",kind:"connection",retry:{disposition:"auto",by:"daemon",...g?.attempt!==void 0?{attempt:g.attempt}:{}},message:typeof c.errorMessage=="string"?c.errorMessage:"engine error (no errorMessage recorded)",...g!==null?{since:g.since}:{},anchor_entry_id:g?.anchorEntryId??s.sessionManager.getLeafId?.()??void 0});return}if(c?.stopReason!=="error"){c!==void 0&&await l(s);return}if(o){await l(s);return}if(ee(c)){await l(s);return}const S=typeof c.errorMessage=="string"?c.errorMessage:"engine error (no errorMessage recorded)";if(Q({stopReason:"error",errorMessage:S})){await l(s);return}const b=Y(c,s.model?.contextWindow),A=re(c),T=g!==null&&A.disposition!=="auto"&&Ae(S,!0),se=T?{kind:"connection",disposition:"auto"}:A.classified,R=T?"auto":A.disposition,C=pe(c),O=ke(c),P=fe(c),$=typeof c.provider=="string"&&c.provider!==""?c.provider:s.model?.provider,L=typeof c.model=="string"&&c.model!==""?c.model:s.model?.id,D=P===void 0||$===void 0||L===void 0?void 0:{provider:$,model:L,...P};b&&await l(s);const M={link:"pi\u2192provider",op:"generation turn",kind:b?"context-overflow":se.kind,retry:b?{disposition:"fatal"}:R==="auto"?{disposition:"auto",by:"daemon",...C!==void 0?{nextAt:new Date(C).toISOString()}:{},...g?.attempt!==void 0?{attempt:g.attempt}:{}}:{disposition:R,...O!==void 0?{nextAt:new Date(O).toISOString()}:{}},message:S,...!b&&R==="auto"&&g!==null?{since:g.since}:{},anchor_entry_id:!b&&R==="auto"&&g?.anchorEntryId!==void 0?g.anchorEntryId:s.sessionManager.getLeafId?.()??void 0,...D===void 0?{}:{provider_error:D}};M.retry.disposition==="auto"?await u(s,M):(await l(s),await y(M))}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 _(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 _(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";return await this.recordFault(x(E(t.fault))),await this.schedule(),"retry-armed"}hasProgressPastAnchor(e,t){const n=e.sessionManager.getBranch(),i=n.findIndex(o=>o.id===t);return(i===-1?n:n.slice(i+1)).some(o=>{if(o.type!=="message")return!1;const{message:d}=o;return d.role==="toolResult"?!0:d.role==="assistant"&&Array.isArray(d.content)&&d.content.length>0})}async schedule(){const e=this.deps.installedGeneration(),t=this.deps.currentSession();if(t.isStreaming||this.timer!==null)return;if(e===void 0){this.abandonTurnEnd();return}const n=await this.state(),i=n.fault;if(!this.isActiveAutoFault(i)||this.sessionId(t)!==null&&!this.pendingEpisodeMatches(t,i,n)){this.clearTimer(),this.abandonTurnEnd();return}const a=Date.now(),o=this.faultRetryDelayMs(i,a);if(o!==null&&this.deps.turnEnds?.holding()===!0&&_e(i.since,o,a)){await this.endRunTurnOnFault(t,i,o);return}o!==null?this.clearAndArmFaultRetry(o,e):this.abandonTurnEnd()}async endRunTurnOnFault(e,t,n){this.clearTimer(),this.abandonTurnEnd(),w({level:"info",event:"broker.runtime.diagnostic",fields:{message:`run turn completed on provider fault: ${t.kind} retry in ${n}ms is past the ${te}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=B("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(),a=this.deps.currentServices(),o=this.deps.formatModelSpec(i.model,i.thinkingLevel);if(o===null)return;const d=V(this.deps.cfg.model,N(),!1);if(d===void 0)return;const p=q(d,G()??this.deps.cfg.cwd,U()),s=K(o),l=ie(p,s),y=l===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(p.slice(l+1),this.deps.registryOf(a),i.getContextUsage()?.tokens,m=>!this.failedRetryRoutes.has(m.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,v=(async()=>{const m=this.deps.registryOf(a).find(u.providerId,u.modelId);if(!m||(await i.setModel(m),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i))return;const f=`${u.providerId}/${u.modelId}`===s.modelSpec?s.thinkingLevel:u.thinkingLevel;i.setThinkingLevel(f??"off"),this.failedRetryRoutes.add(u.routeId),await this.deps.broadcastModelChanged(!1)})().catch(m=>{w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`provider route fallback failed: ${m instanceof Error?m.message:String(m)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.providerFallbackInFlight=null)});this.providerFallbackInFlight=v}maybeFallbackOnUnusableModel(e){if(e.type!=="message_end"||e.message.role!=="assistant"||e.message.stopReason!=="error")return;const t=ce(e.message.errorMessage??"");if(!t&&!le(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(),a=this.deps.currentServices(),o=this.deps.formatModelSpec(i.model,i.thinkingLevel);if(o===null)return;const d=K(o);this.notFoundModels.add(d.modelSpec);const p=V(this.deps.cfg.model,N(),!1),s=p?q(p,G()??this.deps.cfg.cwd,U()):[],l=ie(s,d),y=l===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(s.slice(l+1),this.deps.registryOf(a),i.getContextUsage()?.tokens,f=>!this.notFoundModels.has(`${f.providerId}/${f.modelId}`));if(y.route===void 0){y.capacityExhausted&&this.stageCapacityFailure(n,i);return}const u=y.route,v=`${u.providerId}/${u.modelId}${u.thinkingLevel?`:${u.thinkingLevel}`:""}`,m=(async()=>{const f=this.deps.registryOf(a).find(u.providerId,u.modelId);if(f===void 0){w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback skipped: ${v} is not registered`}});return}if(await i.setModel(f),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i)return;i.setThinkingLevel(u.thinkingLevel??"off"),await this.deps.broadcastModelChanged();const I=this.deps.formatModelSpec(i.model,i.thinkingLevel)??v;if(w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback: ${o} unavailable \u2192 ${I}; re-driving turn`}}),await i.agent.waitForIdle(),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i)return;t&&await this.clearFault("pi\u2192provider");const k=X("recovery",{},ve(o,I,t?"credential":"not-found"));Z(k,{type:"runtime",kind:"recovery"}),await this.deps.turnAdmission.admit(g=>j.fresh(()=>i.prompt(k,{preflightResult:g})))})().catch(f=>{w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback failed: ${f instanceof Error?f.message:String(f)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.notFoundFallbackInFlight=null)});this.notFoundFallbackInFlight=m}nextEligibleFallbackRoute(e,t,n,i){let a=0,o=0;for(const d of e){if(!i(d)||Fe([{...d,...he(d,t),automaticFallback:!0}]).candidates.length===0)continue;const p=t.find(d.providerId,d.modelId);if(p!==void 0){if(a++,this.targetCannotFitContext(p.contextWindow,n)){o++;continue}return{route:d,capacityExhausted:!1}}}return{route:void 0,capacityExhausted:a>0&&a===o}}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 _(this.deps.nodeId,{op:"record-pending",session_id:n,input:t})}pendingEpisodeMatches(e,t,n){const i=this.sessionId(e),a=n.episode;return i!==null&&a!==null&&a.state==="pending"&&a.session_id===i&&a.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 z(E(e),t)?.delayMs??null}clearAndArmFaultRetry(e,t){if(this.deps.installedGeneration()!==t)return;this.clearTimer();const n=Math.min(Math.max(0,e),be),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(a=>{this.abandonTurnEnd(),w({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry fire failed: ${a instanceof Error?a.message:String(a)}`}})})}},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 s=await this.deps.retryProbe();if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming)return;const l=(await this.state()).fault;if(!this.isActiveAutoFault(l)||l.since!==n.since)return;if(!s)return this.clearAndArmFaultRetry(H(l.kind).baseDelayMs,e),"rearmed";i=l}const a=this.deps.turnEnds!==void 0,o=i.retry.attempt??0,d=H(i.kind);if(o>=d.max){await this.recordFault(x(J(E(i)))),await this.clearEpisode(t);return}const p=z(E(i),Date.now());if(p===null){await this.recordFault(x(J(E(i)))),await this.clearEpisode(t);return}if(i.anchor_entry_id!==void 0)try{if((t.sessionManager.getLeafId?.()??null)!==i.anchor_entry_id){const s=await t.navigateTree(i.anchor_entry_id);if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t)return;s.cancelled||this.deps.reWelcomeAll()}}catch(s){w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`fault retry rewind skipped: ${s instanceof Error?s.message:String(s)}`}})}if(!(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t))return this.deps.turnAdmission.admit(async s=>{const l=(await this.state()).fault;if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||!this.isActiveAutoFault(l)||l.since!==i.since){s();return}const y=this.sessionId(t);if(y===null){s();return}if(!(await _(this.deps.nodeId,{op:"admit",session_id:y,fault_id:l.fault_id,retry:p.fault.retry})).applied){s();return}const v=X("recovery",a?{attempt:p.attempt}:{attempt:p.attempt,max:p.max},Re(i.kind,a));Z(v,{type:"runtime",kind:"recovery"}),this.deps.turnEnds?.beginRecovery();try{await j.fresh(()=>t.prompt(v,{preflightResult:s}))}finally{this.deps.turnEnds?.recoveryReturned()}return"prompted"})}}export{Ke as FaultRetry,te as RUN_TURN_RETRY_WINDOW_MS,Ae as isPrefillRejectionDuringEpisode,Re as retryCardBody,_e as runTurnRetryExpired,re as settledProviderFault,He as willDaemonRetry};
1
+ var ae=Object.defineProperty;var m=(r,e)=>ae(r,"name",{value:e,configurable:!0});import{envModelIntent as N,envNodeCwd as G,envProfileId as U}from"../../../shared/env.js";import{isContextOverflow as Y}from"@earendil-works/pi-ai";import{classify as B}from"../../fault-classifier.js";import{isDaemonTransportApiError as de}from"../../../api/errors.js";import{isModelNotFoundError as le,isProviderUnconfiguredError as ce}from"../fault.js";import{brokerRecovery as ue,mutateBrokerFault as W,mutateBrokerProviderRetry as _}from"./daemon-ops.js";import{extractCoolingDeadline as pe}from"../managed-provider-cooling.js";import{extractProviderFailureDetails as fe}from"../../provider-failure.js";import{expandModelCandidates as q,modelRequestFromConfig as V,probeRouteAvailability as he}from"../../model-routes.js";import{operationIdContext as j}from"../../events/operation-id.js";import{emitEvent as w}from"../../events/emit.js";import{CONNECTION_FAULT_RECOVERY_BODY as me,PROVIDER_FAULT_RECOVERY_BODY as ge,RUN_CONNECTION_FAULT_RETRY_BODY as ye,formatCard as X,formatModelFallbackRecovery as ve}from"../../../shared/generated-context.js";import{markFatalOnExhaust as J,nextFaultRetry as z,policyFor as H}from"../fault-recovery.js";import{parseModelSpec as K}from"../model-selection.js";import{endedByAbort as Q}from"../session-cycles.js";import{resolveProviderCandidates as Fe}from"../../subscription-state.js";import{announceMessageOrigin as Z}from"../stamp/channel.js";import{LLM_REFUSAL_DIAGNOSTIC as we,refusalRetryAt as ke}from"./model-adapter.js";import{isSafetyRefusal as ee}from"../../llm/refusal.js";const be=2147483647,te=3e4;function _e(r,e,t){const n=Date.parse(r);return!Number.isFinite(n)||t+e-n>te}m(_e,"runTurnRetryExpired");class ne extends Error{static{m(this,"StaleOutcome")}}const Ee=1e3,Ie=3e4;function Re(r,e){return r!=="connection"?ge:e?ye:me}m(Re,"retryCardBody");function E(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(E,"policyFault");function x(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(x,"faultInput");function ie(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(ie,"liveRouteRank");function re(r){const e=B("pi\u2192provider",r),t=r.diagnostics?.some(n=>n.type===we)??!1;return{classified:e,disposition:t?"fatal":e.disposition}}m(re,"settledProviderFault");function He(r,e){if(r.stopReason!=="error"||Q(r)||ee(r)||Y(r,e))return!1;const{classified:t,disposition:n}=re(r);return n==="auto"&&(t.kind==="connection"||t.kind==="rate-limit"||t.kind==="overloaded")}m(He,"willDaemonRetry");const Se=/does not support assistant message prefill|conversation must end with a user message/i;function Ae(r,e){return e&&Se.test(r)}m(Ae,"isPrefillRejectionDuringEpisode");class Ke{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 ue(this.deps.nodeId)}async recordFault(e){return(await W(this.deps.nodeId,{op:"record",input:e})).fault}async clearFault(e,t=!1){await W(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 _(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 ne},"assertCurrent");let a=Ee;try{for(;;){try{await this.recordStagedOutcome(e,t,i);return}catch(s){if(!de(s))throw s;w({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`settled turn fault write waiting for the daemon: ${s.message}`}})}await new Promise(s=>setTimeout(s,a)),a=Math.min(a*2,Ie),i()}}catch(s){if(!(s instanceof ne))throw s}}async recordStagedOutcome(e,t,n){const{agentEnd:i,watchdogAbort:a,refreshAbort:s,overflowFailure:d,capacityFailure:f}=t,o=e.session,l=m(async F=>{n(),await this.clearEpisode(F)},"clearEpisode"),g=m(async F=>{n(),await this.recordFault(F)},"recordFault"),p=m(async(F,oe)=>{n(),await this.recordPendingProviderFault(F,oe)},"recordPendingProviderFault"),v=await this.state(),h=v.fault,c=v.episode,I=this.sessionId(o),k=this.isActiveAutoFault(h)&&h.link==="pi\u2192provider"?h:c?.state==="admitted"&&c.session_id===I&&this.isActiveAutoFault(c.fault)?c.fault:null,y=k===null?null:{since:k.since,anchorEntryId:k.anchor_entry_id,attempt:k.retry.attempt},u=[...Array.isArray(i?.messages)?i.messages:[]].reverse().find(F=>typeof F=="object"&&F!==null&&F.role==="assistant");if(f!==null||d!==null){await l(o),await g({link:"pi\u2192provider",op:"context overflow recovery",kind:"context-overflow",retry:{disposition:"fatal"},message:f?.errorMessage??d.errorMessage,anchor_entry_id:o.sessionManager.getLeafId?.()??void 0});return}if(a&&u?.stopReason==="aborted"){await p(o,{link:"pi\u2192provider",op:"generation turn",kind:"connection",retry:{disposition:"auto",by:"daemon",...y?.attempt!==void 0?{attempt:y.attempt}:{}},message:typeof u.errorMessage=="string"?u.errorMessage:"engine error (no errorMessage recorded)",...y!==null?{since:y.since}:{},anchor_entry_id:this.retryAnchor(o,y?.anchorEntryId)});return}if(u?.stopReason!=="error"){u!==void 0&&await l(o);return}if(s){await l(o);return}if(ee(u)){await l(o);return}const S=typeof u.errorMessage=="string"?u.errorMessage:"engine error (no errorMessage recorded)";if(Q({stopReason:"error",errorMessage:S})){await l(o);return}const b=Y(u,o.model?.contextWindow),A=re(u),T=y!==null&&A.disposition!=="auto"&&Ae(S,!0),se=T?{kind:"connection",disposition:"auto"}:A.classified,R=T?"auto":A.disposition,C=pe(u),O=ke(u),P=fe(u),$=typeof u.provider=="string"&&u.provider!==""?u.provider:o.model?.provider,L=typeof u.model=="string"&&u.model!==""?u.model:o.model?.id,D=P===void 0||$===void 0||L===void 0?void 0:{provider:$,model:L,...P};b&&await l(o);const M={link:"pi\u2192provider",op:"generation turn",kind:b?"context-overflow":se.kind,retry:b?{disposition:"fatal"}:R==="auto"?{disposition:"auto",by:"daemon",...C!==void 0?{nextAt:new Date(C).toISOString()}:{},...y?.attempt!==void 0?{attempt:y.attempt}:{}}:{disposition:R,...O!==void 0?{nextAt:new Date(O).toISOString()}:{}},message:S,...!b&&R==="auto"&&y!==null?{since:y.since}:{},anchor_entry_id:!b&&R==="auto"?this.retryAnchor(o,y?.anchorEntryId):o.sessionManager.getLeafId?.()??void 0,...D===void 0?{}:{provider_error:D}};M.retry.disposition==="auto"?await p(o,M):(await l(o),await g(M))}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 _(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 _(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";return await this.recordFault(x(E(t.fault))),await this.schedule(),"retry-armed"}hasProgressPastAnchor(e,t){const n=e.sessionManager.getBranch(),i=n.findIndex(s=>s.id===t);return(i===-1?n:n.slice(i+1)).some(s=>{if(s.type!=="message")return!1;const{message:d}=s;return d.role==="toolResult"?!0:d.role==="assistant"&&Array.isArray(d.content)&&d.content.length>0})}async schedule(){const e=this.deps.installedGeneration(),t=this.deps.currentSession();if(t.isStreaming||this.timer!==null)return;if(e===void 0){this.abandonTurnEnd();return}const n=await this.state(),i=n.fault;if(!this.isActiveAutoFault(i)||this.sessionId(t)!==null&&!this.pendingEpisodeMatches(t,i,n)){this.clearTimer(),this.abandonTurnEnd();return}const a=Date.now(),s=this.faultRetryDelayMs(i,a);if(s!==null&&this.deps.turnEnds?.holding()===!0&&_e(i.since,s,a)){await this.endRunTurnOnFault(t,i,s);return}s!==null?this.clearAndArmFaultRetry(s,e):this.abandonTurnEnd()}async endRunTurnOnFault(e,t,n){this.clearTimer(),this.abandonTurnEnd(),w({level:"info",event:"broker.runtime.diagnostic",fields:{message:`run turn completed on provider fault: ${t.kind} retry in ${n}ms is past the ${te}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=B("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(),a=this.deps.currentServices(),s=this.deps.formatModelSpec(i.model,i.thinkingLevel);if(s===null)return;const d=V(this.deps.cfg.model,N(),!1);if(d===void 0)return;const f=q(d,G()??this.deps.cfg.cwd,U()),o=K(s),l=ie(f,o),g=l===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(f.slice(l+1),this.deps.registryOf(a),i.getContextUsage()?.tokens,h=>!this.failedRetryRoutes.has(h.routeId));if(g.route===void 0){g.capacityExhausted&&(this.stageCapacityFailure(n,i),queueMicrotask(()=>{this.deps.installedGeneration()===n&&this.deps.currentSession()===i&&i.abortRetry()}));return}const p=g.route,v=(async()=>{const h=this.deps.registryOf(a).find(p.providerId,p.modelId);if(!h||(await i.setModel(h),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i))return;const c=`${p.providerId}/${p.modelId}`===o.modelSpec?o.thinkingLevel:p.thinkingLevel;i.setThinkingLevel(c??"off"),this.failedRetryRoutes.add(p.routeId),await this.deps.broadcastModelChanged(!1)})().catch(h=>{w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`provider route fallback failed: ${h instanceof Error?h.message:String(h)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.providerFallbackInFlight=null)});this.providerFallbackInFlight=v}maybeFallbackOnUnusableModel(e){if(e.type!=="message_end"||e.message.role!=="assistant"||e.message.stopReason!=="error")return;const t=ce(e.message.errorMessage??"");if(!t&&!le(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(),a=this.deps.currentServices(),s=this.deps.formatModelSpec(i.model,i.thinkingLevel);if(s===null)return;const d=K(s);this.notFoundModels.add(d.modelSpec);const f=V(this.deps.cfg.model,N(),!1),o=f?q(f,G()??this.deps.cfg.cwd,U()):[],l=ie(o,d),g=l===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(o.slice(l+1),this.deps.registryOf(a),i.getContextUsage()?.tokens,c=>!this.notFoundModels.has(`${c.providerId}/${c.modelId}`));if(g.route===void 0){g.capacityExhausted&&this.stageCapacityFailure(n,i);return}const p=g.route,v=`${p.providerId}/${p.modelId}${p.thinkingLevel?`:${p.thinkingLevel}`:""}`,h=(async()=>{const c=this.deps.registryOf(a).find(p.providerId,p.modelId);if(c===void 0){w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback skipped: ${v} is not registered`}});return}if(await i.setModel(c),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i)return;i.setThinkingLevel(p.thinkingLevel??"off"),await this.deps.broadcastModelChanged();const I=this.deps.formatModelSpec(i.model,i.thinkingLevel)??v;if(w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback: ${s} unavailable \u2192 ${I}; re-driving turn`}}),await i.agent.waitForIdle(),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i)return;t&&await this.clearFault("pi\u2192provider");const k=X("recovery",{},ve(s,I,t?"credential":"not-found"));Z(k,{type:"runtime",kind:"recovery"}),await this.deps.turnAdmission.admit(y=>j.fresh(()=>i.prompt(k,{preflightResult:y})))})().catch(c=>{w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback failed: ${c instanceof Error?c.message:String(c)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.notFoundFallbackInFlight=null)});this.notFoundFallbackInFlight=h}nextEligibleFallbackRoute(e,t,n,i){let a=0,s=0;for(const d of e){if(!i(d)||Fe([{...d,...he(d,t),automaticFallback:!0}]).candidates.length===0)continue;const f=t.find(d.providerId,d.modelId);if(f!==void 0){if(a++,this.targetCannotFitContext(f.contextWindow,n)){s++;continue}return{route:d,capacityExhausted:!1}}}return{route:void 0,capacityExhausted:a>0&&a===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 _(this.deps.nodeId,{op:"record-pending",session_id:n,input:t})}pendingEpisodeMatches(e,t,n){const i=this.sessionId(e),a=n.episode;return i!==null&&a!==null&&a.state==="pending"&&a.session_id===i&&a.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 z(E(e),t)?.delayMs??null}clearAndArmFaultRetry(e,t){if(this.deps.installedGeneration()!==t)return;this.clearTimer();const n=Math.min(Math.max(0,e),be),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(a=>{this.abandonTurnEnd(),w({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry fire failed: ${a instanceof Error?a.message:String(a)}`}})})}},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 o=await this.deps.retryProbe();if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming)return;const l=(await this.state()).fault;if(!this.isActiveAutoFault(l)||l.since!==n.since)return;if(!o)return this.clearAndArmFaultRetry(H(l.kind).baseDelayMs,e),"rearmed";i=l}const a=this.deps.turnEnds!==void 0,s=i.retry.attempt??0,d=H(i.kind);if(s>=d.max){await this.recordFault(x(J(E(i)))),await this.clearEpisode(t);return}const f=z(E(i),Date.now());if(f===null){await this.recordFault(x(J(E(i)))),await this.clearEpisode(t);return}if(!(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t))return this.deps.turnAdmission.admit(async o=>{const l=(await this.state()).fault;if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||!this.isActiveAutoFault(l)||l.since!==i.since){o();return}const g=this.sessionId(t);if(g===null){o();return}if(!(await _(this.deps.nodeId,{op:"admit",session_id:g,fault_id:l.fault_id,retry:f.fault.retry})).applied){o();return}const v=i.anchor_entry_id;if(v!==void 0&&(t.sessionManager.getLeafId?.()??null)!==v&&!this.hasProgressPastAnchor(t,v))try{const c=await t.navigateTree(v);if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t){o();return}c.cancelled||this.deps.reWelcomeAll()}catch(c){w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`fault retry rewind skipped: ${c instanceof Error?c.message:String(c)}`}})}const h=X("recovery",a?{attempt:f.attempt}:{attempt:f.attempt,max:f.max},Re(i.kind,a));Z(h,{type:"runtime",kind:"recovery"}),this.deps.turnEnds?.beginRecovery();try{await j.fresh(()=>t.prompt(h,{preflightResult:o}))}finally{this.deps.turnEnds?.recoveryReturned()}return"prompted"})}}export{Ke as FaultRetry,te as RUN_TURN_RETRY_WINDOW_MS,Ae as isPrefillRejectionDuringEpisode,Re as retryCardBody,_e as runTurnRetryExpired,re as settledProviderFault,He as willDaemonRetry};
@@ -1,3 +1,3 @@
1
- var b=Object.defineProperty;var s=(e,r)=>b(e,"name",{value:r,configurable:!0});import{envProfileId as T}from"../../shared/env.js";import{mkdirSync as H}from"node:fs";import{join as V}from"node:path";import{readMergedLaunchConfig as M}from"../config.js";import{fork as U}from"node:child_process";import{fileURLToPath as D}from"node:url";import{freshChildNodeExecPath as F,recheckChildNode as $}from"./branded-host.js";import{LAUNCH_SYSTEM_PROMPT as G}from"./launch-prompt.js";import{nodeEnv as K}from"./nodes.js";import{editorLabel as W}from"../canvas/index.js";import{CANVAS_EXTENSIONS as Ie,CANVAS_STOPHOOK_PATH as Oe,CANVAS_INBOX_WATCHER_PATH as ye,CANVAS_RECAP_PATH as ve,CANVAS_GOAL_CAPTURE_PATH as Le,CANVAS_PASSIVE_CONTEXT_PATH as Re,CANVAS_CONTEXT_INTRO_PATH as be,CANVAS_TOOL_GUIDE_PATH as He,CANVAS_DOC_SUBSTRATE_PATH as Ve,CANVAS_STRUCTURED_OUTPUT_PATH as Me,CANVAS_BASH_VALVE_PATH as Ue}from"./canvas-extensions.js";import{nodeRunDir as X}from"../canvas/paths.js";import{writeFileBeneath as B}from"../spaces/open-beneath.js";import{projectOutputSchema as j}from"./structured-output.js";import{CANVAS_EXTENSIONS as z,mergeCanvasExtensions as E,assertMandatoryCanvasExtensions as Y}from"./canvas-extensions.js";import{modelLadders as a,defaultProvider as P,STRENGTH_ALIASES as p,MODEL_PROVIDER_KEYS as S,MODEL_STRENGTHS as x,THINKING_SUFFIXES as Q,stripThinkingSuffix as m}from"./model-selection.js";import{OPENAI_PROVIDER_ENV as Fe,configuredLadderProviders as $e,inferPortableModelSelection as Ge}from"./model-selection.js";function C(e,r=a()){return r[e]}s(C,"ladderFor");const g={light:0,medium:1,strong:2,ultra:3};function J(e,r,n){const t=m(e);for(const o of[!0,!1]){const i=o?x:[...x].sort((c,u)=>g[c]-g[u]);for(const c of n)for(const u of i){const f=r[c][u];if(!(o?f!==e:m(f)!==t))return{provider:c,strength:u}}}return null}s(J,"findLadderCell");function k(e){const r=e.lastIndexOf("/"),n=r===-1?void 0:e.slice(0,r),t=r===-1?e:e.slice(r+1),o=p[t];return o!==void 0?o:n!==void 0&&n!=="anthropic"?null:t==="opus"?"strong":t==="sonnet"?"medium":t==="haiku"?"light":null}s(k,"modelTokenStrength");function w(e,r=a()){const n=k(e);return n!==null?n:J(A(e,r),r,S)?.strength??null}s(w,"resolvedModelStrength");function Z(e){return e==="review"||e.startsWith("review/")||e.includes("/reviewers/")}s(Z,"isReviewQualityKind");function q(e,r,n,t=a()){if(r===void 0)return n;if(!Z(e)||n===void 0)return r;const o=w(r,t),i=w(n,t);return o===null||i===null?r:g[o]<g[i]?n:r}s(q,"floorReviewModel");function N(e){if(e.includes("/")){const r=e.lastIndexOf("/"),n=e.slice(0,r),t=e.slice(r+1);return n!==""&&(p[t]!==void 0||n==="anthropic"&&(t==="opus"||t==="sonnet"||t==="haiku"))}return p[e]!==void 0||e==="opus"||e==="sonnet"||e==="haiku"}s(N,"isPortableModelToken");function I(e,r=a()){const n=k(e);if(n!==null)return e.includes("/")?{family:e.slice(0,e.lastIndexOf("/")),strength:n}:e==="opus"||e==="sonnet"||e==="haiku"?{family:"anthropic",strength:n}:{family:P(r),strength:n}}s(I,"logicalModelIntent");function _e(e,r=a()){return N(e)?I(e,r):void 0}s(_e,"explicitModelIntent");function A(e,r=a()){if(e.includes("/")){const t=e.indexOf("/"),o=e.slice(0,t),i=e.slice(t+1),c=p[i];if(c!==void 0&&(o==="anthropic"||o==="openai"))return C(o,r)[c];if(o==="anthropic"){if(i==="opus")return r.anthropic.strong;if(i==="sonnet")return r.anthropic.medium;if(i==="haiku")return r.anthropic.light}return e}const n=p[e];return n!==void 0?C(P(r),r)[n]:e==="opus"?r.anthropic.strong:e==="sonnet"?r.anthropic.medium:e==="haiku"?r.anthropic.light:e}s(A,"normalizeModel");function O(e){const r=e.lastIndexOf(":");if(r<=0)return;const n=e.slice(r+1);return Q.has(n)?n:void 0}s(O,"thinkingSuffixOf");function ee(e=S){const r=e.length>0?e:S,n=a(),t=[];for(const o of x)for(const i of r)t.push(n[i][o]);return t}s(ee,"interleavedLadderCycle");function Te(e,r=1,n){const t=ee(n),o=A(e),i=m(o),c=O(o);let u=t.findIndex(f=>m(f)===i&&O(f)===c);return u===-1&&(u=t.findIndex(f=>m(f)===i)),u===-1?t[0]:t[(u+r+t.length)%t.length]}s(Te,"nextLadderModel");function y(e,r,n,t){const o=t.kinds[e],i=t.modelLadders,c=r==="orchestrator"?o?.orchestratorModel??o?.model:o?.model,u=n.modelExact===!0?n.model:q(e,n.model,c,i),f=n.modelExact===!0||n.modelOverride===!0&&n.model!==void 0&&!N(n.model),l=n.modelExact===!0?u:u!==void 0?A(u,i):void 0,h=f?void 0:n.modelIntent??(u!==void 0?I(u,i):void 0);return{launch:{model:l,modelIntent:h,modelExact:f?!0:void 0,tools:o?.tools??[],extensions:E([...z,...o?.extensions??[]]),systemPrompt:G,env:{...n.extraEnv??{},...h?{CRTR_MODEL_INTENT:`${h.family}/${h.strength}`}:{}}},lifecycle:n.lifecycle}}s(y,"buildLaunchSpecFromMerged");function Ee(e,r,n){return y(e,r,n,M(n.cwd??process.cwd(),n.profileId!==void 0?n.profileId:T()||null))}s(Ee,"buildLaunchSpec");async function Pe(e,r,n){const t=n.cwd??process.cwd(),o=n.profileId!==void 0?n.profileId:T()||null,i=await re(t,o);return y(e,r,n,i)}s(Pe,"buildLaunchSpecAsync");const v=15e3;function ne(e,r,n){const t=r!==null?`was killed by ${r}`:`exited with code ${e}`,o=n.trim().split(`
1
+ var V=Object.defineProperty;var s=(e,r)=>V(e,"name",{value:r,configurable:!0});import{envProfileId as T}from"../../shared/env.js";import{mkdirSync as M}from"node:fs";import{join as U}from"node:path";import{readMergedLaunchConfig as D}from"../config.js";import{fork as F}from"node:child_process";import{fileURLToPath as $}from"node:url";import{freshChildNodeExecPath as G,recheckChildNode as K}from"./branded-host.js";import{LAUNCH_SYSTEM_PROMPT as W}from"./launch-prompt.js";import{nodeEnv as X}from"./nodes.js";import{editorLabel as B}from"../canvas/index.js";import{CANVAS_EXTENSIONS as ye,CANVAS_STOPHOOK_PATH as ve,CANVAS_INBOX_WATCHER_PATH as Le,CANVAS_RECAP_PATH as Re,CANVAS_GOAL_CAPTURE_PATH as be,CANVAS_PASSIVE_CONTEXT_PATH as He,CANVAS_CONTEXT_INTRO_PATH as Ve,CANVAS_TOOL_GUIDE_PATH as Me,CANVAS_DOC_SUBSTRATE_PATH as Ue,CANVAS_STRUCTURED_OUTPUT_PATH as De,CANVAS_BASH_VALVE_PATH as Fe}from"./canvas-extensions.js";import{nodeRunDir as j}from"../canvas/paths.js";import{writeFileBeneath as z}from"../spaces/open-beneath.js";import{projectOutputSchema as Y}from"./structured-output.js";import{CANVAS_EXTENSIONS as Q,mergeCanvasExtensions as P,assertMandatoryCanvasExtensions as J}from"./canvas-extensions.js";import{modelLadders as a,defaultProvider as C,STRENGTH_ALIASES as p,MODEL_PROVIDER_KEYS as x,MODEL_STRENGTHS as A,THINKING_SUFFIXES as Z,stripThinkingSuffix as m}from"./model-selection.js";import{OPENAI_PROVIDER_ENV as Ge,configuredLadderProviders as Ke,inferPortableModelSelection as We}from"./model-selection.js";function k(e,r=a()){return r[e]}s(k,"ladderFor");const S={light:0,medium:1,strong:2,ultra:3};function q(e,r,n){const t=m(e);for(const o of[!0,!1]){const i=o?A:[...A].sort((c,u)=>S[c]-S[u]);for(const c of n)for(const u of i){const f=r[c][u];if(!(o?f!==e:m(f)!==t))return{provider:c,strength:u}}}return null}s(q,"findLadderCell");function N(e){const r=e.lastIndexOf("/"),n=r===-1?void 0:e.slice(0,r),t=r===-1?e:e.slice(r+1),o=p[t];return o!==void 0?o:n!==void 0&&n!=="anthropic"?null:t==="opus"?"strong":t==="sonnet"?"medium":t==="haiku"?"light":null}s(N,"modelTokenStrength");function w(e,r=a()){const n=N(e);return n!==null?n:q(_(e,r),r,x)?.strength??null}s(w,"resolvedModelStrength");function ee(e){return e==="review"||e.startsWith("review/")||e.includes("/reviewers/")}s(ee,"isReviewQualityKind");function ne(e,r,n,t=a()){if(r===void 0)return n;if(!ee(e)||n===void 0)return r;const o=w(r,t),i=w(n,t);return o===null||i===null?r:S[o]<S[i]?n:r}s(ne,"floorReviewModel");function I(e){if(e.includes("/")){const r=e.lastIndexOf("/"),n=e.slice(0,r),t=e.slice(r+1);return n!==""&&(p[t]!==void 0||n==="anthropic"&&(t==="opus"||t==="sonnet"||t==="haiku"))}return p[e]!==void 0||e==="opus"||e==="sonnet"||e==="haiku"}s(I,"isPortableModelToken");function O(e,r=a()){const n=N(e);if(n!==null)return e.includes("/")?{family:e.slice(0,e.lastIndexOf("/")),strength:n}:e==="opus"||e==="sonnet"||e==="haiku"?{family:"anthropic",strength:n}:{family:C(r),strength:n}}s(O,"logicalModelIntent");function Te(e,r=a()){return I(e)?O(e,r):void 0}s(Te,"explicitModelIntent");function _(e,r=a()){if(e.includes("/")){const t=e.indexOf("/"),o=e.slice(0,t),i=e.slice(t+1),c=p[i];if(c!==void 0&&(o==="anthropic"||o==="openai"))return k(o,r)[c];if(o==="anthropic"){if(i==="opus")return r.anthropic.strong;if(i==="sonnet")return r.anthropic.medium;if(i==="haiku")return r.anthropic.light}return e}const n=p[e];return n!==void 0?k(C(r),r)[n]:e==="opus"?r.anthropic.strong:e==="sonnet"?r.anthropic.medium:e==="haiku"?r.anthropic.light:e}s(_,"normalizeModel");function y(e){const r=e.lastIndexOf(":");if(r<=0)return;const n=e.slice(r+1);return Z.has(n)?n:void 0}s(y,"thinkingSuffixOf");function re(e=x){const r=e.length>0?e:x,n=a(),t=[];for(const o of A)for(const i of r)t.push(n[i][o]);return t}s(re,"interleavedLadderCycle");function Pe(e,r=1,n){const t=re(n),o=_(e),i=m(o),c=y(o);let u=t.findIndex(f=>m(f)===i&&y(f)===c);return u===-1&&(u=t.findIndex(f=>m(f)===i)),u===-1?t[0]:t[(u+r+t.length)%t.length]}s(Pe,"nextLadderModel");function v(e,r,n,t){const o=t.kinds[e],i=t.modelLadders,c=r==="orchestrator"?o?.orchestratorModel??o?.model:o?.model,u=n.modelExact===!0?n.model:ne(e,n.model,c,i),f=n.modelExact===!0||n.modelOverride===!0&&n.model!==void 0&&!I(n.model),l=n.modelExact===!0?u:u!==void 0?_(u,i):void 0,h=f?void 0:n.modelIntent??(u!==void 0?O(u,i):void 0);return{launch:{model:l,modelIntent:h,modelExact:f?!0:void 0,tools:o?.tools??[],extensions:P([...Q,...o?.extensions??[]]),systemPrompt:W,env:{...n.extraEnv??{},...h?{CRTR_MODEL_INTENT:`${h.family}/${h.strength}`}:{}}},lifecycle:n.lifecycle}}s(v,"buildLaunchSpecFromMerged");function Ce(e,r,n){return v(e,r,n,D(n.cwd??process.cwd(),n.profileId!==void 0?n.profileId:T()||null))}s(Ce,"buildLaunchSpec");async function ke(e,r,n){const t=n.cwd??process.cwd(),o=n.profileId!==void 0?n.profileId:T()||null,i=await oe(t,o);return v(e,r,n,i)}s(ke,"buildLaunchSpecAsync");const L=15e3;function te(e,r,n){const t=r!==null?`was killed by ${r}`:`exited with code ${e}`,o=n.trim().split(`
2
2
  `).slice(-8).join(`
3
- `).slice(-1500);return`launch config subprocess ${t}${o!==""?`: ${o}`:" (no stderr output)"}`}s(ne,"describeLaunchConfigExit");class _ extends Error{static{s(this,"LaunchConfigWorkerCrash")}}async function re(e,r,n={}){const t=n.execPath??await F(),o=n.workerPath??D(new URL("./launch-config-worker.js",import.meta.url));try{return await L(e,r,t,o)}catch(i){if(!(i instanceof _))throw i;const c=await $(t);if(c===t)throw i;return L(e,r,c,o)}}s(re,"readMergedLaunchConfigAsync");function L(e,r,n,t){return new Promise((o,i)=>{const c=U(t,[],{execPath:n,stdio:["ignore","ignore","pipe","ipc"],serialization:"advanced"});let u="";c.stderr?.setEncoding("utf8").on("data",d=>{u=(u+d).slice(-8192)}),c.send({cwd:e,profileId:r});let f=!1;const l=s(d=>{f||(f=!0,clearTimeout(h),c.connected&&c.disconnect(),c.killed||c.kill("SIGKILL"),d())},"finish"),h=setTimeout(()=>l(()=>i(new Error(`timed out after ${v}ms reading launch config for ${e}`))),v);c.once("message",d=>{d.error!==void 0?l(()=>i(new Error(d.error))):d.config!==void 0?l(()=>o(d.config)):l(()=>i(new Error("launch config subprocess returned no result")))}),c.once("error",d=>l(()=>i(new _(`launch config subprocess failed: ${d.message}`)))),c.once("close",(d,R)=>{d!==0&&!f&&l(()=>i(new _(ne(d,R,u))))})})}s(L,"runLaunchConfigWorker");function te(e,r){try{const n=X(e);H(n,{recursive:!0});const t=V(n,"system-prompt.md");return B(n,t,r,{atomic:!0}),t}catch{return null}}s(te,"writeSystemPromptFile");function Ce(e,r={}){const n=e.launch,t=E(n?.extensions);Y(t);const o=[];for(const i of t)o.push("-e",i);if(o.push("-n",W(e)),r.forkFrom!==void 0&&r.forkFrom!=="")o.push("--fork",r.forkFrom);else{const i=r.resumeSessionPath??r.resumeSessionId;i!==void 0&&o.push("--session",i),r.newCycle===!0&&o.push("--crtr-new-cycle")}if(n?.model!==void 0&&o.push("--model",n.model),n?.modelExact===!0&&o.push("--crtr-model-exact"),n?.tools!==void 0&&n.tools.length>0&&o.push("--tools",n.tools.join(",")),n?.systemPrompt!==void 0&&n.systemPrompt!==""){const i=te(e.node_id,n.systemPrompt)??n.systemPrompt;o.push("--append-system-prompt",i)}return r.prompt!==void 0&&r.prompt!==""&&o.push(r.prompt),j(e.node_id),{argv:o,env:K(e)}}s(Ce,"buildPiArgv");function ke(e){for(const[t,o]of Object.entries(e.env))process.env[t]=o;const r={cwd:e.env.CRTR_NODE_CWD??process.cwd(),extensionPaths:[]},n=e.argv;for(let t=0;t<n.length;t++){const o=n[t];switch(o){case"-e":r.extensionPaths.push(n[++t]);break;case"-n":r.editorName=n[++t];break;case"--fork":r.forkFrom=n[++t];break;case"--session":{const i=n[++t];i.includes("/")||i.endsWith(".jsonl")?r.resumeSessionPath=i:r.resumeSessionId=i;break}case"--model":r.model=n[++t];break;case"--crtr-model-exact":r.modelExact=!0;break;case"--tools":r.tools=n[++t].split(",").filter(i=>i!=="");break;case"--append-system-prompt":r.appendSystemPromptPath=n[++t];break;case"--crtr-new-cycle":r.newCycle=!0;break;default:r.firstPrompt=o;break}}return r}s(ke,"piInvocationToSdkConfig");export{Ue as CANVAS_BASH_VALVE_PATH,be as CANVAS_CONTEXT_INTRO_PATH,Ve as CANVAS_DOC_SUBSTRATE_PATH,Ie as CANVAS_EXTENSIONS,Le as CANVAS_GOAL_CAPTURE_PATH,ye as CANVAS_INBOX_WATCHER_PATH,Re as CANVAS_PASSIVE_CONTEXT_PATH,ve as CANVAS_RECAP_PATH,Oe as CANVAS_STOPHOOK_PATH,Me as CANVAS_STRUCTURED_OUTPUT_PATH,He as CANVAS_TOOL_GUIDE_PATH,Fe as OPENAI_PROVIDER_ENV,Ee as buildLaunchSpec,Pe as buildLaunchSpecAsync,Ce as buildPiArgv,$e as configuredLadderProviders,ne as describeLaunchConfigExit,_e as explicitModelIntent,Ge as inferPortableModelSelection,ee as interleavedLadderCycle,N as isPortableModelToken,I as logicalModelIntent,k as modelTokenStrength,Te as nextLadderModel,A as normalizeModel,ke as piInvocationToSdkConfig,re as readMergedLaunchConfigAsync};
3
+ `).slice(-1500);return`launch config subprocess ${t}${o!==""?`: ${o}`:" (no stderr output)"}`}s(te,"describeLaunchConfigExit");class R extends Error{static{s(this,"LaunchConfigWorkerCrash")}}async function oe(e,r,n={}){const t=n.execPath??await G(),o=n.workerPath??$(new URL("./launch-config-worker.js",import.meta.url));try{return await b(e,r,t,o)}catch(i){if(!(i instanceof R))throw i;const c=await K(t);if(c===t)throw i;return b(e,r,c,o)}}s(oe,"readMergedLaunchConfigAsync");function b(e,r,n,t){return new Promise((o,i)=>{const c=F(t,[],{execPath:n,stdio:["ignore","ignore","pipe","ipc"],serialization:"advanced"});let u="";c.stderr?.setEncoding("utf8").on("data",d=>{u=(u+d).slice(-8192)}),c.send({cwd:e,profileId:r});let f=!1;const l=s(d=>{f||(f=!0,clearTimeout(h),c.connected&&c.disconnect(),c.killed||c.kill("SIGKILL"),d())},"finish"),h=setTimeout(()=>l(()=>i(new Error(`timed out after ${L}ms reading launch config for ${e}`))),L);c.once("message",d=>{d.error!==void 0?l(()=>i(new Error(d.error))):d.config!==void 0?l(()=>o(d.config)):l(()=>i(new Error("launch config subprocess returned no result")))});let g=null;c.on("error",d=>{g??=d}),c.once("close",(d,H)=>{if(f)return;const E=te(d,H,u);l(()=>i(new R(g===null?E:`${E} (${g.message})`)))})})}s(b,"runLaunchConfigWorker");function ie(e,r){try{const n=j(e);M(n,{recursive:!0});const t=U(n,"system-prompt.md");return z(n,t,r,{atomic:!0}),t}catch{return null}}s(ie,"writeSystemPromptFile");function Ne(e,r={}){const n=e.launch,t=P(n?.extensions);J(t);const o=[];for(const i of t)o.push("-e",i);if(o.push("-n",B(e)),r.forkFrom!==void 0&&r.forkFrom!=="")o.push("--fork",r.forkFrom);else{const i=r.resumeSessionPath??r.resumeSessionId;i!==void 0&&o.push("--session",i),r.newCycle===!0&&o.push("--crtr-new-cycle")}if(n?.model!==void 0&&o.push("--model",n.model),n?.modelExact===!0&&o.push("--crtr-model-exact"),n?.tools!==void 0&&n.tools.length>0&&o.push("--tools",n.tools.join(",")),n?.systemPrompt!==void 0&&n.systemPrompt!==""){const i=ie(e.node_id,n.systemPrompt)??n.systemPrompt;o.push("--append-system-prompt",i)}return r.prompt!==void 0&&r.prompt!==""&&o.push(r.prompt),Y(e.node_id),{argv:o,env:X(e)}}s(Ne,"buildPiArgv");function we(e){for(const[t,o]of Object.entries(e.env))process.env[t]=o;const r={cwd:e.env.CRTR_NODE_CWD??process.cwd(),extensionPaths:[]},n=e.argv;for(let t=0;t<n.length;t++){const o=n[t];switch(o){case"-e":r.extensionPaths.push(n[++t]);break;case"-n":r.editorName=n[++t];break;case"--fork":r.forkFrom=n[++t];break;case"--session":{const i=n[++t];i.includes("/")||i.endsWith(".jsonl")?r.resumeSessionPath=i:r.resumeSessionId=i;break}case"--model":r.model=n[++t];break;case"--crtr-model-exact":r.modelExact=!0;break;case"--tools":r.tools=n[++t].split(",").filter(i=>i!=="");break;case"--append-system-prompt":r.appendSystemPromptPath=n[++t];break;case"--crtr-new-cycle":r.newCycle=!0;break;default:r.firstPrompt=o;break}}return r}s(we,"piInvocationToSdkConfig");export{Fe as CANVAS_BASH_VALVE_PATH,Ve as CANVAS_CONTEXT_INTRO_PATH,Ue as CANVAS_DOC_SUBSTRATE_PATH,ye as CANVAS_EXTENSIONS,be as CANVAS_GOAL_CAPTURE_PATH,Le as CANVAS_INBOX_WATCHER_PATH,He as CANVAS_PASSIVE_CONTEXT_PATH,Re as CANVAS_RECAP_PATH,ve as CANVAS_STOPHOOK_PATH,De as CANVAS_STRUCTURED_OUTPUT_PATH,Me as CANVAS_TOOL_GUIDE_PATH,Ge as OPENAI_PROVIDER_ENV,Ce as buildLaunchSpec,ke as buildLaunchSpecAsync,Ne as buildPiArgv,Ke as configuredLadderProviders,te as describeLaunchConfigExit,Te as explicitModelIntent,We as inferPortableModelSelection,re as interleavedLadderCycle,I as isPortableModelToken,O as logicalModelIntent,N as modelTokenStrength,Pe as nextLadderModel,_ as normalizeModel,we as piInvocationToSdkConfig,oe as readMergedLaunchConfigAsync};
@@ -1,18 +1,18 @@
1
- var Z=Object.defineProperty;var o=(e,r)=>Z(e,"name",{value:r,configurable:!0});import{existsSync as k,mkdirSync as P,readFileSync as ee,realpathSync as H,rmSync as te,writeFileSync as re}from"node:fs";import{createHash as ne}from"node:crypto";import{dirname as oe,isAbsolute as ae,join as se,relative as ie,resolve as N,sep as ce}from"node:path";import{gitAsync as he,gitSync as i}from"./git.js";import{crtrHome as C}from"./canvas/paths.js";import{closeWorktreeAndRepoint as ue,getNode as v,recordLandIntent as le}from"./canvas/index.js";import{CONFIG_FILE as de,CRTR_DIR_NAME as O,STATE_FILE as fe}from"../types.js";import{withExclusiveDirectoryLock as pe,withExclusiveDirectoryLockAsync as me}from"./exclusive-lock.js";import{parseStatusPorcelain as be,planLandIntoCheckout as ke,refDeleteTransactionStdin as _e}from"./worktree-landing.js";import{shellQuote as w}from"../shared/shell-quote.js";class f extends Error{static{o(this,"WorktreeError")}code;next;detail;constructor(r,t,n,a){super(t),this.name="WorktreeError",this.code=r,this.next=n,this.detail=a}}function _(e,r,t,n){const a=i(r,e);if(a.status!==0){const s=(a.stderr.trim()||a.stdout.trim()).trim();throw new f(t,`git ${r.join(" ")} failed`,n,s)}return a.stdout.trim()}o(_,"runGit");function F(){return`${C()}/worktrees`}o(F,"managedWorktreesRoot");function ge(e){return`${F()}/${e}`}o(ge,"managedWorktreePath");function Xe(e){return e?.managed_worktree?.state==="open"}o(Xe,"hasOpenManagedWorktree");async function x(e){const t=(await v(e))?.managed_worktree;return t?.state==="open"?t:null}o(x,"openManagedWorktreeForNode");function we(e){const r=i(["rev-parse","--show-toplevel"],e);if(r.status!==0){const t=(r.stderr.trim()||r.stdout.trim()).trim();throw new f("not_git_repo",`cwd ${e} is not a git repository`,"Pass --cwd <real repo> pointing inside a git repository.",t)}return r.stdout.trim()}o(we,"repoRootFor");function ye(e){const r=ie(E(F()),E(e));return r!==""&&r!==".."&&!r.startsWith(`..${ce}`)&&!ae(r)}o(ye,"isManagedWorktreeBase");function $e(e,r){const t=`refs/heads/${r}`,n=i(["show-ref","--verify","--quiet",t],e);if(n.status===0)return!0;if(n.status===1&&n.executionFailed!==!0)return!1;const a=n.stderr.trim()||"<empty>",s=n.processError===void 0?"":`
1
+ var Z=Object.defineProperty;var o=(e,r)=>Z(e,"name",{value:r,configurable:!0});import{existsSync as k,mkdirSync as H,readFileSync as ee,realpathSync as N,rmSync as te,writeFileSync as re}from"node:fs";import{createHash as ne}from"node:crypto";import{dirname as oe,isAbsolute as ae,join as se,relative as ie,resolve as C,sep as ce}from"node:path";import{gitAsync as he,gitSync as i}from"./git.js";import{crtrHome as O}from"./canvas/paths.js";import{closeWorktreeAndRepoint as ue,getNode as v,recordLandIntent as le}from"./canvas/index.js";import{CONFIG_FILE as de,CRTR_DIR_NAME as A,STATE_FILE as fe}from"../types.js";import{withExclusiveDirectoryLock as pe,withExclusiveDirectoryLockAsync as me}from"./exclusive-lock.js";import{parseStatusPorcelain as be,planLandIntoCheckout as ke,refDeleteTransactionStdin as _e}from"./worktree-landing.js";import{shellQuote as w}from"../shared/shell-quote.js";class f extends Error{static{o(this,"WorktreeError")}code;next;detail;constructor(r,t,n,a){super(t),this.name="WorktreeError",this.code=r,this.next=n,this.detail=a}}function _(e,r,t,n){const a=i(r,e);if(a.status!==0){const s=(a.stderr.trim()||a.stdout.trim()).trim();throw new f(t,`git ${r.join(" ")} failed`,n,s)}return a.stdout.trim()}o(_,"runGit");function F(){return`${O()}/worktrees`}o(F,"managedWorktreesRoot");function ge(e){return`${F()}/${e}`}o(ge,"managedWorktreePath");function Xe(e){return e?.managed_worktree?.state==="open"}o(Xe,"hasOpenManagedWorktree");async function x(e){const t=(await v(e))?.managed_worktree;return t?.state==="open"?t:null}o(x,"openManagedWorktreeForNode");function we(e){const r=i(["rev-parse","--show-toplevel"],e);if(r.status!==0){const t=(r.stderr.trim()||r.stdout.trim()).trim();throw new f("not_git_repo",`cwd ${e} is not a git repository`,"Pass --cwd <real repo> pointing inside a git repository.",t)}return r.stdout.trim()}o(we,"repoRootFor");function ye(e){const r=ie(E(F()),E(e));return r!==""&&r!==".."&&!r.startsWith(`..${ce}`)&&!ae(r)}o(ye,"isManagedWorktreeBase");function $e(e,r){const t=`refs/heads/${r}`,n=i(["show-ref","--verify","--quiet",t],e);if(n.status===0)return!0;if(n.status===1&&n.executionFailed!==!0)return!1;const a=n.stderr.trim()||"<empty>",s=n.processError===void 0?"":`
2
2
  process error: ${n.processError}`;throw new f("recorded_branch_inspection_failed",`could not inspect recorded branch '${r}' (git show-ref exited with status ${n.status})`,"Inspect the repository and its Git metadata, then retry.",`status: ${n.status}
3
- stderr: ${a}${s}`)}o($e,"recordedBranchExists");function A(e,r){const t=`refs/heads/${r}`,n=i(["show-ref","--verify","--quiet",t],e);if(n.status===1&&n.executionFailed!==!0)throw new f("missing_base_branch",`managed worktree base '${r}' does not name an existing local branch`,"The base must be a local branch that landing can fast-forward \u2014 freshen your local branch from origin first (rebase-not-merge), then base on it.");if(n.status!==0){const a=n.stderr.trim()||"<empty>",s=n.processError===void 0?"":`
3
+ stderr: ${a}${s}`)}o($e,"recordedBranchExists");function R(e,r){const t=`refs/heads/${r}`,n=i(["show-ref","--verify","--quiet",t],e);if(n.status===1&&n.executionFailed!==!0)throw new f("missing_base_branch",`managed worktree base '${r}' does not name an existing local branch`,"The base must be a local branch that landing can fast-forward \u2014 freshen your local branch from origin first (rebase-not-merge), then base on it.");if(n.status!==0){const a=n.stderr.trim()||"<empty>",s=n.processError===void 0?"":`
4
4
  process error: ${n.processError}`;throw new f("base_branch_inspection_failed",`could not inspect local base branch '${r}' (git show-ref exited with status ${n.status})`,"Inspect the repository and its Git metadata, then retry.",`status: ${n.status}
5
- stderr: ${a}${s}`)}return _(e,["rev-parse","--verify",t],"base_branch_resolve_failed","Inspect the local base branch, then retry.")}o(A,"resolveLandableBase");function ve(e,r){const t=_(e,["worktree","list","--porcelain"],"worktree_list_failed","Inspect the repository worktrees manually, then retry.");return I(t,r)}o(ve,"checkoutPathForBranch");function I(e,r){let t=null;for(const n of e.split(`
6
- `))if(n.startsWith("worktree "))t=n.slice(9).trim();else if(n===`branch refs/heads/${r}`)return t;return null}o(I,"checkoutPathForBranchInList");function xe(e,r){return e===r||E(e)===E(r)}o(xe,"samePath");function E(e){try{return H.native(e)}catch{return N(e)}}o(E,"canonicalPath");const q=3e4;function G(e){const r=`${C()}/worktree-locks`;return P(r,{recursive:!0}),`${r}/${ne("sha256").update(e).digest("hex")}`}o(G,"worktreeLockPathForCommonDir");function S(e){const r=_(e,["rev-parse","--git-common-dir"],"git_common_dir_failed","Inspect the repository git metadata, then retry.");return H(N(e,r))}o(S,"commonGitDir");function B(e,r){const t=S(e);return pe(G(t),r,{timeoutMs:q,timeoutError:o(()=>new f("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${t}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(B,"withRepositoryWorktreeLock");async function j(e,r){const t=S(e);return me(G(t),r,{timeoutMs:q,timeoutError:o(()=>new f("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${t}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(j,"withRepositoryWorktreeLockAsync");function D(e,r,t){const n=i(["worktree","list","--porcelain"],e);if(n.status!==0)return null;let a=null;for(const s of n.stdout.split(`
5
+ stderr: ${a}${s}`)}return _(e,["rev-parse","--verify",t],"base_branch_resolve_failed","Inspect the local base branch, then retry.")}o(R,"resolveLandableBase");function ve(e,r){const t=_(e,["worktree","list","--porcelain"],"worktree_list_failed","Inspect the repository worktrees manually, then retry.");return I(t,r)}o(ve,"checkoutPathForBranch");function I(e,r){let t=null;for(const n of e.split(`
6
+ `))if(n.startsWith("worktree "))t=n.slice(9).trim();else if(n===`branch refs/heads/${r}`)return t;return null}o(I,"checkoutPathForBranchInList");function xe(e,r){return e===r||E(e)===E(r)}o(xe,"samePath");function E(e){try{return N.native(e)}catch{return C(e)}}o(E,"canonicalPath");const q=3e4;function G(e){const r=`${O()}/worktree-locks`;return H(r,{recursive:!0}),`${r}/${ne("sha256").update(e).digest("hex")}`}o(G,"worktreeLockPathForCommonDir");function S(e){const r=_(e,["rev-parse","--git-common-dir"],"git_common_dir_failed","Inspect the repository git metadata, then retry.");return N(C(e,r))}o(S,"commonGitDir");function B(e,r){const t=S(e);return pe(G(t),r,{timeoutMs:q,timeoutError:o(()=>new f("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${t}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(B,"withRepositoryWorktreeLock");async function j(e,r){const t=S(e);return me(G(t),r,{timeoutMs:q,timeoutError:o(()=>new f("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${t}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(j,"withRepositoryWorktreeLockAsync");function D(e,r,t){const n=i(["worktree","list","--porcelain"],e);if(n.status!==0)return null;let a=null;for(const s of n.stdout.split(`
7
7
  `))if(s.startsWith("worktree "))a=s.slice(9).trim();else if(a!==null&&xe(a,r)&&s===`branch refs/heads/${t}`)return!0;return!1}o(D,"registeredWorktree");function Ee(e,r,t){const n=[],a=`refs/heads/${r}`,s=i(["rev-parse","--verify","--quiet",a],e);s.status===0&&n.push({ref:a,sha:s.stdout.trim()});const u=i(["rev-parse","--symbolic-full-name","--verify",`${r}@{upstream}`],e),h=u.status===0&&u.stdout.trim().startsWith("refs/")?u.stdout.trim():`refs/remotes/origin/${r}`,m=i(["rev-parse","--verify","--quiet",h],e);if(m.status===0&&n.push({ref:h,sha:m.stdout.trim()}),n.length===0)return null;for(const d of n)if(i(["merge-base","--is-ancestor",t,d.sha],e).status===0)return{containingRef:d.ref,containingSha:d.sha};for(const d of n){const l=i(["cherry",d.sha,t],e);if(l.status!==0)continue;if(l.stdout.split(`
8
8
  `).map(c=>c.trim()).filter(c=>c!=="").every(c=>c.startsWith("-")))return{containingRef:d.ref,containingSha:d.sha}}for(const d of n){const l=i(["rev-parse","--verify",`${d.sha}^{tree}`],e);if(l.status!==0)continue;const b=i(["merge-tree","--write-tree",d.sha,t],e);if(b.status===0&&b.stdout.split(`
9
- `)[0]?.trim()===l.stdout.trim())return{containingRef:d.ref,containingSha:d.sha}}return null}o(Ee,"proveBranchContained");function K(e,r){const t=i(["rev-parse","--verify",`refs/heads/${e.branch}`],e.repo_root);if(t.status!==0||t.stdout.trim()!==e.base_sha)return;const n=D(e.repo_root,e.path,e.branch);if(n!==null){if(n){if(i(["worktree","remove","--force",e.path],e.repo_root).status!==0||D(e.repo_root,e.path,e.branch)!==!1)return}else if(r&&k(e.path))try{te(e.path,{recursive:!0,force:!0})}catch{return}try{i(["update-ref","-d",`refs/heads/${e.branch}`,e.base_sha],e.repo_root)}catch{}}}o(K,"rollbackManagedWorktreeLocked");function ze(e){B(e.repo_root,()=>K(e,!1))}o(ze,"rollbackManagedWorktree");const Se="# crtr: exclude crtr's own untracked project-scope bookkeeping (auto-added at managed-worktree creation)",De=[`/${O}/${de}`,`/${O}/${fe}`];function We(e){try{const r=se(S(e),"info","exclude");P(oe(r),{recursive:!0});let t="";try{t=ee(r,"utf8")}catch{}const n=new Set(t.split(`
9
+ `)[0]?.trim()===l.stdout.trim())return{containingRef:d.ref,containingSha:d.sha}}return null}o(Ee,"proveBranchContained");function K(e,r){const t=i(["rev-parse","--verify",`refs/heads/${e.branch}`],e.repo_root);if(t.status!==0||t.stdout.trim()!==e.base_sha)return;const n=D(e.repo_root,e.path,e.branch);if(n!==null){if(n){if(i(["worktree","remove","--force",e.path],e.repo_root).status!==0||D(e.repo_root,e.path,e.branch)!==!1)return}else if(r&&k(e.path))try{te(e.path,{recursive:!0,force:!0})}catch{return}try{i(["update-ref","-d",`refs/heads/${e.branch}`,e.base_sha],e.repo_root)}catch{}}}o(K,"rollbackManagedWorktreeLocked");function ze(e){B(e.repo_root,()=>K(e,!1))}o(ze,"rollbackManagedWorktree");const Se="# crtr: exclude crtr's own untracked project-scope bookkeeping (auto-added at managed-worktree creation)",De=[`/${A}/${de}`,`/${A}/${fe}`,`/${A}/operations.db*`];function We(e){try{const r=se(S(e),"info","exclude");H(oe(r),{recursive:!0});let t="";try{t=ee(r,"utf8")}catch{}const n=new Set(t.split(`
10
10
  `).map(h=>h.trim())),a=De.filter(h=>!n.has(h));if(a.length===0)return;const u=(t.length>0&&!t.endsWith(`
11
11
  `)?`
12
12
  `:"")+Se+`
13
13
  `+a.join(`
14
14
  `)+`
15
- `;re(r,t+u)}catch{}}o(We,"seedManagedWorktreeExclude");function Qe(e,r,t="main"){const n=we(e);if(ye(n))throw new f("managed_worktree_base",`cwd ${n} is a managed worktree and cannot be used as a managed worktree base`,"Pass `--cwd` pointing at the repository\u2019s primary checkout and keep `--worktree` set to the desired existing local base branch, including the parent branch.");return B(n,()=>{const a=A(n,t),s=ge(r),u=`crtr/${r}`;if(k(s))throw new f("worktree_path_exists",`managed worktree path already exists: ${s}`,"Remove the stale path or choose a fresh node id.");if(i(["show-ref","--verify","--quiet",`refs/heads/${u}`],n).status===0)throw new f("worktree_branch_exists",`managed worktree branch already exists: ${u}`,"Remove the stale branch or choose a fresh node id.");const h={state:"open",path:s,branch:u,repo_root:n,base_ref:t,base_sha:a,created:new Date().toISOString()};_(n,["branch",u,a],"worktree_branch_create_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.");try{return _(n,["worktree","add",s,u],"worktree_add_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree."),We(n),h}catch(m){throw K(h,!0),m}})}o(Qe,"createManagedWorktree");function Ae(e){return _(e,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")}o(Ae,"statusPorcelain");function U(e){return _(e,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")}o(U,"currentBranch");function X(e,r){return _(e,["rev-parse","--git-path",r],"git_path_failed","Inspect the worktree manually, then retry.")}o(X,"gitPath");function R(e){return k(X(e,"rebase-merge"))||k(X(e,"rebase-apply"))}o(R,"isRebaseInProgress");async function $(e,r,t="pending"){const n=await v(e);await ue(e,{cwd:r.repo_root,cleanup:t,closedAt:new Date().toISOString(),unbindSession:n?.pi_session_id!=null||n?.pi_session_file!=null})}o($,"persistClosedWorktreeState");async function Re(e,r){const t=(await v(e))?.managed_worktree;t==null||t.state!=="open"||await le(e,{at:new Date().toISOString(),blocked:r})}o(Re,"recordBlockedLandIntent");function Me(e,r,t,n){const a=i(["status","--porcelain"],e);if(a.status!==0)return{ok:!1,detail:(a.stderr.trim()||a.stdout.trim()).trim()};const s=i(["diff","--name-only",t,n],e);if(s.status!==0)return{ok:!1,detail:(s.stderr.trim()||s.stdout.trim()).trim()};const u=s.stdout.split(`
15
+ `;re(r,t+u)}catch{}}o(We,"seedManagedWorktreeExclude");function Qe(e,r,t="main"){const n=we(e);if(ye(n))throw new f("managed_worktree_base",`cwd ${n} is a managed worktree and cannot be used as a managed worktree base`,"Pass `--cwd` pointing at the repository\u2019s primary checkout and keep `--worktree` set to the desired existing local base branch, including the parent branch.");return B(n,()=>{const a=R(n,t),s=ge(r),u=`crtr/${r}`;if(k(s))throw new f("worktree_path_exists",`managed worktree path already exists: ${s}`,"Remove the stale path or choose a fresh node id.");if(i(["show-ref","--verify","--quiet",`refs/heads/${u}`],n).status===0)throw new f("worktree_branch_exists",`managed worktree branch already exists: ${u}`,"Remove the stale branch or choose a fresh node id.");const h={state:"open",path:s,branch:u,repo_root:n,base_ref:t,base_sha:a,created:new Date().toISOString()};_(n,["branch",u,a],"worktree_branch_create_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.");try{return _(n,["worktree","add",s,u],"worktree_add_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree."),We(n),h}catch(m){throw K(h,!0),m}})}o(Qe,"createManagedWorktree");function Ae(e){return _(e,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")}o(Ae,"statusPorcelain");function U(e){return _(e,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")}o(U,"currentBranch");function X(e,r){return _(e,["rev-parse","--git-path",r],"git_path_failed","Inspect the worktree manually, then retry.")}o(X,"gitPath");function M(e){return k(X(e,"rebase-merge"))||k(X(e,"rebase-apply"))}o(M,"isRebaseInProgress");async function $(e,r,t="pending"){const n=await v(e);await ue(e,{cwd:r.repo_root,cleanup:t,closedAt:new Date().toISOString(),unbindSession:n?.pi_session_id!=null||n?.pi_session_file!=null})}o($,"persistClosedWorktreeState");async function Re(e,r){const t=(await v(e))?.managed_worktree;t==null||t.state!=="open"||await le(e,{at:new Date().toISOString(),blocked:r})}o(Re,"recordBlockedLandIntent");function Me(e,r,t,n){const a=i(["status","--porcelain"],e);if(a.status!==0)return{ok:!1,detail:(a.stderr.trim()||a.stdout.trim()).trim()};const s=i(["diff","--name-only",t,n],e);if(s.status!==0)return{ok:!1,detail:(s.stderr.trim()||s.stdout.trim()).trim()};const u=s.stdout.split(`
16
16
  `).map(g=>g.trim()).filter(g=>g!==""),h=ke(be(a.stdout),u);if(h.kind==="refuse")return{ok:!1,detail:`untracked files in ${e} would be overwritten by the incoming commits: ${h.collisions.join(", ")}`};const m=o(()=>{const g=i(["merge","--ff-only",n],e);return g.status===0?{ok:!0}:{ok:!1,detail:(g.stderr.trim()||g.stdout.trim()).trim()}},"merge");if(h.kind==="direct")return m();const d=i(["rev-parse","--verify","--quiet","refs/stash"],e),l=d.status===0?d.stdout.trim():null,b=i(["stash","push","--message",`crtr landing ${r}`],e);if(b.status!==0)return{ok:!1,detail:(b.stderr.trim()||b.stdout.trim()).trim()};const c=i(["rev-parse","--verify","--quiet","refs/stash"],e),p=c.status===0?c.stdout.trim():null;if(p===null||p===l)return m();const y=`local changes from ${e} are retained in stash ${p}; recover them with \`git -C ${w(e)} stash apply ${p}\` and resolve any conflict.`,W=m();return W.ok?i(["stash","apply",p],e).status===0?(z(e,p),{ok:!0}):(i(["reset","--hard",n],e),{ok:!0,stash:`the shared base checkout was reset to landed commit ${n} because local changes conflicted with it. ${y}`}):i(["stash","apply",p],e).status===0?(z(e,p),W):{ok:!1,detail:`${W.detail}
17
- ${y}`}}o(Me,"landIntoBaseCheckout");function z(e,r){const t=i(["rev-parse","--verify","--quiet","refs/stash"],e);t.status===0&&t.stdout.trim()===r&&i(["stash","drop"],e)}o(z,"dropStashIfStillTip");function M(e){const r=`git -C ${w(e.repo_root)} worktree remove ${w(e.path)}`;return{worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${e.path} remains intact. After its node exits, run \`${r}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`cleanup pending: local branch ${e.branch} remains checked out at ${e.path}; delete it only after that checkout is removed and its landed commit is still contained in ${e.base_ref}.`}}o(M,"deferredCleanupResult");async function Le(e,r,t){if(D(r.repo_root,r.path,t)!==!0)throw new f("wrong_branch",`managed worktree's recorded branch ${r.branch} no longer exists, and its checkout at ${r.path} is not registered to ${t} either`,`Inspect ${r.path} and its git worktree registration, then ask the user with \`crtr human send\` if you are unsure how to proceed.`);const n=_(r.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.");if(Ee(r.path,r.base_ref,n)===null)throw new f("branch_moved_unlanded",`managed worktree's recorded branch ${r.branch} no longer exists; its checkout is now on ${t} at ${n}, which carries content local base branch '${r.base_ref}' does not yet contain`,`Land or push ${t} through its own workflow (merge its PR, or fast-forward '${r.base_ref}' to include it) before closing, or ask the user with \`crtr human send\` if you are unsure how ${t} should be delivered.`);const s=S(r.repo_root),u=D(r.repo_root,r.path,t)===!0,h=U(r.path)===t,m=_(r.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")===n;if(!u||!h||!m)throw new f("wrong_branch",`managed worktree at ${r.path} changed while closing`,`Inspect ${r.path}, then rerun \`crtr node worktree close\`.`);const d={observed_branch:t,observed_sha:n,common_dir:s,authorized:new Date().toISOString()};await $(e,{...r,disposal:d},"pending");const l=`git -C ${w(r.repo_root)} worktree remove ${w(r.path)}`;return{node_id:e,branch:t,worktree_path:r.path,landed_sha:n,worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${r.path} remains intact. After its node exits, run \`${l}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`${t} is not a crtr-managed branch and will never be deleted automatically; it is preserved once the checkout above is removed.`}}o(Le,"closeBranchMovedWorktree");async function Te(e,r){const t=(await v(e))?.managed_worktree;if(t==null)return{message:`node ${e} has no managed worktree`,next:`Nothing to ${r} for this node.`};if(t.state==="abandoned"){const n=t.abandoned?.branch??t.branch;return{message:`node ${e}'s managed worktree was already abandoned`,next:`Nothing to ${r} for this node; its work was retained on branch ${n}.`}}return t.state==="closed"?t.cleanup==="complete"?{message:`node ${e}'s managed worktree is already closed and its cleanup is complete`,next:`Nothing to ${r} for this node.`}:{message:`node ${e}'s managed worktree is already closed; this is not lost ownership \u2014 its checkout at ${t.path} is scheduled for automatic cleanup once that is safe`,next:`Nothing to ${r} for this node. The daemon sweep finishes removing ${t.path} on its own; inspect it directly only if it still needs manual attention.`}:{message:`node ${e} has no open managed worktree`,next:`Nothing to ${r} for this node.`}}o(Te,"noManagedWorktreeMessage");async function Q(e){if(await v(e)===null)throw new f("unknown_node",`unknown node: ${e}`,"Run this from a live node, or pass a real node id once that command supports it.");const t=await x(e);if(t===null){const{message:c,next:p}=await Te(e,"close");throw new f("no_managed_worktree",c,p)}if(!k(t.path)){const c=L(t);if(c===null)throw new f("worktree_missing",`managed worktree path is missing: ${t.path}`,J(t));if(await V(e,t,c)===null)throw new f("worktree_missing",`managed worktree path is missing: ${t.path}`,J(t));return{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:c.branchSha??t.base_sha,worktree_removed:!0,branch_deleted:!0}}if(R(t.path))throw new f("rebase_in_progress","this managed worktree is mid-rebase",`Resolve conflicts in ${t.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`.`);if(Ae(t.path)!=="")throw new f("dirty_worktree","managed worktree has uncommitted changes",`Commit the work in ${t.path} (or ask the user with \`crtr human send\` if you do not know whether to keep it), then rerun \`crtr node worktree close\`.`);const a=U(t.path);if(a!==t.branch){const c=a==="HEAD"?"detached HEAD":a,p=$e(t.path,t.branch);if(p||a==="HEAD")throw new f("wrong_branch",`managed worktree is not on its recorded branch (expected ${t.branch}, found ${c})`,p?`Check out ${t.branch} in ${t.path} before closing, or ask the user with \`crtr human send\` if you are unsure why it changed, then rerun \`crtr node worktree close\`.`:`The recorded branch ${t.branch} no longer exists, and ${t.path} is in detached HEAD, so there is no branch to preserve. Check out or create a branch there before closing, or ask the user with \`crtr human send\` if you are unsure how to proceed.`);return await Le(e,t,a)}let s;try{s=A(t.path,t.base_ref)}catch(c){const p=_(t.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`.");if(!(c instanceof f)||c.code!=="missing_base_branch"||p!==t.base_sha)throw c;return await $(e,t,"pending"),{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:t.base_sha,...M(t)}}const u=i(["rebase",s],t.path);if(u.status!==0){const c=(u.stderr.trim()||u.stdout.trim()).trim();throw new f("rebase_failed",`rebase onto local base branch '${t.base_ref}' did not complete`,`Resolve conflicts in ${t.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`. If the blocker is not mechanical, ask the user with \`crtr human send\`.`,c)}const h=_(t.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`."),m=o(async(c,p,y)=>(await Re(e,c),new f("land_base_failed",c,p,y)),"blocked"),d=i(["merge-base","--is-ancestor",s,h],t.path);if(d.status!==0)throw await m(`rebased worktree is not a fast-forward of local base branch '${t.base_ref}'`,"Ask the user with `crtr human send` how to land this managed worktree.",(d.stderr.trim()||d.stdout.trim()).trim());const l=ve(t.path,t.base_ref);if(l!==null){const c=i(["rev-parse","HEAD"],l),p=c.stdout.trim();if(c.status!==0||p!==s){const y=c.status!==0?(c.stderr.trim()||p).trim():`expected ${s}, found ${p}`;throw await m(`local base branch '${t.base_ref}' changed while this worktree was closing`,`Reconcile the base checkout at ${l}, then rerun \`crtr node worktree close\`. If you are unsure, ask the user with \`crtr human send\`.`,y)}}let b;if(l!==null){const c=Me(l,t.branch,s,h);if(!c.ok)throw await m(`could not fast-forward local base branch '${t.base_ref}' to this worktree`,`Local base branch '${t.base_ref}' is checked out at ${l} in a state that blocks a fast-forward. Reconcile it there, then rerun \`crtr node worktree close\`. If you are unsure, ask the user with \`crtr human send\`.`,c.detail);b=c.stash}else{const c=i(["update-ref",`refs/heads/${t.base_ref}`,h,s],t.path);if(c.status!==0)throw await m(`could not fast-forward local base branch '${t.base_ref}' to this worktree`,"Ask the user with `crtr human send` how to land this managed worktree.",(c.stderr.trim()||c.stdout.trim()).trim())}return await $(e,t,"pending"),{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:h,...M(t),...b===void 0?{}:{base_checkout_stash:b}}}o(Q,"closeManagedWorktreeLocked");async function Je(e){const r=await x(e);return r===null?await Q(e):await j(k(r.path)?r.path:r.repo_root,async()=>await Q(e))}o(Je,"closeManagedWorktree");function Pe(e,r,t,n){const a=_e(e.branch,r,t,n),s=i(["update-ref","--stdin"],e.repo_root,a),u=s.status===0;if(u)return{branchDeleted:u};const h=(s.stderr.trim()||s.stdout.trim()).trim();return{branchDeleted:u,branchDeleteError:`local branch ${e.branch} could not be deleted automatically (${h}); run \`git -C ${w(e.repo_root)} branch -D ${w(e.branch)}\` after confirming it is merged.`}}o(Pe,"bestEffortDeleteBranchWithContainmentProof");function L(e){if(e.state!=="open"||k(e.path))return null;const r=`refs/heads/${e.branch}`,t=i(["show-ref","--verify","--quiet",r],e.repo_root);if(t.status===1)return{branchSha:null};if(t.status!==0)return null;const n=i(["rev-parse","--verify",r],e.repo_root);if(n.status!==0)return null;const a=n.stdout.trim(),s=i(["worktree","list","--porcelain"],e.repo_root);if(s.status!==0||I(s.stdout,e.branch)!==null)return null;const u=[],h=`refs/heads/${e.base_ref}`,m=i(["show-ref","--verify","--quiet",h],e.repo_root);if(m.status===0){const l=i(["rev-parse","--verify",h],e.repo_root);if(l.status!==0)return null;u.push({ref:h,sha:l.stdout.trim()})}else if(m.status!==1)return null;const d=i(["rev-parse","--symbolic-full-name","--verify",`${e.base_ref}@{upstream}`],e.repo_root);if(d.status===0){const l=d.stdout.trim(),b=i(["rev-parse","--verify",l],e.repo_root);l.startsWith("refs/")&&b.status===0&&u.push({ref:l,sha:b.stdout.trim()})}else{const l=`refs/remotes/origin/${e.base_ref}`,b=i(["show-ref","--verify","--quiet",l],e.repo_root);if(b.status===0){const c=i(["rev-parse","--verify",l],e.repo_root);c.status===0&&u.push({ref:l,sha:c.stdout.trim()})}else if(b.status!==1)return null}for(const l of u)if(i(["merge-base","--is-ancestor",a,l.sha],e.repo_root).status===0)return{branchSha:a,containingRef:l.ref,containingSha:l.sha};return null}o(L,"proveAbsentPathManagedWorktree");function J(e){return`The recorded checkout path is gone, but crtr cannot prove ${e.branch} is already delivered. Push or land ${e.branch}, then rerun \`crtr push final\`; if you cannot establish what happened to the branch, ask the user with \`crtr human send\`.`}o(J,"missingWorktreeRecoveryHint");function T(e){if(e.state!=="open"||!k(e.path)||R(e.path))return null;const r=i(["rev-parse","--abbrev-ref","HEAD"],e.path);if(r.status!==0||r.stdout.trim()!==e.branch)return null;const t=i(["status","--porcelain"],e.path);if(t.status!==0||t.stdout.trim()!=="")return null;const n=i(["rev-parse","HEAD"],e.path);if(n.status!==0)return null;const a=n.stdout.trim();let s;try{s=A(e.path,e.base_ref)}catch(h){if(h instanceof f&&h.code==="missing_base_branch"&&a===e.base_sha)return{branchSha:a,baseSha:e.base_sha};throw h}return i(["merge-base","--is-ancestor",a,s],e.path).status!==0?null:{branchSha:a,baseSha:s}}o(T,"proveCleanEmptyManagedWorktree");async function V(e,r,t){if(k(r.path))return null;if(t.branchSha===null)return await $(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0};if(t.containingRef===void 0||t.containingSha===void 0)return null;const{branchDeleted:n}=Pe(r,t.branchSha,t.containingRef,t.containingSha);return n?(await $(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0}):null}o(V,"autoDropAbsentPathManagedWorktreeLocked");async function He(e,r,t){const n=T(r);return n===null||n.branchSha!==t.branchSha?null:(await $(e,r,"pending"),{node_id:e,branch:r.branch,worktree_path:r.path,...M(r)})}o(He,"autoDropCleanManagedWorktreeLocked");function Y(e){if(e.state!=="open"||!k(e.path)||R(e.path))return null;const r=i(["status","--porcelain"],e.path);if(r.status!==0||r.stdout.trim()!=="")return null;const t=i(["rev-parse","HEAD"],e.path);return t.status===0?t.stdout.trim():null}o(Y,"cleanCurrentCheckoutTip");async function Ve(e){const r=Y(e);if(r===null)return!1;const t=process.env.GIT_SSH_COMMAND??process.env.GIT_SSH??"ssh",n=await he(["ls-remote","--exit-code","--heads","origin"],e.path,void 0,{timeoutMs:1e4,env:{...process.env,GIT_TERMINAL_PROMPT:"0",GIT_SSH_COMMAND:`${t} -o BatchMode=yes`}});return n.status!==0||!n.stdout.split(`
18
- `).some(s=>{const[u,h]=s.split(" ",2);return u===r&&h?.startsWith("refs/heads/")===!0})?!1:Y(e)===r}o(Ve,"branchIsPushedAtCurrentTip");async function Ye(e){const r=await x(e);if(r===null)return null;const t=!k(r.path);return(t?L(r)===null:T(r)===null)?null:await j(t?r.repo_root:r.path,async()=>{const n=await x(e);if(n===null)return null;if(!k(n.path)){const s=L(n);return s===null?null:await V(e,n,s)}const a=T(n);return a===null?null:await He(e,n,a)})}o(Ye,"autoDropCleanManagedWorktree");export{q as WORKTREE_LOCK_WAIT_MS,f as WorktreeError,Ye as autoDropCleanManagedWorktree,He as autoDropCleanManagedWorktreeLocked,Ve as branchIsPushedAtCurrentTip,I as checkoutPathForBranchInList,Je as closeManagedWorktree,Qe as createManagedWorktree,Xe as hasOpenManagedWorktree,ye as isManagedWorktreeBase,R as isRebaseInProgress,ge as managedWorktreePath,F as managedWorktreesRoot,J as missingWorktreeRecoveryHint,x as openManagedWorktreeForNode,$ as persistClosedWorktreeState,T as proveCleanEmptyManagedWorktree,ze as rollbackManagedWorktree,xe as samePath,G as worktreeLockPathForCommonDir};
17
+ ${y}`}}o(Me,"landIntoBaseCheckout");function z(e,r){const t=i(["rev-parse","--verify","--quiet","refs/stash"],e);t.status===0&&t.stdout.trim()===r&&i(["stash","drop"],e)}o(z,"dropStashIfStillTip");function L(e){const r=`git -C ${w(e.repo_root)} worktree remove ${w(e.path)}`;return{worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${e.path} remains intact. After its node exits, run \`${r}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`cleanup pending: local branch ${e.branch} remains checked out at ${e.path}; delete it only after that checkout is removed and its landed commit is still contained in ${e.base_ref}.`}}o(L,"deferredCleanupResult");async function Le(e,r,t){if(D(r.repo_root,r.path,t)!==!0)throw new f("wrong_branch",`managed worktree's recorded branch ${r.branch} no longer exists, and its checkout at ${r.path} is not registered to ${t} either`,`Inspect ${r.path} and its git worktree registration, then ask the user with \`crtr human send\` if you are unsure how to proceed.`);const n=_(r.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.");if(Ee(r.path,r.base_ref,n)===null)throw new f("branch_moved_unlanded",`managed worktree's recorded branch ${r.branch} no longer exists; its checkout is now on ${t} at ${n}, which carries content local base branch '${r.base_ref}' does not yet contain`,`Land or push ${t} through its own workflow (merge its PR, or fast-forward '${r.base_ref}' to include it) before closing, or ask the user with \`crtr human send\` if you are unsure how ${t} should be delivered.`);const s=S(r.repo_root),u=D(r.repo_root,r.path,t)===!0,h=U(r.path)===t,m=_(r.path,["rev-parse","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry.")===n;if(!u||!h||!m)throw new f("wrong_branch",`managed worktree at ${r.path} changed while closing`,`Inspect ${r.path}, then rerun \`crtr node worktree close\`.`);const d={observed_branch:t,observed_sha:n,common_dir:s,authorized:new Date().toISOString()};await $(e,{...r,disposal:d},"pending");const l=`git -C ${w(r.repo_root)} worktree remove ${w(r.path)}`;return{node_id:e,branch:t,worktree_path:r.path,landed_sha:n,worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${r.path} remains intact. After its node exits, run \`${l}\` after confirming it is safe to delete.`,branch_deleted:!1,branch_delete_error:`${t} is not a crtr-managed branch and will never be deleted automatically; it is preserved once the checkout above is removed.`}}o(Le,"closeBranchMovedWorktree");async function Te(e,r){const t=(await v(e))?.managed_worktree;if(t==null)return{message:`node ${e} has no managed worktree`,next:`Nothing to ${r} for this node.`};if(t.state==="abandoned"){const n=t.abandoned?.branch??t.branch;return{message:`node ${e}'s managed worktree was already abandoned`,next:`Nothing to ${r} for this node; its work was retained on branch ${n}.`}}return t.state==="closed"?t.cleanup==="complete"?{message:`node ${e}'s managed worktree is already closed and its cleanup is complete`,next:`Nothing to ${r} for this node.`}:{message:`node ${e}'s managed worktree is already closed; this is not lost ownership \u2014 its checkout at ${t.path} is scheduled for automatic cleanup once that is safe`,next:`Nothing to ${r} for this node. The daemon sweep finishes removing ${t.path} on its own; inspect it directly only if it still needs manual attention.`}:{message:`node ${e} has no open managed worktree`,next:`Nothing to ${r} for this node.`}}o(Te,"noManagedWorktreeMessage");async function Q(e){if(await v(e)===null)throw new f("unknown_node",`unknown node: ${e}`,"Run this from a live node, or pass a real node id once that command supports it.");const t=await x(e);if(t===null){const{message:c,next:p}=await Te(e,"close");throw new f("no_managed_worktree",c,p)}if(!k(t.path)){const c=T(t);if(c===null)throw new f("worktree_missing",`managed worktree path is missing: ${t.path}`,J(t));if(await V(e,t,c)===null)throw new f("worktree_missing",`managed worktree path is missing: ${t.path}`,J(t));return{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:c.branchSha??t.base_sha,worktree_removed:!0,branch_deleted:!0}}if(M(t.path))throw new f("rebase_in_progress","this managed worktree is mid-rebase",`Resolve conflicts in ${t.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`.`);if(Ae(t.path)!=="")throw new f("dirty_worktree","managed worktree has uncommitted changes",`Commit the work in ${t.path} (or ask the user with \`crtr human send\` if you do not know whether to keep it), then rerun \`crtr node worktree close\`.`);const a=U(t.path);if(a!==t.branch){const c=a==="HEAD"?"detached HEAD":a,p=$e(t.path,t.branch);if(p||a==="HEAD")throw new f("wrong_branch",`managed worktree is not on its recorded branch (expected ${t.branch}, found ${c})`,p?`Check out ${t.branch} in ${t.path} before closing, or ask the user with \`crtr human send\` if you are unsure why it changed, then rerun \`crtr node worktree close\`.`:`The recorded branch ${t.branch} no longer exists, and ${t.path} is in detached HEAD, so there is no branch to preserve. Check out or create a branch there before closing, or ask the user with \`crtr human send\` if you are unsure how to proceed.`);return await Le(e,t,a)}let s;try{s=R(t.path,t.base_ref)}catch(c){const p=_(t.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`.");if(!(c instanceof f)||c.code!=="missing_base_branch"||p!==t.base_sha)throw c;return await $(e,t,"pending"),{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:t.base_sha,...L(t)}}const u=i(["rebase",s],t.path);if(u.status!==0){const c=(u.stderr.trim()||u.stdout.trim()).trim();throw new f("rebase_failed",`rebase onto local base branch '${t.base_ref}' did not complete`,`Resolve conflicts in ${t.path}, run \`git rebase --continue\` there, then rerun \`crtr node worktree close\`. If the blocker is not mechanical, ask the user with \`crtr human send\`.`,c)}const h=_(t.path,["rev-parse","HEAD"],"landed_sha_failed","Inspect the worktree checkout manually, then rerun `crtr node worktree close`."),m=o(async(c,p,y)=>(await Re(e,c),new f("land_base_failed",c,p,y)),"blocked"),d=i(["merge-base","--is-ancestor",s,h],t.path);if(d.status!==0)throw await m(`rebased worktree is not a fast-forward of local base branch '${t.base_ref}'`,"Ask the user with `crtr human send` how to land this managed worktree.",(d.stderr.trim()||d.stdout.trim()).trim());const l=ve(t.path,t.base_ref);if(l!==null){const c=i(["rev-parse","HEAD"],l),p=c.stdout.trim();if(c.status!==0||p!==s){const y=c.status!==0?(c.stderr.trim()||p).trim():`expected ${s}, found ${p}`;throw await m(`local base branch '${t.base_ref}' changed while this worktree was closing`,`Reconcile the base checkout at ${l}, then rerun \`crtr node worktree close\`. If you are unsure, ask the user with \`crtr human send\`.`,y)}}let b;if(l!==null){const c=Me(l,t.branch,s,h);if(!c.ok)throw await m(`could not fast-forward local base branch '${t.base_ref}' to this worktree`,`Local base branch '${t.base_ref}' is checked out at ${l} in a state that blocks a fast-forward. Reconcile it there, then rerun \`crtr node worktree close\`. If you are unsure, ask the user with \`crtr human send\`.`,c.detail);b=c.stash}else{const c=i(["update-ref",`refs/heads/${t.base_ref}`,h,s],t.path);if(c.status!==0)throw await m(`could not fast-forward local base branch '${t.base_ref}' to this worktree`,"Ask the user with `crtr human send` how to land this managed worktree.",(c.stderr.trim()||c.stdout.trim()).trim())}return await $(e,t,"pending"),{node_id:e,branch:t.branch,worktree_path:t.path,landed_sha:h,...L(t),...b===void 0?{}:{base_checkout_stash:b}}}o(Q,"closeManagedWorktreeLocked");async function Je(e){const r=await x(e);return r===null?await Q(e):await j(k(r.path)?r.path:r.repo_root,async()=>await Q(e))}o(Je,"closeManagedWorktree");function Pe(e,r,t,n){const a=_e(e.branch,r,t,n),s=i(["update-ref","--stdin"],e.repo_root,a),u=s.status===0;if(u)return{branchDeleted:u};const h=(s.stderr.trim()||s.stdout.trim()).trim();return{branchDeleted:u,branchDeleteError:`local branch ${e.branch} could not be deleted automatically (${h}); run \`git -C ${w(e.repo_root)} branch -D ${w(e.branch)}\` after confirming it is merged.`}}o(Pe,"bestEffortDeleteBranchWithContainmentProof");function T(e){if(e.state!=="open"||k(e.path))return null;const r=`refs/heads/${e.branch}`,t=i(["show-ref","--verify","--quiet",r],e.repo_root);if(t.status===1)return{branchSha:null};if(t.status!==0)return null;const n=i(["rev-parse","--verify",r],e.repo_root);if(n.status!==0)return null;const a=n.stdout.trim(),s=i(["worktree","list","--porcelain"],e.repo_root);if(s.status!==0||I(s.stdout,e.branch)!==null)return null;const u=[],h=`refs/heads/${e.base_ref}`,m=i(["show-ref","--verify","--quiet",h],e.repo_root);if(m.status===0){const l=i(["rev-parse","--verify",h],e.repo_root);if(l.status!==0)return null;u.push({ref:h,sha:l.stdout.trim()})}else if(m.status!==1)return null;const d=i(["rev-parse","--symbolic-full-name","--verify",`${e.base_ref}@{upstream}`],e.repo_root);if(d.status===0){const l=d.stdout.trim(),b=i(["rev-parse","--verify",l],e.repo_root);l.startsWith("refs/")&&b.status===0&&u.push({ref:l,sha:b.stdout.trim()})}else{const l=`refs/remotes/origin/${e.base_ref}`,b=i(["show-ref","--verify","--quiet",l],e.repo_root);if(b.status===0){const c=i(["rev-parse","--verify",l],e.repo_root);c.status===0&&u.push({ref:l,sha:c.stdout.trim()})}else if(b.status!==1)return null}for(const l of u)if(i(["merge-base","--is-ancestor",a,l.sha],e.repo_root).status===0)return{branchSha:a,containingRef:l.ref,containingSha:l.sha};return null}o(T,"proveAbsentPathManagedWorktree");function J(e){return`The recorded checkout path is gone, but crtr cannot prove ${e.branch} is already delivered. Push or land ${e.branch}, then rerun \`crtr push final\`; if you cannot establish what happened to the branch, ask the user with \`crtr human send\`.`}o(J,"missingWorktreeRecoveryHint");function P(e){if(e.state!=="open"||!k(e.path)||M(e.path))return null;const r=i(["rev-parse","--abbrev-ref","HEAD"],e.path);if(r.status!==0||r.stdout.trim()!==e.branch)return null;const t=i(["status","--porcelain"],e.path);if(t.status!==0||t.stdout.trim()!=="")return null;const n=i(["rev-parse","HEAD"],e.path);if(n.status!==0)return null;const a=n.stdout.trim();let s;try{s=R(e.path,e.base_ref)}catch(h){if(h instanceof f&&h.code==="missing_base_branch"&&a===e.base_sha)return{branchSha:a,baseSha:e.base_sha};throw h}return i(["merge-base","--is-ancestor",a,s],e.path).status!==0?null:{branchSha:a,baseSha:s}}o(P,"proveCleanEmptyManagedWorktree");async function V(e,r,t){if(k(r.path))return null;if(t.branchSha===null)return await $(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0};if(t.containingRef===void 0||t.containingSha===void 0)return null;const{branchDeleted:n}=Pe(r,t.branchSha,t.containingRef,t.containingSha);return n?(await $(e,r,"complete"),{node_id:e,branch:r.branch,worktree_path:r.path,worktree_removed:!0,branch_deleted:!0}):null}o(V,"autoDropAbsentPathManagedWorktreeLocked");async function He(e,r,t){const n=P(r);return n===null||n.branchSha!==t.branchSha?null:(await $(e,r,"pending"),{node_id:e,branch:r.branch,worktree_path:r.path,...L(r)})}o(He,"autoDropCleanManagedWorktreeLocked");function Y(e){if(e.state!=="open"||!k(e.path)||M(e.path))return null;const r=i(["status","--porcelain"],e.path);if(r.status!==0||r.stdout.trim()!=="")return null;const t=i(["rev-parse","HEAD"],e.path);return t.status===0?t.stdout.trim():null}o(Y,"cleanCurrentCheckoutTip");async function Ve(e){const r=Y(e);if(r===null)return!1;const t=process.env.GIT_SSH_COMMAND??process.env.GIT_SSH??"ssh",n=await he(["ls-remote","--exit-code","--heads","origin"],e.path,void 0,{timeoutMs:1e4,env:{...process.env,GIT_TERMINAL_PROMPT:"0",GIT_SSH_COMMAND:`${t} -o BatchMode=yes`}});return n.status!==0||!n.stdout.split(`
18
+ `).some(s=>{const[u,h]=s.split(" ",2);return u===r&&h?.startsWith("refs/heads/")===!0})?!1:Y(e)===r}o(Ve,"branchIsPushedAtCurrentTip");async function Ye(e){const r=await x(e);if(r===null)return null;const t=!k(r.path);return(t?T(r)===null:P(r)===null)?null:await j(t?r.repo_root:r.path,async()=>{const n=await x(e);if(n===null)return null;if(!k(n.path)){const s=T(n);return s===null?null:await V(e,n,s)}const a=P(n);return a===null?null:await He(e,n,a)})}o(Ye,"autoDropCleanManagedWorktree");export{q as WORKTREE_LOCK_WAIT_MS,f as WorktreeError,Ye as autoDropCleanManagedWorktree,He as autoDropCleanManagedWorktreeLocked,Ve as branchIsPushedAtCurrentTip,I as checkoutPathForBranchInList,Je as closeManagedWorktree,Qe as createManagedWorktree,Xe as hasOpenManagedWorktree,ye as isManagedWorktreeBase,M as isRebaseInProgress,ge as managedWorktreePath,F as managedWorktreesRoot,J as missingWorktreeRecoveryHint,x as openManagedWorktreeForNode,$ as persistClosedWorktreeState,P as proveCleanEmptyManagedWorktree,ze as rollbackManagedWorktree,xe as samePath,G as worktreeLockPathForCommonDir};