@north-light/cli 0.3.538 → 0.3.540

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 (87) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/api/dto/profiles.d.ts +2 -0
  4. package/dist/builtin-memory/crouter-sdk/release-notes.md +10 -0
  5. package/dist/builtin-memory/northlight-apps/concepts/apps-and-sign-in.md +1 -1
  6. package/dist/builtin-memory/northlight-apps/concepts/connections.md +1 -1
  7. package/dist/builtin-memory/northlight-apps/errors.md +1 -1
  8. package/dist/builtin-memory/northlight-apps/guides/many-users.md +2 -2
  9. package/dist/clients/attach/viewer.js +454 -454
  10. package/dist/commands/profile/list.js +1 -1
  11. package/dist/commands/profile/show.js +1 -1
  12. package/dist/commands/sys/join.js +1 -1
  13. package/dist/commands/sys/sync-shared.d.ts +1 -1
  14. package/dist/commands/sys/sync-shared.js +1 -1
  15. package/dist/core/canvas/canvas.d.ts +0 -6
  16. package/dist/core/canvas/canvas.js +17 -22
  17. package/dist/core/canvas/crons.js +10 -10
  18. package/dist/core/canvas/db.js +4 -4
  19. package/dist/core/canvas/export-sqlite.js +1 -1
  20. package/dist/core/canvas/legacy-person.d.ts +4 -0
  21. package/dist/core/canvas/legacy-person.js +1 -0
  22. package/dist/core/canvas/migrations.js +58 -58
  23. package/dist/core/canvas/node-tokens.d.ts +0 -4
  24. package/dist/core/canvas/node-tokens.js +3 -3
  25. package/dist/core/canvas/person.d.ts +39 -0
  26. package/dist/core/canvas/person.js +18 -0
  27. package/dist/core/canvas/schema-pg.sql +11 -0
  28. package/dist/core/directory/join.d.ts +2 -2
  29. package/dist/core/environments.d.ts +10 -0
  30. package/dist/core/environments.js +1 -0
  31. package/dist/core/grants/local.d.ts +4 -4
  32. package/dist/core/grants/local.js +1 -1
  33. package/dist/core/grants/mirror.d.ts +1 -1
  34. package/dist/core/grants/on-sync.js +1 -1
  35. package/dist/core/grants/sync.js +1 -1
  36. package/dist/core/graph/kinds/environment.d.ts +2 -0
  37. package/dist/core/graph/kinds/environment.js +2 -0
  38. package/dist/core/graph/kinds/registry.js +1 -1
  39. package/dist/core/graph/objects.d.ts +1 -1
  40. package/dist/core/graph/objects.js +7 -7
  41. package/dist/core/graph/reader.d.ts +0 -1
  42. package/dist/core/graph/reader.js +3 -3
  43. package/dist/core/layout-migrate/index.js +3 -3
  44. package/dist/core/layout-migrate/stores.js +3 -3
  45. package/dist/core/peer/stubs.js +1 -1
  46. package/dist/core/runs/events.d.ts +0 -2
  47. package/dist/core/runs/events.js +4 -4
  48. package/dist/core/runs/operations.d.ts +0 -3
  49. package/dist/core/runs/operations.js +6 -6
  50. package/dist/core/runtime/close.js +1 -1
  51. package/dist/core/runtime/host.js +3 -3
  52. package/dist/core/runtime/launch-target.d.ts +10 -1
  53. package/dist/core/runtime/launch-target.js +1 -1
  54. package/dist/core/runtime/nodes.js +1 -1
  55. package/dist/core/scoped-state/db.js +2 -2
  56. package/dist/core/scoped-state/profiles.js +1 -1
  57. package/dist/core/scoped-state/schema.d.ts +4 -0
  58. package/dist/core/scoped-state/schema.js +3 -3
  59. package/dist/core/spaces/process.js +1 -1
  60. package/dist/daemon/api/handlers/crons.js +1 -1
  61. package/dist/daemon/api/handlers/hook-exec.js +1 -1
  62. package/dist/daemon/api/handlers/memory.js +5 -5
  63. package/dist/daemon/api/handlers/messages.js +2 -2
  64. package/dist/daemon/api/handlers/nodes.js +1 -1
  65. package/dist/daemon/api/handlers/profiles.js +1 -1
  66. package/dist/daemon/api/reader.js +1 -1
  67. package/dist/daemon/api/router.js +1 -1
  68. package/dist/daemon/cron/armed-context.js +1 -1
  69. package/dist/daemon/cron/authority.d.ts +1 -2
  70. package/dist/daemon/cron/authority.js +1 -1
  71. package/dist/daemon/crtrd.js +12 -12
  72. package/dist/daemon/reconcilers/storage-maintenance.d.ts +3 -0
  73. package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
  74. package/dist/migrations/004-canvas-documents/apply.js +1 -1
  75. package/dist/migrations/004-canvas-documents/index.js +2 -2
  76. package/dist/types.d.ts +2 -0
  77. package/docs/apps/concepts/apps-and-sign-in.md +1 -1
  78. package/docs/apps/concepts/connections.md +1 -1
  79. package/docs/apps/errors.md +1 -1
  80. package/docs/apps/guides/many-users.md +2 -2
  81. package/docs/cli/join-northlight.md +1 -1
  82. package/docs/sdk/release-notes.md +10 -0
  83. package/package.json +2 -2
  84. package/packages/crouter-identity/package.json +1 -1
  85. package/runtime.lock.json +12 -12
  86. package/dist/daemon/joined-owner.d.ts +0 -5
  87. package/dist/daemon/joined-owner.js +0 -1
@@ -1 +1 @@
1
- var T=Object.defineProperty;var a=(n,e)=>T(n,"name",{value:e,configurable:!0});import{createObject as O,ensureOwner as y,getById as c}from"../graph/objects.js";import{upsertKind as x}from"../graph/custom.js";import{CrtrError as d}from"../errors.js";import{ExitCode as E}from"../../types.js";import{normalizeMachineId as A}from"./names.js";const b=["public","friends","private"],H=a(n=>{const e=n.act.act;return e===void 0?{sub:n.sub,app:null,node:null,node_scopes:null,place:null}:{sub:n.sub,app:e.sub,node:e.node,node_scopes:e.node_scopes,place:e.place}},"identityOfEnvelope");async function _(n,e){const i=typeof e=="string"?await c(n,e):e;if(i===null)return null;const t=await n.prepare("SELECT home_machine, envelope_json FROM peer_stubs WHERE object = ?").get(i.id);return t?{object:i,home_machine:t.home_machine,identity:t.envelope_json===null?null:JSON.parse(t.envelope_json)}:null}a(_,"stubOf");const L=a(async(n,e)=>await n.prepare("SELECT 1 FROM peer_stubs WHERE object = ?").get(e.id)!==void 0,"isStub");async function l(n){return await y(n,"person","local-user","user:local","user")}a(l,"localPerson");async function f(n,e){return e==="user:local"?await l(n):await y(n,"app","app",e,e.replace(/^app:/,""))}a(f,"localApp");async function U(n,e){return e.type==="app"?await f(n,`app:${e.name}`):e.type==="person"?await l(n):null}a(U,"localOwnerActor");async function h(n,e,i){if(e!==null)return e.app===null?await l(n):await f(n,e.app);const t=i?.owner;return t?.type==="app"?await f(n,t.external_id??`app:${t.handle}`):await l(n)}a(h,"ownerFor");async function N(n,e,i,t,m,r){const p=m===void 0?null:H(m),u=r?.privacy??"public";if(!b.includes(u))throw new d("invalid_event",`invalid privacy ${JSON.stringify(u)}`,E.GENERAL,{id:e});const o=await c(n,e);if(o!==null){const s=await _(n,o);if(s===null)throw new d("conflict",`${e} is a local ${o.type}, not a stub of ${t}`,E.GENERAL,{id:e});if(s.home_machine!==t)throw new d("conflict",`${e} is already a stub of ${s.home_machine}, not ${t}`,E.GENERAL,{id:e,home_machine:s.home_machine});return p!==null&&(await n.prepare("UPDATE peer_stubs SET envelope_json = ? WHERE object = ?").run(JSON.stringify(p),o.id),await n.prepare("UPDATE objects SET reception = 2 WHERE id = ?").run(o.id)),r!==void 0&&(await v(n,i,r),r.privacy!==void 0&&o.privacy!==u)?(await n.prepare("UPDATE objects SET privacy = ? WHERE id = ?").run(u,o.id),await c(n,o.id)):o}const R=await h(n,p,r);await v(n,i,r);const w=await O(n,{id:e,type:i,kind:r?.kind??null,owner:R.id,privacy:u,name:r?.name??e,preview:r?.preview??"",home_machine:t});return await n.prepare("INSERT INTO peer_stubs (object, home_machine, envelope_json, created_at) VALUES (?, ?, ?, ?)").run(w.id,t,p===null?null:JSON.stringify(p),new Date().toISOString()),p!==null&&await n.prepare("UPDATE objects SET reception = 2 WHERE id = ?").run(w.id),await c(n,w.id)}a(N,"ensureStub");async function v(n,e,i){const t=i?.kind_decl;t===void 0||t.name!==e||await x(n,{name:t.name,owner:await h(n,null,{...i,owner:t.owner}),description:t.description,events:t.events,read:null,source:"peer"})}a(v,"holdKind");async function C(n,e,i){const t=await N(n,e,"node",A(i));return await n.prepare("UPDATE objects SET reception = 2 WHERE id = ?").run(t.id),await c(n,t.id)}a(C,"ensureNodeStub");async function I(n,e,i){const t=await _(n,e);t===null||t.home_machine===i||(await n.prepare("UPDATE peer_stubs SET home_machine = ? WHERE object = ?").run(i,t.object.id),await n.prepare("UPDATE objects SET home_machine = ? WHERE id = ?").run(i,t.object.id))}a(I,"rehomeStub");async function F(n,e){const i=await S(n,e.owner===null?null:await c(n,e.owner)),t=await j(n,e.type);return{id:e.id,type:e.type,kind:e.kind,name:e.name,preview:e.preview,owner:i,privacy:e.privacy,...t===null?{}:{kind_decl:t}}}a(F,"describeObject");async function S(n,e){if(e===null||e.type!=="person"&&e.type!=="app"&&e.type!=="repo"&&e.type!=="profile")return null;const i=await n.prepare("SELECT scheme, external_id FROM identities WHERE object = ?").get(e.id);return{id:e.id,type:e.type,scheme:i?.scheme??null,external_id:i?.external_id??null,handle:e.name}}a(S,"ownerRefOf");async function j(n,e){const i=await n.prepare("SELECT owner, description, events_json FROM object_kinds WHERE name = ?").get(e),t=i===void 0?null:await S(n,await c(n,i.owner));return i===void 0||t===null?null:{name:e,description:i.description,events:JSON.parse(i.events_json),owner:t}}a(j,"kindDeclarationOf");async function J(n,e,i){const t=await j(n,e.type);return t===null?i:{...i,kind_decl:t}}a(J,"withKindDecl");export{F as describeObject,C as ensureNodeStub,N as ensureStub,H as identityOfEnvelope,L as isStub,j as kindDeclarationOf,U as localOwnerActor,I as rehomeStub,_ as stubOf,J as withKindDecl};
1
+ var T=Object.defineProperty;var a=(n,e)=>T(n,"name",{value:e,configurable:!0});import{createObject as O,getById as c,ownerForGrantee as y}from"../graph/objects.js";import{upsertKind as x}from"../graph/custom.js";import{CrtrError as w}from"../errors.js";import{ExitCode as d}from"../../types.js";import{normalizeMachineId as A}from"./names.js";import{personId as b}from"../canvas/person.js";const g=["public","friends","private"],H=a(n=>{const e=n.act.act;return e===void 0?{sub:n.sub,app:null,node:null,node_scopes:null,place:null}:{sub:n.sub,app:e.sub,node:e.node,node_scopes:e.node_scopes,place:e.place}},"identityOfEnvelope");async function _(n,e){const i=typeof e=="string"?await c(n,e):e;if(i===null)return null;const t=await n.prepare("SELECT home_machine, envelope_json FROM peer_stubs WHERE object = ?").get(i.id);return t?{object:i,home_machine:t.home_machine,identity:t.envelope_json===null?null:JSON.parse(t.envelope_json)}:null}a(_,"stubOf");const C=a(async(n,e)=>await n.prepare("SELECT 1 FROM peer_stubs WHERE object = ?").get(e.id)!==void 0,"isStub");async function E(n){return await y(n,b())}a(E,"localPerson");async function f(n,e){return await y(n,e)}a(f,"localApp");async function I(n,e){return e.type==="app"?await f(n,`app:${e.name}`):e.type==="person"?await E(n):null}a(I,"localOwnerActor");async function h(n,e,i){if(e!==null)return e.app===null?await E(n):await f(n,e.app);const t=i?.owner;return t?.type==="app"?await f(n,t.external_id??`app:${t.handle}`):await E(n)}a(h,"ownerFor");async function N(n,e,i,t,m,r){const p=m===void 0?null:H(m),u=r?.privacy??"public";if(!g.includes(u))throw new w("invalid_event",`invalid privacy ${JSON.stringify(u)}`,d.GENERAL,{id:e});const o=await c(n,e);if(o!==null){const s=await _(n,o);if(s===null)throw new w("conflict",`${e} is a local ${o.type}, not a stub of ${t}`,d.GENERAL,{id:e});if(s.home_machine!==t)throw new w("conflict",`${e} is already a stub of ${s.home_machine}, not ${t}`,d.GENERAL,{id:e,home_machine:s.home_machine});return p!==null&&(await n.prepare("UPDATE peer_stubs SET envelope_json = ? WHERE object = ?").run(JSON.stringify(p),o.id),await n.prepare("UPDATE objects SET reception = 2 WHERE id = ?").run(o.id)),r!==void 0&&(await v(n,i,r),r.privacy!==void 0&&o.privacy!==u)?(await n.prepare("UPDATE objects SET privacy = ? WHERE id = ?").run(u,o.id),await c(n,o.id)):o}const R=await h(n,p,r);await v(n,i,r);const l=await O(n,{id:e,type:i,kind:r?.kind??null,owner:R.id,privacy:u,name:r?.name??e,preview:r?.preview??"",home_machine:t});return await n.prepare("INSERT INTO peer_stubs (object, home_machine, envelope_json, created_at) VALUES (?, ?, ?, ?)").run(l.id,t,p===null?null:JSON.stringify(p),new Date().toISOString()),p!==null&&await n.prepare("UPDATE objects SET reception = 2 WHERE id = ?").run(l.id),await c(n,l.id)}a(N,"ensureStub");async function v(n,e,i){const t=i?.kind_decl;t===void 0||t.name!==e||await x(n,{name:t.name,owner:await h(n,null,{...i,owner:t.owner}),description:t.description,events:t.events,read:null,source:"peer"})}a(v,"holdKind");async function F(n,e,i){const t=await N(n,e,"node",A(i));return await n.prepare("UPDATE objects SET reception = 2 WHERE id = ?").run(t.id),await c(n,t.id)}a(F,"ensureNodeStub");async function J(n,e,i){const t=await _(n,e);t===null||t.home_machine===i||(await n.prepare("UPDATE peer_stubs SET home_machine = ? WHERE object = ?").run(i,t.object.id),await n.prepare("UPDATE objects SET home_machine = ? WHERE id = ?").run(i,t.object.id))}a(J,"rehomeStub");async function M(n,e){const i=await S(n,e.owner===null?null:await c(n,e.owner)),t=await j(n,e.type);return{id:e.id,type:e.type,kind:e.kind,name:e.name,preview:e.preview,owner:i,privacy:e.privacy,...t===null?{}:{kind_decl:t}}}a(M,"describeObject");async function S(n,e){if(e===null||e.type!=="person"&&e.type!=="app"&&e.type!=="repo"&&e.type!=="profile")return null;const i=await n.prepare("SELECT scheme, external_id FROM identities WHERE object = ?").get(e.id);return{id:e.id,type:e.type,scheme:i?.scheme??null,external_id:i?.external_id??null,handle:e.name}}a(S,"ownerRefOf");async function j(n,e){const i=await n.prepare("SELECT owner, description, events_json FROM object_kinds WHERE name = ?").get(e),t=i===void 0?null:await S(n,await c(n,i.owner));return i===void 0||t===null?null:{name:e,description:i.description,events:JSON.parse(i.events_json),owner:t}}a(j,"kindDeclarationOf");async function G(n,e,i){const t=await j(n,e.type);return t===null?i:{...i,kind_decl:t}}a(G,"withKindDecl");export{M as describeObject,F as ensureNodeStub,N as ensureStub,H as identityOfEnvelope,C as isStub,j as kindDeclarationOf,I as localOwnerActor,J as rehomeStub,_ as stubOf,G as withKindDecl};
@@ -17,8 +17,6 @@ export declare function readRunEvents(runId: string, after?: number): Promise<{
17
17
  }>;
18
18
  export declare function subscribeRunEvents(runId: string, wake: () => void): Promise<() => void>;
19
19
  export declare function notifyRunDeleted(runId: string): void;
20
- /** OWN-11 for a joined runtime: the legacy `user:local` grantee is the terminal app. */
21
- export declare function rewriteLocalRunEventGrantee(db: CanvasDb): Promise<number>;
22
20
  /** A run event cursor stays monotonic. Keep control/settlement/usage history
23
21
  * for the observer's 30-day recovery window, but full payload events for 14
24
22
  * days only; older conversation content is in the transcript store. The stream
@@ -1,7 +1,7 @@
1
- var y=Object.defineProperty;var a=(e,t)=>y(e,"name",{value:t,configurable:!0});import{afterCommit as S,withCanvasWrite as l,openDb as w}from"../canvas/db.js";import{emitEvent as g}from"../events/emit.js";import{repairRunConversations as O}from"../canvas/sessions.js";const _=new Map;let f;function D(e){f=e}a(D,"onRunEventAppended");async function R(e,t,n,r,s){const o=await e.prepare("SELECT event_cursor, grantee, metadata_json FROM node_rows WHERE node_id = ? AND run_id = ?").get(t,t);if(!o||o.event_cursor===null)throw new Error(`missing run event cursor: ${t}`);const c={type:r,run_id:t,node_id:n,sequence_number:o.event_cursor,ts:new Date().toISOString(),metadata:o.metadata_json?JSON.parse(o.metadata_json):{},...s};return await e.prepare("INSERT INTO run_events (run_id, sequence_number, node_id, type, ts, body_json, grantee) VALUES (?, ?, ?, ?, ?, ?, ?)").run(t,c.sequence_number,n,r,c.ts,JSON.stringify(c),o.grantee),await e.prepare("UPDATE nodes SET event_cursor = event_cursor + 1, last_activity_at = ? WHERE node_id = ?").run(c.ts,t),S(()=>{for(const p of _.get(t)??[])p();const u=a(p=>{g({level:"error",event:"runs.notice_hook_failed",error:p})},"failed");try{Promise.resolve(f?.(t)).catch(u)}catch(p){u(p)}}),c}a(R,"appendRunEvent");async function L(e,t,n,r){return await l(async s=>await R(s,e,t,n,r))}a(L,"emitRunEvent");async function M(e,t,n,r){await l(async s=>{(await s.prepare("SELECT body_json FROM run_events WHERE run_id = ? AND node_id = ? AND type = ?").all(e,t,n)).some(c=>JSON.parse(c.body_json).outcome?.revision===r.outcome.revision)||await R(s,e,t,n,r)})}a(M,"emitRunSettlement");async function C(e,t=0){await O(e);const n=await w(),r=await n.prepare("SELECT event_cursor FROM nodes WHERE node_id = ? AND run_id = ?").get(e,e);if(!r)throw new Error(`run not found: ${e}`);const s=await n.prepare("SELECT MIN(sequence_number) AS sequence FROM run_events WHERE run_id = ?").get(e),o=await n.prepare("SELECT COUNT(*) AS count, COALESCE(SUM(LENGTH(body_json)), 0) AS bytes FROM run_events WHERE run_id = ? AND sequence_number > ?").get(e,t);return{events:(o.count>1e3||o.bytes>32*1024*1024?[]:await n.prepare("SELECT body_json FROM run_events WHERE run_id = ? AND sequence_number > ? ORDER BY sequence_number LIMIT 1000").all(e,t)).map(u=>JSON.parse(u.body_json)),earliest_sequence:s.sequence,latest_sequence:r.event_cursor-1,backlog_count:o.count,backlog_bytes:o.bytes}}a(C,"readRunEvents");async function A(e,t){let n=_.get(e);return n||(n=new Set,_.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&_.delete(e)}}a(A,"subscribeRunEvents");function H(e){queueMicrotask(()=>{for(const t of _.get(e)??[])t()})}a(H,"notifyRunDeleted");async function W(e){return Number((await e.prepare("UPDATE run_events SET grantee = 'app:terminal' WHERE grantee = 'user:local'").run()).changes)}a(W,"rewriteLocalRunEventGrantee");async function x(e,t=128){return await l(async n=>{const r=new Date(e-12096e5).toISOString(),s=new Date(e-720*60*6e4).toISOString(),o=Number((await n.prepare(`DELETE FROM run_events WHERE seq IN
1
+ var y=Object.defineProperty;var c=(e,t)=>y(e,"name",{value:t,configurable:!0});import{afterCommit as S,withCanvasWrite as d,openDb as w}from"../canvas/db.js";import{emitEvent as g}from"../events/emit.js";import{repairRunConversations as O}from"../canvas/sessions.js";const E=new Map;let f;function D(e){f=e}c(D,"onRunEventAppended");async function R(e,t,n,r,s){const o=await e.prepare("SELECT event_cursor, grantee, metadata_json FROM node_rows WHERE node_id = ? AND run_id = ?").get(t,t);if(!o||o.event_cursor===null)throw new Error(`missing run event cursor: ${t}`);const a={type:r,run_id:t,node_id:n,sequence_number:o.event_cursor,ts:new Date().toISOString(),metadata:o.metadata_json?JSON.parse(o.metadata_json):{},...s};return await e.prepare("INSERT INTO run_events (run_id, sequence_number, node_id, type, ts, body_json, grantee) VALUES (?, ?, ?, ?, ?, ?, ?)").run(t,a.sequence_number,n,r,a.ts,JSON.stringify(a),o.grantee),await e.prepare("UPDATE nodes SET event_cursor = event_cursor + 1, last_activity_at = ? WHERE node_id = ?").run(a.ts,t),S(()=>{for(const _ of E.get(t)??[])_();const u=c(_=>{g({level:"error",event:"runs.notice_hook_failed",error:_})},"failed");try{Promise.resolve(f?.(t)).catch(u)}catch(_){u(_)}}),a}c(R,"appendRunEvent");async function M(e,t,n,r){return await d(async s=>await R(s,e,t,n,r))}c(M,"emitRunEvent");async function L(e,t,n,r){await d(async s=>{(await s.prepare("SELECT body_json FROM run_events WHERE run_id = ? AND node_id = ? AND type = ?").all(e,t,n)).some(a=>JSON.parse(a.body_json).outcome?.revision===r.outcome.revision)||await R(s,e,t,n,r)})}c(L,"emitRunSettlement");async function C(e,t=0){await O(e);const n=await w(),r=await n.prepare("SELECT event_cursor FROM nodes WHERE node_id = ? AND run_id = ?").get(e,e);if(!r)throw new Error(`run not found: ${e}`);const s=await n.prepare("SELECT MIN(sequence_number) AS sequence FROM run_events WHERE run_id = ?").get(e),o=await n.prepare("SELECT COUNT(*) AS count, COALESCE(SUM(LENGTH(body_json)), 0) AS bytes FROM run_events WHERE run_id = ? AND sequence_number > ?").get(e,t);return{events:(o.count>1e3||o.bytes>32*1024*1024?[]:await n.prepare("SELECT body_json FROM run_events WHERE run_id = ? AND sequence_number > ? ORDER BY sequence_number LIMIT 1000").all(e,t)).map(u=>JSON.parse(u.body_json)),earliest_sequence:s.sequence,latest_sequence:r.event_cursor-1,backlog_count:o.count,backlog_bytes:o.bytes}}c(C,"readRunEvents");async function A(e,t){let n=E.get(e);return n||(n=new Set,E.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&E.delete(e)}}c(A,"subscribeRunEvents");function H(e){queueMicrotask(()=>{for(const t of E.get(e)??[])t()})}c(H,"notifyRunDeleted");async function W(e,t=128){return await d(async n=>{const r=new Date(e-12096e5).toISOString(),s=new Date(e-720*60*6e4).toISOString(),o=Number((await n.prepare(`DELETE FROM run_events WHERE seq IN
2
2
  (SELECT seq FROM run_events WHERE ts < ? AND type IN
3
3
  ('node.tool_call.completed', 'node.tool_call.started', 'node.system_prompt.set',
4
- 'node.thinking.done', 'node.output_text.done') ORDER BY ts LIMIT ?)`).run(r,t)).changes),c=Number((await n.prepare(`DELETE FROM run_events WHERE seq IN
5
- (SELECT seq FROM run_events WHERE ts < ? ORDER BY ts LIMIT ?)`).run(s,t-o)).changes);return o+c})}a(x,"expireRunEvents");async function h(e,t=0,n=256,r=8){return await l(async s=>{const o=await s.prepare(`SELECT e.seq, e.type, e.ts, length(e.body_json) AS bytes,
4
+ 'node.thinking.done', 'node.output_text.done') ORDER BY ts LIMIT ?)`).run(r,t)).changes),a=Number((await n.prepare(`DELETE FROM run_events WHERE seq IN
5
+ (SELECT seq FROM run_events WHERE ts < ? ORDER BY ts LIMIT ?)`).run(s,t-o)).changes);return o+a})}c(W,"expireRunEvents");async function x(e,t=0,n=256,r=8){return await d(async s=>{const o=await s.prepare(`SELECT e.seq, e.type, e.ts, length(e.body_json) AS bytes,
6
6
  n.finalized_at FROM run_events e LEFT JOIN nodes n ON n.node_id = e.node_id
7
- WHERE e.seq > ? ORDER BY e.seq LIMIT ?`).all(t,n);if(!o.length)return{cursor:0,compacted:0};let c=0,u=t;const p=new Date(e-1440*6e4).toISOString(),m=s.prepare("SELECT body_json FROM run_events WHERE seq = ?"),v=s.prepare("UPDATE run_events SET body_json = ? WHERE seq = ?");for(const i of o){if(u=i.seq,!["node.tool_call.completed","node.tool_call.started","node.system_prompt.set","node.thinking.done"].includes(i.type)||i.ts>=p||i.bytes<1024||i.type==="node.tool_call.started"&&!i.finalized_at)continue;const E=JSON.parse((await m.get(i.seq)).body_json);if(E.content_redacted!==!0&&(i.type==="node.tool_call.completed"?delete E.result:i.type==="node.tool_call.started"?delete E.arguments:delete E.text,E.content_redacted=!0,await v.run(JSON.stringify(E),i.seq),++c>=r))break}return{cursor:u,compacted:c}})}a(h,"compactRunEvents");const d=new Map;function j(e,t){let n=d.get(e);return n||(n=new Set,d.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&d.delete(e)}}a(j,"registerGrantStream");function F(e){const t=[...d.get(e)??[]];for(const n of t)n();return t.length}a(F,"closeGrantStreams");export{R as appendRunEvent,F as closeGrantStreams,h as compactRunEvents,L as emitRunEvent,M as emitRunSettlement,x as expireRunEvents,H as notifyRunDeleted,D as onRunEventAppended,C as readRunEvents,j as registerGrantStream,W as rewriteLocalRunEventGrantee,A as subscribeRunEvents};
7
+ WHERE e.seq > ? ORDER BY e.seq LIMIT ?`).all(t,n);if(!o.length)return{cursor:0,compacted:0};let a=0,u=t;const _=new Date(e-1440*6e4).toISOString(),m=s.prepare("SELECT body_json FROM run_events WHERE seq = ?"),v=s.prepare("UPDATE run_events SET body_json = ? WHERE seq = ?");for(const i of o){if(u=i.seq,!["node.tool_call.completed","node.tool_call.started","node.system_prompt.set","node.thinking.done"].includes(i.type)||i.ts>=_||i.bytes<1024||i.type==="node.tool_call.started"&&!i.finalized_at)continue;const p=JSON.parse((await m.get(i.seq)).body_json);if(p.content_redacted!==!0&&(i.type==="node.tool_call.completed"?delete p.result:i.type==="node.tool_call.started"?delete p.arguments:delete p.text,p.content_redacted=!0,await v.run(JSON.stringify(p),i.seq),++a>=r))break}return{cursor:u,compacted:a}})}c(x,"compactRunEvents");const l=new Map;function h(e,t){let n=l.get(e);return n||(n=new Set,l.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&l.delete(e)}}c(h,"registerGrantStream");function j(e){const t=[...l.get(e)??[]];for(const n of t)n();return t.length}c(j,"closeGrantStreams");export{R as appendRunEvent,j as closeGrantStreams,x as compactRunEvents,M as emitRunEvent,L as emitRunSettlement,W as expireRunEvents,H as notifyRunDeleted,D as onRunEventAppended,C as readRunEvents,h as registerGrantStream,A as subscribeRunEvents};
@@ -1,4 +1,3 @@
1
- import { type CanvasDb } from '../canvas/db.js';
2
1
  import type { NodeMeta } from '../canvas/types.js';
3
2
  import type { AppPrincipal } from '../../daemon/api/principal.js';
4
3
  import { type ResolvedAttachments } from '../storage/attachments.js';
@@ -12,8 +11,6 @@ export interface StorageDeps {
12
11
  client: () => StorageClient;
13
12
  budgetMs?: number;
14
13
  }
15
- /** OWN-11 for a joined runtime: the legacy `user:local` grantee is the terminal app. */
16
- export declare function rewriteLocalIdempotencyGrantee(db: CanvasDb): Promise<number>;
17
14
  export declare class RunError extends Error {
18
15
  readonly status: number;
19
16
  readonly code: string;
@@ -1,14 +1,14 @@
1
- var Oe=Object.defineProperty;var c=(e,t)=>Oe(e,"name",{value:t,configurable:!0});import{coveredByAny as be}from"../../shared/env.js";import{createHash as X}from"node:crypto";import{ApiError as De}from"../../api/errors.js";import{CrtrError as Ie}from"../errors.js";import{isAbsolute as z,relative as Me,resolve as Ae,sep as Te,join as Le}from"node:path";import{SessionManager as Ce}from"@earendil-works/pi-coding-agent";import{covers as U}from"@north-light/identity";import{openDb as P,withCanvasWrite as W}from"../canvas/db.js";import{deleteNodeWithOwned as ke}from"../graph/deletion.js";import{getById as xe}from"../graph/objects.js";import{repairRunConversations as Q}from"../canvas/sessions.js";import{getNode as N,nodeFiles as We,removeNodeFiles as Fe,updateNode as He}from"../canvas/canvas.js";import{fullName as qe}from"../canvas/labels.js";import{renameWindow as Ue}from"../runtime/placement.js";import{liveExecutionForNode as V}from"../canvas/executions.js";import{conversationStore as Z,isConversationNotFound as Pe}from"../conversation-store/index.js";import{PARKED_RESIDENT_SQL as $e}from"../canvas/canvas.js";import{WAKE_MAIL_SQL as Be}from"../canvas/deliveries.js";import{enqueueNodeMail as je}from"../feed/messages.js";import{newestReportBody as Je}from"../feed/reports.js";import{ensureAppProfile as Ye,loadExactProfileManifest as Ge}from"../profiles/manifest.js";import{stopNodeSandboxes as K}from"../spaces/stop.js";import{BrokerUnreachableError as Xe}from"../runtime/broker-request.js";import{interruptLive as ze}from"../runtime/interactive-deliver.js";import{isBrokerLive as ee}from"../runtime/model-swap.js";import{newNodeId as Qe}from"../runtime/nodes.js";import{reviveNode as $}from"../runtime/revive.js";import{spawnChildPrepared as Ve}from"../runtime/spawn.js";import{isPortableModelToken as Ze}from"../runtime/launch.js";import{parseDurationMs as Ke}from"../wake.js";import{expandModelCandidates as et,parseModelSpec as tt}from"../model-routes.js";import{payerOfGrant as B}from"../grants/mirror.js";import{isolatedBrokerLauncherAvailable as nt}from"../runtime/host.js";import{resolveRunKind as at}from"../runtime/launch-target.js";import{cycleAwareMessages as st}from"../runtime/session-cycles.js";import{originFor as te}from"../runtime/stamp/protocol.js";import{readOutputRequest as rt,readOutputResult as ne}from"../runtime/structured-output.js";import{admitLegacyStructuredOutput as ae}from"../../daemon/structured-requests/sidecar.js";import{runSchemaRepromptPending as it}from"../runtime/stop-guard.js";import{toRunOutcomeDTO as ot}from"../../daemon/api/map.js";import{openAssistantMessageStartSequence as dt}from"../../daemon/runs/observer.js";import{closeNode as se}from"../runtime/close.js";import{parseScope as ct}from"@north-light/identity";import{appendRunEvent as re,subscribeRunEvents as ut}from"./events.js";import{runActivity as mt}from"./activity.js";import{cancelOpenQuestions as ie,openNoticeIds as lt,openQuestionIds as oe}from"./questions.js";import{appSpace as j}from"../layout.js";import{ensureAppSpace as pt}from"../spaces/space.js";import{assertUploadsUsable as de,resolveStorageUrls as _t}from"../storage/attachments.js";import{storageClient as ft,storageConfig as wt}from"../storage/client.js";import{deleteUploadRow as gt,insertUploadUses as ce,releaseRunUploads as Et}from"../storage/tables.js";import{abortProviderCalls as ue}from"../providers/inflight.js";import{filterAsync as me,mapAsync as T}from"../async-array.js";const le=c((e,t,s)=>_t(t,e,s),"defaultResolver"),yt=3e3;async function vn(e){return Number((await e.prepare("UPDATE idempotency_keys SET grantee = 'app:terminal' WHERE grantee = 'user:local'").run()).changes)}c(vn,"rewriteLocalIdempotencyGrantee");class S extends Error{static{c(this,"RunError")}status;code;param;details;scope;retryable;constructor(t,s,a,o,m,i,r=t===503){super(a),this.status=t,this.code=s,this.param=o,this.details=m,this.scope=i,this.retryable=r}}const R=c((e,t)=>new S(400,"invalid_request",e,t),"invalid"),J=c(()=>new S(404,"not_found","run not found"),"missing"),Y=c(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"usage"),D=c(async()=>await P(),"db");async function F(e,t,s=!1){const a=await N(e);if(!a||a.parent!==null||a.run_id!==e||a.grantee!==t||!s&&a.deleting_at)throw J();return a}c(F,"ownedRun");const pe=["waiting_on_user","idle","settled"];async function A(e){if(e.status==="canceled"||e.status==="dead"||e.status==="done"&&e.intent!=="parked")return"settled";const t=await(await D()).prepare(`SELECT 1 FROM node_rows n WHERE n.run_id = ? AND n.status = 'active' AND
1
+ var Oe=Object.defineProperty;var u=(e,t)=>Oe(e,"name",{value:t,configurable:!0});import{coveredByAny as be}from"../../shared/env.js";import{createHash as G}from"node:crypto";import{ApiError as De}from"../../api/errors.js";import{CrtrError as Me}from"../errors.js";import{isAbsolute as z,relative as Ie,resolve as Ae,sep as Te,join as Le}from"node:path";import{SessionManager as Ce}from"@earendil-works/pi-coding-agent";import{covers as U}from"@north-light/identity";import{openDb as P,withCanvasWrite as W}from"../canvas/db.js";import{deleteNodeWithOwned as ke}from"../graph/deletion.js";import{getById as xe}from"../graph/objects.js";import{repairRunConversations as Q}from"../canvas/sessions.js";import{getNode as v,nodeFiles as We,removeNodeFiles as Fe,updateNode as qe}from"../canvas/canvas.js";import{fullName as He}from"../canvas/labels.js";import{renameWindow as Ue}from"../runtime/placement.js";import{liveExecutionForNode as V}from"../canvas/executions.js";import{conversationStore as Z,isConversationNotFound as Pe}from"../conversation-store/index.js";import{PARKED_RESIDENT_SQL as $e}from"../canvas/canvas.js";import{WAKE_MAIL_SQL as Be}from"../canvas/deliveries.js";import{enqueueNodeMail as je}from"../feed/messages.js";import{newestReportBody as Je}from"../feed/reports.js";import{ensureAppProfile as Ye,loadExactProfileManifest as Xe}from"../profiles/manifest.js";import{stopNodeSandboxes as K}from"../spaces/stop.js";import{BrokerUnreachableError as Ge}from"../runtime/broker-request.js";import{interruptLive as ze}from"../runtime/interactive-deliver.js";import{isBrokerLive as ee}from"../runtime/model-swap.js";import{newNodeId as Qe}from"../runtime/nodes.js";import{reviveNode as $}from"../runtime/revive.js";import{spawnChildPrepared as Ve}from"../runtime/spawn.js";import{isPortableModelToken as Ze}from"../runtime/launch.js";import{parseDurationMs as Ke}from"../wake.js";import{expandModelCandidates as et,parseModelSpec as tt}from"../model-routes.js";import{payerOfGrant as B}from"../grants/mirror.js";import{isolatedBrokerLauncherAvailable as nt}from"../runtime/host.js";import{resolveRunKind as st,resolveRunModel as at}from"../runtime/launch-target.js";import{cycleAwareMessages as it}from"../runtime/session-cycles.js";import{originFor as te}from"../runtime/stamp/protocol.js";import{readOutputRequest as rt,readOutputResult as ne}from"../runtime/structured-output.js";import{admitLegacyStructuredOutput as se}from"../../daemon/structured-requests/sidecar.js";import{runSchemaRepromptPending as ot}from"../runtime/stop-guard.js";import{toRunOutcomeDTO as dt}from"../../daemon/api/map.js";import{openAssistantMessageStartSequence as ct}from"../../daemon/runs/observer.js";import{closeNode as ae}from"../runtime/close.js";import{parseScope as ut}from"@north-light/identity";import{appendRunEvent as ie,subscribeRunEvents as mt}from"./events.js";import{runActivity as lt}from"./activity.js";import{cancelOpenQuestions as re,openNoticeIds as pt,openQuestionIds as oe}from"./questions.js";import{appSpace as j}from"../layout.js";import{ensureAppSpace as _t}from"../spaces/space.js";import{assertUploadsUsable as de,resolveStorageUrls as ft}from"../storage/attachments.js";import{storageClient as wt,storageConfig as gt}from"../storage/client.js";import{deleteUploadRow as Et,insertUploadUses as ce,releaseRunUploads as yt}from"../storage/tables.js";import{abortProviderCalls as ue}from"../providers/inflight.js";import{filterAsync as me,mapAsync as L}from"../async-array.js";const le=u((e,t,a)=>ft(t,e,a),"defaultResolver"),ht=3e3;class S extends Error{static{u(this,"RunError")}status;code;param;details;scope;retryable;constructor(t,a,n,o,m,r,i=t===503){super(n),this.status=t,this.code=a,this.param=o,this.details=m,this.scope=r,this.retryable=i}}const h=u((e,t)=>new S(400,"invalid_request",e,t),"invalid"),J=u(()=>new S(404,"not_found","run not found"),"missing"),Y=u(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"usage"),D=u(async()=>await P(),"db");async function F(e,t,a=!1){const n=await v(e);if(!n||n.parent!==null||n.run_id!==e||n.grantee!==t||!a&&n.deleting_at)throw J();return n}u(F,"ownedRun");const pe=["waiting_on_user","idle","settled"];async function T(e){if(e.status==="canceled"||e.status==="dead"||e.status==="done"&&e.intent!=="parked")return"settled";const t=await(await D()).prepare(`SELECT 1 FROM node_rows n WHERE n.run_id = ? AND n.status = 'active' AND
2
2
  (n.last_settled_at IS NULL OR EXISTS (
3
3
  SELECT 1 FROM run_events e WHERE e.run_id = ? AND e.node_id = n.node_id AND e.type = 'node.turn.started'
4
4
  AND e.sequence_number > COALESCE((SELECT MAX(c.sequence_number) FROM run_events c
5
5
  WHERE c.run_id = e.run_id AND c.node_id = e.node_id AND c.type = 'node.turn.completed'), 0)
6
- )) LIMIT 1`).get(e.node_id,e.node_id),s=!e.last_settled_at&&e.status!=="done";return t||s||await it(e)?"running":(await oe(e.node_id)).length?"waiting_on_user":await(await D()).prepare(`SELECT 1 FROM deliveries d JOIN nodes n ON n.node_id = d.destination_id
6
+ )) LIMIT 1`).get(e.node_id,e.node_id),a=!e.last_settled_at&&e.status!=="done";return t||a||await ot(e)?"running":(await oe(e.node_id)).length?"waiting_on_user":await(await D()).prepare(`SELECT 1 FROM deliveries d JOIN nodes n ON n.node_id = d.destination_id
7
7
  WHERE n.run_id = ? AND ${Be("d","'armed', 'pending'")}
8
- AND (n.status IN ('active', 'idle') OR ${$e("n")}) LIMIT 1`).get(e.node_id)?"running":"idle"}c(A,"runStatus");async function On(e){const t=await N(e);return t!==null&&await A(t)==="running"}c(On,"runIsRunning");async function L(e){await Q(e.node_id),e=await N(e.node_id)??e;const t=await A(e),s=t==="settled"?[]:await oe(e.node_id),a=await Je(e.node_id)||void 0,o=t==="settled"?ot(e):null,m=o?.kind==="result"?ne(e.node_id,e.grantee):void 0;return{run_id:e.node_id,name:e.name,status:t,...o?{outcome:o}:{},...m===void 0?{}:{result:m},...a!==void 0?{last_report:a}:{},pending_question_ids:s,open_notice_ids:t==="settled"?[]:await lt(e.node_id),...e.last_turn_error_json?{last_turn_error:JSON.parse(e.last_turn_error_json)}:{},usage:JSON.parse(e.usage_json??JSON.stringify(Y())),payer:await B(e.grant_id),scopes:e.scopes,metadata:e.metadata_json?JSON.parse(e.metadata_json):{},created_at:e.created,...t==="settled"&&e.outcome_at?{settled_at:e.outcome_at}:{},latest_sequence:(e.event_cursor??1)-1,last_activity_at:e.last_activity_at,activity:await mt(e.node_id,t)}}c(L,"runObject");const G=864e5,_e=255;function bn(e){if(e!==void 0){if(typeof e!="string"||!e.length||e.length>_e)throw R(`Idempotency-Key must be 1-${_e} characters`);return e}}c(bn,"idempotencyKeyOf");async function H(e,t,s,a){a??=await P();const o=await a.prepare("SELECT body_hash, run_id, response_json, created_at FROM idempotency_keys WHERE grantee = ? AND key = ?").get(e,t);if(!(!o||Date.now()-Date.parse(o.created_at)>=G)){if(o.body_hash!==s)throw new S(409,"idempotency_conflict","key used with a different request");return{run_id:o.run_id,response:JSON.parse(o.response_json)}}}c(H,"findIdempotent");async function fe(e,t,s,a,o,m,i){await e.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key = ? AND created_at <= ?").run(t,s,new Date(Date.now()-G).toISOString()),await e.prepare("INSERT INTO idempotency_keys (grantee, key, body_hash, run_id, response_json, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(t,s,a,o,JSON.stringify(m),i)}c(fe,"recordIdempotent");async function Dn(e=Date.now()){return Number((await(await P()).prepare("DELETE FROM idempotency_keys WHERE created_at <= ?").run(new Date(e-G).toISOString())).changes)}c(Dn,"pruneExpiredIdempotencyKeys");async function we(e,t,s,a,o){const m=a.text;let i=0;return{messageId:(await je(t,{kind:"message",label:m.slice(0,120),senderNodeId:"human",senderName:s,bodyJson:o?{body:m}:{body:m,start_turn:!1},urgency:o?"normal":"deferred",wake:o?1:0,withinEnqueue:c(async(u,f)=>{a.uses.length&&(await de(s,a.uses,u),await ce(a.uses.map(({upload_id:y,path:g})=>({upload_id:y,path:g,run_id:t})),u)),i=(await re(u,t,t,"node.message.accepted",{message_id:f,start_turn:o})).sequence_number},"withinEnqueue")})).messageId,sequence:i}}c(we,"acceptRunMessage");class ge extends Error{static{c(this,"IdempotentReplay")}winner;constructor(t){super("idempotent replay"),this.winner=t}}const ht=1440*60*1e3;function Ee(e){const t=typeof e=="string"?Ke(e):null;if(t===null||!Number.isSafeInteger(t))throw R(`deadline '${String(e)}' is not a duration; use one or more <int>[smhd] parts such as 10m or 1h30m`,"deadline");if(t<=0)throw R("deadline must be greater than zero","deadline");if(t>ht)throw R("deadline must be at most 24h","deadline");return t}c(Ee,"parseRunDeadlineMs");async function In(e,t,s,a=le){if(t.isolated&&!nt())throw new S(503,"isolated_unavailable","isolated runs require the Linux root launcher","isolated");t.deadline!==void 0&&Ee(t.deadline);const o=X("sha256").update(JSON.stringify(t)).digest("hex");if(s){const n=await H(e.grantee,s,o);if(n){const l=await F(n.run_id,e.grantee);return!l.pi_pid&&await A(l)==="running"&&(t.output_schema!==void 0&&!l.outcome_at&&rt(l.node_id).state==="absent"&&ne(l.node_id,l.grantee)===void 0&&await ae(l.node_id,"terminal",t.output_schema),await $(l.node_id,{resume:!0,capacity:"freeze"})),n.response}}await pt(e.grantee);const m=t.profile===void 0?Ye(e.grantee):Ge(t.profile),i=m.manifest.grantee??"app:terminal";if(i!==e.grantee){const n=`crtr:memory:rw:${i}:profile:${m.profileId}`;if(!e.resolved.some(l=>U(l,n))||t.scopes!==null&&!t.scopes.some(l=>U(l,n)))throw new S(403,"scope_missing",`missing scope ${n}`,"profile",void 0,n)}if(t.model!==void 0){const n=t.model;if(Ze(n)){if(n.includes("/")&&await B(e.grant)!=="app"){const l=n.lastIndexOf("/"),E=n.slice(0,l),h=n.slice(l+1);if(["ultra","strong","medium","light"].includes(h)&&et({kind:"logical",family:E,strength:h,spec:n},j(e.grantee).files,m.profileId).every(b=>b.family!==E))throw R(`model '${n}' names a model family that is not configured in this daemon`,"model")}}else if(tt(n)===void 0)throw R(`model '${n}' is not a strength (ultra, strong, medium, light), a family/strength, or a provider/model id`,"model")}const r=await Qe(),u=Le(j(e.grantee).nodes,r);if(t.isolated&&t.cwd!==void 0){const n=Me(u,Ae(t.cwd));if(!z(t.cwd)||n===".."||n.startsWith(`..${Te}`)||z(n))throw R("cwd must be inside the isolated root node folder","cwd")}const f=await a(r,e.grantee,t.prompt),y=f.text;let g;try{g=t.message===void 0?void 0:await a(r,e.grantee,t.message)}catch(n){throw f.cleanup(),n}const w=await B(e.grant);let d;try{await Ve({kind:at(m),mode:"base",root:!0,rootLifecycle:"resident",readiness:"launch",cwd:t.isolated?t.cwd??u:j(e.grantee).files,profile:m.profileId,prompt:y,nodeId:r,isolated:t.isolated??!1,...t.model===void 0?{}:{model:t.model},...t.deadline===void 0?{}:{deadlineAt:new Date(Date.now()+Ee(t.deadline)).toISOString()},...t.scopes===null?{}:{scopes:t.scopes},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant,client_id:e.client_id,metadata_json:JSON.stringify(t.metadata),run_id:r,event_cursor:1,usage_json:JSON.stringify(Y())},withinCreate:c(async(n,l)=>{if(s){const h=await H(e.grantee,s,o,n);if(h)throw new ge(h)}const E=f.uses;if(E.length&&(await de(e.grantee,E,n),await ce(E.map(({upload_id:h,path:b})=>({upload_id:h,path:b,run_id:r})),n)),d={run_id:r,node_id:r,grantee:e.grantee,grant:e.grant,rev:e.rev,scopes:t.scopes,payer:w,status:"running",created_at:l.created,metadata:t.metadata,sequence_number:0},await re(n,r,r,"run.started",{scopes:t.scopes,payer:w,usage:Y()}),g){const h=await we(n,r,e.grantee,g,!1);d.message={run_id:r,message_id:h.messageId,sequence_number:h.sequence}}s&&await fe(n,e.grantee,s,o,r,d,l.created)},"withinCreate")},t.output_schema===void 0?void 0:async n=>{await ae(n,"terminal",t.output_schema)})}catch(n){if(await N(r)||(f.cleanup(),g?.cleanup()),n instanceof ge)return n.winner.response;if(t.model!==void 0&&n instanceof Ie&&n.code==="usage")throw R(n.message,"model");if(n instanceof De&&(n.code==="isolated_run"||n.code==="invalid_request")){const l=n.details&&typeof n.details=="object"&&"param"in n.details&&typeof n.details.param=="string"?n.details.param:void 0;throw new S(n.status,n.code,n.message,l)}throw n}if(!d)throw new Error("run creation did not commit");return d}c(In,"startRun");async function Mn(e,t){let s;if(t.after)try{const r=JSON.parse(Buffer.from(t.after,"base64url").toString());if(!r||typeof r!="object"||!("activity"in r)||typeof r.activity!="string"||!("id"in r)||typeof r.id!="string")throw new Error("invalid cursor");s={activity:r.activity,id:r.id}}catch{throw R("invalid list cursor","after")}const a=await(await D()).prepareUncached(`SELECT node_id, COALESCE(last_activity_at, created) AS activity FROM node_rows
8
+ AND (n.status IN ('active', 'idle') OR ${$e("n")}) LIMIT 1`).get(e.node_id)?"running":"idle"}u(T,"runStatus");async function On(e){const t=await v(e);return t!==null&&await T(t)==="running"}u(On,"runIsRunning");async function C(e){await Q(e.node_id),e=await v(e.node_id)??e;const t=await T(e),a=t==="settled"?[]:await oe(e.node_id),n=await Je(e.node_id)||void 0,o=t==="settled"?dt(e):null,m=o?.kind==="result"?ne(e.node_id,e.grantee):void 0;return{run_id:e.node_id,name:e.name,status:t,...o?{outcome:o}:{},...m===void 0?{}:{result:m},...n!==void 0?{last_report:n}:{},pending_question_ids:a,open_notice_ids:t==="settled"?[]:await pt(e.node_id),...e.last_turn_error_json?{last_turn_error:JSON.parse(e.last_turn_error_json)}:{},usage:JSON.parse(e.usage_json??JSON.stringify(Y())),payer:await B(e.grant_id),scopes:e.scopes,metadata:e.metadata_json?JSON.parse(e.metadata_json):{},created_at:e.created,...t==="settled"&&e.outcome_at?{settled_at:e.outcome_at}:{},latest_sequence:(e.event_cursor??1)-1,last_activity_at:e.last_activity_at,activity:await lt(e.node_id,t)}}u(C,"runObject");const X=864e5,_e=255;function bn(e){if(e!==void 0){if(typeof e!="string"||!e.length||e.length>_e)throw h(`Idempotency-Key must be 1-${_e} characters`);return e}}u(bn,"idempotencyKeyOf");async function q(e,t,a,n){n??=await P();const o=await n.prepare("SELECT body_hash, run_id, response_json, created_at FROM idempotency_keys WHERE grantee = ? AND key = ?").get(e,t);if(!(!o||Date.now()-Date.parse(o.created_at)>=X)){if(o.body_hash!==a)throw new S(409,"idempotency_conflict","key used with a different request");return{run_id:o.run_id,response:JSON.parse(o.response_json)}}}u(q,"findIdempotent");async function fe(e,t,a,n,o,m,r){await e.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key = ? AND created_at <= ?").run(t,a,new Date(Date.now()-X).toISOString()),await e.prepare("INSERT INTO idempotency_keys (grantee, key, body_hash, run_id, response_json, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(t,a,n,o,JSON.stringify(m),r)}u(fe,"recordIdempotent");async function Dn(e=Date.now()){return Number((await(await P()).prepare("DELETE FROM idempotency_keys WHERE created_at <= ?").run(new Date(e-X).toISOString())).changes)}u(Dn,"pruneExpiredIdempotencyKeys");async function we(e,t,a,n,o){const m=n.text;let r=0;return{messageId:(await je(t,{kind:"message",label:m.slice(0,120),senderNodeId:"human",senderName:a,bodyJson:o?{body:m}:{body:m,start_turn:!1},urgency:o?"normal":"deferred",wake:o?1:0,withinEnqueue:u(async(c,f)=>{n.uses.length&&(await de(a,n.uses,c),await ce(n.uses.map(({upload_id:y,path:g})=>({upload_id:y,path:g,run_id:t})),c)),r=(await ie(c,t,t,"node.message.accepted",{message_id:f,start_turn:o})).sequence_number},"withinEnqueue")})).messageId,sequence:r}}u(we,"acceptRunMessage");class ge extends Error{static{u(this,"IdempotentReplay")}winner;constructor(t){super("idempotent replay"),this.winner=t}}const Rt=1440*60*1e3;function Ee(e){const t=typeof e=="string"?Ke(e):null;if(t===null||!Number.isSafeInteger(t))throw h(`deadline '${String(e)}' is not a duration; use one or more <int>[smhd] parts such as 10m or 1h30m`,"deadline");if(t<=0)throw h("deadline must be greater than zero","deadline");if(t>Rt)throw h("deadline must be at most 24h","deadline");return t}u(Ee,"parseRunDeadlineMs");async function Mn(e,t,a,n=le){if(t.isolated&&!nt())throw new S(503,"isolated_unavailable","isolated runs require the Linux root launcher","isolated");t.deadline!==void 0&&Ee(t.deadline);const o=G("sha256").update(JSON.stringify(t)).digest("hex");if(a){const s=await q(e.grantee,a,o);if(s){const l=await F(s.run_id,e.grantee);return!l.pi_pid&&await T(l)==="running"&&(t.output_schema!==void 0&&!l.outcome_at&&rt(l.node_id).state==="absent"&&ne(l.node_id,l.grantee)===void 0&&await se(l.node_id,"terminal",t.output_schema),await $(l.node_id,{resume:!0,capacity:"freeze"})),s.response}}await _t(e.grantee);const m=t.profile===void 0?Ye(e.grantee):Xe(t.profile),r=m.manifest.grantee??"app:terminal";if(r!==e.grantee){const s=`crtr:memory:rw:${r}:profile:${m.profileId}`;if(!e.resolved.some(l=>U(l,s))||t.scopes!==null&&!t.scopes.some(l=>U(l,s)))throw new S(403,"scope_missing",`missing scope ${s}`,"profile",void 0,s)}if(t.model!==void 0){const s=t.model;if(Ze(s)){if(s.includes("/")&&await B(e.grant)!=="app"){const l=s.lastIndexOf("/"),E=s.slice(0,l),N=s.slice(l+1);if(["ultra","strong","medium","light"].includes(N)&&et({kind:"logical",family:E,strength:N,spec:s},j(e.grantee).files,m.profileId).every(R=>R.family!==E))throw h(`model '${s}' names a model family that is not configured in this daemon`,"model")}}else if(tt(s)===void 0)throw h(`model '${s}' is not a strength (ultra, strong, medium, light), a family/strength, or a provider/model id`,"model")}const i=await Qe(),c=Le(j(e.grantee).nodes,i);if(t.isolated&&t.cwd!==void 0){const s=Ie(c,Ae(t.cwd));if(!z(t.cwd)||s===".."||s.startsWith(`..${Te}`)||z(s))throw h("cwd must be inside the isolated root node folder","cwd")}const f=await n(i,e.grantee,t.prompt),y=f.text;let g;try{g=t.message===void 0?void 0:await n(i,e.grantee,t.message)}catch(s){throw f.cleanup(),s}const w=await B(e.grant);let d;try{const s=at(m,t.model);await Ve({kind:st(m),mode:"base",root:!0,rootLifecycle:"resident",readiness:"launch",cwd:t.isolated?t.cwd??c:j(e.grantee).files,profile:m.profileId,prompt:y,nodeId:i,isolated:t.isolated??!1,...s===void 0?{}:{model:s},...t.deadline===void 0?{}:{deadlineAt:new Date(Date.now()+Ee(t.deadline)).toISOString()},...t.scopes===null?{}:{scopes:t.scopes},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant,client_id:e.client_id,metadata_json:JSON.stringify(t.metadata),run_id:i,event_cursor:1,usage_json:JSON.stringify(Y())},withinCreate:u(async(l,E)=>{if(a){const R=await q(e.grantee,a,o,l);if(R)throw new ge(R)}const N=f.uses;if(N.length&&(await de(e.grantee,N,l),await ce(N.map(({upload_id:R,path:M})=>({upload_id:R,path:M,run_id:i})),l)),d={run_id:i,node_id:i,grantee:e.grantee,grant:e.grant,rev:e.rev,scopes:t.scopes,payer:w,status:"running",created_at:E.created,metadata:t.metadata,sequence_number:0},await ie(l,i,i,"run.started",{scopes:t.scopes,payer:w,usage:Y()}),g){const R=await we(l,i,e.grantee,g,!1);d.message={run_id:i,message_id:R.messageId,sequence_number:R.sequence}}a&&await fe(l,e.grantee,a,o,i,d,E.created)},"withinCreate")},t.output_schema===void 0?void 0:async l=>{await se(l,"terminal",t.output_schema)})}catch(s){if(await v(i)||(f.cleanup(),g?.cleanup()),s instanceof ge)return s.winner.response;if(t.model!==void 0&&s instanceof Me&&s.code==="usage")throw h(s.message,"model");if(s instanceof De&&(s.code==="isolated_run"||s.code==="invalid_request")){const l=s.details&&typeof s.details=="object"&&"param"in s.details&&typeof s.details.param=="string"?s.details.param:void 0;throw new S(s.status,s.code,s.message,l)}throw s}if(!d)throw new Error("run creation did not commit");return d}u(Mn,"startRun");async function In(e,t){let a;if(t.after)try{const i=JSON.parse(Buffer.from(t.after,"base64url").toString());if(!i||typeof i!="object"||!("activity"in i)||typeof i.activity!="string"||!("id"in i)||typeof i.id!="string")throw new Error("invalid cursor");a={activity:i.activity,id:i.id}}catch{throw h("invalid list cursor","after")}const n=await(await D()).prepareUncached(`SELECT node_id, COALESCE(last_activity_at, created) AS activity FROM node_rows
9
9
  WHERE run_id = node_id AND deleting_at IS NULL ${e===null?"":"AND grantee = ?"}
10
- ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e]),o=await me(await T((await me(a,async({node_id:r})=>!t.visible||await t.visible(r))).filter(({activity:r,node_id:u})=>!s||r<s.activity||r===s.activity&&u<s.id),async r=>({root:await N(r.node_id),activity:r.activity})),async({root:r})=>!t.status||await A(r)===t.status),m=o.slice(0,t.limit),i=m.at(-1);return{runs:await T(m,async({root:r})=>await L(r)),next:o.length>t.limit&&i?Buffer.from(JSON.stringify({activity:i.activity,id:i.root.node_id})).toString("base64url"):null}}c(Mn,"listRuns");async function An(e,t,s){const a=await F(e,t),o=c(async()=>await L(await F(e,t)),"read");return!s||pe.includes(await A(a))?await o():new Promise((m,i)=>{let r=c(()=>{},"unsubscribe");const u=c(()=>{clearTimeout(d),clearInterval(w),r()},"settle"),f=c(async()=>{u();try{m(await o())}catch(n){i(n)}},"finish"),y=c(async()=>{pe.includes(String((await o()).status))&&await f()},"check"),g=c(()=>{y().catch(n=>{u(),i(n)})},"run"),w=setInterval(g,250),d=setTimeout(()=>{f()},s*1e3);ut(e,g).then(n=>{r=n},n=>{u(),i(n)})})}c(An,"waitRun");async function Tn(e,t,s=le,a=!0,o){const m=e.status==="canceled"||e.outcome_reason==="closed",i=e.grantee??"app:terminal",r=X("sha256").update(JSON.stringify(["run.message",e.node_id,t,a])).digest("hex"),u=c(async w=>{const d=await N(e.node_id),n=await(await D()).prepare("SELECT 1 FROM deliveries WHERE message_id = ? AND state = 'pending' AND canceled_at IS NULL").get(String(w.message_id));return d&&!m&&a&&!d.pi_pid&&n&&await $(e.node_id,{resume:!0,capacity:"freeze"}),w},"replay");if(o){const w=await H(i,o,r);if(w)return u(w.response)}if(m)throw new S(409,"run_settled","canceled run cannot receive a message");const f=await s(e.node_id,i,t),y=a&&e.final_report&&!e.pi_pid?(await import("../runtime/reopen.js")).commitReopenResident:void 0;let g;try{g=await W(async w=>{if(o){const l=await H(i,o,r,w);if(l)return{replayed:l.response}}await y?.(e.node_id);const d=await we(w,e.node_id,i,f,a),n={run_id:e.node_id,message_id:d.messageId,sequence_number:d.sequence};return o&&await fe(w,i,o,r,e.node_id,n,new Date().toISOString()),{result:n}})}catch(w){throw f.cleanup(),w}return"replayed"in g?(f.cleanup(),u(g.replayed)):(a&&!e.pi_pid&&await $(e.node_id,{resume:!0,capacity:"freeze"}),g.result)}c(Tn,"messageRun");async function Rt(e){const t=c(()=>new S(503,"service_unavailable",`node ${e} broker was unavailable before the interrupt reached it`),"unavailable");for(let s=0;s<2;s++){const a=await N(e);if(!a)throw J();if(!ee(a)){if(a.streaming)throw t();return}const o=(await V(e))?.executionId;try{await ze(e)==="turn"&&ue({nodes:[e]});return}catch(m){if(!(m instanceof Xe))throw m;const i=await N(e);if(!i)throw J();if(!i.streaming)return;if(s===0&&ee(i)&&(await V(e))?.executionId!==o)continue;throw t()}}throw t()}c(Rt,"interruptNode");async function Ln(e,t="stop"){const s=t==="stop"?(await(await D()).prepare("SELECT node_id FROM nodes WHERE run_id = ?").all(e.node_id)).map(({node_id:a})=>a):[e.node_id];return await Promise.all(s.map(a=>Rt(a))),await L(await N(e.node_id)??e)}c(Ln,"interruptRun");async function Cn(e,t={}){const s=(await(await D()).prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(i=>i.node_id);ue({run:e.node_id});const a=[];if(await A(e)!=="settled")a.push(...(await se(e.node_id,{preserveEmpty:!0,stopReason:"run_canceled"})).closed);else for(const i of s){if(i===e.node_id)continue;const r=await N(i);r&&r.status!=="done"&&r.status!=="canceled"&&r.status!=="dead"&&a.push(...(await se(i,{preserveEmpty:!0,stopReason:"run_canceled"})).closed)}if(await ie({run_id:e.node_id},"the run was canceled"),(await Promise.all([...new Set([e.node_id,...a])].map(i=>K(i,"run_canceled")))).some(i=>!i.confirmed))throw new S(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const m=await N(e.node_id);return await L(m??e)}c(Cn,"cancelRun");const ye=200;async function kn(e,t){if(typeof t!="string"||!t.trim())throw R("name is required","name");const s=t.trim();if(s.length>ye)throw R(`name exceeds ${ye} characters`,"name");const a=await He(e.node_id,{name:s});return a.window&&Ue(a.window,qe(a)),await L(a)}c(kn,"renameRun");async function xn(e,t={config:wt,client:ft}){await ie({run_id:e.node_id},"the run was deleted");const s=await W(async u=>(await u.prepare("UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id = ?").run(new Date().toISOString(),e.node_id),(await u.prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(f=>f.node_id)));if((await Promise.all(s.map(u=>K(u,"run_deleted",{deleting:!0})))).some(u=>!u.confirmed))throw new S(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const o=await T(s,async u=>{const f=await We(u);if(f.app===null)throw new Error(`run node missing during deletion: ${u}`);return f}),m=await(await D()).prepare("SELECT DISTINCT session_id FROM sessions WHERE session_id IS NOT NULL AND node_id IN (SELECT node_id FROM nodes WHERE run_id = ?)").all(e.node_id);for(const{session_id:u}of m)try{await Z().delete(u)}catch(f){if(!Pe(f))throw f}const i=await W(async u=>{for(const w of s){const d=await xe(u,w);d!==null&&d.deleted_at===null&&await ke(u,d)}const f=await Et([e.node_id],u);await u.prepare("DELETE FROM run_events WHERE run_id = ?").run(e.node_id),await u.prepare("DELETE FROM idempotency_keys WHERE run_id = ?").run(e.node_id);const y=s.map(()=>"?").join(","),g=await u.prepareUncached(`SELECT message_id FROM messages WHERE sender_node_id IN (${y}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${y})`).all(...s,...s);await u.prepareUncached(`DELETE FROM deliveries WHERE destination_id IN (${y})`).run(...s);for(const{message_id:w}of g)await u.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(w,w);return f});for(const u of o)await Fe(u);const{notifyRunDeleted:r}=await import("./events.js");return r(e.node_id),{uploads_pending:await St(i,t)}}c(xn,"deleteRun");async function St(e,t){if(!e.length||!t.config())return e.length;let s;try{s=t.client()}catch{return e.length}const a=new Set,o=e.map(i=>s.deleteUpload(i).then(async()=>{await gt(i),a.add(i)},()=>{}));let m;return await Promise.race([Promise.all(o),new Promise(i=>{m=setTimeout(i,t.budgetMs??yt),m.unref?.()})]),clearTimeout(m),e.length-a.size}c(St,"deleteReleasedUploads");async function Wn(e,t,s){await F(e,t),await Q(e);const a=await W(async d=>{const n=await d.prepare("SELECT node_id, parent FROM nodes WHERE run_id = ? ORDER BY CASE WHEN node_id = ? THEN 0 ELSE 1 END, created, node_id").all(e,e),l=new Map;for(const k of n)l.set(k.node_id,(await d.prepare("SELECT session_id FROM sessions WHERE node_id = ? GROUP BY session_id ORDER BY min(created_at)").all(k.node_id)).map(x=>x.session_id));const h=(await d.prepare("SELECT event_cursor FROM nodes WHERE node_id = ?").get(e)).event_cursor-1,b=await d.prepare(`SELECT node_id, sequence_number FROM run_events
10
+ ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e]),o=await me(await L((await me(n,async({node_id:i})=>!t.visible||await t.visible(i))).filter(({activity:i,node_id:c})=>!a||i<a.activity||i===a.activity&&c<a.id),async i=>({root:await v(i.node_id),activity:i.activity})),async({root:i})=>!t.status||await T(i)===t.status),m=o.slice(0,t.limit),r=m.at(-1);return{runs:await L(m,async({root:i})=>await C(i)),next:o.length>t.limit&&r?Buffer.from(JSON.stringify({activity:r.activity,id:r.root.node_id})).toString("base64url"):null}}u(In,"listRuns");async function An(e,t,a){const n=await F(e,t),o=u(async()=>await C(await F(e,t)),"read");return!a||pe.includes(await T(n))?await o():new Promise((m,r)=>{let i=u(()=>{},"unsubscribe");const c=u(()=>{clearTimeout(d),clearInterval(w),i()},"settle"),f=u(async()=>{c();try{m(await o())}catch(s){r(s)}},"finish"),y=u(async()=>{pe.includes(String((await o()).status))&&await f()},"check"),g=u(()=>{y().catch(s=>{c(),r(s)})},"run"),w=setInterval(g,250),d=setTimeout(()=>{f()},a*1e3);mt(e,g).then(s=>{i=s},s=>{c(),r(s)})})}u(An,"waitRun");async function Tn(e,t,a=le,n=!0,o){const m=e.status==="canceled"||e.outcome_reason==="closed",r=e.grantee??"app:terminal",i=G("sha256").update(JSON.stringify(["run.message",e.node_id,t,n])).digest("hex"),c=u(async w=>{const d=await v(e.node_id),s=await(await D()).prepare("SELECT 1 FROM deliveries WHERE message_id = ? AND state = 'pending' AND canceled_at IS NULL").get(String(w.message_id));return d&&!m&&n&&!d.pi_pid&&s&&await $(e.node_id,{resume:!0,capacity:"freeze"}),w},"replay");if(o){const w=await q(r,o,i);if(w)return c(w.response)}if(m)throw new S(409,"run_settled","canceled run cannot receive a message");const f=await a(e.node_id,r,t),y=n&&e.final_report&&!e.pi_pid?(await import("../runtime/reopen.js")).commitReopenResident:void 0;let g;try{g=await W(async w=>{if(o){const l=await q(r,o,i,w);if(l)return{replayed:l.response}}await y?.(e.node_id);const d=await we(w,e.node_id,r,f,n),s={run_id:e.node_id,message_id:d.messageId,sequence_number:d.sequence};return o&&await fe(w,r,o,i,e.node_id,s,new Date().toISOString()),{result:s}})}catch(w){throw f.cleanup(),w}return"replayed"in g?(f.cleanup(),c(g.replayed)):(n&&!e.pi_pid&&await $(e.node_id,{resume:!0,capacity:"freeze"}),g.result)}u(Tn,"messageRun");async function St(e){const t=u(()=>new S(503,"service_unavailable",`node ${e} broker was unavailable before the interrupt reached it`),"unavailable");for(let a=0;a<2;a++){const n=await v(e);if(!n)throw J();if(!ee(n)){if(n.streaming)throw t();return}const o=(await V(e))?.executionId;try{await ze(e)==="turn"&&ue({nodes:[e]});return}catch(m){if(!(m instanceof Ge))throw m;const r=await v(e);if(!r)throw J();if(!r.streaming)return;if(a===0&&ee(r)&&(await V(e))?.executionId!==o)continue;throw t()}}throw t()}u(St,"interruptNode");async function Ln(e,t="stop"){const a=t==="stop"?(await(await D()).prepare("SELECT node_id FROM nodes WHERE run_id = ?").all(e.node_id)).map(({node_id:n})=>n):[e.node_id];return await Promise.all(a.map(n=>St(n))),await C(await v(e.node_id)??e)}u(Ln,"interruptRun");async function Cn(e,t={}){const a=(await(await D()).prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(r=>r.node_id);ue({run:e.node_id});const n=[];if(await T(e)!=="settled")n.push(...(await ae(e.node_id,{preserveEmpty:!0,stopReason:"run_canceled"})).closed);else for(const r of a){if(r===e.node_id)continue;const i=await v(r);i&&i.status!=="done"&&i.status!=="canceled"&&i.status!=="dead"&&n.push(...(await ae(r,{preserveEmpty:!0,stopReason:"run_canceled"})).closed)}if(await re({run_id:e.node_id},"the run was canceled"),(await Promise.all([...new Set([e.node_id,...n])].map(r=>K(r,"run_canceled")))).some(r=>!r.confirmed))throw new S(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const m=await v(e.node_id);return await C(m??e)}u(Cn,"cancelRun");const ye=200;async function kn(e,t){if(typeof t!="string"||!t.trim())throw h("name is required","name");const a=t.trim();if(a.length>ye)throw h(`name exceeds ${ye} characters`,"name");const n=await qe(e.node_id,{name:a});return n.window&&Ue(n.window,He(n)),await C(n)}u(kn,"renameRun");async function xn(e,t={config:gt,client:wt}){await re({run_id:e.node_id},"the run was deleted");const a=await W(async c=>(await c.prepare("UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id = ?").run(new Date().toISOString(),e.node_id),(await c.prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(f=>f.node_id)));if((await Promise.all(a.map(c=>K(c,"run_deleted",{deleting:!0})))).some(c=>!c.confirmed))throw new S(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const o=await L(a,async c=>{const f=await We(c);if(f.app===null)throw new Error(`run node missing during deletion: ${c}`);return f}),m=await(await D()).prepare("SELECT DISTINCT session_id FROM sessions WHERE session_id IS NOT NULL AND node_id IN (SELECT node_id FROM nodes WHERE run_id = ?)").all(e.node_id);for(const{session_id:c}of m)try{await Z().delete(c)}catch(f){if(!Pe(f))throw f}const r=await W(async c=>{for(const w of a){const d=await xe(c,w);d!==null&&d.deleted_at===null&&await ke(c,d)}const f=await yt([e.node_id],c);await c.prepare("DELETE FROM run_events WHERE run_id = ?").run(e.node_id),await c.prepare("DELETE FROM idempotency_keys WHERE run_id = ?").run(e.node_id);const y=a.map(()=>"?").join(","),g=await c.prepareUncached(`SELECT message_id FROM messages WHERE sender_node_id IN (${y}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${y})`).all(...a,...a);await c.prepareUncached(`DELETE FROM deliveries WHERE destination_id IN (${y})`).run(...a);for(const{message_id:w}of g)await c.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(w,w);return f});for(const c of o)await Fe(c);const{notifyRunDeleted:i}=await import("./events.js");return i(e.node_id),{uploads_pending:await Nt(r,t)}}u(xn,"deleteRun");async function Nt(e,t){if(!e.length||!t.config())return e.length;let a;try{a=t.client()}catch{return e.length}const n=new Set,o=e.map(r=>a.deleteUpload(r).then(async()=>{await Et(r),n.add(r)},()=>{}));let m;return await Promise.race([Promise.all(o),new Promise(r=>{m=setTimeout(r,t.budgetMs??ht),m.unref?.()})]),clearTimeout(m),e.length-n.size}u(Nt,"deleteReleasedUploads");async function Wn(e,t,a){await F(e,t),await Q(e);const n=await W(async d=>{const s=await d.prepare("SELECT node_id, parent FROM nodes WHERE run_id = ? ORDER BY CASE WHEN node_id = ? THEN 0 ELSE 1 END, created, node_id").all(e,e),l=new Map;for(const k of s)l.set(k.node_id,(await d.prepare("SELECT session_id FROM sessions WHERE node_id = ? GROUP BY session_id ORDER BY min(created_at)").all(k.node_id)).map(x=>x.session_id));const N=(await d.prepare("SELECT event_cursor FROM nodes WHERE node_id = ?").get(e)).event_cursor-1,R=await d.prepare(`SELECT node_id, sequence_number FROM run_events
11
11
  WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.done', 'node.output_text.done', 'usage')
12
- ORDER BY sequence_number`).all(e,h),C=await d.prepare(`SELECT node_id, sequence_number, type, body_json FROM run_events
12
+ ORDER BY sequence_number`).all(e,N),M=await d.prepare(`SELECT node_id, sequence_number, type, body_json FROM run_events
13
13
  WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.delta', 'node.output_text.delta')
14
- ORDER BY sequence_number`).all(e,h);return{nodes:n,sessionIds:l,latestSequence:h,completed:b,deltas:C}}),{nodes:o,sessionIds:m,latestSequence:i,completed:r,deltas:u}=a,f=await T(o,async d=>({node_id:d.node_id,parent_node_id:d.parent,conversations:await T(m.get(d.node_id)??[],async n=>{const{header:l,entries:E}=await Z().read(n);if(s==="entries")return{conversation_id:n,header:l,entries:E};const h=Ce.inMemory(l.cwd,void 0,[l,...E]),b=st(h)??[],C=new Map(E.map((_,v)=>[_.id,v])),k=E.filter(_=>_.type==="custom"&&_.customType==="crtr-system-prompt").map(_=>({id:_.id,message:{role:"custom",customType:"crtr-system-prompt",data:"data"in _?_.data:void 0,timestamp:_.timestamp}})),x=E.filter(_=>_.type==="custom_message"&&_.customType==="app_message").map(_=>({id:_.id,message:{role:"custom",customType:"app_message",content:"content"in _?_.content:"",timestamp:_.timestamp}})),he=d.node_id===e?E.find(_=>{if(_.type!=="message"||_.message.role!=="user")return!1;const v=te(E,_.message.timestamp);return v?.type==="runtime"&&v.kind==="kickoff"})?.id:void 0,Re=[...b.filter(({id:_})=>!x.some(v=>v.id===_)),...k,...x].sort((_,v)=>(C.get(_.id)??Number.MAX_SAFE_INTEGER)-(C.get(v.id)??Number.MAX_SAFE_INTEGER)).map(({id:_,message:v})=>{const p=v,I=p.usage,q=p.role==="user"?te(E,Number(p.timestamp)):null,Se=q?.type==="human"||_===he,M=p.role==="toolResult"?"tool_result":p.role==="bashExecution"?"bash_execution":p.role==="compactionSummary"?"compaction_summary":p.role==="branchSummary"?"branch_summary":p.role==="custom"&&p.customType==="crtr-system-prompt"?"system":p.role==="user"?Se?"user":"custom":p.role,Ne=M==="custom"?p.role==="custom"?p.customType:q?.type==="runtime"?q.kind:"unknown":void 0,ve=M==="system"&&p.customType==="crtr-system-prompt"?[{type:"text",text:p.data?.text??""}]:typeof p.content=="string"?[{type:"text",text:p.content}]:Array.isArray(p.content)?p.content.map(O=>O.type==="toolCall"?{type:"tool_call",tool_call_id:O.id,name:O.name,arguments:O.arguments}:O.type==="image"?{type:"image",mime_type:O.mimeType}:O.type==="thinking"?{type:"thinking",text:O.thinking}:O):[];return{entry_id:_,role:M,...M==="custom"?{custom_type:Ne}:{},time:typeof p.timestamp=="string"?p.timestamp:new Date(Number(p.timestamp??Date.now())).toISOString(),content:ve,...M==="assistant"?{model:p.model,provider:p.provider,stop_reason:p.stopReason,...p.errorMessage?{error_message:p.errorMessage}:{},...I?{usage:{input_tokens:I.input??0,output_tokens:I.output??0,cache_read_tokens:I.cacheRead??0,cache_write_tokens:I.cacheWrite??0,cost_usd:I.cost?.total??0}}:{}}:{},...M==="tool_result"?{tool_call_id:p.toolCallId,tool_name:p.toolName,is_error:p.isError}:{}}})??[];return{conversation_id:n,messages:Re}})})),y=new Map(f.map(d=>[d.node_id,dt(d.node_id)]));for(const d of r){const n=y.get(d.node_id);n!=null&&d.sequence_number>=n&&y.set(d.node_id,null)}const g=new Map;for(const d of u){const n=y.get(d.node_id);if(n==null||d.sequence_number<n)continue;const l=JSON.parse(d.body_json),E=g.get(d.node_id)??{thinking:"",text:""};d.type==="node.thinking.delta"?E.thinking+=l.delta:E.text+=l.delta,g.set(d.node_id,E)}const w=f.map(d=>({...d,in_progress_message:y.get(d.node_id)==null?null:g.get(d.node_id)??{thinking:"",text:""}}));return{run_id:e,latest_sequence:i,nodes:w}}c(Wn,"traceRun");function Fn(e,t,s){if(t===void 0){if(!e.some(a=>U(a,"crtr:llm")))throw new S(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return null}if(!Array.isArray(t)||t.some(a=>typeof a!="string"))throw R("scopes must be a list of scope strings","scopes");for(const a of t){try{ct(a)}catch{throw R(`invalid scope ${a}`,"scopes")}if(/^crtr:files:(?:read|rw):app:/.test(a)&&a.split(":")[4]===s.slice(4))throw R(`own-app files cannot narrow a run: ${a}`,"scopes");if(!(/^crtr:memory:(?:read|rw):app:/.test(a)&&a.split(":")[4]===s.slice(4))&&!be(e,a))throw new S(403,"scope_missing",`missing scope ${a}`,void 0,void 0,a)}if(!t.includes("crtr:llm"))throw new S(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return t}c(Fn,"requestedScopes");export{G as IDEMPOTENCY_WINDOW_MS,ht as RUN_DEADLINE_MAX_MS,yt as RUN_DELETE_STORAGE_BUDGET_MS,ye as RUN_NAME_MAX,S as RunError,Cn as cancelRun,xn as deleteRun,bn as idempotencyKeyOf,Ln as interruptRun,Mn as listRuns,Tn as messageRun,F as ownedRun,Ee as parseRunDeadlineMs,Dn as pruneExpiredIdempotencyKeys,kn as renameRun,Fn as requestedScopes,vn as rewriteLocalIdempotencyGrantee,On as runIsRunning,L as runObject,In as startRun,Wn as traceRun,An as waitRun};
14
+ ORDER BY sequence_number`).all(e,N);return{nodes:s,sessionIds:l,latestSequence:N,completed:R,deltas:M}}),{nodes:o,sessionIds:m,latestSequence:r,completed:i,deltas:c}=n,f=await L(o,async d=>({node_id:d.node_id,parent_node_id:d.parent,conversations:await L(m.get(d.node_id)??[],async s=>{const{header:l,entries:E}=await Z().read(s);if(a==="entries")return{conversation_id:s,header:l,entries:E};const N=Ce.inMemory(l.cwd,void 0,[l,...E]),R=it(N)??[],M=new Map(E.map((_,O)=>[_.id,O])),k=E.filter(_=>_.type==="custom"&&_.customType==="crtr-system-prompt").map(_=>({id:_.id,message:{role:"custom",customType:"crtr-system-prompt",data:"data"in _?_.data:void 0,timestamp:_.timestamp}})),x=E.filter(_=>_.type==="custom_message"&&_.customType==="app_message").map(_=>({id:_.id,message:{role:"custom",customType:"app_message",content:"content"in _?_.content:"",timestamp:_.timestamp}})),he=d.node_id===e?E.find(_=>{if(_.type!=="message"||_.message.role!=="user")return!1;const O=te(E,_.message.timestamp);return O?.type==="runtime"&&O.kind==="kickoff"})?.id:void 0,Re=[...R.filter(({id:_})=>!x.some(O=>O.id===_)),...k,...x].sort((_,O)=>(M.get(_.id)??Number.MAX_SAFE_INTEGER)-(M.get(O.id)??Number.MAX_SAFE_INTEGER)).map(({id:_,message:O})=>{const p=O,I=p.usage,H=p.role==="user"?te(E,Number(p.timestamp)):null,Se=H?.type==="human"||_===he,A=p.role==="toolResult"?"tool_result":p.role==="bashExecution"?"bash_execution":p.role==="compactionSummary"?"compaction_summary":p.role==="branchSummary"?"branch_summary":p.role==="custom"&&p.customType==="crtr-system-prompt"?"system":p.role==="user"?Se?"user":"custom":p.role,Ne=A==="custom"?p.role==="custom"?p.customType:H?.type==="runtime"?H.kind:"unknown":void 0,ve=A==="system"&&p.customType==="crtr-system-prompt"?[{type:"text",text:p.data?.text??""}]:typeof p.content=="string"?[{type:"text",text:p.content}]:Array.isArray(p.content)?p.content.map(b=>b.type==="toolCall"?{type:"tool_call",tool_call_id:b.id,name:b.name,arguments:b.arguments}:b.type==="image"?{type:"image",mime_type:b.mimeType}:b.type==="thinking"?{type:"thinking",text:b.thinking}:b):[];return{entry_id:_,role:A,...A==="custom"?{custom_type:Ne}:{},time:typeof p.timestamp=="string"?p.timestamp:new Date(Number(p.timestamp??Date.now())).toISOString(),content:ve,...A==="assistant"?{model:p.model,provider:p.provider,stop_reason:p.stopReason,...p.errorMessage?{error_message:p.errorMessage}:{},...I?{usage:{input_tokens:I.input??0,output_tokens:I.output??0,cache_read_tokens:I.cacheRead??0,cache_write_tokens:I.cacheWrite??0,cost_usd:I.cost?.total??0}}:{}}:{},...A==="tool_result"?{tool_call_id:p.toolCallId,tool_name:p.toolName,is_error:p.isError}:{}}})??[];return{conversation_id:s,messages:Re}})})),y=new Map(f.map(d=>[d.node_id,ct(d.node_id)]));for(const d of i){const s=y.get(d.node_id);s!=null&&d.sequence_number>=s&&y.set(d.node_id,null)}const g=new Map;for(const d of c){const s=y.get(d.node_id);if(s==null||d.sequence_number<s)continue;const l=JSON.parse(d.body_json),E=g.get(d.node_id)??{thinking:"",text:""};d.type==="node.thinking.delta"?E.thinking+=l.delta:E.text+=l.delta,g.set(d.node_id,E)}const w=f.map(d=>({...d,in_progress_message:y.get(d.node_id)==null?null:g.get(d.node_id)??{thinking:"",text:""}}));return{run_id:e,latest_sequence:r,nodes:w}}u(Wn,"traceRun");function Fn(e,t,a){if(t===void 0){if(!e.some(n=>U(n,"crtr:llm")))throw new S(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return null}if(!Array.isArray(t)||t.some(n=>typeof n!="string"))throw h("scopes must be a list of scope strings","scopes");for(const n of t){try{ut(n)}catch{throw h(`invalid scope ${n}`,"scopes")}if(/^crtr:files:(?:read|rw):app:/.test(n)&&n.split(":")[4]===a.slice(4))throw h(`own-app files cannot narrow a run: ${n}`,"scopes");if(!(/^crtr:memory:(?:read|rw):app:/.test(n)&&n.split(":")[4]===a.slice(4))&&!be(e,n))throw new S(403,"scope_missing",`missing scope ${n}`,void 0,void 0,n)}if(!t.includes("crtr:llm"))throw new S(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return t}u(Fn,"requestedScopes");export{X as IDEMPOTENCY_WINDOW_MS,Rt as RUN_DEADLINE_MAX_MS,ht as RUN_DELETE_STORAGE_BUDGET_MS,ye as RUN_NAME_MAX,S as RunError,Cn as cancelRun,xn as deleteRun,bn as idempotencyKeyOf,Ln as interruptRun,In as listRuns,Tn as messageRun,F as ownedRun,Ee as parseRunDeadlineMs,Dn as pruneExpiredIdempotencyKeys,kn as renameRun,Fn as requestedScopes,On as runIsRunning,C as runObject,Mn as startRun,Wn as traceRun,An as waitRun};
@@ -1 +1 @@
1
- var k=Object.defineProperty;var d=(t,o)=>k(t,"name",{value:o,configurable:!0});import{getNode as w,subscriptionsOf as u,subscribersOf as E}from"../canvas/index.js";import{transition as _}from"./lifecycle.js";import{stopNodeSandboxes as $}from"../spaces/stop.js";import{tearDownNode as D,reapIfEmpty as N}from"./placement.js";import{crtrHome as L,jobDir as O}from"../canvas/paths.js";import{contextDir as x,nodeAgentDir as A}from"../canvas/node-agent-paths.js";import{readLiveSession as S}from"../canvas/sessions.js";import{discoverLifecycleHookRegistry as j}from"../command-hooks/discovery.js";import{invokeLifecycleHooks as H}from"../command-hooks/transport/exec-lifecycle.js";import{emitEvent as h}from"../events/emit.js";import{enqueueNodeMail as B}from"../feed/messages.js";import{withCanvasWrite as g}from"../canvas/db.js";import{recordActedOn as C}from"../graph/edges.js";import{publish as R}from"../graph/events.js";import{getById as v,nodeObject as T,ownerForGrantee as Y}from"../graph/objects.js";import{mapAsync as q}from"../async-array.js";const P={deferred:3,normal:5,urgent:7,critical:7};async function z(t,o,c,a,l="normal",r=null,s){await g(async e=>{const n=await v(e,t);n===null||n.type!=="node"||await R(e,{event:o,object:n,actor:r,level:P[l],attrs:{},messageBody:{...a,body:c},...s===void 0?{}:{onlyWatchers:s}})})}d(z,"publishNotice");async function F(t){const o=new Set([t]);let c=!0;for(;c;){c=!1;for(const a of[...o])for(const l of await u(a)){const r=l.node_id;o.has(r)||(await E(r)).every(s=>o.has(s.node_id))&&(o.add(r),c=!0)}}return o}d(F,"closingSet");async function G(t,o){const c=[],a=new Set([t]),l=[t];for(;l.length>0;){const r=l.shift();c.push(r);for(const s of await u(r))o.has(s.node_id)&&!a.has(s.node_id)&&(a.add(s.node_id),l.push(s.node_id))}for(const r of o)a.has(r)||c.push(r);return c.reverse()}d(G,"killOrder");async function J(t,o){const c=t?"You were CLOSED by the user from the canvas":"You were CANCELED \u2014 an ancestor of yours was closed from the canvas";if(o.length===0)return`${c}. Your pi session is preserved; this resume reopened it.`;const a=await q(o.slice(0,4),async r=>{const s=await w(r);return s!==null?`${s.name} (${r})`:r}),l=o.length>a.length?` +${o.length-a.length} more`:"";return`${c}. ${o.length} child node(s) you subscribe to were canceled with you and are no longer running: ${a.join(", ")}${l}. Resuming will NOT restore them \u2014 re-spawn if you still need that work.`}d(J,"cancellationLabel");async function ce(t,o={}){if(await w(t)===null)throw new Error(`closeNode: unknown node ${t}`);const c=o.rootEvent??"cancel",a=await F(t),l=await G(t,a),r=[];for(const e of a)for(const n of await u(e))!a.has(n.node_id)&&!r.includes(n.node_id)&&r.push(n.node_id);const s=[];for(const e of l)try{const n=await w(e);if(n===null)continue;const p=(await u(e)).map(i=>i.node_id).filter(i=>a.has(i));let m;try{const i=j(n.cwd,n.profile_id).plans.get("node:close")??[];if(i.length>0){const f={node:{id:e,name:n.name,kind:n.kind,mode:n.mode,lifecycle:n.lifecycle,cwd:n.cwd,nodeDir:await A(e),contextDir:await x(e),jobDir:O(e)},runtime:{isBirth:!1,canvasHome:L(),profile:n.profile_id??null}};m=d(()=>H(i,f),"afterTeardown")}}catch(i){h({level:"error",event:"node.close_hook.discovery_failed",node_id:e,error:i})}const b=$(e,o.stopReason??"node_closed",{...e===t?{excludePid:o.callerPid}:{},...o.teardownAfter?{startAfter:o.teardownAfter}:{},...m?{after:m}:{}});try{o.registerDetached?.(b.then(()=>{}))}catch(i){h({level:"error",event:"node.close_hook.detached_registration_failed",node_id:e,error:i})}if(await g(async i=>{const f=await v(i,e);if(f===null||f.type!=="node")return;const y=o.closer!==void 0?await T(i,o.closer):await Y(i,"user:local");await C(i,y,f),await z(e,"notice.close",`Child closed \u2014 ${n.name} (${e}) was closed from the canvas and is no longer running.`,{reason:"child-closed",child:e},"normal",y)}),o.preserveEmpty||n.run_id!==null&&n.pi_pid!==null&&(await S(e))?.sessionId!=null||!await N(e,void 0,!1)){const i=n.final_report!==null&&n.final_report!==void 0;e===t&&c==="finish"||i?(n.status==="active"||n.status==="idle")&&await _(e,"finish"):await _(e,"cancel",{reason:"closed"}),await D(e),await B(e,{kind:"message",label:await J(e===t,p),bodyJson:{reason:"user-close",cascade_root:t,canceled_children:p},urgency:"normal",wake:1})}s.push(e)}catch(n){h({level:"error",event:"node.close.failed",node_id:e,error:n})}return{root:t,closed:s,spared:r}}d(ce,"closeNode");export{ce as closeNode,F as closingSet,z as publishNotice};
1
+ var k=Object.defineProperty;var d=(t,o)=>k(t,"name",{value:o,configurable:!0});import{getNode as w,subscriptionsOf as u,subscribersOf as E}from"../canvas/index.js";import{transition as _}from"./lifecycle.js";import{stopNodeSandboxes as $}from"../spaces/stop.js";import{tearDownNode as D,reapIfEmpty as N}from"./placement.js";import{crtrHome as L,jobDir as O}from"../canvas/paths.js";import{contextDir as x,nodeAgentDir as A}from"../canvas/node-agent-paths.js";import{readLiveSession as S}from"../canvas/sessions.js";import{discoverLifecycleHookRegistry as j}from"../command-hooks/discovery.js";import{invokeLifecycleHooks as H}from"../command-hooks/transport/exec-lifecycle.js";import{emitEvent as h}from"../events/emit.js";import{enqueueNodeMail as B}from"../feed/messages.js";import{withCanvasWrite as g}from"../canvas/db.js";import{recordActedOn as C}from"../graph/edges.js";import{publish as R}from"../graph/events.js";import{getById as v,nodeObject as T,ownerForGrantee as Y}from"../graph/objects.js";import{mapAsync as q}from"../async-array.js";import{personId as P}from"../canvas/person.js";const z={deferred:3,normal:5,urgent:7,critical:7};async function F(t,o,c,a,l="normal",r=null,s){await g(async e=>{const n=await v(e,t);n===null||n.type!=="node"||await R(e,{event:o,object:n,actor:r,level:z[l],attrs:{},messageBody:{...a,body:c},...s===void 0?{}:{onlyWatchers:s}})})}d(F,"publishNotice");async function G(t){const o=new Set([t]);let c=!0;for(;c;){c=!1;for(const a of[...o])for(const l of await u(a)){const r=l.node_id;o.has(r)||(await E(r)).every(s=>o.has(s.node_id))&&(o.add(r),c=!0)}}return o}d(G,"closingSet");async function J(t,o){const c=[],a=new Set([t]),l=[t];for(;l.length>0;){const r=l.shift();c.push(r);for(const s of await u(r))o.has(s.node_id)&&!a.has(s.node_id)&&(a.add(s.node_id),l.push(s.node_id))}for(const r of o)a.has(r)||c.push(r);return c.reverse()}d(J,"killOrder");async function M(t,o){const c=t?"You were CLOSED by the user from the canvas":"You were CANCELED \u2014 an ancestor of yours was closed from the canvas";if(o.length===0)return`${c}. Your pi session is preserved; this resume reopened it.`;const a=await q(o.slice(0,4),async r=>{const s=await w(r);return s!==null?`${s.name} (${r})`:r}),l=o.length>a.length?` +${o.length-a.length} more`:"";return`${c}. ${o.length} child node(s) you subscribe to were canceled with you and are no longer running: ${a.join(", ")}${l}. Resuming will NOT restore them \u2014 re-spawn if you still need that work.`}d(M,"cancellationLabel");async function de(t,o={}){if(await w(t)===null)throw new Error(`closeNode: unknown node ${t}`);const c=o.rootEvent??"cancel",a=await G(t),l=await J(t,a),r=[];for(const e of a)for(const n of await u(e))!a.has(n.node_id)&&!r.includes(n.node_id)&&r.push(n.node_id);const s=[];for(const e of l)try{const n=await w(e);if(n===null)continue;const p=(await u(e)).map(i=>i.node_id).filter(i=>a.has(i));let m;try{const i=j(n.cwd,n.profile_id).plans.get("node:close")??[];if(i.length>0){const f={node:{id:e,name:n.name,kind:n.kind,mode:n.mode,lifecycle:n.lifecycle,cwd:n.cwd,nodeDir:await A(e),contextDir:await x(e),jobDir:O(e)},runtime:{isBirth:!1,canvasHome:L(),profile:n.profile_id??null}};m=d(()=>H(i,f),"afterTeardown")}}catch(i){h({level:"error",event:"node.close_hook.discovery_failed",node_id:e,error:i})}const b=$(e,o.stopReason??"node_closed",{...e===t?{excludePid:o.callerPid}:{},...o.teardownAfter?{startAfter:o.teardownAfter}:{},...m?{after:m}:{}});try{o.registerDetached?.(b.then(()=>{}))}catch(i){h({level:"error",event:"node.close_hook.detached_registration_failed",node_id:e,error:i})}if(await g(async i=>{const f=await v(i,e);if(f===null||f.type!=="node")return;const y=o.closer!==void 0?await T(i,o.closer):await Y(i,P());await C(i,y,f),await F(e,"notice.close",`Child closed \u2014 ${n.name} (${e}) was closed from the canvas and is no longer running.`,{reason:"child-closed",child:e},"normal",y)}),o.preserveEmpty||n.run_id!==null&&n.pi_pid!==null&&(await S(e))?.sessionId!=null||!await N(e,void 0,!1)){const i=n.final_report!==null&&n.final_report!==void 0;e===t&&c==="finish"||i?(n.status==="active"||n.status==="idle")&&await _(e,"finish"):await _(e,"cancel",{reason:"closed"}),await D(e),await B(e,{kind:"message",label:await M(e===t,p),bodyJson:{reason:"user-close",cascade_root:t,canceled_children:p},urgency:"normal",wake:1})}s.push(e)}catch(n){h({level:"error",event:"node.close.failed",node_id:e,error:n})}return{root:t,closed:s,spared:r}}d(de,"closeNode");export{de as closeNode,G as closingSet,F as publishNotice};
@@ -1,3 +1,3 @@
1
- var be=Object.defineProperty;var l=(e,t)=>be(e,"name",{value:t,configurable:!0});import{assertGranteeRunsHere as Pe}from"./unsandboxed.js";import{spawn as Re}from"node:child_process";import{mkdirSync as ue,existsSync as j,unlinkSync as fe,openSync as F,closeSync as N,writeSync as xe,chmodSync as Oe,lstatSync as Te}from"node:fs";import{ensureAppSpace as De}from"../spaces/space.js";import{sandboxCacheEnv as Ne}from"../spaces/cache-env.js";import{compileLaunch as Ae,launchStillGranted as Ce}from"../spaces/mounts.js";import{grantsGeneration as de}from"../grants/mirror.js";import{LAUNCHER_SOCKET as z}from"../../launcher/spec.js";import{launcherRequest as M}from"../../launcher/client.js";import{piAgentDir as Le}from"./package-health.js";import{existsBeneath as V,readFileBeneath as Be,readTextBeneathIfExists as $e,setModeBeneath as P,withDirBeneath as X,writeFileBeneath as G}from"../spaces/open-beneath.js";import{dirname as Q,join as w,resolve as je,isAbsolute as Fe}from"node:path";import{fileURLToPath as me}from"node:url";import{connectViewSocket as Me}from"./view-socket.js";import{appSpace as K}from"../layout.js";import{readConfig as Ge,readMergedLaunchConfig as Ke}from"../config.js";import{readRotationConfig as He}from"../subscription-state.js";import{getNode as _}from"../canvas/index.js";import{scopesFilePath as Y,crtrHome as pe,nodeRunDir as Z,nodeRunJobDir as Je,jobDir as qe,viewSocketPath as Ue,nodeAgentDir as We,contextDir as ze,nodeHome as Ve}from"../canvas/paths.js";import{encodeFrame as Xe}from"./broker-protocol.js";import{buildBrokerEnv as Qe}from"./spawn-env.js";import{hostExecPathFresh as Ye}from"./branded-host.js";import{isPidAlive as I,killProcessTreePids as he,isAnyPidAlive as Ze,captureTeardownSnapshot as Ie}from"../canvas/pid.js";import{assertMandatoryCanvasExtensions as er}from"./canvas-extensions.js";import{appMemoryFull as rr,appMemoryTooSmall as tr,boundFleet as nr,brokerCapReached as or,brokerThresholdsForDaemon as ir}from"./fleet.js";import{admitBrokerMemory as sr}from"./memory-admission.js";import{emitEvent as E}from"../events/emit.js";import{bindBrokerEventSource as ar,eventSource as cr}from"../events/source.js";import{envBrokerEngine as lr}from"../../shared/env.js";import{conversationIdFromPath as ur,conversationStore as fr,ConversationNotFoundError as we,storeAddress as dr}from"../conversation-store/index.js";import{takeNodeToken as mr}from"../canvas/node-tokens.js";function H(){const e=Q(me(import.meta.url));return w(e,"broker-cli.js")}l(H,"resolveBrokerEntry");const pr="@earendil-works/pi-coding-agent";function hr(e,t){const n=w(Le(),"models.json"),r=w(t,"models.json"),o=$e(Q(n),n);if(o===null)return;const d=JSON.parse(o),s=l(f=>Array.isArray(f)?f.map(s):f===null||typeof f!="object"?f:Object.fromEntries(Object.entries(f).filter(([i])=>!/^(apiKey|authorization|headers|token|secret)$/i.test(i)).map(([i,m])=>[i,s(m)])),"scrub"),u=`${JSON.stringify(s(d),null,2)}
2
- `;if(n===r&&o!==u){if(JSON.stringify(d)!==JSON.stringify(s(d)))throw new Error("terminal models.json contains credentials; move them into daemon secrets");P(e,r,432);return}G(e,r,u,{atomic:!0,mode:432}),P(e,r,432)}l(hr,"writeBrokerModels");function ge(e){return e.env.CRTR_BROKER_ENGINE??lr()}l(ge,"resolveEngineSpec");function Ee(e){return e.includes("/")||e.endsWith(".jsonl")}l(Ee,"isPlainLaunchSessionPath");function wr(e,t){return Fe(e)?e:je(t,e)}l(wr,"resolvePlainLaunchPath");async function gr(e,t,n,r){if(n==="--session"&&!Ee(r))throw new Error(`[broker] refusing to launch ${e}: launch recipe passes bare --session id '${r}' \u2014 the broker fatally rejects bare ids; pass the .jsonl path instead.`);if(!Ee(r))return;const o=wr(r,t),d=ur(o);try{if(d===null)throw new we(o);await fr().header(d)}catch(s){if(!(s instanceof we))throw s;const u=n==="--session"?"session file":"fork source";throw new Error(`[broker] refusing to launch ${e}: launch recipe references missing ${u} '${r}' (${o})`)}}l(gr,"preflightLaunchSessionArg");async function Er(e,t,n){const r=Z(e);ue(r,{recursive:!0}),process.platform==="linux"&&j(z)&&Te(r).uid===process.getuid()&&Oe(r,504);const o=w(r,"broker-launch.json"),d={user:Ge("user"),launch:Ke(n,t.env.CRTR_PROFILE_ID||null),rotation:He()};G(r,o,JSON.stringify({...t,configSnapshot:d}),{atomic:!0}),t.env.CRTR_SCOPES_FILE!==void 0&&G(r,Y(e),JSON.stringify((await _(e))?.scopes??[]),{atomic:!0});const s=Be(r,o).toString("utf8"),u=JSON.parse(s);if(!Array.isArray(u.argv))throw new Error(`[broker] refusing to launch ${e}: broker-launch.json is missing the argv array`);const f=ge(t);try{const c=import.meta.resolve(f);if(c.startsWith("file:")&&!j(me(c)))throw new Error(`resolved to missing file ${c}`)}catch(c){throw new Error(`[broker] refusing to launch ${e}: cannot resolve broker engine '${f}' from ${H()} \u2014 ${c.message}`)}const i=u.argv,m=[];for(let c=0;c<i.length;c++){const R=i[c];if(R==="-e"){const A=i[++c];A!==void 0&&m.push(A);continue}if(R!=="--session"&&R!=="--fork")continue;const O=i[++c];O!==void 0&&await gr(e,n,R,O)}try{er(m)}catch(c){throw new Error(`[broker] refusing to launch ${e}: ${c.message}`)}}l(Er,"preflightBrokerLaunch");const kr=2e3,vr=2e3,_r=150;async function J(e,t){const n=Date.now()+t;for(;Ze(e);){const r=n-Date.now();if(r<=0)return!1;await new Promise(o=>setTimeout(o,Math.min(_r,r)))}return!0}l(J,"waitForTreeExit");async function yr(e,t,n){return e.length===0?{confirmed:!0}:n&&await J(e,kr)?{confirmed:!0}:(he(e,"SIGTERM",t),await J(e,vr)?{confirmed:!0}:(he(e,"SIGKILL",t),await J(e,5e3)?{confirmed:!0}:{confirmed:!1,error:new Error(`broker process tree for ${e.join(", ")} remained alive after SIGKILL`)}))}l(yr,"terminateBrokerTree");async function ke(e,t){const n=await _(e),r=n?.pi_pid,o=r!=null?Ie(r,n?.pi_pid_identity??null,t.excludePid):null;return o?.reused===!0&&E({level:"warn",event:"broker.teardown.pid_reuse_refused",node_id:e,fields:{recorded_pid:r,expected_identity:n?.pi_pid_identity??null}}),{tree:o?.tree??[],identities:o?.identities??null,nodeId:e,launcherPid:j(z)&&o?.tree.includes(r)?r:null}}l(ke,"prepareBrokerTeardown");async function ve(e){if(e.launcherPid!==null){try{await M({operation:"stop",pid:e.launcherPid})}catch(t){return I(e.launcherPid)?{confirmed:!1,error:t instanceof Error?t:new Error(String(t))}:{confirmed:!0}}return await J([e.launcherPid],5e3)?{confirmed:!0}:{confirmed:!1,error:new Error(`launcher pid ${e.launcherPid} remained alive`)}}return new Promise(t=>{let n=!1,r=!1;const o=l(f=>{r||(r=!0,t(f))},"settle"),d=l(()=>{try{fe(Ue(e.nodeId))}catch{}},"cleanupSocket"),s=l(f=>{yr(e.tree,e.identities,f).then(o,i=>o({confirmed:!1,error:i instanceof Error?i:new Error(String(i))}))},"finish"),u=Me(e.nodeId);u.once("error",()=>{n||(d(),s(!1))}),u.once("connect",()=>{n=!0;try{u.write(Xe({type:"shutdown"}))}catch{}u.end(),d(),s(!0)})})}l(ve,"runPreparedBrokerTeardown");const q=new Map;async function rt(e,t={},n){const r=q.get(e);if(r!==void 0)return n!==void 0&&r.after.push(n),r.completion;let o;const d=new Promise(i=>{o=i}),s={completion:d,resolve:o,after:n===void 0?[]:[n]};q.set(e,s);let u;try{u=await ke(e,t)}catch(i){const m={confirmed:!1,error:i instanceof Error?i:new Error(String(i))};return q.delete(e),E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:m.error}),s.resolve(m),d}const f=l(()=>{q.delete(e),ve(u).then(async i=>{if(i.confirmed)for(const m of s.after)try{await m()}catch(c){E({level:"error",event:"broker.teardown.after_failed",node_id:e,error:c})}else E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:i.error});s.resolve(i)},i=>{const m={confirmed:!1,error:i instanceof Error?i:new Error(String(i))};E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:m.error}),s.resolve(m)})},"run");return t.startAfter===void 0?setImmediate(f):t.startAfter.then(f,f),d}l(rt,"requestBrokerTeardown");function _e(){return process.platform==="linux"&&j(z)}l(_e,"isolatedBrokerLauncherAvailable");const tt={async launch(e,t,n){if(Pe((await _(e))?.grantee),(await _(e))?.isolated===!0&&!_e())throw new Error("isolated broker requires the Linux root launcher");await Er(e,t,n.cwd);const r=nr();if(!r.reserveSlot(e))throw or(ir().automaticReviveCap,r.size());let o=null,d,s=!1;try{const u=qe(e);ue(u,{recursive:!0}),o=F(w(u,"broker.log"),"a");const f=ge(t),i={...Qe(t),CRTR_BROKER_ENGINE:f,CRTR_STORE_ADDRESS:dr()};if(f!==pr){const a=t.env.NODE_OPTIONS??process.env.NODE_OPTIONS;a!==void 0&&(i.NODE_OPTIONS=a)}cr()===void 0&&ar(e);const m=(await _(e))?.run_id;if(m){const a=await _(m);if(!a||a.deleting_at)throw new Error("run is deleting")}const c=t.env.CRTR_EXECUTION_ID;if(!c)throw new Error("broker launch has no execution id");const R=mr(c),O=_e();if((await _(e))?.isolated===!0&&!O)throw new Error("isolated broker requires the Linux root launcher");if(process.platform==="linux"&&process.env.CRTR_LAUNCHER_SOCKET&&!O)throw new Error("root launcher socket is unavailable; refusing an unsandboxed broker");if(O){const a=await _(e);if(!a)throw new Error(`missing node ${e}`);const h=a.grantee??"app:terminal",x=await sr(e,{useMemo:!1});if(x.verdict==="denied")throw rr(x);if(x.verdict==="too_small")throw tr(x);const y=await De(h);Object.assign(i,Ne(h));const S={uid:process.getuid(),gid:y.gid},g=Z(e);for(const[p,k]of[[K(h).nodes,We(e,h)],[K(h).nodes,ze(e,h)],[g,Je(e)]])X(p,k,()=>{},!0),P(p,k,1528,S);P(g,w(g,"broker-launch.json"),416,S),V(g,Y(e))&&P(g,Y(e),416,S);const te=w(g,"system-prompt.md");V(g,te)&&P(g,te,432,S);for(const p of["output-schema.json","situational-context.md"]){const k=w(g,p);V(g,k)&&P(g,k,432,S)}const ne=a.isolated===!0,b=Ve(e,h,ne);ne&&(X(K(h).nodes,b,()=>{},!0),P(K(h).nodes,b,1528,S),i.HOME=b,i.PI_CODING_AGENT_DIR=w(b,".pi","agent"));const W=w(b,".pi","agent");X(b,W,()=>{},!0),hr(b,W);const oe=w(W,"settings.json"),ye=w(Q(H()),"../../builtin-pi-packages/pi-crtr-extensions");G(b,oe,`${JSON.stringify({packages:[ye]})}
3
- `,{atomic:!0,mode:432}),P(b,oe,432);const ie=w(Z(e),`token-${c}`),L=F(ie,"wx+",384);fe(ie),xe(L,R);const se=[F(`/proc/self/fd/${L}`,"r"),F(`/proc/self/fd/${L}`,"r")];N(L);const ae={nodeId:e,app:h,sub:a.sub??"user:local",grantId:a.grant_id??null,scopes:a.scopes??null,isolated:a.isolated===!0,...a.isolated===!0?{cwd:a.cwd}:{},command:[process.execPath,H(),"--canvas-home",pe(),"--epoch",r.epoch(),e],env:Object.fromEntries(Object.entries({...i,...a.isolated===!0?{PWD:a.cwd}:{}}).filter(p=>p[1]!==void 0)),auditMissing:l(p=>E({level:"warn",event:"broker.mount.missing",node_id:e,fields:{source:p}}),"auditMissing")};try{let p;for(const D of se){const B=await de(),v=await Ae(ae);v.broker=!0;const $=await M({operation:"launch",spec:v},[D]);if(!("pid"in $))throw new Error("launcher did not return a pid");if(await de()===B||await Ce(ae,v)){p=$.pid;break}E({level:"info",event:"broker.launch.regranted",node_id:e,fields:{pid:$.pid}}),await M({operation:"stop",pid:$.pid,signal:"SIGKILL"}).catch(()=>{})}if(p===void 0)throw new Error("grant changed during every launch attempt");const k=p,Se=Date.now(),ce=new Promise(D=>{const B=setInterval(()=>{I(k)||(clearInterval(B),M({operation:"exit.status",pid:k}).then(v=>"oomKilled"in v&&v.oomKilled,()=>!1).then(v=>{E({level:v?"warn":"info",event:"broker.exited",node_id:e,fields:{pid:k,uptime_ms:Date.now()-Se,oom_killed:v}}),D({code:null,signal:null,...v?{oomKilled:!0}:{}})}))},250);B.unref()});N(o),o=null;const le={pid:k,exited:ce};return r.register(e,le),s=!0,ce.then(async D=>await r.onChildExit(e,D,c)),le}finally{for(const p of se)N(p)}}const A=await Ye(),T=Re(A,[H(),"--canvas-home",pe(),"--epoch",r.epoch(),e],{cwd:n.cwd,detached:!0,stdio:["ignore",o,o,"pipe"],env:i});d=T;const C=T.stdio[3];if(!C||!("end"in C))throw new Error("broker token pipe did not open");C.on("error",a=>E({event:"broker.token_pipe.failed",level:"error",node_id:e,error:a})),C.end(R),N(o),o=null;const ee=Date.now(),re=new Promise(a=>{let h=!1;const x=l(y=>{h||(h=!0,a(y))},"finish");T.once("exit",(y,S)=>{E({level:y===0?"info":"warn",event:"broker.exited",node_id:e,fields:{code:y,signal:S,uptime_ms:Date.now()-ee}}),x({code:y,signal:S})}),T.once("error",y=>{E({level:"error",event:"broker.launch.failed",node_id:e,error:y,fields:{uptime_ms:Date.now()-ee}}),x({code:null,signal:null})})});T.unref();const U={pid:T.pid??null,exited:re};return U.pid!==null&&(r.register(e,U),s=!0,re.then(async a=>await r.onChildExit(e,a,c))),U}catch(u){if(!s&&d?.pid!=null)try{d.kill("SIGTERM")}catch{}throw u}finally{s||r.releaseReservation(e),o!==null&&N(o)}},async isAlive(e){return I((typeof e=="string"?await _(e):e)?.pi_pid)},async teardown(e,t={}){try{return ve(await ke(e,t))}catch(n){return Promise.resolve({confirmed:!1,error:n instanceof Error?n:new Error(String(n))})}},async signal(e,t){const n=(await _(e))?.pi_pid;if(n!=null)try{process.kill(n,t)}catch{}}};export{tt as headlessBrokerHost,_e as isolatedBrokerLauncherAvailable,Er as preflightBrokerLaunch,rt as requestBrokerTeardown};
1
+ var be=Object.defineProperty;var l=(e,t)=>be(e,"name",{value:t,configurable:!0});import{assertGranteeRunsHere as Pe}from"./unsandboxed.js";import{spawn as Re}from"node:child_process";import{mkdirSync as ue,existsSync as j,unlinkSync as fe,openSync as F,closeSync as N,writeSync as xe,chmodSync as Oe,lstatSync as Te}from"node:fs";import{ensureAppSpace as De}from"../spaces/space.js";import{sandboxCacheEnv as Ne}from"../spaces/cache-env.js";import{compileLaunch as Ae,launchStillGranted as Ce}from"../spaces/mounts.js";import{grantsGeneration as de}from"../grants/mirror.js";import{LAUNCHER_SOCKET as z}from"../../launcher/spec.js";import{launcherRequest as M}from"../../launcher/client.js";import{piAgentDir as Le}from"./package-health.js";import{existsBeneath as V,readFileBeneath as Be,readTextBeneathIfExists as $e,setModeBeneath as P,withDirBeneath as X,writeFileBeneath as G}from"../spaces/open-beneath.js";import{dirname as Q,join as w,resolve as je,isAbsolute as Fe}from"node:path";import{fileURLToPath as me}from"node:url";import{connectViewSocket as Me}from"./view-socket.js";import{appSpace as K}from"../layout.js";import{readConfig as Ge,readMergedLaunchConfig as Ke}from"../config.js";import{readRotationConfig as He}from"../subscription-state.js";import{getNode as _}from"../canvas/index.js";import{scopesFilePath as Y,crtrHome as pe,nodeRunDir as Z,nodeRunJobDir as Je,jobDir as qe,viewSocketPath as Ue,nodeAgentDir as We,contextDir as ze,nodeHome as Ve}from"../canvas/paths.js";import{encodeFrame as Xe}from"./broker-protocol.js";import{buildBrokerEnv as Qe}from"./spawn-env.js";import{hostExecPathFresh as Ye}from"./branded-host.js";import{isPidAlive as I,killProcessTreePids as he,isAnyPidAlive as Ze,captureTeardownSnapshot as Ie}from"../canvas/pid.js";import{assertMandatoryCanvasExtensions as er}from"./canvas-extensions.js";import{appMemoryFull as rr,appMemoryTooSmall as tr,boundFleet as nr,brokerCapReached as or,brokerThresholdsForDaemon as ir}from"./fleet.js";import{admitBrokerMemory as sr}from"./memory-admission.js";import{emitEvent as E}from"../events/emit.js";import{bindBrokerEventSource as ar,eventSource as cr}from"../events/source.js";import{envBrokerEngine as lr}from"../../shared/env.js";import{conversationIdFromPath as ur,conversationStore as fr,ConversationNotFoundError as we,storeAddress as dr}from"../conversation-store/index.js";import{takeNodeToken as mr}from"../canvas/node-tokens.js";import{personId as pr}from"../canvas/person.js";function H(){const e=Q(me(import.meta.url));return w(e,"broker-cli.js")}l(H,"resolveBrokerEntry");const hr="@earendil-works/pi-coding-agent";function wr(e,t){const n=w(Le(),"models.json"),r=w(t,"models.json"),o=$e(Q(n),n);if(o===null)return;const d=JSON.parse(o),s=l(f=>Array.isArray(f)?f.map(s):f===null||typeof f!="object"?f:Object.fromEntries(Object.entries(f).filter(([i])=>!/^(apiKey|authorization|headers|token|secret)$/i.test(i)).map(([i,m])=>[i,s(m)])),"scrub"),u=`${JSON.stringify(s(d),null,2)}
2
+ `;if(n===r&&o!==u){if(JSON.stringify(d)!==JSON.stringify(s(d)))throw new Error("terminal models.json contains credentials; move them into daemon secrets");P(e,r,432);return}G(e,r,u,{atomic:!0,mode:432}),P(e,r,432)}l(wr,"writeBrokerModels");function ge(e){return e.env.CRTR_BROKER_ENGINE??lr()}l(ge,"resolveEngineSpec");function Ee(e){return e.includes("/")||e.endsWith(".jsonl")}l(Ee,"isPlainLaunchSessionPath");function gr(e,t){return Fe(e)?e:je(t,e)}l(gr,"resolvePlainLaunchPath");async function Er(e,t,n,r){if(n==="--session"&&!Ee(r))throw new Error(`[broker] refusing to launch ${e}: launch recipe passes bare --session id '${r}' \u2014 the broker fatally rejects bare ids; pass the .jsonl path instead.`);if(!Ee(r))return;const o=gr(r,t),d=ur(o);try{if(d===null)throw new we(o);await fr().header(d)}catch(s){if(!(s instanceof we))throw s;const u=n==="--session"?"session file":"fork source";throw new Error(`[broker] refusing to launch ${e}: launch recipe references missing ${u} '${r}' (${o})`)}}l(Er,"preflightLaunchSessionArg");async function kr(e,t,n){const r=Z(e);ue(r,{recursive:!0}),process.platform==="linux"&&j(z)&&Te(r).uid===process.getuid()&&Oe(r,504);const o=w(r,"broker-launch.json"),d={user:Ge("user"),launch:Ke(n,t.env.CRTR_PROFILE_ID||null),rotation:He()};G(r,o,JSON.stringify({...t,configSnapshot:d}),{atomic:!0}),t.env.CRTR_SCOPES_FILE!==void 0&&G(r,Y(e),JSON.stringify((await _(e))?.scopes??[]),{atomic:!0});const s=Be(r,o).toString("utf8"),u=JSON.parse(s);if(!Array.isArray(u.argv))throw new Error(`[broker] refusing to launch ${e}: broker-launch.json is missing the argv array`);const f=ge(t);try{const c=import.meta.resolve(f);if(c.startsWith("file:")&&!j(me(c)))throw new Error(`resolved to missing file ${c}`)}catch(c){throw new Error(`[broker] refusing to launch ${e}: cannot resolve broker engine '${f}' from ${H()} \u2014 ${c.message}`)}const i=u.argv,m=[];for(let c=0;c<i.length;c++){const R=i[c];if(R==="-e"){const A=i[++c];A!==void 0&&m.push(A);continue}if(R!=="--session"&&R!=="--fork")continue;const O=i[++c];O!==void 0&&await Er(e,n,R,O)}try{er(m)}catch(c){throw new Error(`[broker] refusing to launch ${e}: ${c.message}`)}}l(kr,"preflightBrokerLaunch");const vr=2e3,_r=2e3,yr=150;async function J(e,t){const n=Date.now()+t;for(;Ze(e);){const r=n-Date.now();if(r<=0)return!1;await new Promise(o=>setTimeout(o,Math.min(yr,r)))}return!0}l(J,"waitForTreeExit");async function Sr(e,t,n){return e.length===0?{confirmed:!0}:n&&await J(e,vr)?{confirmed:!0}:(he(e,"SIGTERM",t),await J(e,_r)?{confirmed:!0}:(he(e,"SIGKILL",t),await J(e,5e3)?{confirmed:!0}:{confirmed:!1,error:new Error(`broker process tree for ${e.join(", ")} remained alive after SIGKILL`)}))}l(Sr,"terminateBrokerTree");async function ke(e,t){const n=await _(e),r=n?.pi_pid,o=r!=null?Ie(r,n?.pi_pid_identity??null,t.excludePid):null;return o?.reused===!0&&E({level:"warn",event:"broker.teardown.pid_reuse_refused",node_id:e,fields:{recorded_pid:r,expected_identity:n?.pi_pid_identity??null}}),{tree:o?.tree??[],identities:o?.identities??null,nodeId:e,launcherPid:j(z)&&o?.tree.includes(r)?r:null}}l(ke,"prepareBrokerTeardown");async function ve(e){if(e.launcherPid!==null){try{await M({operation:"stop",pid:e.launcherPid})}catch(t){return I(e.launcherPid)?{confirmed:!1,error:t instanceof Error?t:new Error(String(t))}:{confirmed:!0}}return await J([e.launcherPid],5e3)?{confirmed:!0}:{confirmed:!1,error:new Error(`launcher pid ${e.launcherPid} remained alive`)}}return new Promise(t=>{let n=!1,r=!1;const o=l(f=>{r||(r=!0,t(f))},"settle"),d=l(()=>{try{fe(Ue(e.nodeId))}catch{}},"cleanupSocket"),s=l(f=>{Sr(e.tree,e.identities,f).then(o,i=>o({confirmed:!1,error:i instanceof Error?i:new Error(String(i))}))},"finish"),u=Me(e.nodeId);u.once("error",()=>{n||(d(),s(!1))}),u.once("connect",()=>{n=!0;try{u.write(Xe({type:"shutdown"}))}catch{}u.end(),d(),s(!0)})})}l(ve,"runPreparedBrokerTeardown");const q=new Map;async function nt(e,t={},n){const r=q.get(e);if(r!==void 0)return n!==void 0&&r.after.push(n),r.completion;let o;const d=new Promise(i=>{o=i}),s={completion:d,resolve:o,after:n===void 0?[]:[n]};q.set(e,s);let u;try{u=await ke(e,t)}catch(i){const m={confirmed:!1,error:i instanceof Error?i:new Error(String(i))};return q.delete(e),E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:m.error}),s.resolve(m),d}const f=l(()=>{q.delete(e),ve(u).then(async i=>{if(i.confirmed)for(const m of s.after)try{await m()}catch(c){E({level:"error",event:"broker.teardown.after_failed",node_id:e,error:c})}else E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:i.error});s.resolve(i)},i=>{const m={confirmed:!1,error:i instanceof Error?i:new Error(String(i))};E({level:"error",event:"broker.teardown.unconfirmed",node_id:e,error:m.error}),s.resolve(m)})},"run");return t.startAfter===void 0?setImmediate(f):t.startAfter.then(f,f),d}l(nt,"requestBrokerTeardown");function _e(){return process.platform==="linux"&&j(z)}l(_e,"isolatedBrokerLauncherAvailable");const ot={async launch(e,t,n){if(Pe((await _(e))?.grantee),(await _(e))?.isolated===!0&&!_e())throw new Error("isolated broker requires the Linux root launcher");await kr(e,t,n.cwd);const r=nr();if(!r.reserveSlot(e))throw or(ir().automaticReviveCap,r.size());let o=null,d,s=!1;try{const u=qe(e);ue(u,{recursive:!0}),o=F(w(u,"broker.log"),"a");const f=ge(t),i={...Qe(t),CRTR_BROKER_ENGINE:f,CRTR_STORE_ADDRESS:dr()};if(f!==hr){const a=t.env.NODE_OPTIONS??process.env.NODE_OPTIONS;a!==void 0&&(i.NODE_OPTIONS=a)}cr()===void 0&&ar(e);const m=(await _(e))?.run_id;if(m){const a=await _(m);if(!a||a.deleting_at)throw new Error("run is deleting")}const c=t.env.CRTR_EXECUTION_ID;if(!c)throw new Error("broker launch has no execution id");const R=mr(c),O=_e();if((await _(e))?.isolated===!0&&!O)throw new Error("isolated broker requires the Linux root launcher");if(process.platform==="linux"&&process.env.CRTR_LAUNCHER_SOCKET&&!O)throw new Error("root launcher socket is unavailable; refusing an unsandboxed broker");if(O){const a=await _(e);if(!a)throw new Error(`missing node ${e}`);const h=a.grantee??"app:terminal",x=await sr(e,{useMemo:!1});if(x.verdict==="denied")throw rr(x);if(x.verdict==="too_small")throw tr(x);const y=await De(h);Object.assign(i,Ne(h));const S={uid:process.getuid(),gid:y.gid},g=Z(e);for(const[p,k]of[[K(h).nodes,We(e,h)],[K(h).nodes,ze(e,h)],[g,Je(e)]])X(p,k,()=>{},!0),P(p,k,1528,S);P(g,w(g,"broker-launch.json"),416,S),V(g,Y(e))&&P(g,Y(e),416,S);const te=w(g,"system-prompt.md");V(g,te)&&P(g,te,432,S);for(const p of["output-schema.json","situational-context.md"]){const k=w(g,p);V(g,k)&&P(g,k,432,S)}const ne=a.isolated===!0,b=Ve(e,h,ne);ne&&(X(K(h).nodes,b,()=>{},!0),P(K(h).nodes,b,1528,S),i.HOME=b,i.PI_CODING_AGENT_DIR=w(b,".pi","agent"));const W=w(b,".pi","agent");X(b,W,()=>{},!0),wr(b,W);const oe=w(W,"settings.json"),ye=w(Q(H()),"../../builtin-pi-packages/pi-crtr-extensions");G(b,oe,`${JSON.stringify({packages:[ye]})}
3
+ `,{atomic:!0,mode:432}),P(b,oe,432);const ie=w(Z(e),`token-${c}`),L=F(ie,"wx+",384);fe(ie),xe(L,R);const se=[F(`/proc/self/fd/${L}`,"r"),F(`/proc/self/fd/${L}`,"r")];N(L);const ae={nodeId:e,app:h,sub:a.sub??pr(),grantId:a.grant_id??null,scopes:a.scopes??null,isolated:a.isolated===!0,...a.isolated===!0?{cwd:a.cwd}:{},command:[process.execPath,H(),"--canvas-home",pe(),"--epoch",r.epoch(),e],env:Object.fromEntries(Object.entries({...i,...a.isolated===!0?{PWD:a.cwd}:{}}).filter(p=>p[1]!==void 0)),auditMissing:l(p=>E({level:"warn",event:"broker.mount.missing",node_id:e,fields:{source:p}}),"auditMissing")};try{let p;for(const D of se){const B=await de(),v=await Ae(ae);v.broker=!0;const $=await M({operation:"launch",spec:v},[D]);if(!("pid"in $))throw new Error("launcher did not return a pid");if(await de()===B||await Ce(ae,v)){p=$.pid;break}E({level:"info",event:"broker.launch.regranted",node_id:e,fields:{pid:$.pid}}),await M({operation:"stop",pid:$.pid,signal:"SIGKILL"}).catch(()=>{})}if(p===void 0)throw new Error("grant changed during every launch attempt");const k=p,Se=Date.now(),ce=new Promise(D=>{const B=setInterval(()=>{I(k)||(clearInterval(B),M({operation:"exit.status",pid:k}).then(v=>"oomKilled"in v&&v.oomKilled,()=>!1).then(v=>{E({level:v?"warn":"info",event:"broker.exited",node_id:e,fields:{pid:k,uptime_ms:Date.now()-Se,oom_killed:v}}),D({code:null,signal:null,...v?{oomKilled:!0}:{}})}))},250);B.unref()});N(o),o=null;const le={pid:k,exited:ce};return r.register(e,le),s=!0,ce.then(async D=>await r.onChildExit(e,D,c)),le}finally{for(const p of se)N(p)}}const A=await Ye(),T=Re(A,[H(),"--canvas-home",pe(),"--epoch",r.epoch(),e],{cwd:n.cwd,detached:!0,stdio:["ignore",o,o,"pipe"],env:i});d=T;const C=T.stdio[3];if(!C||!("end"in C))throw new Error("broker token pipe did not open");C.on("error",a=>E({event:"broker.token_pipe.failed",level:"error",node_id:e,error:a})),C.end(R),N(o),o=null;const ee=Date.now(),re=new Promise(a=>{let h=!1;const x=l(y=>{h||(h=!0,a(y))},"finish");T.once("exit",(y,S)=>{E({level:y===0?"info":"warn",event:"broker.exited",node_id:e,fields:{code:y,signal:S,uptime_ms:Date.now()-ee}}),x({code:y,signal:S})}),T.once("error",y=>{E({level:"error",event:"broker.launch.failed",node_id:e,error:y,fields:{uptime_ms:Date.now()-ee}}),x({code:null,signal:null})})});T.unref();const U={pid:T.pid??null,exited:re};return U.pid!==null&&(r.register(e,U),s=!0,re.then(async a=>await r.onChildExit(e,a,c))),U}catch(u){if(!s&&d?.pid!=null)try{d.kill("SIGTERM")}catch{}throw u}finally{s||r.releaseReservation(e),o!==null&&N(o)}},async isAlive(e){return I((typeof e=="string"?await _(e):e)?.pi_pid)},async teardown(e,t={}){try{return ve(await ke(e,t))}catch(n){return Promise.resolve({confirmed:!1,error:n instanceof Error?n:new Error(String(n))})}},async signal(e,t){const n=(await _(e))?.pi_pid;if(n!=null)try{process.kill(n,t)}catch{}}};export{ot as headlessBrokerHost,_e as isolatedBrokerLauncherAvailable,kr as preflightBrokerLaunch,nt as requestBrokerTeardown};
@@ -14,10 +14,19 @@ export declare function resolveCreateKind(kind: string | undefined, profile: Pro
14
14
  /** The daemon's default kind for an app-started run (`run.start`) whose profile
15
15
  * stores no `default_kind` — every app's own default profile, which
16
16
  * `ensureAppProfile` creates without one. */
17
- export declare const RUN_DEFAULT_KIND = "developer";
17
+ export declare const RUN_DEFAULT_KIND = "general";
18
+ /** The model a `run.start` root gets when the start names no `model` and its
19
+ * profile stores no `default_kind`: the default provider's medium rung, which
20
+ * may rotate to another family at the same strength. An app-paid grant maps it
21
+ * to the app's medium inference rung. */
22
+ export declare const RUN_DEFAULT_MODEL = "medium";
18
23
  /** The kind a `run.start` root gets: its profile's `default_kind`, else
19
24
  * {@link RUN_DEFAULT_KIND}. `profile.list` reports this same value. */
20
25
  export declare function resolveRunKind(profile: ProfileEntry): string;
26
+ /** The model a `run.start` root gets: the start's `model`, else
27
+ * {@link RUN_DEFAULT_MODEL} for a profile without a `default_kind`, else the
28
+ * profile kind's own model (undefined). */
29
+ export declare function resolveRunModel(profile: ProfileEntry, requested: string | undefined): string | undefined;
21
30
  /** Whether a node of this kind is ever launched as a persona (and so needs a launch recipe). */
22
31
  export declare function isLaunchKind(kind: string): boolean;
23
32
  /** Validate a kind against the same cwd/profile configuration used to build its launch.
@@ -1 +1 @@
1
- var u=Object.defineProperty;var i=(e,n)=>u(e,"name",{value:n,configurable:!0});import{appSpace as p}from"../layout.js";import{envProfileId as m}from"../../shared/env.js";import{resolve as s}from"node:path";import{getNode as w}from"../canvas/canvas.js";import{usage as v}from"../errors.js";import{assertProfileActive as h}from"../profiles/manifest.js";import{buildLaunchSpecAsync as _,readMergedLaunchConfigAsync as x}from"./launch.js";import{resolveProfileId as y}from"./spawn.js";function k(e,n){return e??n?.manifest.default_kind??"general"}i(k,"resolveCreateKind");const K="developer";function j(e){return e.manifest.default_kind??K}i(j,"resolveRunKind");const N=new Set(["human"]);function M(e){return!N.has(e)}i(M,"isLaunchKind");async function g(e,n=process.cwd(),r=m()||null){const t=Object.keys((await x(n,r)).kinds);if(!t.includes(e))throw v(`unknown kind: ${e}`,{received:e,next:`Valid kinds: ${t.sort().join(", ")}.`})}i(g,"assertLaunchKind");async function R(e,n={}){const r=e.parent??void 0,t=e.cwd??process.cwd(),o=await y(e.profile,r??null,t,{readOnlySelection:n.readOnlyProfileSelection}),a=h(o),d=k(e.kind,a);await n.validateKind?.(d);const c=await L({...e,kind:d},r,o,t);n.validateKind===void 0&&await g(d,c,o);const l=e.mode??"base",f=(await _(d,l,{lifecycle:e.root_lifecycle??(e.root===!0?"resident":"terminal"),hasManager:e.root!==!0,...e.model!==void 0?{model:e.model,modelOverride:!0}:{},cwd:c,profileId:o})).launch;return{profileId:o,profile:a,kind:d,mode:l,cwd:c,launch:f}}i(R,"resolveLaunchTarget");async function L(e,n,r,t){if(e.pin_cwd!==void 0&&e.pin_cwd!=="")return s(e.pin_cwd);if(n!=null){const o=await w(n);if(o!==null)return o.cwd}return e.cwd!==void 0&&e.cwd!==""?s(e.cwd):process.platform==="linux"?p("terminal").files:t}i(L,"resolveNodeCwd");export{K as RUN_DEFAULT_KIND,g as assertLaunchKind,M as isLaunchKind,k as resolveCreateKind,R as resolveLaunchTarget,L as resolveNodeCwd,j as resolveRunKind};
1
+ var f=Object.defineProperty;var o=(e,n)=>f(e,"name",{value:n,configurable:!0});import{appSpace as p}from"../layout.js";import{envProfileId as m}from"../../shared/env.js";import{resolve as s}from"node:path";import{getNode as w}from"../canvas/canvas.js";import{usage as _}from"../errors.js";import{assertProfileActive as v}from"../profiles/manifest.js";import{buildLaunchSpecAsync as x,readMergedLaunchConfigAsync as h}from"./launch.js";import{resolveProfileId as y}from"./spawn.js";function k(e,n){return e??n?.manifest.default_kind??"general"}o(k,"resolveCreateKind");const L="general",N="medium";function b(e){return e.manifest.default_kind??L}o(b,"resolveRunKind");function E(e,n){return n??(e.manifest.default_kind===void 0?N:void 0)}o(E,"resolveRunModel");const g=new Set(["human"]);function T(e){return!g.has(e)}o(T,"isLaunchKind");async function K(e,n=process.cwd(),r=m()||null){const i=Object.keys((await h(n,r)).kinds);if(!i.includes(e))throw _(`unknown kind: ${e}`,{received:e,next:`Valid kinds: ${i.sort().join(", ")}.`})}o(K,"assertLaunchKind");async function j(e,n={}){const r=e.parent??void 0,i=e.cwd??process.cwd(),t=await y(e.profile,r??null,i,{readOnlySelection:n.readOnlyProfileSelection}),a=v(t),d=k(e.kind,a);await n.validateKind?.(d);const c=await A({...e,kind:d},r,t,i);n.validateKind===void 0&&await K(d,c,t);const l=e.mode??"base",u=(await x(d,l,{lifecycle:e.root_lifecycle??(e.root===!0?"resident":"terminal"),hasManager:e.root!==!0,...e.model!==void 0?{model:e.model,modelOverride:!0}:{},cwd:c,profileId:t})).launch;return{profileId:t,profile:a,kind:d,mode:l,cwd:c,launch:u}}o(j,"resolveLaunchTarget");async function A(e,n,r,i){if(e.pin_cwd!==void 0&&e.pin_cwd!=="")return s(e.pin_cwd);if(n!=null){const t=await w(n);if(t!==null)return t.cwd}return e.cwd!==void 0&&e.cwd!==""?s(e.cwd):process.platform==="linux"?p("terminal").files:i}o(A,"resolveNodeCwd");export{L as RUN_DEFAULT_KIND,N as RUN_DEFAULT_MODEL,K as assertLaunchKind,T as isLaunchKind,k as resolveCreateKind,j as resolveLaunchTarget,A as resolveNodeCwd,b as resolveRunKind,E as resolveRunModel};
@@ -1 +1 @@
1
- var K=Object.defineProperty;var l=(e,r)=>K(e,"name",{value:r,configurable:!0});import{envHomeOverride as X,envRuntimeHomeOverride as z,INBOX_BETWEEN_TURNS_ENV as V,SCOPES_FILE_ENV as Y,scopeAllowed as C}from"../../shared/env.js";import{isOwnMemoryScope as Q,validateScopes as Z}from"../scopes.js";import{activeGrant as B,terminalGrant as k}from"../grants/mirror.js";import{grantSnapshotMissing as ee}from"../../daemon/readiness.js";import{appendRunEvent as ne}from"../runs/events.js";import{readConfig as S}from"../config.js";import{InputError as T}from"../io.js";import{ApiError as g}from"../../api/errors.js";import{assertGranteeRunsHere as re}from"./unsandboxed.js";import{randomBytes as te}from"node:crypto";import{existsSync as P}from"node:fs";import{resolve as ie,relative as ae,isAbsolute as U,join as oe,sep as le}from"node:path";import{appSpace as se}from"../layout.js";import{scopesFilePath as de}from"../canvas/paths.js";import{createNode as ce,assertRunNotDeleting as ue,conversationOwner as fe,getNode as u,getRow as me}from"../canvas/index.js";import{ensureAppSpace as pe}from"../spaces/space.js";import{LAUNCHER_SOCKET as H}from"../../launcher/spec.js";import{resolveInstallId as _e}from"../canvas/install-id.js";import{isAppRun as we}from"../canvas/app-run.js";import{contextDir as ge}from"../canvas/paths.js";import{conversationIdFromPath as he}from"../conversation-store/index.js";import{usage as L}from"../errors.js";import{assertProfileAvailableForBirth as ve}from"../profiles/deletion-reservation.js";import{loadExactProfileManifest as Re}from"../profiles/manifest.js";import{admitNodeCreate as ye}from"../command-hooks/lifecycle-create.js";import{getById as Ee,nodeObject as I,ownerForGrantee as Ce}from"../graph/objects.js";import{recordActedOn as Ie}from"../graph/edges.js";import{recordWatch as Ne,copyWatchesForFork as ke}from"../graph/watches.js";import{publish as Se}from"../graph/events.js";import{canvasDatabaseUrl as Te}from"../canvas/db.js";import{someAsync as Oe}from"../async-array.js";import{admitIntoManagedCheckout as xe}from"../worktree-mutation-async.js";let O;const j=new Map;function hn(e){O=e}l(hn,"setInstallId");async function De(){if(O!==void 0)return O;const e=await Te(),r=j.get(e);if(r!==void 0)return r;const a=await _e();return j.set(e,a),a}l(De,"installId");function be(){return`${Date.now().toString(36)}-${te(4).toString("hex")}`}l(be,"newJobId");async function Ae(){return`${await De()}-${be()}`}l(Ae,"newNodeId");const x=128,Me=/^[a-z0-9-]+$/;class q extends Error{static{l(this,"NodeIdConflictError")}nodeId;constructor(r){super(`node already exists: ${r}`),this.nodeId=r,this.name="NodeIdConflictError"}}function D(e){if(e.length===0||e.length>x)throw L(`invalid --node-id '${e}': must be 1-${x} characters`,{next:`Pass an id-safe string (lowercase letters, digits, hyphens) of at most ${x} characters.`});if(!Me.test(e))throw L(`invalid --node-id '${e}': must contain only lowercase letters, digits, and hyphens (e.g. 'remote42', 'my-node-1')`,{next:"Use only lowercase letters, digits, and hyphens."})}l(D,"validateNodeIdFormat");async function vn(e){if(D(e),await u(e)!==null)throw new q(e)}l(vn,"preflightNodeId");async function $e(e){if(D(e),await u(e)!==null)throw new q(e);return e}l($e,"validatedExplicitNodeId");async function Rn(e){let r=e;const a=new Set;for(;;){if(a.has(r))return r;a.add(r);const s=await me(r);if(s===null||s.parent==null)return r;r=s.parent}}l(Rn,"rootOfSpine");function yn(){const e=process.env;return{nodeId:e.CRTR_NODE_ID??null,kind:e.CRTR_KIND??null,mode:e.CRTR_MODE??null}}l(yn,"currentNodeContext");function Fe(e){return we(e)}l(Fe,"inboxBetweenTurns");function En(e){const r={CRTR_NODE_ID:e.node_id,CRTR_KIND:e.kind,CRTR_MODE:e.mode,CRTR_LIFECYCLE:e.lifecycle,CRTR_NODE_CWD:e.cwd,CRTR_CONTEXT_DIR:ge(e.node_id,e.grantee),CRTR_CYCLES:(e.cycles??0).toString(),CRTR_PROFILE_ID:e.profile_id??"",PI_CLAUDE_OAUTH_REINJECT_SCOPE:"never"};e.parent&&(r.CRTR_PARENT_NODE_ID=e.parent),e.run_id&&(r.CRTR_RUN_ID=e.run_id),Fe(e)&&(r[V]="1"),e.scopes!==null&&e.scopes!==void 0&&(r[Y]=de(e.node_id)),r.CRTR_GRANTEE=e.grantee??"app:terminal";const a=X();a!==void 0&&a!==""&&(r.CRTR_HOME=a);const s=z();s!==void 0&&s!==""&&(r.CRTR_RUNTIME_HOME=s);const f=process.env.CRTR_SUBTREE;return f!==void 0&&f!==""&&(r.CRTR_SUBTREE=f),{...r,...e.launch?.env??{}}}l(En,"nodeEnv");async function Be(e){const r=he(e),a=r===null?null:await fe(r);return a===null?null:await u(a)}l(Be,"forkSourceNode");async function Cn(e){const r=e.nodeId!==void 0?(D(e.nodeId),e.nodeId):await Ae(),a=e.parent??null,s=a===null,f=e.spawnedBy??a,p=e.creator??null,b=e.mode??"base",N=e.lifecycle??(s?"resident":"terminal"),h=e.run===void 0?await k():null,n={node_id:r,name:e.name??e.kind,description:e.description,cycles:0,created:new Date().toISOString(),cwd:e.cwd,host_kind:"broker",profile_id:e.profile_id??null,kind:e.kind,mode:b,lifecycle:N,persona_ack:{kind:e.kind,mode:b,lifecycle:N},status:"active",parent:a,spawned_by:f,creator:p,fork_from:e.forkFrom,fork_source_file:e.forkSourceFile,review_binding:e.reviewBinding,model_override:e.modelOverride??null,scopes:e.scopes??null,isolated:e.isolated===!0,grantee:e.run?.grantee??"app:terminal",sub:e.run?.sub??h?.sub??null,grant_id:e.run?.grant_id??h?.id??null,client_id:e.run?.client_id??null,metadata_json:s?e.run?.metadata_json??null:null,run_id:s?r:e.run?.run_id??null,event_cursor:s?e.run?.event_cursor??1:null,usage_json:s?e.run?.usage_json??JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}):null,passive_default:e.passiveDefault??!1,...e.managedWorktree!==void 0?{managed_worktree:e.managedWorktree}:{},intent:null,deadline_at:e.deadlineAt??null,pi_session_id:null,pi_session_file:null,launch:e.launch},y=e.scopes===void 0?void 0:Z(e.scopes),v=a===null?null:await u(a);if(a!==null&&v===null)throw new Error(`cannot spawn under unknown parent node: ${a}`);if(v?.run_id&&(await u(v.run_id))?.deleting_at)throw new Error("run is deleting");const c=p===null?null:await u(p);if(p!==null&&c===null)throw new Error(`cannot spawn from unknown creator node: ${p}`);if(c?.run_id&&(await u(c.run_id))?.deleting_at)throw new Error("run is deleting");const A=e.forkSourceFile==null?null:await Be(e.forkSourceFile),M=e.replaces==null?null:await u(e.replaces),_=v??A??M??c??(f===null?null:await u(f)),W=[v,c,A,M,f===null?null:await u(f)],$=await Oe(W,async t=>t?.isolated===!0||t?.run_id!=null&&(await u(t.run_id))?.isolated===!0);if($&&e.isolated===!1)throw new g(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});if(n.isolated=$||e.isolated===!0,_!==null){const t=c??_;if(c&&_.grantee&&c.grantee&&c.grantee!==_.grantee)throw new g(403,"other_app_run","cannot create under another app's node");const i=c&&c!==_?[_,c]:[_],d=[],w=new Set;for(const o of i){o.scopes!==null&&o.scopes!==void 0&&d.push(o.scopes);const m=o.grantee==="app:terminal"?await k():o.grant_id?await B(o.grant_id):null;if(m){const R=[...m.resolved];w.add(R),d.push(R)}else if(!(o.grantee==="app:terminal"&&!S("user").runtime?.issuer)){const R=[];w.add(R),d.push(R)}}const E=l(o=>Q(o,t.grantee==="user:local"?"app:terminal":t.grantee??"app:terminal"),"ownMemory"),F=l((o,m)=>E(m)&&w.has(o)||C(o,m),"allowed");if(y!==void 0&&!y.every(o=>d.every(m=>F(m,o))))throw new T({error:"unknown_scope",message:"child scopes must be covered by the creating node, its source and their current grants",received:y,field:"scopes",next:"Request only scopes the creating node currently holds."});if(n.scopes=y??(i.every(o=>o.scopes==null)&&i.every(o=>o.grant_id===t.grant_id&&o.sub===t.sub)?null:i.length===1?_.scopes??null:[...new Set(d.flat().filter(o=>d.every(m=>F(m,o))))]),n.scopes?.includes("crtr:act")&&!C(n.scopes,"crtr:llm"))throw new T({error:"invalid_scope",message:"crtr:act requires crtr:llm",received:n.scopes,field:"scopes",next:"Include crtr:llm or omit crtr:act."});n.grantee=t.grantee==="user:local"?"app:terminal":t.grantee,n.sub=t.sub??(n.grantee==="app:terminal"?h?.sub:null)??null,n.grant_id=t.grant_id??(n.grantee==="app:terminal"?h?.id:null)??null,n.client_id=t.client_id,n.run_id=s?r:t.run_id??null}if(re(n.grantee),e.run===void 0&&n.grantee==="app:terminal"&&S("user").runtime?.issuer&&h===null)throw await ee()?new g(503,"runtime_starting","the first grant snapshot from the directory has not committed"):new g(403,"scope_missing","terminal grant is not active");if(n.isolated){if(process.platform!=="linux"||!P(H))throw new g(400,"isolated_unavailable","isolated nodes require the Linux root launcher");if(e.managedWorktree!==void 0)throw new g(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const t=oe(se(n.grantee??"app:terminal").nodes,r),i=ae(t,ie(n.cwd));if(!U(n.cwd)||i===".."||i.startsWith(`..${le}`)||U(i))throw new g(400,"invalid_request","isolated node cwd must be inside its own folder",{param:"cwd"})}if(n.profile_id){const t=Re(n.profile_id).manifest.grantee??"app:terminal";if(t!==n.grantee){const i=`crtr:memory:rw:${t}:profile:${n.profile_id}`,d=n.grantee==="app:terminal"?await k():n.grant_id?await B(n.grant_id):null;if(!(n.grantee==="app:terminal"&&!S("user").runtime?.issuer)&&(!d||d.grantee!==n.grantee||!C(d.resolved,i))||!C(n.scopes,i))throw new T({error:"scope_missing",message:`missing scope ${i}`,received:n.profile_id,field:"profile",next:"Select a profile covered by this run and its current grant."})}}if(ve(n.profile_id??null),!n.grantee)throw new Error("node creation requires an app");const G=await ye({kind:n.kind,mode:n.mode,cwd:n.cwd,profile:n.profile_id??null,root:s,parentId:a,requestingNodeId:p,lifecycle:N,replaces:e.replaces??null},n.grantee),J=l(async(t,i)=>{const d=await I(t,i.node_id),w=p??a??e.actor??f??null,E=w!==null&&(await Ee(t,w))?.type==="node"?await I(t,w):await Ce(t,e.run===void 0?"user:local":i.grantee);await Ie(t,E,d),a!==null&&e.subscribe!==!1&&await Ne(t,await I(t,a),d,"spawn"),e.forkSourceNode&&await ke(t,await I(t,e.forkSourceNode),d),await Se(t,{event:"created",object:d,actor:E,attrs:{name:i.name,kind:i.kind,creator:p??null}})},"recordBirth");try{e.nodeId!==void 0&&await $e(e.nodeId),await e.beforeCreate?.(),(process.platform!=="linux"||P(H))&&await pe(n.grantee??"app:terminal"),await xe(n.cwd,r,()=>ce(n,{outcomeDelivery:e.outcomeDelivery,withinCreate:l(async(t,i)=>{i.run_id&&i.run_id!==i.node_id&&await ue(t,i.run_id),s&&e.run===void 0&&await ne(t,r,r,"run.started",{scopes:i.scopes??null,payer:"user",usage:JSON.parse(i.usage_json)}),await e.withinCreate?.(t,i),await J(t,i)},"withinCreate")}))}finally{G()}return n}l(Cn,"spawnNode");export{q as NodeIdConflictError,yn as currentNodeContext,Be as forkSourceNode,Fe as inboxBetweenTurns,be as newJobId,Ae as newNodeId,En as nodeEnv,vn as preflightNodeId,Rn as rootOfSpine,hn as setInstallId,Cn as spawnNode};
1
+ var J=Object.defineProperty;var l=(e,r)=>J(e,"name",{value:r,configurable:!0});import{envHomeOverride as K,envRuntimeHomeOverride as X,INBOX_BETWEEN_TURNS_ENV as z,SCOPES_FILE_ENV as V,scopeAllowed as I}from"../../shared/env.js";import{isOwnMemoryScope as Y,validateScopes as Q}from"../scopes.js";import{activeGrant as F,terminalGrant as k}from"../grants/mirror.js";import{grantSnapshotMissing as Z}from"../../daemon/readiness.js";import{appendRunEvent as ee}from"../runs/events.js";import{personId as ne}from"../canvas/person.js";import{InputError as S}from"../io.js";import{ApiError as g}from"../../api/errors.js";import{assertGranteeRunsHere as re}from"./unsandboxed.js";import{randomBytes as te}from"node:crypto";import{existsSync as B}from"node:fs";import{resolve as ie,relative as oe,isAbsolute as P,join as ae,sep as le}from"node:path";import{appSpace as se}from"../layout.js";import{scopesFilePath as de}from"../canvas/paths.js";import{createNode as ce,assertRunNotDeleting as ue,conversationOwner as fe,getNode as u,getRow as pe}from"../canvas/index.js";import{ensureAppSpace as me}from"../spaces/space.js";import{LAUNCHER_SOCKET as U}from"../../launcher/spec.js";import{resolveInstallId as _e}from"../canvas/install-id.js";import{isAppRun as we}from"../canvas/app-run.js";import{contextDir as ge}from"../canvas/paths.js";import{conversationIdFromPath as he}from"../conversation-store/index.js";import{usage as H}from"../errors.js";import{assertProfileAvailableForBirth as ve}from"../profiles/deletion-reservation.js";import{loadExactProfileManifest as Re}from"../profiles/manifest.js";import{admitNodeCreate as ye}from"../command-hooks/lifecycle-create.js";import{getById as Ee,nodeObject as C,ownerForGrantee as Ie}from"../graph/objects.js";import{recordActedOn as Ce}from"../graph/edges.js";import{recordWatch as Ne,copyWatchesForFork as ke}from"../graph/watches.js";import{publish as Se}from"../graph/events.js";import{canvasDatabaseUrl as Te}from"../canvas/db.js";import{someAsync as Oe}from"../async-array.js";import{admitIntoManagedCheckout as xe}from"../worktree-mutation-async.js";let T;const L=new Map;function hn(e){T=e}l(hn,"setInstallId");async function De(){if(T!==void 0)return T;const e=await Te(),r=L.get(e);if(r!==void 0)return r;const o=await _e();return L.set(e,o),o}l(De,"installId");function be(){return`${Date.now().toString(36)}-${te(4).toString("hex")}`}l(be,"newJobId");async function Ae(){return`${await De()}-${be()}`}l(Ae,"newNodeId");const O=128,Me=/^[a-z0-9-]+$/;class j extends Error{static{l(this,"NodeIdConflictError")}nodeId;constructor(r){super(`node already exists: ${r}`),this.nodeId=r,this.name="NodeIdConflictError"}}function x(e){if(e.length===0||e.length>O)throw H(`invalid --node-id '${e}': must be 1-${O} characters`,{next:`Pass an id-safe string (lowercase letters, digits, hyphens) of at most ${O} characters.`});if(!Me.test(e))throw H(`invalid --node-id '${e}': must contain only lowercase letters, digits, and hyphens (e.g. 'remote42', 'my-node-1')`,{next:"Use only lowercase letters, digits, and hyphens."})}l(x,"validateNodeIdFormat");async function vn(e){if(x(e),await u(e)!==null)throw new j(e)}l(vn,"preflightNodeId");async function $e(e){if(x(e),await u(e)!==null)throw new j(e);return e}l($e,"validatedExplicitNodeId");async function Rn(e){let r=e;const o=new Set;for(;;){if(o.has(r))return r;o.add(r);const s=await pe(r);if(s===null||s.parent==null)return r;r=s.parent}}l(Rn,"rootOfSpine");function yn(){const e=process.env;return{nodeId:e.CRTR_NODE_ID??null,kind:e.CRTR_KIND??null,mode:e.CRTR_MODE??null}}l(yn,"currentNodeContext");function Fe(e){return we(e)}l(Fe,"inboxBetweenTurns");function En(e){const r={CRTR_NODE_ID:e.node_id,CRTR_KIND:e.kind,CRTR_MODE:e.mode,CRTR_LIFECYCLE:e.lifecycle,CRTR_NODE_CWD:e.cwd,CRTR_CONTEXT_DIR:ge(e.node_id,e.grantee),CRTR_CYCLES:(e.cycles??0).toString(),CRTR_PROFILE_ID:e.profile_id??"",PI_CLAUDE_OAUTH_REINJECT_SCOPE:"never"};e.parent&&(r.CRTR_PARENT_NODE_ID=e.parent),e.run_id&&(r.CRTR_RUN_ID=e.run_id),Fe(e)&&(r[z]="1"),e.scopes!==null&&e.scopes!==void 0&&(r[V]=de(e.node_id)),r.CRTR_GRANTEE=e.grantee??"app:terminal";const o=K();o!==void 0&&o!==""&&(r.CRTR_HOME=o);const s=X();s!==void 0&&s!==""&&(r.CRTR_RUNTIME_HOME=s);const f=process.env.CRTR_SUBTREE;return f!==void 0&&f!==""&&(r.CRTR_SUBTREE=f),{...r,...e.launch?.env??{}}}l(En,"nodeEnv");async function Be(e){const r=he(e),o=r===null?null:await fe(r);return o===null?null:await u(o)}l(Be,"forkSourceNode");async function In(e){const r=e.nodeId!==void 0?(x(e.nodeId),e.nodeId):await Ae(),o=e.parent??null,s=o===null,f=e.spawnedBy??o,m=e.creator??null,D=e.mode??"base",N=e.lifecycle??(s?"resident":"terminal"),h=e.run===void 0?await k():null,n={node_id:r,name:e.name??e.kind,description:e.description,cycles:0,created:new Date().toISOString(),cwd:e.cwd,host_kind:"broker",profile_id:e.profile_id??null,kind:e.kind,mode:D,lifecycle:N,persona_ack:{kind:e.kind,mode:D,lifecycle:N},status:"active",parent:o,spawned_by:f,creator:m,fork_from:e.forkFrom,fork_source_file:e.forkSourceFile,review_binding:e.reviewBinding,model_override:e.modelOverride??null,scopes:e.scopes??null,isolated:e.isolated===!0,grantee:e.run?.grantee??"app:terminal",sub:e.run?.sub??h?.sub??null,grant_id:e.run?.grant_id??h?.id??null,client_id:e.run?.client_id??null,metadata_json:s?e.run?.metadata_json??null:null,run_id:s?r:e.run?.run_id??null,event_cursor:s?e.run?.event_cursor??1:null,usage_json:s?e.run?.usage_json??JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}):null,passive_default:e.passiveDefault??!1,...e.managedWorktree!==void 0?{managed_worktree:e.managedWorktree}:{},intent:null,deadline_at:e.deadlineAt??null,pi_session_id:null,pi_session_file:null,launch:e.launch},y=e.scopes===void 0?void 0:Q(e.scopes),v=o===null?null:await u(o);if(o!==null&&v===null)throw new Error(`cannot spawn under unknown parent node: ${o}`);if(v?.run_id&&(await u(v.run_id))?.deleting_at)throw new Error("run is deleting");const c=m===null?null:await u(m);if(m!==null&&c===null)throw new Error(`cannot spawn from unknown creator node: ${m}`);if(c?.run_id&&(await u(c.run_id))?.deleting_at)throw new Error("run is deleting");const b=e.forkSourceFile==null?null:await Be(e.forkSourceFile),A=e.replaces==null?null:await u(e.replaces),_=v??b??A??c??(f===null?null:await u(f)),q=[v,c,b,A,f===null?null:await u(f)],M=await Oe(q,async t=>t?.isolated===!0||t?.run_id!=null&&(await u(t.run_id))?.isolated===!0);if(M&&e.isolated===!1)throw new g(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});if(n.isolated=M||e.isolated===!0,_!==null){const t=c??_;if(c&&_.grantee&&c.grantee&&c.grantee!==_.grantee)throw new g(403,"other_app_run","cannot create under another app's node");const i=c&&c!==_?[_,c]:[_],d=[],w=new Set;for(const a of i){a.scopes!==null&&a.scopes!==void 0&&d.push(a.scopes);const p=a.grantee==="app:terminal"?await k():a.grant_id?await F(a.grant_id):null;if(p){const R=[...p.resolved];w.add(R),d.push(R)}else{const R=[];w.add(R),d.push(R)}}const E=l(a=>Y(a,t.grantee??"app:terminal"),"ownMemory"),$=l((a,p)=>E(p)&&w.has(a)||I(a,p),"allowed");if(y!==void 0&&!y.every(a=>d.every(p=>$(p,a))))throw new S({error:"unknown_scope",message:"child scopes must be covered by the creating node, its source and their current grants",received:y,field:"scopes",next:"Request only scopes the creating node currently holds."});if(n.scopes=y??(i.every(a=>a.scopes==null)&&i.every(a=>a.grant_id===t.grant_id&&a.sub===t.sub)?null:i.length===1?_.scopes??null:[...new Set(d.flat().filter(a=>d.every(p=>$(p,a))))]),n.scopes?.includes("crtr:act")&&!I(n.scopes,"crtr:llm"))throw new S({error:"invalid_scope",message:"crtr:act requires crtr:llm",received:n.scopes,field:"scopes",next:"Include crtr:llm or omit crtr:act."});n.grantee=t.grantee,n.sub=t.sub??(n.grantee==="app:terminal"?h?.sub:null)??null,n.grant_id=t.grant_id??(n.grantee==="app:terminal"?h?.id:null)??null,n.client_id=t.client_id,n.run_id=s?r:t.run_id??null}if(re(n.grantee),e.run===void 0&&n.grantee==="app:terminal"&&h===null)throw await Z()?new g(503,"runtime_starting","the first grant snapshot from the directory has not committed"):new g(403,"scope_missing","terminal grant is not active");if(n.isolated){if(process.platform!=="linux"||!B(U))throw new g(400,"isolated_unavailable","isolated nodes require the Linux root launcher");if(e.managedWorktree!==void 0)throw new g(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const t=ae(se(n.grantee??"app:terminal").nodes,r),i=oe(t,ie(n.cwd));if(!P(n.cwd)||i===".."||i.startsWith(`..${le}`)||P(i))throw new g(400,"invalid_request","isolated node cwd must be inside its own folder",{param:"cwd"})}if(n.profile_id){const t=Re(n.profile_id).manifest.grantee??"app:terminal";if(t!==n.grantee){const i=`crtr:memory:rw:${t}:profile:${n.profile_id}`,d=n.grantee==="app:terminal"?await k():n.grant_id?await F(n.grant_id):null;if(!d||d.grantee!==n.grantee||!I(d.resolved,i)||!I(n.scopes,i))throw new S({error:"scope_missing",message:`missing scope ${i}`,received:n.profile_id,field:"profile",next:"Select a profile covered by this run and its current grant."})}}if(ve(n.profile_id??null),!n.grantee)throw new Error("node creation requires an app");const W=await ye({kind:n.kind,mode:n.mode,cwd:n.cwd,profile:n.profile_id??null,root:s,parentId:o,requestingNodeId:m,lifecycle:N,replaces:e.replaces??null},n.grantee),G=l(async(t,i)=>{const d=await C(t,i.node_id),w=m??o??e.actor??f??null,E=w!==null&&(await Ee(t,w))?.type==="node"?await C(t,w):await Ie(t,e.run===void 0?ne():i.grantee);await Ce(t,E,d),o!==null&&e.subscribe!==!1&&await Ne(t,await C(t,o),d,"spawn"),e.forkSourceNode&&await ke(t,await C(t,e.forkSourceNode),d),await Se(t,{event:"created",object:d,actor:E,attrs:{name:i.name,kind:i.kind,creator:m??null}})},"recordBirth");try{e.nodeId!==void 0&&await $e(e.nodeId),await e.beforeCreate?.(),(process.platform!=="linux"||B(U))&&await me(n.grantee??"app:terminal"),await xe(n.cwd,r,()=>ce(n,{outcomeDelivery:e.outcomeDelivery,withinCreate:l(async(t,i)=>{i.run_id&&i.run_id!==i.node_id&&await ue(t,i.run_id),s&&e.run===void 0&&await ee(t,r,r,"run.started",{scopes:i.scopes??null,payer:"user",usage:JSON.parse(i.usage_json)}),await e.withinCreate?.(t,i),await G(t,i)},"withinCreate")}))}finally{W()}return n}l(In,"spawnNode");export{j as NodeIdConflictError,yn as currentNodeContext,Be as forkSourceNode,Fe as inboxBetweenTurns,be as newJobId,Ae as newNodeId,En as nodeEnv,vn as preflightNodeId,Rn as rootOfSpine,hn as setInstallId,In as spawnNode};
@@ -1,2 +1,2 @@
1
- var T=Object.defineProperty;var n=(e,o)=>T(e,"name",{value:o,configurable:!0});import{DatabaseSync as p}from"node:sqlite";import{forgetPrepared as R}from"../sqlite-statements.js";import{chmodSync as m,existsSync as s,mkdirSync as y,readdirSync as O,rmSync as I,statSync as P,writeFileSync as v}from"node:fs";import{join as i}from"node:path";import j from"proper-lockfile";import{CONFIG_FILE as S,PROFILE_DIR as N,STATE_FILE as f}from"../../types.js";import{withExclusiveDirectoryLock as _}from"../exclusive-lock.js";import{syncDirectory as k,syncFile as D}from"../fs-utils.js";import{layoutNeedsMigration as C}from"../layout.js";import{providerStore as G,projectStore as g,userStore as $}from"./paths.js";import{createScopedSchema as B,ensureProviderModelCooldowns as F,PROVIDER_SCHEMA_VERSION as w,SCOPED_SCHEMA_VERSION as d}from"./schema.js";const l=new Map,U=/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/;function H(e,o,r){e.prepare("UPDATE scope_owners SET root=? WHERE owner_key=?").run(r,o)}n(H,"writeOwnerRoot");function c(e){return Number(e.prepare("PRAGMA user_version").get().user_version)}n(c,"version");function L(e,o){const r=c(e);if(r===0)throw new Error(`scoped SQL store is uninitialized: ${o.path}; crtr was updated and \`crtr sys daemon restart\` converts local state`);const t=o.kind==="provider"?w:d;if(r>t)throw new Error(`scoped SQL store ${o.path} uses unsupported schema version ${r}`);if(r!==t)throw new Error(`scoped SQL store ${o.path} requires an explicit conversion`)}n(L,"assertVersion");function x(e,o){if(o.kind==="provider"){c(e)===w&&(e.exec("PRAGMA busy_timeout = 5000"),F(e));return}if(!(c(e)>=d)){e.exec("PRAGMA busy_timeout = 5000; BEGIN IMMEDIATE");try{c(e)===1&&(e.prepare("PRAGMA table_info(profiles)").all().some(({name:t})=>t==="grantee")||e.exec("ALTER TABLE profiles ADD COLUMN grantee TEXT"),e.exec("PRAGMA user_version = 2")),c(e)===2&&(e.prepare("PRAGMA table_info(profiles)").all().some(({name:t})=>t==="home")&&e.exec("ALTER TABLE profiles DROP COLUMN home"),e.exec("PRAGMA user_version = 3")),c(e)===3&&(e.exec("ALTER TABLE profiles ADD COLUMN delivery_limits_json TEXT NOT NULL DEFAULT '{}' CHECK(json_valid(delivery_limits_json))"),e.exec("PRAGMA user_version = 4")),c(e)===4&&(e.exec("CREATE TABLE IF NOT EXISTS legacy_project_caps (profile_id TEXT NOT NULL, path TEXT NOT NULL, memory TEXT NOT NULL CHECK(memory IN ('none','name','preview')), PRIMARY KEY(profile_id,path))"),e.prepare("PRAGMA table_info(profile_projects)").all().some(({name:t})=>t==="memory")&&e.exec(`INSERT INTO legacy_project_caps SELECT profile_id, path, memory FROM profile_projects WHERE memory != 'content';
2
- ALTER TABLE profile_projects DROP COLUMN memory;`),e.exec(`PRAGMA user_version = ${d}`)),e.exec("COMMIT")}catch(r){throw e.exec("ROLLBACK"),r}}}n(x,"upgradeScopedStore");function ae(e){const o=new p(e.path);try{x(o,e),L(o,e)}finally{o.close()}}n(ae,"assertScopedStoreReady");function K(e){if(!M(e))return;const o=new p(e.path,{readOnly:!0});let r;try{r=c(o)}finally{o.close()}if(r===0)for(const t of[e.path,`${e.path}-wal`,`${e.path}-shm`])I(t,{force:!0})}n(K,"discardIncompleteScopedStore");function X(e,o){return _(i(e,".init.lock"),()=>{const r=i(e,f),t=Date.now()+5e3;let a=10;for(;;){let E;try{E=j.lockSync(r,{realpath:!1,retries:0,stale:3e4,update:1e4})}catch(u){if(u.code!=="ELOCKED")throw u;const A=t-Date.now();if(A<=0)throw new Error(`timed out waiting for project state conversion lock: ${r}`,{cause:u});Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,Math.min(a,A)),a=Math.min(a*2,250);continue}try{return o()}finally{E()}}})}n(X,"withProjectStateLock");function h(e){s(e)&&m(e,384);for(const o of["-wal","-shm"])s(`${e}${o}`)&&m(`${e}${o}`,384)}n(h,"secure");function M(e){return s(e.path)}n(M,"scopedStoreExists");function z(e){if(e.kind==="project")return[i(e.root,f)];if(e.kind==="provider")return[i(e.root,"anthropic-subscriptions.json"),i(e.root,"openai-codex-subscriptions.json"),i(e.root,"provider-rotation.json")];const o=[i(e.root,S),i(e.root,f),i(e.root,"profile-defaults.json")],r=i(e.root,N);if(s(r))for(const t of O(r,{withFileTypes:!0}))t.isDirectory()&&U.test(t.name)&&o.push(i(r,t.name,"profile.json"),i(r,t.name,S),i(r,t.name,f));return o}n(z,"legacyScopedStorePaths");function Q(e){const o=e.kind==="user"?$(e.root):e.kind==="project"?g(e.root):G(e.root);o.kind!=="project"&&y(o.root,{recursive:!0,mode:448});const r=n(()=>{if(o.kind==="project"&&K(o),M(o))return;const t=z(o).find(s);if(t!==void 0)throw new Error(`legacy scoped state exists at ${t}; crtr was updated and \`crtr sys daemon restart\` converts local state`);V(o,a=>{B(a,o.kind),o.kind!=="provider"&&H(a,o.kind,o.root)})},"initialize");o.kind==="project"?X(o.root,r):_(i(o.root,".init.lock"),r)}n(Q,"initializeFreshScopedStore");function W(e){if(C())throw new Error("layout_migration_required");const o=l.get(e.path);if(o!==void 0)return o;Q(e);const r=new p(e.path);try{return r.exec("PRAGMA busy_timeout = 5000; PRAGMA journal_mode = WAL; PRAGMA foreign_keys = ON;"),x(r,e),L(r,e),h(e.path),l.set(e.path,r),r}catch(t){throw r.close(),t}}n(W,"openScopedStore");function V(e,o){if(s(e.path))throw new Error(`scoped SQL store already exists: ${e.path}`);if(e.kind!=="project"&&y(e.root,{recursive:!0,mode:448}),!P(e.root).isDirectory())throw new Error(`scoped SQL root is not a directory: ${e.root}`);(e.kind!=="project"||process.platform!=="linux")&&m(e.root,448),v(e.path,"",{mode:384});const r=new p(e.path);try{r.exec("PRAGMA busy_timeout = 5000; PRAGMA journal_mode = WAL; PRAGMA foreign_keys = ON; BEGIN IMMEDIATE;"),o(r),r.exec("COMMIT")}catch(t){try{r.exec("ROLLBACK")}catch{}throw t}finally{h(e.path),r.close()}D(e.path),k(e.root)}n(V,"createScopedStore");function se(e,o){const r=W(e);if(r.isTransaction)return o(r);r.exec("BEGIN IMMEDIATE");try{const t=o(r);return r.exec("COMMIT"),h(e.path),t}catch(t){try{r.exec("ROLLBACK")}catch{}throw t}}n(se,"withScopedWrite");function pe(){for(const e of l.values())R(e),e.close();l.clear()}n(pe,"closeScopedStores");export{U as PROFILE_ID_SHAPE,ae as assertScopedStoreReady,pe as closeScopedStores,V as createScopedStore,K as discardIncompleteScopedStore,Q as initializeFreshScopedStore,z as legacyScopedStorePaths,W as openScopedStore,M as scopedStoreExists,X as withProjectStateLock,se as withScopedWrite,H as writeOwnerRoot};
1
+ var O=Object.defineProperty;var n=(e,o)=>O(e,"name",{value:o,configurable:!0});import{DatabaseSync as f}from"node:sqlite";import{forgetPrepared as I}from"../sqlite-statements.js";import{chmodSync as d,existsSync as s,mkdirSync as y,readdirSync as P,rmSync as N,statSync as v,writeFileSync as j}from"node:fs";import{join as i}from"node:path";import k from"proper-lockfile";import{CONFIG_FILE as S,PROFILE_DIR as C,STATE_FILE as l}from"../../types.js";import{withExclusiveDirectoryLock as _}from"../exclusive-lock.js";import{syncDirectory as D,syncFile as G}from"../fs-utils.js";import{layoutNeedsMigration as g}from"../layout.js";import{providerStore as $,projectStore as B,userStore as F}from"./paths.js";import{createScopedSchema as U,ensureProviderModelCooldowns as H,PROFILE_ENVIRONMENT_COLUMN as L,PROVIDER_SCHEMA_VERSION as w,SCOPED_SCHEMA_VERSION as p}from"./schema.js";const u=new Map,K=/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/;function X(e,o,r){e.prepare("UPDATE scope_owners SET root=? WHERE owner_key=?").run(r,o)}n(X,"writeOwnerRoot");function c(e){return Number(e.prepare("PRAGMA user_version").get().user_version)}n(c,"version");function T(e,o){const r=c(e);if(r===0)throw new Error(`scoped SQL store is uninitialized: ${o.path}; crtr was updated and \`crtr sys daemon restart\` converts local state`);const t=o.kind==="provider"?w:p;if(r>t)throw new Error(`scoped SQL store ${o.path} uses unsupported schema version ${r}`);if(r!==t)throw new Error(`scoped SQL store ${o.path} requires an explicit conversion`)}n(T,"assertVersion");function M(e){return e.prepare("PRAGMA table_info(profiles)").all().some(({name:o})=>o===L)}n(M,"profilesHaveEnvironmentColumn");function R(e,o){if(o.kind==="provider"){c(e)===w&&(e.exec("PRAGMA busy_timeout = 5000"),H(e));return}if(!(c(e)>p)&&(e.exec("PRAGMA busy_timeout = 5000"),!(c(e)===p&&M(e)))){e.exec("BEGIN IMMEDIATE");try{c(e)===1&&(e.prepare("PRAGMA table_info(profiles)").all().some(({name:t})=>t==="grantee")||e.exec("ALTER TABLE profiles ADD COLUMN grantee TEXT"),e.exec("PRAGMA user_version = 2")),c(e)===2&&(e.prepare("PRAGMA table_info(profiles)").all().some(({name:t})=>t==="home")&&e.exec("ALTER TABLE profiles DROP COLUMN home"),e.exec("PRAGMA user_version = 3")),c(e)===3&&(e.exec("ALTER TABLE profiles ADD COLUMN delivery_limits_json TEXT NOT NULL DEFAULT '{}' CHECK(json_valid(delivery_limits_json))"),e.exec("PRAGMA user_version = 4")),c(e)===4&&(e.exec("CREATE TABLE IF NOT EXISTS legacy_project_caps (profile_id TEXT NOT NULL, path TEXT NOT NULL, memory TEXT NOT NULL CHECK(memory IN ('none','name','preview')), PRIMARY KEY(profile_id,path))"),e.prepare("PRAGMA table_info(profile_projects)").all().some(({name:t})=>t==="memory")&&e.exec(`INSERT INTO legacy_project_caps SELECT profile_id, path, memory FROM profile_projects WHERE memory != 'content';
2
+ ALTER TABLE profile_projects DROP COLUMN memory;`),e.exec(`PRAGMA user_version = ${p}`)),c(e)===p&&!M(e)&&e.exec(`ALTER TABLE profiles ADD COLUMN ${L} TEXT`),e.exec("COMMIT")}catch(r){throw e.exec("ROLLBACK"),r}}}n(R,"upgradeScopedStore");function pe(e){const o=new f(e.path);try{R(o,e),T(o,e)}finally{o.close()}}n(pe,"assertScopedStoreReady");function z(e){if(!x(e))return;const o=new f(e.path,{readOnly:!0});let r;try{r=c(o)}finally{o.close()}if(r===0)for(const t of[e.path,`${e.path}-wal`,`${e.path}-shm`])N(t,{force:!0})}n(z,"discardIncompleteScopedStore");function Q(e,o){return _(i(e,".init.lock"),()=>{const r=i(e,l),t=Date.now()+5e3;let a=10;for(;;){let A;try{A=k.lockSync(r,{realpath:!1,retries:0,stale:3e4,update:1e4})}catch(m){if(m.code!=="ELOCKED")throw m;const h=t-Date.now();if(h<=0)throw new Error(`timed out waiting for project state conversion lock: ${r}`,{cause:m});Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,Math.min(a,h)),a=Math.min(a*2,250);continue}try{return o()}finally{A()}}})}n(Q,"withProjectStateLock");function E(e){s(e)&&d(e,384);for(const o of["-wal","-shm"])s(`${e}${o}`)&&d(`${e}${o}`,384)}n(E,"secure");function x(e){return s(e.path)}n(x,"scopedStoreExists");function V(e){if(e.kind==="project")return[i(e.root,l)];if(e.kind==="provider")return[i(e.root,"anthropic-subscriptions.json"),i(e.root,"openai-codex-subscriptions.json"),i(e.root,"provider-rotation.json")];const o=[i(e.root,S),i(e.root,l),i(e.root,"profile-defaults.json")],r=i(e.root,C);if(s(r))for(const t of P(r,{withFileTypes:!0}))t.isDirectory()&&K.test(t.name)&&o.push(i(r,t.name,"profile.json"),i(r,t.name,S),i(r,t.name,l));return o}n(V,"legacyScopedStorePaths");function W(e){const o=e.kind==="user"?F(e.root):e.kind==="project"?B(e.root):$(e.root);o.kind!=="project"&&y(o.root,{recursive:!0,mode:448});const r=n(()=>{if(o.kind==="project"&&z(o),x(o))return;const t=V(o).find(s);if(t!==void 0)throw new Error(`legacy scoped state exists at ${t}; crtr was updated and \`crtr sys daemon restart\` converts local state`);Y(o,a=>{U(a,o.kind),o.kind!=="provider"&&X(a,o.kind,o.root)})},"initialize");o.kind==="project"?Q(o.root,r):_(i(o.root,".init.lock"),r)}n(W,"initializeFreshScopedStore");function q(e){if(g())throw new Error("layout_migration_required");const o=u.get(e.path);if(o!==void 0)return o;W(e);const r=new f(e.path);try{return r.exec("PRAGMA busy_timeout = 5000; PRAGMA journal_mode = WAL; PRAGMA foreign_keys = ON;"),R(r,e),T(r,e),E(e.path),u.set(e.path,r),r}catch(t){throw r.close(),t}}n(q,"openScopedStore");function Y(e,o){if(s(e.path))throw new Error(`scoped SQL store already exists: ${e.path}`);if(e.kind!=="project"&&y(e.root,{recursive:!0,mode:448}),!v(e.root).isDirectory())throw new Error(`scoped SQL root is not a directory: ${e.root}`);(e.kind!=="project"||process.platform!=="linux")&&d(e.root,448),j(e.path,"",{mode:384});const r=new f(e.path);try{r.exec("PRAGMA busy_timeout = 5000; PRAGMA journal_mode = WAL; PRAGMA foreign_keys = ON; BEGIN IMMEDIATE;"),o(r),r.exec("COMMIT")}catch(t){try{r.exec("ROLLBACK")}catch{}throw t}finally{E(e.path),r.close()}G(e.path),D(e.root)}n(Y,"createScopedStore");function fe(e,o){const r=q(e);if(r.isTransaction)return o(r);r.exec("BEGIN IMMEDIATE");try{const t=o(r);return r.exec("COMMIT"),E(e.path),t}catch(t){try{r.exec("ROLLBACK")}catch{}throw t}}n(fe,"withScopedWrite");function le(){for(const e of u.values())I(e),e.close();u.clear()}n(le,"closeScopedStores");export{K as PROFILE_ID_SHAPE,pe as assertScopedStoreReady,le as closeScopedStores,Y as createScopedStore,z as discardIncompleteScopedStore,W as initializeFreshScopedStore,V as legacyScopedStorePaths,q as openScopedStore,x as scopedStoreExists,Q as withProjectStateLock,fe as withScopedWrite,X as writeOwnerRoot};
@@ -1 +1 @@
1
- var c=Object.defineProperty;var o=(t,e)=>c(t,"name",{value:e,configurable:!0});import{prepared as n}from"../sqlite-statements.js";import{canonicalRoot as m,userStore as S}from"./paths.js";import{profileSpace as R}from"../layout.js";import{openScopedStore as d,withScopedWrite as f}from"./db.js";function s(t){return S(t)}o(s,"store");function O(t,e){return{profileId:t.id,manifest:{schema_version:1,name:t.name,projects:e,paused_at:t.paused_at,...t.default_kind===null?{}:{default_kind:t.default_kind},...t.grantee===null?{}:{grantee:t.grantee},...t.delivery_limits_json==="{}"?{}:{delivery_limits:JSON.parse(t.delivery_limits_json)},...Object.keys(JSON.parse(t.metadata_json)).length===0?{}:{metadata:JSON.parse(t.metadata_json)},created_at:t.created_at,last_used_at:t.last_used_at}}}o(O,"rowToProfile");function u(t,e){const r=n(t,"SELECT id, name, created_at, last_used_at, paused_at, default_kind, grantee, metadata_json, delivery_limits_json FROM profiles WHERE id=?").get(e);if(!r)return null;const i=n(t,"SELECT path FROM profile_projects WHERE profile_id=? ORDER BY position").all(e);return O(r,i)}o(u,"get");function I(t){const e=d(s(t));return n(e,"SELECT id FROM profiles ORDER BY rowid").all().map(({id:i})=>u(e,i)).filter(Boolean)}o(I,"listStoredProfiles");function N(t,e){return u(d(s(t)),e)}o(N,"getStoredProfile");function k(t,e){return n(d(s(t)),"SELECT paused_at FROM profiles WHERE id=?").get(e)?.paused_at!=null}o(k,"profilePaused");function h(t){const e=n(d(s(t)),"SELECT id FROM profiles WHERE paused_at IS NOT NULL").all();return new Set(e.map(r=>r.id))}o(h,"pausedProfileIds");function D(t,e){f(s(t),r=>{n(r,"INSERT INTO profiles(id,name,created_at,last_used_at,paused_at,default_kind,grantee,metadata_json,delivery_limits_json) VALUES(?,?,?,?,?,?,?,?,?)").run(e.profileId,e.manifest.name,e.manifest.created_at,e.manifest.last_used_at,e.manifest.paused_at,e.manifest.default_kind??null,e.manifest.grantee??null,JSON.stringify(e.manifest.metadata??{}),JSON.stringify(e.manifest.delivery_limits??{})),n(r,"INSERT INTO scope_owners(owner_key,kind,root,profile_id) VALUES(?,?,?,?)").run(`profile:${e.profileId}`,"profile",m(R(e.manifest.grantee??"app:terminal",e.profileId).dir),e.profileId);const i=n(r,"INSERT INTO profile_projects(profile_id,path,position) VALUES(?,?,?)");e.manifest.projects.forEach((l,a)=>i.run(e.profileId,l.path,a))})}o(D,"insertStoredProfile");function F(t,e,r){return f(s(t),i=>{const l=u(i,e);if(!l)return null;const a=r(l.manifest);n(i,"UPDATE profiles SET name=?, last_used_at=?, paused_at=?, default_kind=?, grantee=?, metadata_json=?, delivery_limits_json=? WHERE id=?").run(a.name,a.last_used_at,a.paused_at,a.default_kind??null,a.grantee??null,JSON.stringify(a.metadata??{}),JSON.stringify(a.delivery_limits??{}),e),n(i,"DELETE FROM profile_projects WHERE profile_id=?").run(e);const p=n(i,"INSERT INTO profile_projects(profile_id,path,position) VALUES(?,?,?)");return a.projects.forEach((E,_)=>p.run(e,E.path,_)),{profileId:e,manifest:a}})}o(F,"updateStoredProfile");function M(t,e){return f(s(t),r=>{if(!u(r,e))return!1;const i=`profile:${e}`;return n(r,"DELETE FROM scope_settings WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_bookkeeping WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_marketplaces WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_plugins WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_owners WHERE owner_key=?").run(i),n(r,"DELETE FROM profiles WHERE id=?").run(e),!0})}o(M,"deleteStoredProfile");function W(t){f(s(t),e=>{n(e,"UPDATE profiles SET grantee = 'app:terminal' WHERE grantee = 'user:local'").run()})}o(W,"rewriteLocalProfileGrantees");function x(t){const e=d(s(t));return Object.fromEntries(n(e,"SELECT directory,profile_id FROM profile_defaults").all().map(r=>[r.directory,r.profile_id]))}o(x,"readStoredDefaults");function H(t,e){return f(s(t),r=>{const i=Object.fromEntries(n(r,"SELECT directory,profile_id FROM profile_defaults").all().map(p=>[p.directory,p.profile_id])),l=e(i);n(r,"DELETE FROM profile_defaults").run();const a=n(r,"INSERT INTO profile_defaults(directory,profile_id) VALUES(?,?)");for(const[p,E]of Object.entries(l))a.run(p,E);return l})}o(H,"mutateStoredDefaults");function P(t){return n(d(s(t)),"SELECT profile_id, path, memory FROM legacy_project_caps ORDER BY profile_id, path").all().map(e=>({profileId:e.profile_id,path:e.path,memory:e.memory}))}o(P,"listLegacyProjectCaps");function v(t,e){f(s(t),r=>{const i=n(r,"UPDATE profiles SET delivery_limits_json=? WHERE id=?");for(const l of e)if(i.run(JSON.stringify(l.deliveryLimits),l.profileId).changes!==1)throw new Error(`no stored profile ${l.profileId}`);n(r,"DELETE FROM legacy_project_caps").run()})}o(v,"commitConvertedProjectCaps");export{v as commitConvertedProjectCaps,M as deleteStoredProfile,N as getStoredProfile,D as insertStoredProfile,P as listLegacyProjectCaps,I as listStoredProfiles,H as mutateStoredDefaults,h as pausedProfileIds,k as profilePaused,x as readStoredDefaults,W as rewriteLocalProfileGrantees,F as updateStoredProfile};
1
+ var c=Object.defineProperty;var o=(t,e)=>c(t,"name",{value:e,configurable:!0});import{prepared as n}from"../sqlite-statements.js";import{canonicalRoot as m,userStore as S}from"./paths.js";import{profileSpace as R}from"../layout.js";import{openScopedStore as p,withScopedWrite as f}from"./db.js";import{LEGACY_LOCAL_PERSON as O}from"../canvas/legacy-person.js";function s(t){return S(t)}o(s,"store");function T(t,e){return{profileId:t.id,manifest:{schema_version:1,name:t.name,projects:e,paused_at:t.paused_at,...t.default_kind===null?{}:{default_kind:t.default_kind},...t.grantee===null?{}:{grantee:t.grantee},...t.environment_id===null?{}:{environment_id:t.environment_id},...t.delivery_limits_json==="{}"?{}:{delivery_limits:JSON.parse(t.delivery_limits_json)},...Object.keys(JSON.parse(t.metadata_json)).length===0?{}:{metadata:JSON.parse(t.metadata_json)},created_at:t.created_at,last_used_at:t.last_used_at}}}o(T,"rowToProfile");function _(t,e){const r=n(t,"SELECT id, name, created_at, last_used_at, paused_at, default_kind, grantee, metadata_json, delivery_limits_json, environment_id FROM profiles WHERE id=?").get(e);if(!r)return null;const i=n(t,"SELECT path FROM profile_projects WHERE profile_id=? ORDER BY position").all(e);return T(r,i)}o(_,"get");function v(t){const e=p(s(t));return n(e,"SELECT id FROM profiles ORDER BY rowid").all().map(({id:i})=>_(e,i)).filter(Boolean)}o(v,"listStoredProfiles");function k(t,e){return _(p(s(t)),e)}o(k,"getStoredProfile");function h(t,e){return n(p(s(t)),"SELECT paused_at FROM profiles WHERE id=?").get(e)?.paused_at!=null}o(h,"profilePaused");function D(t){const e=n(p(s(t)),"SELECT id FROM profiles WHERE paused_at IS NOT NULL").all();return new Set(e.map(r=>r.id))}o(D,"pausedProfileIds");function F(t,e){f(s(t),r=>{n(r,"INSERT INTO profiles(id,name,created_at,last_used_at,paused_at,default_kind,grantee,metadata_json,delivery_limits_json,environment_id) VALUES(?,?,?,?,?,?,?,?,?,?)").run(e.profileId,e.manifest.name,e.manifest.created_at,e.manifest.last_used_at,e.manifest.paused_at,e.manifest.default_kind??null,e.manifest.grantee??null,JSON.stringify(e.manifest.metadata??{}),JSON.stringify(e.manifest.delivery_limits??{}),e.manifest.environment_id??null),n(r,"INSERT INTO scope_owners(owner_key,kind,root,profile_id) VALUES(?,?,?,?)").run(`profile:${e.profileId}`,"profile",m(R(e.manifest.grantee??"app:terminal",e.profileId).dir),e.profileId);const i=n(r,"INSERT INTO profile_projects(profile_id,path,position) VALUES(?,?,?)");e.manifest.projects.forEach((l,a)=>i.run(e.profileId,l.path,a))})}o(F,"insertStoredProfile");function M(t,e,r){return f(s(t),i=>{const l=_(i,e);if(!l)return null;const a=r(l.manifest);n(i,"UPDATE profiles SET name=?, last_used_at=?, paused_at=?, default_kind=?, grantee=?, metadata_json=?, delivery_limits_json=?, environment_id=? WHERE id=?").run(a.name,a.last_used_at,a.paused_at,a.default_kind??null,a.grantee??null,JSON.stringify(a.metadata??{}),JSON.stringify(a.delivery_limits??{}),a.environment_id??null,e),n(i,"DELETE FROM profile_projects WHERE profile_id=?").run(e);const d=n(i,"INSERT INTO profile_projects(profile_id,path,position) VALUES(?,?,?)");return a.projects.forEach((u,E)=>d.run(e,u.path,E)),{profileId:e,manifest:a}})}o(M,"updateStoredProfile");function P(t,e){return f(s(t),r=>{if(!_(r,e))return!1;const i=`profile:${e}`;return n(r,"DELETE FROM scope_settings WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_bookkeeping WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_marketplaces WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_plugins WHERE owner_key=?").run(i),n(r,"DELETE FROM scope_owners WHERE owner_key=?").run(i),n(r,"DELETE FROM profiles WHERE id=?").run(e),!0})}o(P,"deleteStoredProfile");function W(t){f(s(t),e=>{n(e,"UPDATE profiles SET grantee = 'app:terminal' WHERE grantee = ?").run(O)})}o(W,"rewriteLocalProfileGrantees");function x(t){const e=p(s(t));return Object.fromEntries(n(e,"SELECT directory,profile_id FROM profile_defaults").all().map(r=>[r.directory,r.profile_id]))}o(x,"readStoredDefaults");function H(t,e){return f(s(t),r=>{const i=Object.fromEntries(n(r,"SELECT directory,profile_id FROM profile_defaults").all().map(d=>[d.directory,d.profile_id])),l=e(i);n(r,"DELETE FROM profile_defaults").run();const a=n(r,"INSERT INTO profile_defaults(directory,profile_id) VALUES(?,?)");for(const[d,u]of Object.entries(l))a.run(d,u);return l})}o(H,"mutateStoredDefaults");function C(t){return n(p(s(t)),"SELECT profile_id, path, memory FROM legacy_project_caps ORDER BY profile_id, path").all().map(e=>({profileId:e.profile_id,path:e.path,memory:e.memory}))}o(C,"listLegacyProjectCaps");function A(t,e){f(s(t),r=>{const i=n(r,"UPDATE profiles SET delivery_limits_json=? WHERE id=?");for(const l of e)if(i.run(JSON.stringify(l.deliveryLimits),l.profileId).changes!==1)throw new Error(`no stored profile ${l.profileId}`);n(r,"DELETE FROM legacy_project_caps").run()})}o(A,"commitConvertedProjectCaps");export{A as commitConvertedProjectCaps,P as deleteStoredProfile,k as getStoredProfile,F as insertStoredProfile,C as listLegacyProjectCaps,v as listStoredProfiles,H as mutateStoredDefaults,D as pausedProfileIds,h as profilePaused,x as readStoredDefaults,W as rewriteLocalProfileGrantees,M as updateStoredProfile};
@@ -1,6 +1,10 @@
1
1
  import type { DatabaseSync } from 'node:sqlite';
2
2
  export declare const SCOPED_SCHEMA_VERSION = 5;
3
3
  export declare const PROVIDER_SCHEMA_VERSION = 1;
4
+ /** `profiles.environment_id` (null = the machine environment) is additive to version 5: a store written before it gains the
5
+ * column on open (`upgradeScopedStore`), and an older generation sharing the store names its columns and never reads it, so
6
+ * the version stays 5 and a runtime rollback keeps opening the store. */
7
+ export declare const PROFILE_ENVIRONMENT_COLUMN = "environment_id";
4
8
  /** Adds the model-cooldown table to a provider store that predates it; a no-op (no write) once present. */
5
9
  export declare function ensureProviderModelCooldowns(db: DatabaseSync): void;
6
10
  export declare function createScopedSchema(db: DatabaseSync, kind: 'user' | 'project' | 'provider'): void;
@@ -1,6 +1,6 @@
1
- var t=Object.defineProperty;var T=(e,E)=>t(e,"name",{value:E,configurable:!0});import{prepared as o}from"../sqlite-statements.js";const a=5,_=1,n=`
1
+ var t=Object.defineProperty;var T=(e,E)=>t(e,"name",{value:E,configurable:!0});import{prepared as o}from"../sqlite-statements.js";const n=5,_=1,R="environment_id",a=`
2
2
  CREATE TABLE scope_owners (owner_key TEXT PRIMARY KEY, kind TEXT NOT NULL CHECK(kind IN ('user','profile','project')), root TEXT NOT NULL UNIQUE, profile_id TEXT UNIQUE);
3
- CREATE TABLE profiles (id TEXT PRIMARY KEY, name TEXT NOT NULL, created_at TEXT NOT NULL, last_used_at TEXT, paused_at TEXT, default_kind TEXT, grantee TEXT, metadata_json TEXT NOT NULL CHECK(json_valid(metadata_json)), delivery_limits_json TEXT NOT NULL DEFAULT '{}' CHECK(json_valid(delivery_limits_json)));
3
+ CREATE TABLE profiles (id TEXT PRIMARY KEY, name TEXT NOT NULL, created_at TEXT NOT NULL, last_used_at TEXT, paused_at TEXT, default_kind TEXT, grantee TEXT, metadata_json TEXT NOT NULL CHECK(json_valid(metadata_json)), delivery_limits_json TEXT NOT NULL DEFAULT '{}' CHECK(json_valid(delivery_limits_json)), environment_id TEXT);
4
4
  CREATE TABLE profile_projects (profile_id TEXT NOT NULL REFERENCES profiles(id) ON DELETE CASCADE, path TEXT NOT NULL, position INTEGER NOT NULL, PRIMARY KEY(profile_id,path), UNIQUE(profile_id,position));
5
5
  -- Pre-canvas per-project memory caps awaiting the canvas import, which turns them into delivery_limits and deletes the rows.
6
6
  CREATE TABLE legacy_project_caps (profile_id TEXT NOT NULL, path TEXT NOT NULL, memory TEXT NOT NULL CHECK(memory IN ('none','name','preview')), PRIMARY KEY(profile_id,path));
@@ -13,4 +13,4 @@ CREATE TABLE managed_accounts (provider_id TEXT NOT NULL, id TEXT NOT NULL, labe
13
13
  CREATE INDEX managed_accounts_order ON managed_accounts(provider_id,position);
14
14
  CREATE TABLE rotation_settings (singleton INTEGER PRIMARY KEY CHECK(singleton=1), default_fallback_strength TEXT, revert_when_available INTEGER CHECK(revert_when_available IN (0,1)));
15
15
  CREATE TABLE provider_revision (singleton INTEGER PRIMARY KEY CHECK(singleton=1), revision INTEGER NOT NULL);
16
- ${r}`;function R(e,E){if(e.exec(E==="provider"?N:n),E!=="provider"){const i=E==="user"?"user":"project";o(e,"INSERT INTO scope_owners(owner_key, kind, root, profile_id) VALUES (?, ?, ?, NULL)").run(i,E,"")}else o(e,"INSERT INTO provider_revision(singleton, revision) VALUES (1, 0)").run();e.exec(`PRAGMA user_version = ${E==="provider"?_:a}`)}T(R,"createScopedSchema");export{_ as PROVIDER_SCHEMA_VERSION,a as SCOPED_SCHEMA_VERSION,R as createScopedSchema,l as ensureProviderModelCooldowns};
16
+ ${r}`;function d(e,E){if(e.exec(E==="provider"?N:a),E!=="provider"){const i=E==="user"?"user":"project";o(e,"INSERT INTO scope_owners(owner_key, kind, root, profile_id) VALUES (?, ?, ?, NULL)").run(i,E,"")}else o(e,"INSERT INTO provider_revision(singleton, revision) VALUES (1, 0)").run();e.exec(`PRAGMA user_version = ${E==="provider"?_:n}`)}T(d,"createScopedSchema");export{R as PROFILE_ENVIRONMENT_COLUMN,_ as PROVIDER_SCHEMA_VERSION,n as SCOPED_SCHEMA_VERSION,d as createScopedSchema,l as ensureProviderModelCooldowns};