@north-light/cli 0.3.511 → 0.3.513

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 (63) hide show
  1. package/dist/api/client.d.ts +3 -1
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/command-manifest/object-kinds.js +1 -1
  4. package/dist/api/contract.d.ts +1 -1
  5. package/dist/api/contract.js +1 -1
  6. package/dist/api/dto/canvas.d.ts +12 -0
  7. package/dist/api/dto/custom-objects.d.ts +1 -0
  8. package/dist/api/dto/health.d.ts +8 -0
  9. package/dist/api/dto/objects.d.ts +1 -0
  10. package/dist/api/routes.d.ts +1 -0
  11. package/dist/api/routes.js +1 -1
  12. package/dist/builtin-memory/crouter-sdk/release-notes.md +12 -0
  13. package/dist/builtin-memory/crouter-sdk/resources.md +1 -1
  14. package/dist/clients/attach/chrome/canvas-panels.d.ts +5 -0
  15. package/dist/clients/attach/chrome/canvas-panels.js +1 -1
  16. package/dist/clients/attach/chrome/object-wait.d.ts +4 -0
  17. package/dist/clients/attach/chrome/object-wait.js +1 -0
  18. package/dist/clients/attach/chrome/roster.d.ts +3 -0
  19. package/dist/clients/attach/chrome/roster.js +4 -4
  20. package/dist/clients/attach/session/editor-inventory.js +2 -2
  21. package/dist/clients/attach/session/readonly-popup.d.ts +15 -0
  22. package/dist/clients/attach/session/readonly-popup.js +1 -0
  23. package/dist/clients/attach/viewer.js +604 -604
  24. package/dist/commands/custom-object/register.js +1 -1
  25. package/dist/core/canvas/canvas.d.ts +0 -13
  26. package/dist/core/canvas/canvas.js +16 -18
  27. package/dist/core/canvas/migrations.js +40 -22
  28. package/dist/core/canvas/remote-canvas-source.js +1 -1
  29. package/dist/core/canvas/render-source.d.ts +1 -1
  30. package/dist/core/canvas/types.d.ts +0 -4
  31. package/dist/core/canvas/waits.d.ts +23 -2
  32. package/dist/core/canvas/waits.js +9 -4
  33. package/dist/core/graph/custom.d.ts +1 -0
  34. package/dist/core/graph/custom.js +2 -2
  35. package/dist/core/graph/events.js +6 -6
  36. package/dist/core/graph/kinds/custom.d.ts +1 -0
  37. package/dist/core/graph/kinds/custom.js +1 -1
  38. package/dist/core/graph/kinds/kind.d.ts +3 -0
  39. package/dist/core/graph/object-waits.d.ts +11 -0
  40. package/dist/core/graph/object-waits.js +1 -0
  41. package/dist/core/human/page-schema.d.ts +1 -1
  42. package/dist/core/llm/run.js +2 -2
  43. package/dist/core/runtime/broker/model-adapter.js +2 -2
  44. package/dist/core/runtime/lifecycle.js +2 -2
  45. package/dist/core/runtime/nodes.js +1 -1
  46. package/dist/core/runtime/stop-guard.js +3 -3
  47. package/dist/daemon/api/app-listener.js +12 -12
  48. package/dist/daemon/api/handlers/canvas.js +5 -5
  49. package/dist/daemon/api/handlers/health.js +3 -3
  50. package/dist/daemon/api/handlers/nodes.js +1 -1
  51. package/dist/daemon/api/handlers/objects.js +3 -3
  52. package/dist/daemon/api/map.js +2 -2
  53. package/dist/daemon/api/operations.js +1 -1
  54. package/dist/daemon/api/server.js +1 -1
  55. package/dist/daemon/readiness.d.ts +10 -0
  56. package/dist/daemon/readiness.js +1 -0
  57. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  58. package/dist/types.d.ts +1 -0
  59. package/docs/sdk/release-notes.md +12 -0
  60. package/docs/sdk/resources.md +1 -1
  61. package/package.json +2 -2
  62. package/packages/crouter-identity/package.json +1 -1
  63. package/runtime.lock.json +12 -12
@@ -1 +1 @@
1
- var y=Object.defineProperty;var u=(s,e)=>y(s,"name",{value:e,configurable:!0});import{createRemoteExec as h}from"./remote-transport.js";const d=1e3;function r(s){return typeof s=="object"&&s!==null&&!Array.isArray(s)}u(r,"isPlainObject");function w(s){if(!r(s))return!1;const e=s.node;if(!r(e)||typeof e.node_id!="string"||!Array.isArray(s.reports)||!Array.isArray(s.managers))return!1;const t=s.artifacts;if(!r(t)||typeof t.report!="number"||typeof t.doc!="number"||typeof t.roadmap!="number"||typeof s.pending_crons!="number")return!1;const o=s.paths;if(!r(o)||typeof o.context_dir!="string"||"session_file"in o&&o.session_file!==null&&typeof o.session_file!="string"||typeof s.follow_up!="string")return!1;const n=s.fault;if(n!=null){if(!r(n)||typeof n.link!="string"||typeof n.kind!="string"||typeof n.since!="string")return!1;const i=n.retry;if(!r(i)||typeof i.disposition!="string")return!1}return!0}u(w,"isValidShowResponse");function p(s){return r(s)&&typeof s.node_id=="string"&&typeof s.name=="string"&&typeof s.kind=="string"&&typeof s.status=="string"&&typeof s.active=="boolean"}u(p,"isValidNeighborEntry");function g(s){return r(s)&&typeof s.from_id=="string"&&typeof s.to_id=="string"&&typeof s.active=="boolean"&&typeof s.created=="string"}u(g,"isValidRosterEdge");function _(s){return r(s)?typeof s.node_id=="string"&&typeof s.name=="string"&&typeof s.kind=="string"&&typeof s.mode=="string"&&typeof s.lifecycle=="string"&&typeof s.status=="string"&&typeof s.cwd=="string"&&typeof s.created=="string":!1}u(_,"isValidNodeRow");class v{static{u(this,"RemoteCanvasSource")}previewEndpoint;relayToken;exec;nodeCache=new Map;rosterCache=new Map;topologyCache=null;viewCache=new Map;constructor(e,t){this.previewEndpoint=e,this.relayToken=t,this.exec=h(e,t)}async execJson(e){let t;try{t=await this.exec("crtr",["--json",...e])}catch{return null}if(!t.ok||t.exitCode!==void 0&&t.exitCode!==0)return null;try{return JSON.parse(t.stdout.trim())}catch{return null}}async fetchShow(e){const t=this.nodeCache.get(e);if(t&&Date.now()-t.at<d)return t.value;const o=await this.execJson(["node","inspect","show",e]),n=w(o)?{...o,fault:o.fault??null,reports:o.reports.filter(p),managers:o.managers.filter(p)}:null;return this.nodeCache.set(e,{value:n,at:Date.now()}),n}topology(){if(this.topologyCache!==null&&Date.now()-this.topologyCache.at<d)return this.topologyCache.value;const e=this.loadTopology();return this.topologyCache={value:e,at:Date.now()},e}async loadTopology(){const e=await this.exec("crtr",["--json","canvas","roster"]);if(!e.ok||e.exitCode!==void 0&&e.exitCode!==0)throw new Error("remote canvas topology is unavailable");let t;try{t=JSON.parse(e.stdout.trim())}catch{throw new Error("remote canvas topology is invalid")}if(!r(t)||!Array.isArray(t.edges)||!t.edges.every(g))throw new Error("remote canvas topology is invalid");const o=t.edges,n={subscriptions:new Map,subscribers:new Map};for(const i of o){const a={node_id:i.to_id,active:i.active,created:i.created},c={node_id:i.from_id,active:i.active,created:i.created},l=n.subscriptions.get(i.from_id);l===void 0?n.subscriptions.set(i.from_id,[a]):l.push(a);const f=n.subscribers.get(i.to_id);f===void 0?n.subscribers.set(i.to_id,[c]):f.push(c)}return n}async rawShow(e){return this.fetchShow(e)}async getNode(e){return(await this.fetchShow(e))?.node??null}async getRow(e){const t=(await this.fetchShow(e))?.node;return t?{node_id:t.node_id,name:t.name,kind:t.kind,mode:t.mode,lifecycle:t.lifecycle,status:t.status,cwd:t.cwd,host_kind:t.host_kind??null,profile_id:t.profile_id??null,scopes:t.scopes??null,isolated:t.isolated===!0,parent:t.parent??null,created:t.created,intent:t.intent??null,waiting_for:t.waiting_for??null,frozen_at:t.frozen_at??null,terminal_reason:t.terminal_reason??null,pi_pid:t.pi_pid??null,pi_pid_identity:t.pi_pid_identity??null,window:t.window??null,tmux_session:t.tmux_session??null,pane:t.pane??null,fault:t.fault??null,streaming:t.streaming===!0}:null}async listNodes(e){const t=JSON.stringify(e?.status??null),o=this.rosterCache.get(t);if(o&&Date.now()-o.at<d)return o.value;const n=["node","inspect","list"];e?.status&&n.push("--status",(Array.isArray(e.status)?e.status:[e.status]).join(","));const i=await this.execJson(n),c=(r(i)&&Array.isArray(i.nodes)?i.nodes:[]).filter(_).map(l=>({...l,isolated:l.isolated===!0}));return this.rosterCache.set(t,{value:c,at:Date.now()}),c}async subscriptionsOf(e){return(await this.topology()).subscriptions.get(e)??[]}async subscribersOf(e){return(await this.topology()).subscribers.get(e)??[]}async view(e){const t=this.viewCache.get(e);if(t&&Date.now()-t.at<d)return t.value;const o=new Set([e]),n=[],i=(await this.subscriptionsOf(e)).map(a=>a.node_id);for(;i.length>0;){const a=i.shift();if(!o.has(a)){o.add(a),n.push(a);for(const c of await this.subscriptionsOf(a))o.has(c.node_id)||i.push(c.node_id)}}return this.viewCache.set(e,{value:n,at:Date.now()}),n}async ticketCountsForNodes(e){if(e.length===0)return{};const t=await this.execJson(["canvas","attention","map","--nodes",e.join(",")]);return r(t)&&r(t.counts)?t.counts:{}}async ticketCountsForView(e){const t=await this.execJson(["canvas","attention","map","--view",e]);return r(t)&&r(t.counts)?t.counts:{}}async hasActiveLiveSubscription(e){const t=(await this.subscriptionsOf(e)).filter(o=>o.active);for(const o of t){const n=await this.getRow(o.node_id);if(n&&(n.status==="active"||n.status==="idle"))return!0}return!1}}export{v as RemoteCanvasSource};
1
+ var y=Object.defineProperty;var u=(s,e)=>y(s,"name",{value:e,configurable:!0});import{createRemoteExec as h}from"./remote-transport.js";const d=1e3;function r(s){return typeof s=="object"&&s!==null&&!Array.isArray(s)}u(r,"isPlainObject");function w(s){if(!r(s))return!1;const e=s.node;if(!r(e)||typeof e.node_id!="string"||!Array.isArray(s.reports)||!Array.isArray(s.managers))return!1;const t=s.artifacts;if(!r(t)||typeof t.report!="number"||typeof t.doc!="number"||typeof t.roadmap!="number"||typeof s.pending_crons!="number")return!1;const o=s.paths;if(!r(o)||typeof o.context_dir!="string"||"session_file"in o&&o.session_file!==null&&typeof o.session_file!="string"||typeof s.follow_up!="string")return!1;const n=s.fault;if(n!=null){if(!r(n)||typeof n.link!="string"||typeof n.kind!="string"||typeof n.since!="string")return!1;const i=n.retry;if(!r(i)||typeof i.disposition!="string")return!1}return!0}u(w,"isValidShowResponse");function p(s){return r(s)&&typeof s.node_id=="string"&&typeof s.name=="string"&&typeof s.kind=="string"&&typeof s.status=="string"&&typeof s.active=="boolean"}u(p,"isValidNeighborEntry");function g(s){return r(s)&&typeof s.from_id=="string"&&typeof s.to_id=="string"&&typeof s.active=="boolean"&&typeof s.created=="string"}u(g,"isValidRosterEdge");function _(s){return r(s)?typeof s.node_id=="string"&&typeof s.name=="string"&&typeof s.kind=="string"&&typeof s.mode=="string"&&typeof s.lifecycle=="string"&&typeof s.status=="string"&&typeof s.cwd=="string"&&typeof s.created=="string":!1}u(_,"isValidNodeRow");class v{static{u(this,"RemoteCanvasSource")}previewEndpoint;relayToken;exec;nodeCache=new Map;rosterCache=new Map;topologyCache=null;viewCache=new Map;constructor(e,t){this.previewEndpoint=e,this.relayToken=t,this.exec=h(e,t)}async execJson(e){let t;try{t=await this.exec("crtr",["--json",...e])}catch{return null}if(!t.ok||t.exitCode!==void 0&&t.exitCode!==0)return null;try{return JSON.parse(t.stdout.trim())}catch{return null}}async fetchShow(e){const t=this.nodeCache.get(e);if(t&&Date.now()-t.at<d)return t.value;const o=await this.execJson(["node","inspect","show",e]),n=w(o)?{...o,fault:o.fault??null,reports:o.reports.filter(p),managers:o.managers.filter(p)}:null;return this.nodeCache.set(e,{value:n,at:Date.now()}),n}topology(){if(this.topologyCache!==null&&Date.now()-this.topologyCache.at<d)return this.topologyCache.value;const e=this.loadTopology();return this.topologyCache={value:e,at:Date.now()},e}async loadTopology(){const e=await this.exec("crtr",["--json","canvas","roster"]);if(!e.ok||e.exitCode!==void 0&&e.exitCode!==0)throw new Error("remote canvas topology is unavailable");let t;try{t=JSON.parse(e.stdout.trim())}catch{throw new Error("remote canvas topology is invalid")}if(!r(t)||!Array.isArray(t.edges)||!t.edges.every(g))throw new Error("remote canvas topology is invalid");const o=t.edges,n={subscriptions:new Map,subscribers:new Map};for(const i of o){const a={node_id:i.to_id,active:i.active,created:i.created},c={node_id:i.from_id,active:i.active,created:i.created},l=n.subscriptions.get(i.from_id);l===void 0?n.subscriptions.set(i.from_id,[a]):l.push(a);const f=n.subscribers.get(i.to_id);f===void 0?n.subscribers.set(i.to_id,[c]):f.push(c)}return n}async rawShow(e){return this.fetchShow(e)}async getNode(e){return(await this.fetchShow(e))?.node??null}async getRow(e){const t=(await this.fetchShow(e))?.node;return t?{node_id:t.node_id,name:t.name,kind:t.kind,mode:t.mode,lifecycle:t.lifecycle,status:t.status,cwd:t.cwd,host_kind:t.host_kind??null,profile_id:t.profile_id??null,scopes:t.scopes??null,isolated:t.isolated===!0,parent:t.parent??null,created:t.created,intent:t.intent??null,frozen_at:t.frozen_at??null,terminal_reason:t.terminal_reason??null,pi_pid:t.pi_pid??null,pi_pid_identity:t.pi_pid_identity??null,window:t.window??null,tmux_session:t.tmux_session??null,pane:t.pane??null,fault:t.fault??null,streaming:t.streaming===!0}:null}async listNodes(e){const t=JSON.stringify(e?.status??null),o=this.rosterCache.get(t);if(o&&Date.now()-o.at<d)return o.value;const n=["node","inspect","list"];e?.status&&n.push("--status",(Array.isArray(e.status)?e.status:[e.status]).join(","));const i=await this.execJson(n),c=(r(i)&&Array.isArray(i.nodes)?i.nodes:[]).filter(_).map(l=>({...l,isolated:l.isolated===!0}));return this.rosterCache.set(t,{value:c,at:Date.now()}),c}async subscriptionsOf(e){return(await this.topology()).subscriptions.get(e)??[]}async subscribersOf(e){return(await this.topology()).subscribers.get(e)??[]}async view(e){const t=this.viewCache.get(e);if(t&&Date.now()-t.at<d)return t.value;const o=new Set([e]),n=[],i=(await this.subscriptionsOf(e)).map(a=>a.node_id);for(;i.length>0;){const a=i.shift();if(!o.has(a)){o.add(a),n.push(a);for(const c of await this.subscriptionsOf(a))o.has(c.node_id)||i.push(c.node_id)}}return this.viewCache.set(e,{value:n,at:Date.now()}),n}async ticketCountsForNodes(e){if(e.length===0)return{};const t=await this.execJson(["canvas","attention","map","--nodes",e.join(",")]);return r(t)&&r(t.counts)?t.counts:{}}async ticketCountsForView(e){const t=await this.execJson(["canvas","attention","map","--view",e]);return r(t)&&r(t.counts)?t.counts:{}}async hasActiveLiveSubscription(e){const t=(await this.subscriptionsOf(e)).filter(o=>o.active);for(const o of t){const n=await this.getRow(o.node_id);if(n&&(n.status==="active"||n.status==="idle"))return!0}return!1}}export{v as RemoteCanvasSource};
@@ -104,7 +104,7 @@ export interface DashboardRow {
104
104
  transcriptLoaded?: boolean;
105
105
  /** The source `NodeRow` this row was built from (dashboardRowFromNodeRow only).
106
106
  * Carries every column listNodes already fetched — the runtime fields
107
- * (host_kind/parent/waiting_for/final_report/finalized_at/pi_pid) plus the
107
+ * (host_kind/parent/final_report/finalized_at/pi_pid) plus the
108
108
  * mirrored identity fields (description/cycles) — so the snapshot handler
109
109
  * reads them here instead of opening meta.json.
110
110
  * Undefined for rows built by other paths (dashboardRowsFromSource). */
@@ -283,9 +283,6 @@ export interface NodeRuntime {
283
283
  * Optional on the hydrated view (a fresh construction omits it); the row
284
284
  * column is always present, defaulting null. */
285
285
  intent?: ExitIntent;
286
- /** Controller whose next inbox delivery permits this terminal node to sleep.
287
- * Mutable durable runtime state: ancestry never implies a wait. */
288
- waiting_for?: string | null;
289
286
  frozen_at?: string | null;
290
287
  respawn_failures?: number;
291
288
  respawn_not_before?: string | null;
@@ -394,7 +391,6 @@ export interface NodeRow {
394
391
  created: string;
395
392
  /** Authoritative runtime columns (see NodeRuntime). */
396
393
  intent: ExitIntent;
397
- waiting_for: string | null;
398
394
  frozen_at?: string | null;
399
395
  respawn_failures?: number;
400
396
  respawn_not_before?: string | null;
@@ -1,3 +1,4 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
1
2
  export interface ControllerWait {
2
3
  waitId: string;
3
4
  nodeId: string;
@@ -10,9 +11,29 @@ export interface ControllerWait {
10
11
  export declare function armControllerWait(nodeId: string, controllerId: string, now?: number): ControllerWait;
11
12
  export declare function readControllerWait(nodeId: string): ControllerWait | null;
12
13
  /** Clear only the wait that was observed for this sender; a rearm wins a race. */
13
- export declare function settleMatchingControllerWait(nodeId: string, senderId: string | null, now: number, db?: import("node:sqlite").DatabaseSync): boolean;
14
+ export declare function settleMatchingControllerWait(nodeId: string, senderId: string | null, now: number, db?: DatabaseSync): boolean;
14
15
  /** Clear a matching wait only if its controller is no longer active or idle. */
15
- export declare function settleDeadControllerWait(nodeId: string, controllerId: string, now: number, db?: import("node:sqlite").DatabaseSync): boolean;
16
+ export declare function settleDeadControllerWait(nodeId: string, controllerId: string, now: number, db?: DatabaseSync): boolean;
16
17
  /** Live rows drive controller-death reconciliation without relying on the retired node columns. */
17
18
  export declare function liveControllerWaits(): ControllerWait[];
19
+ /** A node's live wait on a watched object, ended by the event its kind declares `endsWait` (`waits`, kind `object`). */
20
+ export interface ObjectWait {
21
+ waitId: string;
22
+ nodeId: string;
23
+ objectId: string;
24
+ createdAt: number;
25
+ }
26
+ /** Arm a node's wait on an object, inside the caller's write transaction; an existing live wait on the same object stands. */
27
+ export declare function armObjectWait(db: DatabaseSync, nodeId: string, objectId: string, now?: number): void;
28
+ /** Settle every live wait on `objectId` (its ending event was published), or only `nodeId`'s (it stopped watching). */
29
+ export declare function settleObjectWaits(db: DatabaseSync, objectId: string, nodeId?: string | null, now?: number): number;
30
+ /** A node that ended waits on nothing: settle all of its live object waits. */
31
+ export declare function settleNodeObjectWaits(db: DatabaseSync, nodeId: string, now?: number): number;
32
+ /** Every node's live object waits with the watched object's kind and name, for the roster. A deleted object
33
+ * settles its waits on publish, so none should dangle; a wait whose object row is gone is skipped rather than shown nameless. */
34
+ export declare function liveObjectWaitsNamed(db?: DatabaseSync): Array<ObjectWait & {
35
+ objectType: string;
36
+ objectName: string;
37
+ }>;
38
+ export declare function hasLiveObjectWait(nodeId: string): boolean;
18
39
  export declare function hasLiveControllerWait(nodeId: string): boolean;
@@ -1,6 +1,11 @@
1
- var _=Object.defineProperty;var r=(t,e)=>_(t,"name",{value:e,configurable:!0});import{prepared as a}from"../sqlite-statements.js";import{randomBytes as E}from"node:crypto";import{openDb as s,withCanvasWrite as u}from"./db.js";function l(t){return{waitId:t.wait_id,nodeId:t.node_id,controllerId:t.target_id,generation:t.generation,createdAt:t.created_at,satisfiedAt:t.satisfied_at}}r(l,"waitFrom");function w(){let t;do t=E(16).toString("hex");while(t==="00000000000000000000000000000000");return t}r(w,"newWaitId");function L(t,e,o=Date.now()){return u(n=>{a(n,"UPDATE waits SET satisfied_at = ? WHERE node_id = ? AND satisfied_at IS NULL").run(o,t);const i=Number(a(n,"SELECT COALESCE(MAX(generation), 0) AS generation FROM waits WHERE node_id = ?").get(t).generation)+1,d={wait_id:w(),node_id:t,kind:"controller",target_id:e,generation:i,created_at:o,satisfied_at:null};return a(n,`INSERT INTO waits (wait_id, node_id, kind, target_id, generation, created_at, satisfied_at)
2
- VALUES (?, ?, 'controller', ?, ?, ?, NULL)`).run(d.wait_id,d.node_id,d.target_id,d.generation,d.created_at),l(d)})}r(L,"armControllerWait");function A(t){const e=a(s(),"SELECT * FROM waits WHERE node_id = ? AND kind = 'controller' AND satisfied_at IS NULL").get(t);return e===void 0?null:l(e)}r(A,"readControllerWait");function S(t,e,o,n=s()){if(e===null)return!1;const i=a(n,"SELECT wait_id, generation FROM waits WHERE node_id = ? AND kind = 'controller' AND target_id = ? AND satisfied_at IS NULL").get(t,e);return i===void 0?!1:Number(a(n,"UPDATE waits SET satisfied_at = ? WHERE wait_id = ? AND generation = ? AND satisfied_at IS NULL").run(o,i.wait_id,i.generation).changes)===1}r(S,"settleMatchingControllerWait");function D(t,e,o,n=s()){const i=a(n,`SELECT w.wait_id, w.generation, target.status AS controller_status
1
+ var c=Object.defineProperty;var r=(t,e)=>c(t,"name",{value:e,configurable:!0});import{prepared as i}from"../sqlite-statements.js";import{randomBytes as l}from"node:crypto";import{openDb as o,withCanvasWrite as N}from"./db.js";function _(t){if(t.kind!=="controller")throw new Error(`wait ${t.wait_id} is not a controller wait`);return{waitId:t.wait_id,nodeId:t.node_id,controllerId:t.target_id,generation:t.generation,createdAt:t.created_at,satisfiedAt:t.satisfied_at}}r(_,"waitFrom");function E(){let t;do t=l(16).toString("hex");while(t==="00000000000000000000000000000000");return t}r(E,"newWaitId");function g(t,e,a=Date.now()){return N(n=>{i(n,"UPDATE waits SET satisfied_at = ? WHERE node_id = ? AND kind = 'controller' AND satisfied_at IS NULL").run(a,t);const d=Number(i(n,"SELECT COALESCE(MAX(generation), 0) AS generation FROM waits WHERE node_id = ? AND kind = 'controller'").get(t).generation)+1,s={wait_id:E(),node_id:t,kind:"controller",target_id:e,generation:d,created_at:a,satisfied_at:null};return i(n,`INSERT INTO waits (wait_id, node_id, kind, target_id, generation, created_at, satisfied_at)
2
+ VALUES (?, ?, 'controller', ?, ?, ?, NULL)`).run(s.wait_id,s.node_id,s.target_id,s.generation,s.created_at),_(s)})}r(g,"armControllerWait");function A(t){const e=i(o(),"SELECT * FROM waits WHERE node_id = ? AND kind = 'controller' AND satisfied_at IS NULL").get(t);return e===void 0?null:_(e)}r(A,"readControllerWait");function D(t,e,a,n=o()){if(e===null)return!1;const d=i(n,"SELECT wait_id, generation FROM waits WHERE node_id = ? AND kind = 'controller' AND target_id = ? AND satisfied_at IS NULL").get(t,e);return d===void 0?!1:Number(i(n,"UPDATE waits SET satisfied_at = ? WHERE wait_id = ? AND generation = ? AND satisfied_at IS NULL").run(a,d.wait_id,d.generation).changes)===1}r(D,"settleMatchingControllerWait");function S(t,e,a,n=o()){const d=i(n,`SELECT w.wait_id, w.generation, target.status AS controller_status
3
3
  FROM waits w LEFT JOIN nodes target ON target.node_id = w.target_id
4
- WHERE w.node_id = ? AND w.kind = 'controller' AND w.target_id = ? AND w.satisfied_at IS NULL`).get(t,e);return i===void 0||i.controller_status==="active"||i.controller_status==="idle"?!1:Number(a(n,"UPDATE waits SET satisfied_at = ? WHERE wait_id = ? AND generation = ? AND satisfied_at IS NULL").run(o,i.wait_id,i.generation).changes)===1}r(D,"settleDeadControllerWait");function W(){return a(s(),"SELECT * FROM waits WHERE kind = 'controller' AND satisfied_at IS NULL").all().map(l)}r(W,"liveControllerWaits");function I(t){return a(s(),`SELECT 1 FROM waits w JOIN nodes target ON target.node_id = w.target_id
4
+ WHERE w.node_id = ? AND w.kind = 'controller' AND w.target_id = ? AND w.satisfied_at IS NULL`).get(t,e);return d===void 0||d.controller_status==="active"||d.controller_status==="idle"?!1:Number(i(n,"UPDATE waits SET satisfied_at = ? WHERE wait_id = ? AND generation = ? AND satisfied_at IS NULL").run(a,d.wait_id,d.generation).changes)===1}r(S,"settleDeadControllerWait");function T(){return i(o(),"SELECT * FROM waits WHERE kind = 'controller' AND satisfied_at IS NULL").all().map(_)}r(T,"liveControllerWaits");function R(t,e,a,n=Date.now()){i(t,`INSERT INTO waits (wait_id, node_id, kind, target_id, generation, created_at, satisfied_at)
5
+ SELECT ?, ?, 'object', ?, 1, ?, NULL
6
+ WHERE NOT EXISTS (SELECT 1 FROM waits WHERE node_id = ? AND kind = 'object' AND target_id = ? AND satisfied_at IS NULL)`).run(E(),e,a,n,e,a)}r(R,"armObjectWait");function W(t,e,a=null,n=Date.now()){return Number(a===null?i(t,"UPDATE waits SET satisfied_at = ? WHERE kind = 'object' AND target_id = ? AND satisfied_at IS NULL").run(n,e).changes:i(t,"UPDATE waits SET satisfied_at = ? WHERE kind = 'object' AND target_id = ? AND node_id = ? AND satisfied_at IS NULL").run(n,e,a).changes)}r(W,"settleObjectWaits");function I(t,e,a=Date.now()){return Number(i(t,"UPDATE waits SET satisfied_at = ? WHERE kind = 'object' AND node_id = ? AND satisfied_at IS NULL").run(a,e).changes)}r(I,"settleNodeObjectWaits");function O(t=o()){return i(t,`SELECT w.wait_id, w.node_id, w.target_id, w.created_at, o.type AS object_type, o.name AS object_name
7
+ FROM waits w JOIN objects o ON o.id = w.target_id
8
+ WHERE w.kind = 'object' AND w.satisfied_at IS NULL AND o.deleted_at IS NULL
9
+ ORDER BY w.node_id, w.created_at, w.rowid`).all().map(e=>({waitId:e.wait_id,nodeId:e.node_id,objectId:e.target_id,createdAt:e.created_at,objectType:e.object_type,objectName:e.object_name}))}r(O,"liveObjectWaitsNamed");function U(t){return i(o(),"SELECT 1 FROM waits WHERE node_id = ? AND kind = 'object' AND satisfied_at IS NULL LIMIT 1").get(t)!==void 0}r(U,"hasLiveObjectWait");function m(t){return i(o(),`SELECT 1 FROM waits w JOIN nodes target ON target.node_id = w.target_id
5
10
  WHERE w.node_id = ? AND w.kind = 'controller' AND w.satisfied_at IS NULL
6
- AND target.status IN ('active', 'idle') LIMIT 1`).get(t)!==void 0}r(I,"hasLiveControllerWait");export{L as armControllerWait,I as hasLiveControllerWait,W as liveControllerWaits,A as readControllerWait,D as settleDeadControllerWait,S as settleMatchingControllerWait};
11
+ AND target.status IN ('active', 'idle') LIMIT 1`).get(t)!==void 0}r(m,"hasLiveControllerWait");export{g as armControllerWait,R as armObjectWait,m as hasLiveControllerWait,U as hasLiveObjectWait,T as liveControllerWaits,O as liveObjectWaitsNamed,A as readControllerWait,S as settleDeadControllerWait,D as settleMatchingControllerWait,I as settleNodeObjectWaits,W as settleObjectWaits};
@@ -12,6 +12,7 @@ export type CustomReceiver = 'queue' | {
12
12
  export type CustomEvents = Record<string, {
13
13
  level: Level;
14
14
  supersedes?: boolean;
15
+ ends_wait?: boolean;
15
16
  }>;
16
17
  export interface QueueLine {
17
18
  delivery_id: string;
@@ -1,10 +1,10 @@
1
- var x=Object.defineProperty;var m=(e,t)=>x(e,"name",{value:t,configurable:!0});import{prepared as a}from"../sqlite-statements.js";import{createHash as J,randomBytes as P,timingSafeEqual as q}from"node:crypto";import{CrtrError as j,usage as p}from"../errors.js";import{ExitCode as O}from"../../types.js";import{createObject as F,liveDocumentNamed as M}from"./objects.js";import{invalidateCustomKind as D}from"./kinds/registry.js";import{customReachable as k,mayActAs as L}from"./access.js";import{publish as N,HOP_LIMIT as B,HopLimitError as K}from"./events.js";import{checkoutsForCwd as T}from"./repo-sync/repos.js";import{onQueueSignal as V,signalQueue as R}from"./receivers.js";import{receiverEndpoint as G,isPublicReceiverAddress as Q}from"./receiver-url.js";import{isIP as Y}from"node:net";import{KIND as H,LOCAL as I,MAX_KIND_EVENTS as z,RESERVED_KIND_ROOTS as X,eventCatalogReasons as Z}from"../plugin-objects-manifest.js";const ee=["public","friends","private"];function w(e,t){throw new j(e,t,O.GENERAL)}m(w,"conflict");function A(e){return J("sha256").update(e).digest("hex")}m(A,"hash");function W(){return`crtr_pub_${P(32).toString("base64url")}`}m(W,"issueToken");function te(e){if(e==null)return null;if(e==="queue")return JSON.stringify(e);if(typeof e!="object"||Array.isArray(e))throw p("invalid custom receiver; expected queue, an https url, or argv and absolute cwd");if("url"in e&&typeof e.url=="string")try{const s=G(e.url).hostname.replace(/^\[|\]$/g,"");if(Y(s)&&!Q(s))throw new Error("non-public IP address");return JSON.stringify({url:e.url})}catch(t){throw p(`invalid custom receiver: ${String(t)}`)}if("argv"in e&&Array.isArray(e.argv)&&e.argv.length>0&&e.argv.every(t=>typeof t=="string")&&typeof e.cwd=="string"&&e.cwd.startsWith("/"))return JSON.stringify(e);throw p("invalid custom receiver; url must be https and argv requires a command and absolute cwd")}m(te,"receiverJson");function ge(e,t,s){const{kind:n,name:i,owner:o,description:d,events:l,receiver:c,rotateToken:h,privacy:f}=s;if(f!==void 0&&!ee.includes(f))throw p(`invalid privacy ${JSON.stringify(f)}`);if(L(e,t,o)||w("forbidden",`cannot act as ${o.id}`),!H.test(n)||X.includes(n.split(".")[0]))throw p(`invalid or reserved kind ${n}`);if(!i.trim()||i!==i.trim()||i.includes("\0"))throw p(`invalid object name ${JSON.stringify(i)}`);const E=te(c);if(c!=="queue"&&c!==null&&c!==void 0&&"argv"in c&&t.class!=="person"&&t.class!=="node"&&w("argv_forbidden","argv receiver requires a person or node principal"),l===void 0){const u=a(e,"SELECT source FROM object_kinds WHERE name = ?").get(n);if(u?.source!=="manifest"&&u?.source!=="peer")throw p("events is required unless the kind is declared by an installed plugin")}else if(Object.keys(l).length>z||Object.entries(l).some(([u,g])=>!I.test(u)||!Number.isInteger(g?.level)||g.level<1||g.level>10||g.supersedes!==void 0&&typeof g.supersedes!="boolean"))throw p("invalid custom event catalog");const r=M(e,o.rowid,i);if(r&&!k(e,t,r)&&w("forbidden",`cannot act as ${o.id}`),r&&t.class!=="person"&&t.class!=="node"){const u=a(e,"SELECT receiver_json FROM custom_objects WHERE object = ?").get(r.rowid)?.receiver_json;u&&typeof JSON.parse(u)=="object"&&"argv"in JSON.parse(u)&&w("argv_forbidden","argv receiver requires a person or node principal")}r&&r.type!==n&&w("name_taken",`${o.name}/${i} is already held by ${r.id}`);const v=l===void 0?null:ne(e,{name:n,owner:o,description:d,events:l,read:null,source:"register"});if(!r){const u=F(e,{type:n,kind:null,owner:o.rowid,privacy:f??"public",name:i,preview:""}),g=W();return a(e,`INSERT INTO custom_objects (object, description, receiver_json, token_hash)
1
+ var x=Object.defineProperty;var m=(e,t)=>x(e,"name",{value:t,configurable:!0});import{prepared as a}from"../sqlite-statements.js";import{createHash as J,randomBytes as P,timingSafeEqual as q}from"node:crypto";import{CrtrError as j,usage as p}from"../errors.js";import{ExitCode as O}from"../../types.js";import{createObject as F,liveDocumentNamed as M}from"./objects.js";import{invalidateCustomKind as D}from"./kinds/registry.js";import{customReachable as k,mayActAs as L}from"./access.js";import{publish as N,HOP_LIMIT as B,HopLimitError as K}from"./events.js";import{checkoutsForCwd as T}from"./repo-sync/repos.js";import{onQueueSignal as V,signalQueue as R}from"./receivers.js";import{receiverEndpoint as G,isPublicReceiverAddress as Q}from"./receiver-url.js";import{isIP as Y}from"node:net";import{KIND as H,LOCAL as I,MAX_KIND_EVENTS as z,RESERVED_KIND_ROOTS as X,eventCatalogReasons as Z}from"../plugin-objects-manifest.js";const ee=["public","friends","private"];function w(e,t){throw new j(e,t,O.GENERAL)}m(w,"conflict");function A(e){return J("sha256").update(e).digest("hex")}m(A,"hash");function W(){return`crtr_pub_${P(32).toString("base64url")}`}m(W,"issueToken");function te(e){if(e==null)return null;if(e==="queue")return JSON.stringify(e);if(typeof e!="object"||Array.isArray(e))throw p("invalid custom receiver; expected queue, an https url, or argv and absolute cwd");if("url"in e&&typeof e.url=="string")try{const s=G(e.url).hostname.replace(/^\[|\]$/g,"");if(Y(s)&&!Q(s))throw new Error("non-public IP address");return JSON.stringify({url:e.url})}catch(t){throw p(`invalid custom receiver: ${String(t)}`)}if("argv"in e&&Array.isArray(e.argv)&&e.argv.length>0&&e.argv.every(t=>typeof t=="string")&&typeof e.cwd=="string"&&e.cwd.startsWith("/"))return JSON.stringify(e);throw p("invalid custom receiver; url must be https and argv requires a command and absolute cwd")}m(te,"receiverJson");function ge(e,t,s){const{kind:n,name:i,owner:o,description:d,events:l,receiver:c,rotateToken:h,privacy:f}=s;if(f!==void 0&&!ee.includes(f))throw p(`invalid privacy ${JSON.stringify(f)}`);if(L(e,t,o)||w("forbidden",`cannot act as ${o.id}`),!H.test(n)||X.includes(n.split(".")[0]))throw p(`invalid or reserved kind ${n}`);if(!i.trim()||i!==i.trim()||i.includes("\0"))throw p(`invalid object name ${JSON.stringify(i)}`);const E=te(c);if(c!=="queue"&&c!==null&&c!==void 0&&"argv"in c&&t.class!=="person"&&t.class!=="node"&&w("argv_forbidden","argv receiver requires a person or node principal"),l===void 0){const u=a(e,"SELECT source FROM object_kinds WHERE name = ?").get(n);if(u?.source!=="manifest"&&u?.source!=="peer")throw p("events is required unless the kind is declared by an installed plugin")}else if(Object.keys(l).length>z||Object.entries(l).some(([u,g])=>!I.test(u)||!Number.isInteger(g?.level)||g.level<1||g.level>10||g.supersedes!==void 0&&typeof g.supersedes!="boolean"||g.ends_wait!==void 0&&typeof g.ends_wait!="boolean"))throw p("invalid custom event catalog");const r=M(e,o.rowid,i);if(r&&!k(e,t,r)&&w("forbidden",`cannot act as ${o.id}`),r&&t.class!=="person"&&t.class!=="node"){const u=a(e,"SELECT receiver_json FROM custom_objects WHERE object = ?").get(r.rowid)?.receiver_json;u&&typeof JSON.parse(u)=="object"&&"argv"in JSON.parse(u)&&w("argv_forbidden","argv receiver requires a person or node principal")}r&&r.type!==n&&w("name_taken",`${o.name}/${i} is already held by ${r.id}`);const v=l===void 0?null:ne(e,{name:n,owner:o,description:d,events:l,read:null,source:"register"});if(!r){const u=F(e,{type:n,kind:null,owner:o.rowid,privacy:f??"public",name:i,preview:""}),g=W();return a(e,`INSERT INTO custom_objects (object, description, receiver_json, token_hash)
2
2
  VALUES (?, ?, ?, ?)`).run(u.rowid,d,E,A(g)),t.class==="node"&&a(e,"UPDATE custom_objects SET registered_by = ? WHERE object = ?").run(t.node.id,u.rowid),E!==null&&a(e,"UPDATE objects SET reception = 2 WHERE rowid = ?").run(u.rowid),N(e,{event:"created",object:u,actor:t.class==="object"?t.object:t.class==="person"?t.person:t.class==="app"?t.app:t.node,attrs:{}},$=>T(e,$)),{object:u,token:g,created:!0,changed:[]}}const y=a(e,"SELECT description, receiver_json FROM custom_objects WHERE object = ?").get(r.rowid);y||w("name_taken",`${o.name}/${i} is not a custom object`);const _=[];if(y.description!==d&&_.push("description"),y.receiver_json!==E&&_.push("receiver"),v?.changed.includes("events")&&_.push("events"),v?.changed.includes("description")&&!_.includes("description")&&_.push("description"),f!==void 0&&r.privacy!==f&&_.push("privacy"),h&&_.push("token"),_.length===0)return{object:r,created:!1,changed:_};const S=h?W():void 0,b=_.includes("privacy")?{...r,privacy:f}:r;if(b!==r&&a(e,"UPDATE objects SET privacy = ? WHERE rowid = ?").run(f,r.rowid),a(e,"UPDATE custom_objects SET description = ?, receiver_json = ?, token_hash = COALESCE(?, token_hash) WHERE object = ?").run(d,E,S===void 0?null:A(S),r.rowid),a(e,"UPDATE objects SET reception = ? WHERE rowid = ?").run(E===null?0:2,r.rowid),y.receiver_json!==E){const u=Date.now();if(E===null){const g=a(e,`UPDATE deliveries SET canceled_at = ?, updated_at = ?
3
3
  WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(u,u,r.id);a(e,"UPDATE custom_objects SET dropped = dropped + ? WHERE object = ?").run(g.changes,r.rowid)}else a(e,`UPDATE deliveries SET next_attempt_at = ?, updated_at = ?
4
4
  WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL`).run(E==='"queue"'?null:u,u,r.id);R(r.id)}return h&&R(r.id),N(e,{event:"configured",object:b,actor:t.class==="object"?t.object:t.class==="person"?t.person:t.class==="app"?t.app:t.node,attrs:{changed:_.join(",")}},u=>T(e,u)),{object:b,...S===void 0?{}:{token:S},created:!1,changed:_}}m(ge,"registerCustomObject");function ne(e,t){const{name:s,owner:n,description:i,events:o,read:d,source:l}=t;if(!H.test(s))throw p(`invalid kind ${s}`);if(Z(o,s).length>0)throw p("invalid custom event catalog");const c=a(e,"SELECT owner, description, events_json, read_json, source, declared FROM object_kinds WHERE name = ?").get(s),h=U(o),f=l==="manifest"&&d!==null?JSON.stringify({command:d.command}):null,E=new Date().toISOString();if(!c)return a(e,`INSERT INTO object_kinds (name, owner, description, events_json, created_at, updated_at, read_json, source, declared)
5
5
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1)`).run(s,n.rowid,i,h,E,E,f,l),D(e,s),{outcome:"inserted",changed:[]};const r=c.owner===n.rowid;if(l==="peer"){if(c.source!=="peer")return{outcome:"skipped",changed:[]}}else if(c.source!=="peer"&&(r||w("kind_owned",`${s} belongs to another owner`),l==="register"&&c.source==="manifest"))return{outcome:"skipped",changed:[]};const v=[];return U(JSON.parse(c.events_json))!==h&&v.push("events"),c.description!==i&&v.push("description"),(c.read_json??null)!==f&&v.push("read"),v.length===0&&r&&c.source===l&&c.declared===1?{outcome:"unchanged",changed:v}:(a(e,"UPDATE object_kinds SET owner = ?, description = ?, events_json = ?, read_json = ?, source = ?, declared = 1, updated_at = ? WHERE name = ?").run(n.rowid,i,h,f,l,E,s),D(e,s),{outcome:"updated",changed:v})}m(ne,"upsertKind");function he(e,t,s,n){const i=a(e,"SELECT events_json FROM object_kinds WHERE name = ?").get(t.type);if(!i||t.deleted_at!==null)throw p(`${t.id} is not a live custom object`);const o=JSON.parse(i.events_json);if(!I.test(n.event)||!Object.hasOwn(o,n.event))throw new j("unknown_event",`${t.type} declares ${Object.keys(o).join(", ")}, not ${n.event}`,O.USAGE,{declared:Object.keys(o),event:n.event});if(typeof n.summary!="string")throw p("summary must be text");const d=n.state===void 0?null:JSON.stringify(n.state);if(n.state!==void 0&&d===void 0)throw p("state must be JSON-serializable");if(Buffer.byteLength(JSON.stringify({summary:n.summary,...n.state===void 0?{}:{state:n.state}}))>64*1024)throw new j("body_too_large","custom event summary and state exceed 64 KiB",O.USAGE);if(d!==null&&Buffer.byteLength(d)>64*1024)throw p("state exceeds 64 KiB");const l=n.cause??null,c=l===null?null:a(e,`SELECT depth FROM messages WHERE message_id = ?
6
6
  UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(l,l);if(l!==null&&c===void 0)throw new j("unknown_cause",`no message ${l}`,O.USAGE);if(c&&c.depth>=B)throw new K(c.depth+1);const h=new Date().toISOString();return a(e,"UPDATE custom_objects SET summary = ?, state_json = ?, published_at = ? WHERE object = ?").run(n.summary,d,h,t.rowid),a(e,"UPDATE objects SET preview = ? WHERE rowid = ?").run(n.summary.split(`
7
- `)[0],t.rowid),N(e,{event:`${t.type}.${n.event}`,object:t,actor:s,attrs:{},messageBody:{summary:n.summary,...n.state===void 0?{}:{state:n.state}},...n.cause===void 0?{}:{cause:n.cause}},f=>T(e,f))}m(he,"publishCustom");function ve(e,t,s){if(t.deleted_at!==null)return;N(e,{event:"deleted",object:t,actor:s,attrs:{}},i=>T(e,i));const n=new Date().toISOString();a(e,"UPDATE objects SET deleted_at = ?, reception = 0 WHERE rowid = ?").run(n,t.rowid),a(e,"UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL").run(Date.now(),Date.now(),t.id),R(t.id)}m(ve,"unregisterCustomObject");function U(e){return JSON.stringify(Object.fromEntries(Object.entries(e).sort(([t],[s])=>t.localeCompare(s)).map(([t,s])=>[t,{level:s.level,...s.supersedes===void 0?{}:{supersedes:s.supersedes}}])))}m(U,"canonicalEvents");function we(e,t,s=0){return C(e,t.id,s,"pending")}m(we,"pendingFor");function ye(e,t){const s=a(e,"SELECT destination_id FROM deliveries WHERE delivery_id = ?").get(t);return s?C(e,s.destination_id,-1,"handed_off").find(n=>n.delivery_id===t)??null:null}m(ye,"pushLineFor");function C(e,t,s,n){return a(e,`SELECT d.delivery_id, m.message_id, m.seq, m.event, m.level, m.object_id, m.actor_id, m.body_json, m.body_ref, m.depth, m.created_at,
7
+ `)[0],t.rowid),N(e,{event:`${t.type}.${n.event}`,object:t,actor:s,attrs:{},messageBody:{summary:n.summary,...n.state===void 0?{}:{state:n.state}},...n.cause===void 0?{}:{cause:n.cause}},f=>T(e,f))}m(he,"publishCustom");function ve(e,t,s){if(t.deleted_at!==null)return;N(e,{event:"deleted",object:t,actor:s,attrs:{}},i=>T(e,i));const n=new Date().toISOString();a(e,"UPDATE objects SET deleted_at = ?, reception = 0 WHERE rowid = ?").run(n,t.rowid),a(e,"UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE destination_id = ? AND state = 'pending' AND canceled_at IS NULL").run(Date.now(),Date.now(),t.id),R(t.id)}m(ve,"unregisterCustomObject");function U(e){return JSON.stringify(Object.fromEntries(Object.entries(e).sort(([t],[s])=>t.localeCompare(s)).map(([t,s])=>[t,{level:s.level,...s.supersedes===void 0?{}:{supersedes:s.supersedes},...s.ends_wait===void 0?{}:{ends_wait:s.ends_wait}}])))}m(U,"canonicalEvents");function we(e,t,s=0){return C(e,t.id,s,"pending")}m(we,"pendingFor");function ye(e,t){const s=a(e,"SELECT destination_id FROM deliveries WHERE delivery_id = ?").get(t);return s?C(e,s.destination_id,-1,"handed_off").find(n=>n.delivery_id===t)??null:null}m(ye,"pushLineFor");function C(e,t,s,n){return a(e,`SELECT d.delivery_id, m.message_id, m.seq, m.event, m.level, m.object_id, m.actor_id, m.body_json, m.body_ref, m.depth, m.created_at,
8
8
  o.type, o.name, owner.id AS owner_id
9
9
  FROM deliveries d JOIN messages m ON m.message_id = d.message_id JOIN objects o ON o.id = m.object_id
10
10
  LEFT JOIN objects owner ON owner.rowid = o.owner
@@ -1,11 +1,11 @@
1
- var W=Object.defineProperty;var l=(n,e)=>W(n,"name",{value:e,configurable:!0});import{prepared as h}from"../sqlite-statements.js";import{EventEmitter as $}from"node:events";const v=new $;v.setMaxListeners(0);function pe(n,e){const o=l(s=>e(s),"listener");return v.on(n,o),()=>v.off(n,o)}l(pe,"subscribeDocumentEvents");import{evalCondition as B}from"../predicate.js";import{finalizeNodeMail as J,insertMessage as K}from"../feed/messages.js";import{linkAuthorizes as U,ownerChain as G,readCheck as q}from"./access.js";import{getByRowid as z}from"./objects.js";import{appScopesNow as X,publishLookups as Y,readerForObject as P}from"./reader.js";import{configFromJson as C}from"./watches.js";import{CrtrError as L}from"../errors.js";import{ExitCode as M}from"../../types.js";import{eventSpec as V,kindOf as D}from"./kinds/registry.js";import{machineNameOf as k}from"../peer/names.js";import{RECEIVERS as F}from"./receivers.js";import{DEFAULT_LISTEN as H}from"./types.js";function Q(n,e){if(n.level===void 0||n.level===e.level||e.levels?.includes(n.level))return n.level??e.level;const o=[e.level,...e.levels??[]].filter((s,t,i)=>i.indexOf(s)===t).sort((s,t)=>s-t);throw new L("invalid_level",`${n.object.type} ${n.event} publishes at level ${o.join(", ")}, not ${n.level}`,M.USAGE,{kind:n.object.type,event:n.event,level:n.level,allowed:o})}l(Q,"resolveLevel");function Z(n,e){const o=n.split("."),s=e.split(".");for(let t=0;t<o.length;t++){if(o[t]===">")return t===o.length-1&&s.length>t;if(t>=s.length||o[t]!=="*"&&o[t]!==s[t])return!1}return o.length===s.length}l(Z,"matchSubject");const x={"next-turn":1,"after-turn":2,steer:3,interrupt:4};function ee(n,e){let o=null;for(const s of n){let t=null;for(const i of s)i.threshold<=e&&(!t||i.threshold>t.threshold)&&(t=i);t&&(!o||x[t.delivery]>x[o])&&(o=t.delivery)}return o}l(ee,"resolveDelivery");function Ne(n,e,o){if(e.config)return e.config;const s=h(n,"SELECT listen_json FROM objects WHERE rowid = ?").get(o.rowid);return s?.listen_json?C(s.listen_json):H}l(Ne,"listenConfigFor");function te(n,e){return e.inboxKind??(n.level>=7?"urgent":"update")}l(te,"inboxKind");function ne(n,e){return e.type==="node"?e.id:h(n,`SELECT 1 FROM objects WHERE type IN ('person','app','repo','profile')
2
- AND name = ? AND rowid != ? LIMIT 1`).get(e.name,e.rowid)?`${e.type}:${e.name}`:e.name}l(ne,"ownerHandleOf");function oe(n,e,o){const s=e.object.owner===null?null:h(n,"SELECT * FROM objects WHERE rowid = ?").get(e.object.owner),t=s?.id??null,i=s?ne(n,s):null;return{event:e.event,level:e.level,object:{id:e.object.id,type:e.object.type,kind:e.object.kind,owner:t,owner_handle:i,name:e.object.name,...e.object.home_machine?{machine:k(e.object.home_machine),machine_id:e.object.home_machine}:{}},actor:e.actor?.id??null,...e.actor?.home_machine?{actor_machine:k(e.actor.home_machine),actor_machine_id:e.actor.home_machine}:{},attrs:o,...e.pointer===void 0?{}:{pointer:e.pointer}}}l(oe,"pushFields");function se(n,e,o,s){const t=oe(n,e,s),i=e.messageBody!==void 0&&o.carriesBody===!0,f=i?typeof e.messageBody=="object"&&e.messageBody!==null&&!Array.isArray(e.messageBody)?{...e.messageBody,push:t}:{body:e.messageBody,push:t}:{push:t},d=i?e.messageBody:null,a=d?.bodyRef===void 0?null:d;return{kind:te(e,o),label:a?.label??(typeof d?.label=="string"?d.label:e.object.type.includes(".")&&typeof e.messageBody?.summary=="string"?e.messageBody.summary.split(`
3
- `)[0]:`${e.event} ${e.object.type} ${e.object.name}`),...a?.bodyRef===void 0?{}:{bodyRef:a.bodyRef},...a?.disposition===void 0?{}:{disposition:a.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor===null||e.actor===void 0?null:e.actor.home_machine?`${e.actor.name} on ${k(e.actor.home_machine)}`:e.actor.name,bodyJson:f}}l(se,"messageInput");function ie(n,e,o,s,t){if(!o.supersedes)return[{attrs:e.attrs,destinations:[...s]}];const i=h(n,`SELECT d.delivery_id, d.message_id, d.state, m.body_json FROM deliveries d
1
+ var x=Object.defineProperty;var l=(n,e)=>x(n,"name",{value:e,configurable:!0});import{prepared as h}from"../sqlite-statements.js";import{EventEmitter as $}from"node:events";const S=new $;S.setMaxListeners(0);function ue(n,e){const o=l(s=>e(s),"listener");return S.on(n,o),()=>S.off(n,o)}l(ue,"subscribeDocumentEvents");import{evalCondition as B}from"../predicate.js";import{finalizeNodeMail as J,insertMessage as K}from"../feed/messages.js";import{linkAuthorizes as U,ownerChain as G,readCheck as q}from"./access.js";import{getByRowid as z}from"./objects.js";import{appScopesNow as X,publishLookups as Y,readerForObject as P}from"./reader.js";import{configFromJson as C}from"./watches.js";import{CrtrError as L}from"../errors.js";import{ExitCode as M}from"../../types.js";import{eventSpec as V,kindOf as D}from"./kinds/registry.js";import{machineNameOf as k}from"../peer/names.js";import{settleObjectWaits as Q}from"../canvas/waits.js";import{RECEIVERS as F}from"./receivers.js";import{DEFAULT_LISTEN as W}from"./types.js";function Z(n,e){if(n.level===void 0||n.level===e.level||e.levels?.includes(n.level))return n.level??e.level;const o=[e.level,...e.levels??[]].filter((s,t,i)=>i.indexOf(s)===t).sort((s,t)=>s-t);throw new L("invalid_level",`${n.object.type} ${n.event} publishes at level ${o.join(", ")}, not ${n.level}`,M.USAGE,{kind:n.object.type,event:n.event,level:n.level,allowed:o})}l(Z,"resolveLevel");function ee(n,e){const o=n.split("."),s=e.split(".");for(let t=0;t<o.length;t++){if(o[t]===">")return t===o.length-1&&s.length>t;if(t>=s.length||o[t]!=="*"&&o[t]!==s[t])return!1}return o.length===s.length}l(ee,"matchSubject");const H={"next-turn":1,"after-turn":2,steer:3,interrupt:4};function te(n,e){let o=null;for(const s of n){let t=null;for(const i of s)i.threshold<=e&&(!t||i.threshold>t.threshold)&&(t=i);t&&(!o||H[t.delivery]>H[o])&&(o=t.delivery)}return o}l(te,"resolveDelivery");function ve(n,e,o){if(e.config)return e.config;const s=h(n,"SELECT listen_json FROM objects WHERE rowid = ?").get(o.rowid);return s?.listen_json?C(s.listen_json):W}l(ve,"listenConfigFor");function ne(n,e){return e.inboxKind??(n.level>=7?"urgent":"update")}l(ne,"inboxKind");function oe(n,e){return e.type==="node"?e.id:h(n,`SELECT 1 FROM objects WHERE type IN ('person','app','repo','profile')
2
+ AND name = ? AND rowid != ? LIMIT 1`).get(e.name,e.rowid)?`${e.type}:${e.name}`:e.name}l(oe,"ownerHandleOf");function se(n,e,o){const s=e.object.owner===null?null:h(n,"SELECT * FROM objects WHERE rowid = ?").get(e.object.owner),t=s?.id??null,i=s?oe(n,s):null;return{event:e.event,level:e.level,object:{id:e.object.id,type:e.object.type,kind:e.object.kind,owner:t,owner_handle:i,name:e.object.name,...e.object.home_machine?{machine:k(e.object.home_machine),machine_id:e.object.home_machine}:{}},actor:e.actor?.id??null,...e.actor?.home_machine?{actor_machine:k(e.actor.home_machine),actor_machine_id:e.actor.home_machine}:{},attrs:o,...e.pointer===void 0?{}:{pointer:e.pointer}}}l(se,"pushFields");function ie(n,e,o,s){const t=se(n,e,s),i=e.messageBody!==void 0&&o.carriesBody===!0,f=i?typeof e.messageBody=="object"&&e.messageBody!==null&&!Array.isArray(e.messageBody)?{...e.messageBody,push:t}:{body:e.messageBody,push:t}:{push:t},d=i?e.messageBody:null,a=d?.bodyRef===void 0?null:d;return{kind:ne(e,o),label:a?.label??(typeof d?.label=="string"?d.label:e.object.type.includes(".")&&typeof e.messageBody?.summary=="string"?e.messageBody.summary.split(`
3
+ `)[0]:`${e.event} ${e.object.type} ${e.object.name}`),...a?.bodyRef===void 0?{}:{bodyRef:a.bodyRef},...a?.disposition===void 0?{}:{disposition:a.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor===null||e.actor===void 0?null:e.actor.home_machine?`${e.actor.name} on ${k(e.actor.home_machine)}`:e.actor.name,bodyJson:f}}l(ie,"messageInput");function re(n,e,o,s,t){if(!o.supersedes)return[{attrs:e.attrs,destinations:[...s]}];const i=h(n,`SELECT d.delivery_id, d.message_id, d.state, m.body_json FROM deliveries d
4
4
  JOIN messages m ON m.message_id = d.message_id
5
5
  WHERE d.destination_id = ? AND d.state IN ('pending', 'handed_off')
6
6
  AND d.canceled_at IS NULL AND m.event = ? AND m.object_id = ?
7
- ORDER BY m.seq`),f=h(n,"DELETE FROM deliveries WHERE delivery_id = ?"),d=h(n,"SELECT * FROM objects WHERE id = ?"),a=new Map,_=new Set;for(const m of s){const w=t(m),O=d.get(w),I=O===void 0?void 0:D(n,O.type),j=i.all(w,e.event,e.object.id),b=new Set;for(const c of j)c.state==="handed_off"?I?.supersedeClaimed?.(n,c.delivery_id)&&b.add(c.delivery_id):(f.run(c.delivery_id),_.add(c.message_id));const y=j.filter(c=>c.state==="pending"||b.has(c.delivery_id)).at(-1),R=y?.message_id??null,r=a.get(R)??{prior:y?.body_json?JSON.parse(y.body_json).push?.attrs??null:null,destinations:[]};r.destinations.push(m),a.set(R,r)}const N=h(n,"DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)");for(const m of _)N.run(m,m);return[...a.values()].map(m=>({attrs:o.merge?o.merge(m.prior,e):e.attrs,destinations:m.destinations}))}l(ie,"supersede");function re(n,e,o,s,t){const i=z(n,s);if(i===null||i.type!=="app")return!1;const f=h(n,"SELECT 1 FROM peer_stubs WHERE object = ?").get(e.rowid)!==void 0,d=f?null:X(n,i,t);return(f||d!==null)&&U(n,i,d,e,o)}l(re,"linkDelivers");const T=8;class ce extends L{static{l(this,"HopLimitError")}constructor(e){super("hop_limit",`event depth ${e} exceeds ${T}`,M.GENERAL,{depth:e,limit:T})}}function Oe(n,e,o=()=>[]){const s=V(n,e.object.type,e.event),t={...e,level:Q(e,s)};t.object.type==="document"&&(t.event==="edited"||t.event==="deleted")&&queueMicrotask(()=>v.emit(t.object.id,t.event));const i=e.cause??null,f=i===null?null:h(n,`SELECT depth FROM messages WHERE message_id = ?
8
- UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(i,i);if(i!==null&&f===void 0)throw new L("unknown_cause",`no message ${i}`,M.USAGE,{cause:i});const d=f===null?0:f.depth+1;if(d>T)throw new ce(d);const a=G(n,t.object),_=`w.watcher, o.id AS watcher_id, o.type AS watcher_type, o.reception, s.pattern, s.filter_json,
7
+ ORDER BY m.seq`),f=h(n,"DELETE FROM deliveries WHERE delivery_id = ?"),d=h(n,"SELECT * FROM objects WHERE id = ?"),a=new Map,_=new Set;for(const m of s){const w=t(m),O=d.get(w),I=O===void 0?void 0:D(n,O.type),j=i.all(w,e.event,e.object.id),b=new Set;for(const c of j)c.state==="handed_off"?I?.supersedeClaimed?.(n,c.delivery_id)&&b.add(c.delivery_id):(f.run(c.delivery_id),_.add(c.message_id));const y=j.filter(c=>c.state==="pending"||b.has(c.delivery_id)).at(-1),R=y?.message_id??null,r=a.get(R)??{prior:y?.body_json?JSON.parse(y.body_json).push?.attrs??null:null,destinations:[]};r.destinations.push(m),a.set(R,r)}const N=h(n,"DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)");for(const m of _)N.run(m,m);return[...a.values()].map(m=>({attrs:o.merge?o.merge(m.prior,e):e.attrs,destinations:m.destinations}))}l(re,"supersede");function ce(n,e,o,s,t){const i=z(n,s);if(i===null||i.type!=="app")return!1;const f=h(n,"SELECT 1 FROM peer_stubs WHERE object = ?").get(e.rowid)!==void 0,d=f?null:X(n,i,t);return(f||d!==null)&&U(n,i,d,e,o)}l(ce,"linkDelivers");const T=8;class de extends L{static{l(this,"HopLimitError")}constructor(e){super("hop_limit",`event depth ${e} exceeds ${T}`,M.GENERAL,{depth:e,limit:T})}}function Se(n,e,o=()=>[]){const s=V(n,e.object.type,e.event),t={...e,level:Z(e,s)};t.object.type==="document"&&(t.event==="edited"||t.event==="deleted")&&queueMicrotask(()=>S.emit(t.object.id,t.event));const i=e.cause??null,f=i===null?null:h(n,`SELECT depth FROM messages WHERE message_id = ?
8
+ UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(i,i);if(i!==null&&f===void 0)throw new L("unknown_cause",`no message ${i}`,M.USAGE,{cause:i});const d=f===null?0:f.depth+1;if(d>T)throw new de(d);(s.endsWait===!0||t.event==="deleted")&&Q(n,t.object.id);const a=G(n,t.object),_=`w.watcher, o.id AS watcher_id, o.type AS watcher_type, o.reception, s.pattern, s.filter_json,
9
9
  COALESCE(s.config_json, o.listen_json) AS config_json, w.link_app`,N=n.prepare(s.durable?`SELECT ${_} FROM watches w JOIN watch_subscriptions s ON s.watch = w.rowid
10
10
  JOIN objects o ON o.rowid = w.watcher WHERE w.object = ? AND w.watcher != ? AND o.reception >= 1`:`SELECT ${_} FROM objects o INDEXED BY objects_receiving CROSS JOIN watches w CROSS JOIN watch_subscriptions s
11
- WHERE o.reception = 2 AND w.watcher = o.rowid AND s.watch = w.rowid AND w.object = ? AND w.watcher != ?`).all(t.object.rowid,t.actor?.rowid??-1);if(N.length===0)return{delivered:0,message_id:null,depth:d};const m={event:t.event,level:t.level,id:t.object.id,type:t.object.type,kind:t.object.kind,actor:t.actor?.id??null,...t.attrs},w=new Map;for(const r of N){if(t.onlyWatchers!==void 0&&!t.onlyWatchers.includes(r.watcher_id)||!Z(r.pattern,t.event)||r.filter_json!==null&&!B(JSON.parse(r.filter_json),m))continue;const c=w.get(r.watcher)??{id:r.watcher_id,type:r.watcher_type,reception:r.reception,configs:[],linkApp:r.link_app};c.configs.push(r.config_json?C(r.config_json):H),w.set(r.watcher,c)}const O=(a.length?a[a.length-1]:t.object).type==="repo",I=l(()=>[],"noRepos"),j=q(n,t.object),b=Y(n),y=[];for(const r of w.values()){const c=h(n,"SELECT * FROM objects WHERE id = ?").get(r.id),u=D(n,c.type),S=u.receiverFor?u.receiverFor(n,c):u.receiver;if(S===void 0)continue;const g=ee(r.configs,t.level);if(g===null)continue;const E=P(n,c,O?o:I,b);if(E.scopes===null&&(E.class==="node"||E.owner.type!=="person"))continue;const p=r.linkApp!==null&&!j(E)&&re(n,t.object,c,r.linkApp,b);!p&&!j(E)||y.push({id:r.id,reception:r.reception,delivery:g,receiver:S,...p?{linked:p}:{}})}if(y.length===0)return{delivered:0,message_id:null,depth:d};let R=null;for(const r of ie(n,t,s,y,c=>c.id)){const c=new Map;for(const p of r.destinations)c.set(p.receiver,[...c.get(p.receiver)??[],p]);const u=se(n,t,s,r.attrs),S=[...c].map(([p,A])=>F[p].bodyOwner(A)).find(p=>p!==null)??null,g=J({...u,urgency:"normal"},S),E=K(n,{operationId:g.operationId,createdAt:g.createdAt,senderNodeId:u.senderNodeId,senderName:u.senderName,kind:u.kind,label:u.label,bodyRef:g.bodyRef,bodyJson:g.bodyJson,disposition:u.disposition??null,event:{event:t.event,objectId:t.object.id,level:t.level,actorId:t.actor?.id??null},causeMessageId:i,depth:d});R??=E;for(const[p,A]of c)F[p].enqueue(n,{messageId:E,event:t.event,level:t.level,senderNodeId:u.senderNodeId,elevatesTier:s.elevatesTier===!0,...t.idempotencyKey===void 0?{}:{idempotencyKey:t.idempotencyKey}},A)}return{delivered:y.length,message_id:R,depth:d}}l(Oe,"publish");export{T as HOP_LIMIT,ce as HopLimitError,Ne as listenConfigFor,Z as matchSubject,Oe as publish,ee as resolveDelivery,Q as resolveLevel,pe as subscribeDocumentEvents,ie as supersede};
11
+ WHERE o.reception = 2 AND w.watcher = o.rowid AND s.watch = w.rowid AND w.object = ? AND w.watcher != ?`).all(t.object.rowid,t.actor?.rowid??-1);if(N.length===0)return{delivered:0,message_id:null,depth:d};const m={event:t.event,level:t.level,id:t.object.id,type:t.object.type,kind:t.object.kind,actor:t.actor?.id??null,...t.attrs},w=new Map;for(const r of N){if(t.onlyWatchers!==void 0&&!t.onlyWatchers.includes(r.watcher_id)||!ee(r.pattern,t.event)||r.filter_json!==null&&!B(JSON.parse(r.filter_json),m))continue;const c=w.get(r.watcher)??{id:r.watcher_id,type:r.watcher_type,reception:r.reception,configs:[],linkApp:r.link_app};c.configs.push(r.config_json?C(r.config_json):W),w.set(r.watcher,c)}const O=(a.length?a[a.length-1]:t.object).type==="repo",I=l(()=>[],"noRepos"),j=q(n,t.object),b=Y(n),y=[];for(const r of w.values()){const c=h(n,"SELECT * FROM objects WHERE id = ?").get(r.id),u=D(n,c.type),v=u.receiverFor?u.receiverFor(n,c):u.receiver;if(v===void 0)continue;const g=te(r.configs,t.level);if(g===null)continue;const E=P(n,c,O?o:I,b);if(E.scopes===null&&(E.class==="node"||E.owner.type!=="person"))continue;const p=r.linkApp!==null&&!j(E)&&ce(n,t.object,c,r.linkApp,b);!p&&!j(E)||y.push({id:r.id,reception:r.reception,delivery:g,receiver:v,...p?{linked:p}:{}})}if(y.length===0)return{delivered:0,message_id:null,depth:d};let R=null;for(const r of re(n,t,s,y,c=>c.id)){const c=new Map;for(const p of r.destinations)c.set(p.receiver,[...c.get(p.receiver)??[],p]);const u=ie(n,t,s,r.attrs),v=[...c].map(([p,A])=>F[p].bodyOwner(A)).find(p=>p!==null)??null,g=J({...u,urgency:"normal"},v),E=K(n,{operationId:g.operationId,createdAt:g.createdAt,senderNodeId:u.senderNodeId,senderName:u.senderName,kind:u.kind,label:u.label,bodyRef:g.bodyRef,bodyJson:g.bodyJson,disposition:u.disposition??null,event:{event:t.event,objectId:t.object.id,level:t.level,actorId:t.actor?.id??null},causeMessageId:i,depth:d});R??=E;for(const[p,A]of c)F[p].enqueue(n,{messageId:E,event:t.event,level:t.level,senderNodeId:u.senderNodeId,elevatesTier:s.elevatesTier===!0,...t.idempotencyKey===void 0?{}:{idempotencyKey:t.idempotencyKey}},A)}return{delivered:y.length,message_id:R,depth:d}}l(Se,"publish");export{T as HOP_LIMIT,de as HopLimitError,ve as listenConfigFor,ee as matchSubject,Se as publish,te as resolveDelivery,Z as resolveLevel,ue as subscribeDocumentEvents,re as supersede};
@@ -4,4 +4,5 @@ import type { Level } from '../types.js';
4
4
  export declare function customKind(name: string, declared: Record<string, {
5
5
  level: Level;
6
6
  supersedes?: boolean;
7
+ ends_wait?: boolean;
7
8
  }>): ObjectKind;
@@ -1,4 +1,4 @@
1
- var l=Object.defineProperty;var d=(s,n)=>l(s,"name",{value:n,configurable:!0});import{prepared as m}from"../../sqlite-statements.js";import{recordLiveRead as u}from"./kind.js";import{header as v}from"./render.js";import{objectDTO as j}from"./dto.js";import{receiverForObject as b}from"../receivers.js";function S(s,n){const a=Object.fromEntries(Object.entries(n).map(([t,i])=>[`${s}.${t}`,{...i,carriesBody:!0}]));return{name:s,events:{created:{level:4},read:{level:1},watched:{level:1},deleted:{level:5},configured:{level:1},...a},addressable:"name",receiverFor:b,read(t,i,c,p){u(t,i,c,p.reposFn);const e=m(t,"SELECT description, summary, state_json, published_at, receiver_json, dropped FROM custom_objects WHERE object = ?").get(c.rowid),o=e?.receiver_json==null?null:JSON.parse(e.receiver_json),r={object:j(t,c),description:e?.description??"",summary:e?.summary??null,state:e?.state_json==null?null:JSON.parse(e.state_json),published_at:e?.published_at??null,receiver:typeof o=="string"?o:o&&typeof o=="object"?Object.keys(o)[0]:null,dropped:e?.dropped??0};return{data:r,text:`${v("object",r.object)}
1
+ var l=Object.defineProperty;var d=(s,c)=>l(s,"name",{value:c,configurable:!0});import{prepared as m}from"../../sqlite-statements.js";import{recordLiveRead as u}from"./kind.js";import{header as v}from"./render.js";import{objectDTO as j}from"./dto.js";import{receiverForObject as b}from"../receivers.js";function S(s,c){const a=Object.fromEntries(Object.entries(c).map(([t,{ends_wait:i,...o}])=>[`${s}.${t}`,{...o,carriesBody:!0,...i===void 0?{}:{endsWait:i}}]));return{name:s,events:{created:{level:4},read:{level:1},watched:{level:1},deleted:{level:5},configured:{level:1},...a},addressable:"name",receiverFor:b,read(t,i,o,p){u(t,i,o,p.reposFn);const e=m(t,"SELECT description, summary, state_json, published_at, receiver_json, dropped FROM custom_objects WHERE object = ?").get(o.rowid),n=e?.receiver_json==null?null:JSON.parse(e.receiver_json),r={object:j(t,o),description:e?.description??"",summary:e?.summary??null,state:e?.state_json==null?null:JSON.parse(e.state_json),published_at:e?.published_at??null,receiver:typeof n=="string"?n:n&&typeof n=="object"?Object.keys(n)[0]:null,dropped:e?.dropped??0};return{data:r,text:`${v("object",r.object)}
2
2
  Description: ${r.description}
3
3
  Summary: ${r.summary??""}
4
4
  State: ${JSON.stringify(r.state)}
@@ -27,6 +27,9 @@ export interface EventSpec {
27
27
  elevatesTier?: boolean;
28
28
  /** The inbox kind the message is filed under. Omitted: `urgent` at level 7 and above, else `update`. */
29
29
  inboxKind?: 'final' | 'completed';
30
+ /** A node that watches an object of this kind is waiting on it until this event (a run's
31
+ * `completed`, a pull request's `merged`); `publish` settles that wait (`waits`, kind `object`). */
32
+ endsWait?: boolean;
30
33
  }
31
34
  export type EventCatalog = Readonly<Record<string, EventSpec>>;
32
35
  /** What a read answers: the kind's structured fields and exactly the text `crtr canvas read` prints. */
@@ -0,0 +1,11 @@
1
+ /** A node's wait on a watched object: armed when a node starts watching an object whose kind declares an
2
+ * event with `endsWait` (a workflow run until `completed`), settled by `publish` when that event lands,
3
+ * by the node stopping its watch, or by the node ending. The rows live in `waits` (kind `object`). */
4
+ import type { DatabaseSync } from 'node:sqlite';
5
+ import type { ObjectRow } from './types.js';
6
+ /** Whether an object of this kind is something a node waits on: one of its events ends a wait. */
7
+ export declare function kindEndsWait(db: DatabaseSync, type: string): boolean;
8
+ /** `watcher` (a node) started watching `object`: arm its wait when the object's kind has an ending event. */
9
+ export declare function armObjectWaitForWatch(db: DatabaseSync, watcher: ObjectRow, object: ObjectRow): void;
10
+ /** `watcher` stopped watching `object` entirely: its wait on it is over. */
11
+ export declare function settleObjectWaitForUnwatch(db: DatabaseSync, watcher: ObjectRow, object: ObjectRow): void;
@@ -0,0 +1 @@
1
+ var r=Object.defineProperty;var n=(e,t)=>r(e,"name",{value:t,configurable:!0});import{armObjectWait as o,settleObjectWaits as a}from"../canvas/waits.js";import{kindOf as d}from"./kinds/registry.js";function f(e,t){return Object.values(d(e,t).events).some(i=>i.endsWait===!0)}n(f,"kindEndsWait");function c(e,t,i){t.type!=="node"||!f(e,i.type)||o(e,t.id,i.id)}n(c,"armObjectWaitForWatch");function m(e,t,i){t.type==="node"&&a(e,i.id,t.id)}n(m,"settleObjectWaitForUnwatch");export{c as armObjectWaitForWatch,f as kindEndsWait,m as settleObjectWaitForUnwatch};
@@ -186,8 +186,8 @@ export declare const chartDataRowsSchema: z.ZodArray<z.ZodRecord<z.ZodString, z.
186
186
  export declare const chartConfigSchema: z.ZodObject<{
187
187
  kind: z.ZodEnum<{
188
188
  line: "line";
189
- bar: "bar";
190
189
  area: "area";
190
+ bar: "bar";
191
191
  }>;
192
192
  x: z.ZodString;
193
193
  y: z.ZodArray<z.ZodObject<{
@@ -1,3 +1,3 @@
1
- var ge=Object.defineProperty;var u=(e,t)=>ge(e,"name",{value:t,configurable:!0});import{randomUUID as be}from"node:crypto";import{streamSimple as ve}from"@earendil-works/pi-ai/compat";import{expandModelCandidates as re,parseModelSpec as se,probeRouteAvailability as we}from"../model-routes.js";import{readMergedLaunchConfig as ie}from"../config.js";import{openModelRegistry as xe}from"../runtime/model-registry.js";import{MAX_RATE_LIMIT_COOLDOWN_MS as ae,coolingUntilFor as de,isManagedProvider as H,markSubscriptionRateLimited as ke,markSubscriptionSuccess as je,readSubscriptionPool as Me,retryAfterMsFromHeaders as K,retryAfterMsFromError as qe,refreshSubscriptionCredential as Ae,subscriptionNeedsSuccessWrite as $e}from"../subscription-state.js";import{classifyRateLimit as Ie,describeRateLimitScope as le,rateLimitHeaders as Re}from"../rate-limit-scope.js";import{RunError as s}from"../runs/operations.js";import{recognizeProviderError as ce}from"../provider-error-recognition.js";import{isSafetyRefusal as Se}from"./refusal.js";import{assertUnderCap as Ce,recordModelUsage as ue}from"../usage/ledger.js";import{outboundToken as Le}from"../providers/mint.js";import{discardFile as Oe}from"../providers/token-cache.js";import{ProviderError as Fe}from"../providers/errors.js";import{appCandidates as Ne}from"./app-rungs.js";function De(e){return typeof e=="string"||e===null||Array.isArray(e)&&e.every(t=>t&&typeof t=="object"&&(t.type==="text"&&typeof t.text=="string"||t.type==="image_url"&&typeof t.image_url?.url=="string"&&/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(t.image_url.url)))}u(De,"validContent");function fe(e){return Array.isArray(e)?e.map(t=>{if(t.type==="text")return t;const[,n,r]=/^data:(image\/(?:png|jpeg|gif|webp));base64,(.*)$/.exec(t.image_url.url);return{type:"image",mimeType:n,data:r}}):e??""}u(fe,"imageContent");function st(e){if(!e||typeof e!="object"||Array.isArray(e))throw new s(400,"invalid_request","body must be an object");const t=e;if(!Array.isArray(t.messages)||!t.messages.every(n=>n&&typeof n=="object"&&["system","user","assistant","tool"].includes(n.role)&&De(n.content)))throw new s(400,"invalid_request","messages must be chat messages","messages");if(t.messages.some(n=>["system","assistant"].includes(n.role)&&typeof n.content!="string"&&n.content!==null))throw new s(400,"invalid_request","system and assistant content must be text","messages");if(t.model!==void 0&&typeof t.model!="string")throw new s(400,"invalid_request","model must be a string","model");if(t.model_policy!==void 0&&!["exact","substitute"].includes(t.model_policy))throw new s(400,"invalid_request","invalid model_policy","model_policy");if(t.payer!==void 0&&!["user","app"].includes(t.payer))throw new s(400,"invalid_request","invalid payer","payer");if(t.reasoning_effort!==void 0&&!["minimal","low","medium","high","xhigh","max"].includes(t.reasoning_effort))throw new s(400,"invalid_request","invalid reasoning_effort","reasoning_effort");if(t.stream!==void 0&&typeof t.stream!="boolean")throw new s(400,"invalid_request","stream must be boolean","stream");if(t.tools!==void 0&&(!Array.isArray(t.tools)||!t.tools.every(n=>n?.type==="function"&&typeof n.function?.name=="string"&&n.function.name.length>0&&(n.function.description===void 0||typeof n.function.description=="string")&&(n.function.parameters===void 0||n.function.parameters!==null&&typeof n.function.parameters=="object"&&!Array.isArray(n.function.parameters)&&n.function.parameters.type==="object"))))throw new s(400,"invalid_request","invalid tools","tools");if(t.tool_choice!==void 0&&t.tool_choice!=="auto"&&t.tool_choice!=="none"&&t.tool_choice!=="required"&&(typeof t.tool_choice!="object"||t.tool_choice===null||t.tool_choice.type!=="function"||typeof t.tool_choice.function?.name!="string"))throw new s(400,"invalid_request","invalid tool_choice","tool_choice");if(t.messages.some(n=>n.role==="assistant"&&n.tool_calls!==void 0&&(!Array.isArray(n.tool_calls)||!n.tool_calls.every(r=>r&&typeof r.id=="string"&&typeof r.function?.name=="string"&&typeof r.function.arguments=="string"&&Ee(r.function.arguments)))))throw new s(400,"invalid_request","invalid assistant tool_calls","messages");if(t.stop!==void 0&&!(typeof t.stop=="string"||Array.isArray(t.stop)&&t.stop.every(n=>typeof n=="string")))throw new s(400,"invalid_request","invalid stop","stop");if(t.seed!==void 0&&!Number.isSafeInteger(t.seed))throw new s(400,"invalid_request","invalid seed","seed");for(const[n,r]of[["temperature",t.temperature],["top_p",t.top_p],["max_tokens",t.max_tokens]])if(r!==void 0&&(typeof r!="number"||!Number.isFinite(r)||n==="temperature"&&(r<0||r>2)||n==="top_p"&&(r<0||r>1)||n==="max_tokens"&&(!Number.isSafeInteger(r)||r<1)))throw new s(400,"invalid_request",`invalid ${n}`,n);if(t.response_format!==void 0){const n=t.response_format;if(!n||typeof n!="object"||Array.isArray(n)||!["text","json_object","json_schema"].includes(n.type)||n.type==="json_schema"&&(typeof n.json_schema?.name!="string"||!n.json_schema.name||!n.json_schema.schema||typeof n.json_schema.schema!="object"||Array.isArray(n.json_schema.schema)))throw new s(400,"invalid_request","invalid response_format","response_format")}if(t.metadata!==void 0&&Buffer.byteLength(JSON.stringify(t.metadata)??"")>4096)throw new s(400,"invalid_request","metadata exceeds 4 KB","metadata");return t}u(st,"parseLlmRequest");function Ee(e){try{const t=JSON.parse(e);return t!==null&&typeof t=="object"&&!Array.isArray(t)}catch{return!1}}u(Ee,"isJsonObject");function Pe(e){const t=Date.now(),n=e.messages.map(i=>i.role==="system"?{role:"system",content:i.content??"",timestamp:t}:i.role==="tool"?{role:"toolResult",toolCallId:i.tool_call_id??"",toolName:"",content:typeof i.content=="string"||i.content===null?[{type:"text",text:i.content??""}]:fe(i.content),isError:!1,timestamp:t}:i.role==="assistant"?{role:"assistant",content:[...i.content?[{type:"text",text:i.content}]:[],...(i.tool_calls??[]).map(g=>({type:"toolCall",id:g.id,name:g.function.name,arguments:JSON.parse(g.function.arguments)}))],api:"openai-completions",provider:"openai",model:e.model??"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:t}:{role:"user",content:fe(i.content),timestamp:t}),r=n.findIndex(i=>i.role!=="system"),w=n.slice(0,r<0?n.length:r).map(i=>i.content),y=w.join(`
1
+ var ge=Object.defineProperty;var u=(e,t)=>ge(e,"name",{value:t,configurable:!0});import{randomUUID as be}from"node:crypto";import{streamSimple as ve}from"@earendil-works/pi-ai/compat";import{expandModelCandidates as re,parseModelSpec as ie,probeRouteAvailability as we}from"../model-routes.js";import{readMergedLaunchConfig as se}from"../config.js";import{openModelRegistry as xe}from"../runtime/model-registry.js";import{MAX_RATE_LIMIT_COOLDOWN_MS as ae,coolingUntilFor as de,isManagedProvider as H,markSubscriptionRateLimited as ke,markSubscriptionSuccess as je,readSubscriptionPool as Me,retryAfterMsFromHeaders as K,retryAfterMsFromError as Ae,refreshSubscriptionCredential as qe,subscriptionNeedsSuccessWrite as Re}from"../subscription-state.js";import{classifyRateLimit as $e,describeRateLimitScope as le,rateLimitHeaders as Ie}from"../rate-limit-scope.js";import{RunError as i}from"../runs/operations.js";import{recognizeProviderError as ce}from"../provider-error-recognition.js";import{isSafetyRefusal as Ce}from"./refusal.js";import{assertUnderCap as Oe,recordModelUsage as ue}from"../usage/ledger.js";import{outboundToken as Se}from"../providers/mint.js";import{discardFile as Le}from"../providers/token-cache.js";import{ProviderError as Ee}from"../providers/errors.js";import{appCandidates as Ne}from"./app-rungs.js";function De(e){return typeof e=="string"||e===null||Array.isArray(e)&&e.every(t=>t&&typeof t=="object"&&(t.type==="text"&&typeof t.text=="string"||t.type==="image_url"&&typeof t.image_url?.url=="string"&&/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(t.image_url.url)))}u(De,"validContent");function fe(e){return Array.isArray(e)?e.map(t=>{if(t.type==="text")return t;const[,n,r]=/^data:(image\/(?:png|jpeg|gif|webp));base64,(.*)$/.exec(t.image_url.url);return{type:"image",mimeType:n,data:r}}):e??""}u(fe,"imageContent");function at(e){if(!e||typeof e!="object"||Array.isArray(e))throw new i(400,"invalid_request","body must be an object");const t=e;if(!Array.isArray(t.messages)||!t.messages.every(n=>n&&typeof n=="object"&&["system","user","assistant","tool"].includes(n.role)&&De(n.content)))throw new i(400,"invalid_request","messages must be chat messages","messages");if(t.messages.some(n=>["system","assistant"].includes(n.role)&&typeof n.content!="string"&&n.content!==null))throw new i(400,"invalid_request","system and assistant content must be text","messages");if(t.model!==void 0&&typeof t.model!="string")throw new i(400,"invalid_request","model must be a string","model");if(t.model_policy!==void 0&&!["exact","substitute"].includes(t.model_policy))throw new i(400,"invalid_request","invalid model_policy","model_policy");if(t.payer!==void 0&&!["user","app"].includes(t.payer))throw new i(400,"invalid_request","invalid payer","payer");if(t.reasoning_effort!==void 0&&!["minimal","low","medium","high","xhigh","max"].includes(t.reasoning_effort))throw new i(400,"invalid_request","invalid reasoning_effort","reasoning_effort");if(t.stream!==void 0&&typeof t.stream!="boolean")throw new i(400,"invalid_request","stream must be boolean","stream");if(t.tools!==void 0&&(!Array.isArray(t.tools)||!t.tools.every(n=>n?.type==="function"&&typeof n.function?.name=="string"&&n.function.name.length>0&&(n.function.description===void 0||typeof n.function.description=="string")&&(n.function.parameters===void 0||n.function.parameters!==null&&typeof n.function.parameters=="object"&&!Array.isArray(n.function.parameters)&&n.function.parameters.type==="object"))))throw new i(400,"invalid_request","invalid tools","tools");if(t.tool_choice!==void 0&&t.tool_choice!=="auto"&&t.tool_choice!=="none"&&t.tool_choice!=="required"&&(typeof t.tool_choice!="object"||t.tool_choice===null||t.tool_choice.type!=="function"||typeof t.tool_choice.function?.name!="string"))throw new i(400,"invalid_request","invalid tool_choice","tool_choice");if(t.messages.some(n=>n.role==="assistant"&&n.tool_calls!==void 0&&(!Array.isArray(n.tool_calls)||!n.tool_calls.every(r=>r&&typeof r.id=="string"&&typeof r.function?.name=="string"&&typeof r.function.arguments=="string"&&Fe(r.function.arguments)))))throw new i(400,"invalid_request","invalid assistant tool_calls","messages");if(t.stop!==void 0&&!(typeof t.stop=="string"||Array.isArray(t.stop)&&t.stop.every(n=>typeof n=="string")))throw new i(400,"invalid_request","invalid stop","stop");if(t.seed!==void 0&&!Number.isSafeInteger(t.seed))throw new i(400,"invalid_request","invalid seed","seed");for(const[n,r]of[["temperature",t.temperature],["top_p",t.top_p],["max_tokens",t.max_tokens]])if(r!==void 0&&(typeof r!="number"||!Number.isFinite(r)||n==="temperature"&&(r<0||r>2)||n==="top_p"&&(r<0||r>1)||n==="max_tokens"&&(!Number.isSafeInteger(r)||r<1)))throw new i(400,"invalid_request",`invalid ${n}`,n);if(t.response_format!==void 0){const n=t.response_format;if(!n||typeof n!="object"||Array.isArray(n)||!["text","json_object","json_schema"].includes(n.type)||n.type==="json_schema"&&(typeof n.json_schema?.name!="string"||!n.json_schema.name||!n.json_schema.schema||typeof n.json_schema.schema!="object"||Array.isArray(n.json_schema.schema)))throw new i(400,"invalid_request","invalid response_format","response_format")}if(t.metadata!==void 0&&Buffer.byteLength(JSON.stringify(t.metadata)??"")>4096)throw new i(400,"invalid_request","metadata exceeds 4 KB","metadata");return t}u(at,"parseLlmRequest");function Fe(e){try{const t=JSON.parse(e);return t!==null&&typeof t=="object"&&!Array.isArray(t)}catch{return!1}}u(Fe,"isJsonObject");function Te(e){const t=Date.now(),n=e.messages.map(s=>s.role==="system"?{role:"system",content:s.content??"",timestamp:t}:s.role==="tool"?{role:"toolResult",toolCallId:s.tool_call_id??"",toolName:"",content:typeof s.content=="string"||s.content===null?[{type:"text",text:s.content??""}]:fe(s.content),isError:!1,timestamp:t}:s.role==="assistant"?{role:"assistant",content:[...s.content?[{type:"text",text:s.content}]:[],...(s.tool_calls??[]).map(g=>({type:"toolCall",id:g.id,name:g.function.name,arguments:JSON.parse(g.function.arguments)}))],api:"openai-completions",provider:"openai",model:e.model??"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:t}:{role:"user",content:fe(s.content),timestamp:t}),r=n.findIndex(s=>s.role!=="system"),w=n.slice(0,r<0?n.length:r).map(s=>s.content),y=w.join(`
2
2
 
3
- `),l=e.tools?.map(({function:i})=>({name:i.name,description:i.description??"",parameters:i.parameters??{type:"object",properties:{}}}));return{...y?{systemPrompt:y}:{},messages:n.slice(w.length),...l?{tools:l}:{}}}u(Pe,"contextOf");function me(e,t,n,r){if(!e){const y=ie(n,r),l=Object.values({...y.modelRoutes??{},...y.legacyModelRoutes??{}}),i=l.length?[y.modelLadders.defaultProvider,...y.modelRouting?.familyOrder??[],...l.map(g=>g.family)].find(g=>l.some(F=>F.family===g)):y.modelLadders.defaultProvider??"anthropic";return{kind:"logical",family:i,strength:"strong",spec:`${i}/strong`}}const w=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(e);return w?{kind:"logical",family:w[1],strength:w[2],spec:e}:{kind:t==="exact"?"exact":"concrete",spec:e}}u(me,"requestFor");function Te(e,t){const{input:n,output:r,cacheRead:w,cacheWrite:y}=e.cost;return n===0&&r===0&&w===0&&y===0?null:Number.isFinite(t)&&t>=0?t:null}u(Te,"usdEquivalent");function Ue(e,t){return e.compat?.allowedFallbackModels?.find(r=>r.provider===e.provider&&r.model===t)}u(Ue,"allowedFallback");function it(e){return e?{payer:e.payer??"user",sub:e.sub,grantee:e.grantee,inference_endpoint:e.inference_endpoint??null}:void 0}u(it,"llmCallerOf");function Je(e){return e instanceof Fe?e.code==="server_error"?new s(503,"upstream_model_unavailable",e.message):new s(404,"model_unavailable",e.message):e}u(Je,"mintFailure");async function at(e,t,n,r,w,y){const l=y?.payer==="app"?y:void 0;if(e.payer==="app"&&!l)throw new s(403,"payer_not_app","this grant is not app-paid");if(e.payer==="user"&&l)throw new s(400,"invalid_request","this grant is app-paid; payer cannot be user","payer");if(l&&!l.inference_endpoint)throw new s(404,"model_unavailable",`${l.grantee} is app-paid but its grant carries no inference endpoint`);const i=l?.inference_endpoint??void 0;l||Ce(w);const g=await xe(),F=u(c=>c.routeId!=="concrete"&&c.routeId!=="exact","configured"),Y=u(c=>{const p=g.find(c.providerId,c.modelId);return p!==void 0&&(!e.reasoning_effort||p.reasoning)&&(e.seed===void 0&&e.response_format===void 0||p.api==="openai-completions")&&(e.stop===void 0||["openai-completions","anthropic-messages"].includes(p.api))},"matchesFeatures"),pe=u(()=>{const c=me(e.model,e.model_policy,r?.cwd,r?.profileId),p=re(c.kind==="exact"?{kind:"concrete",spec:c.spec}:c,r?.cwd,r?.profileId).filter(F);let a;if(c.kind==="logical")a=e.model_policy==="exact"?p.filter(m=>m.family===c.family&&m.strength===c.strength).slice(0,1):p;else{const m=se(c.spec),A=p.filter(j=>j.providerId===m?.providerId&&j.modelId===m.modelId&&(e.model_policy!=="exact"||j.thinkingLevel===m.thinkingLevel)&&Y(j));if(e.model_policy==="exact")a=A.slice(0,1);else if(A.length)a=A;else{const j=ie(r?.cwd,r?.profileId),x=[...Object.values(j.modelRoutes??{}).flatMap(v=>Object.entries(v.models)),...[j.modelLadders.anthropic,j.modelLadders.openai].flatMap(v=>Object.entries(v))].find(([,v])=>v===c.spec)?.[0],f=me(void 0,"substitute",r?.cwd,r?.profileId);a=x&&f.kind==="logical"?re({...f,strength:x,spec:c.spec},r?.cwd,r?.profileId).filter(v=>F(v)&&v.strength===x):[]}}return a},"ownerCandidates");let I;if(i?I=Ne(i,e.model,e.model_policy==="exact").flatMap((c,p)=>{const a=se(c.model);return a?[{routeId:"app",credentialSource:"app",family:"app",strength:c.strength,...a,rank:p}]:[]}):I=pe(),I=I.filter(Y),I.length===0)throw e.model_policy==="exact"?new s(400,"substitution_refused","no eligible model route is configured"):new s(404,"model_unavailable","no eligible model route is configured");const ee=`chatcmpl-${be()}`;let R=1/0,b,z;for(const c of I){if(!l&&!we(c,g).authenticated)continue;const p=g.find(c.providerId,c.modelId);if(!p)continue;const a=i?{...p,baseUrl:`${i.base_url}/${p.provider}`}:p,m=c.providerId,A=!l&&H(m)?Me(m).filter(x=>!x.authFailure):[],j=A.filter(x=>de(x,a.id)<=Date.now());if(A.length&&!j.length){const x=Math.min(...A.map(f=>de(f,a.id)));x<R&&(R=x,z=`${a.provider}/${a.id}`);continue}const te=l?[void 0,void 0]:A.length?j:[void 0];for(const[x,f]of te.entries()){const v=Date.now();let oe=f?.access;if(f&&f.expires<=Date.now()&&H(m)){const o=g.getProvider(m)?.auth.oauth;if(!o)throw new s(503,"upstream_model_unavailable",`no OAuth refresh for ${m}`,void 0,{served_model:`${a.provider}/${a.id}`});oe=(await Ae(m,f.label,async(k,ne)=>{const W=await o.refresh({type:"oauth",refresh:k,access:"",expires:0,...ne?{clientId:ne}:{}},t??new AbortController().signal),V=W.clientId;return{refresh:W.refresh,access:W.access,expires:W.expires,...typeof V=="string"&&V?{clientId:V}:{}}})).access}const he=Pe(e);let N;if(l&&i)try{N=await Le({audience:i.base_url,sub:l.sub,grantee:l.grantee,initiating_app:l.grantee,scope:["crtr:llm"]},{signal:t})}catch(o){throw Je(o)}const S=f||l?void 0:await g.getApiKeyAndHeaders(a);if(S&&!S.ok)continue;const h=`${a.provider}/${a.id}`,C=e.model??h;let q=!1,O,M=3e4,$;const B=[],X=[],D=[];let d,E;const P=u((o,_=null)=>({id:ee,object:"chat.completion.chunk",created:Math.floor(v/1e3),model:h,served_model:h,requested_model:C,substituted:C!==h&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(C),choices:[{index:0,delta:o,finish_reason:_}]}),"chunk"),T=u(o=>{B.push(o),n?.(o)},"emit"),_e=ve(a,he,{apiKey:N?.access_token??oe??(S?.ok?S.apiKey:void 0),...S?.ok?{headers:S.headers}:{},signal:t,reasoning:e.reasoning_effort??c.thinkingLevel,...e.temperature!==void 0?{temperature:e.temperature}:{},...e.max_tokens!==void 0?{maxTokens:e.max_tokens}:{},...e.top_p!==void 0?{samplingParams:{top_p:e.top_p}}:{},...e.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:u(o=>{if(!o||typeof o!="object")return;const _=e.tool_choice,k=_==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof _=="object"&&_!==null?a.api==="anthropic-messages"?{type:"tool",name:_.function.name}:_:void 0;return{...o,...k?{tool_choice:k}:{},...e.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof e.stop=="string"?[e.stop]:e.stop}:{},...e.seed!==void 0?{seed:e.seed}:{},...e.response_format!==void 0?{response_format:e.response_format}:{}}},"onPayload"),fetch:u(async(o,_)=>{const k=await fetch(o,_);return O=k.status,k.status===429&&(q=!0,$=k.headers,M=K(k.headers)??15e3),k},"fetch"),onResponse:u(o=>{o.status===429&&(q=!0,$=o.headers,M=K(o.headers)??15e3)},"onResponse")});try{for await(const o of _e)o.type==="text_delta"&&(X.push(o.delta),T(P({content:o.delta}))),o.type==="thinking_delta"&&(D.push(o.delta),T(P({reasoning_content:o.delta}))),o.type==="done"&&(d=o.message),o.type==="error"&&(d=o.error)}catch(o){const _=qe(o);$===void 0&&o&&typeof o=="object"&&"headers"in o&&Object.keys(Re(o.headers)).length&&($=o.headers);const k=ce({status:o&&typeof o=="object"&&"status"in o?o.status:void 0,message:o instanceof Error?o.message:String(o)});if(l)E=o;else{if(!q&&k.kind!=="rate-limit")throw B.length?new s(503,"upstream_model_unavailable",o instanceof Error?o.message:String(o),void 0,{served_model:h}):o;q=!0,M=_??M}}if(l){if(O===401){if(N&&Oe(N.key),x===0)continue;throw new s(404,"model_unavailable",`${i.base_url} refuses this runtime's tokens`,void 0,{served_model:h})}if(q&&K($)===void 0)throw new s(503,"upstream_model_unavailable",`${h} was rate-limited without a retry-after`,void 0,{served_model:h});if(!q&&(E!==void 0||O!==void 0&&O>=500))throw new s(503,"upstream_model_unavailable",E instanceof Error?E.message:d?.errorMessage??"inference endpoint failed",void 0,{served_model:h})}if(!q&&d?.stopReason==="error"&&ce({message:d.errorMessage}).kind==="rate-limit"&&(q=!0,M=15e3),q){const o=Ie($,a.id);if(K($)===void 0&&o.resetAt!==void 0&&(M=Math.max(M,o.resetAt-Date.now())),b={provider:m,model:a.id,...f?{account:f.label}:{},observation:o},console.error(`[llm] 429 from ${h}${f?` account "${f.label}"`:""}: scope=${le(o.scope)} retry_after_ms=${M} headers=${JSON.stringify(o.headers)}`),f&&H(m)&&await ke(m,f.label,M,v,Date.now(),o.scope),Date.now()+Math.min(M,ae)<R&&(R=Date.now()+Math.min(M,ae),z=h),X.length||D.length)throw new s(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:h});if(l)break;continue}const U=Se(d);if(O===400&&e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(d?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(d?.errorMessage??""))throw new s(400,"invalid_request",d.errorMessage,"tool_choice",{served_model:h});if(!d||d.stopReason==="error"&&!U||d.stopReason==="aborted")throw new s(503,"upstream_model_unavailable",d?.errorMessage??"model request failed",void 0,{served_model:h});f&&H(m)&&$e(f,a.id)&&await je(m,f.label,v,a.id);const Z=d.content.filter(o=>o.type==="toolCall").map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:JSON.stringify(o.arguments)}})),G=U?"content_filter":d.stopReason==="toolUse"?"tool_calls":d.stopReason==="length"?"length":"stop",Q=d.responseModel?Ue(a,d.responseModel):void 0,L=Q?`${a.provider}/${Q.model}`:h,J={prompt_tokens:d.usage.input,completion_tokens:d.usage.output,total_tokens:d.usage.totalTokens,cache_read_tokens:d.usage.cacheRead,cache_write_tokens:d.usage.cacheWrite,usd_equivalent:Te(Q??a,d.usage.cost.total),served_model:L};l?ue(l.grantee,"app",L,d.usage,J.usd_equivalent):w&&ue(w.grantee,"user",L,d.usage,J.usd_equivalent);const ye={id:ee,object:"chat.completion",created:Math.floor(v/1e3),model:L,served_model:L,requested_model:C,substituted:C!==L&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(C),choices:[{index:0,message:{role:"assistant",content:X.join("")||null,...D.length?{reasoning_content:D.join("")}:{},...Z.length?{tool_calls:Z}:{},...U?{refusal:d.errorMessage??"Content filtered"}:{}},finish_reason:G}],usage:J,...e.metadata!==void 0?{metadata:e.metadata}:{}};for(const[o,_]of Z.entries())T(P({tool_calls:[{index:o,..._}]}));return T({...P({},G),...U?{choices:[{index:0,delta:{},finish_reason:G,message:{refusal:d.errorMessage??"Content filtered"}}]}:{},usage:J}),{completion:ye,chunks:B}}}throw R!==1/0?new s(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((R-Date.now())/1e3)),served_model:b?`${b.provider}/${b.model}`:z,...b?{rate_limit:{provider:b.provider,model:b.model,...b.account?{account:b.account}:{},scope:le(b.observation.scope),...b.observation.claim?{claim:b.observation.claim}:{},headers:b.observation.headers}}:{}},void 0,!0):e.model_policy==="exact"?new s(400,"substitution_refused","the requested model is unavailable"):new s(404,"model_unavailable","no usable model route is available")}u(at,"runLlm");export{it as llmCallerOf,st as parseLlmRequest,at as runLlm};
3
+ `),l=e.tools?.map(({function:s})=>({name:s.name,description:s.description??"",parameters:s.parameters??{type:"object",properties:{}}}));return{...y?{systemPrompt:y}:{},messages:n.slice(w.length),...l?{tools:l}:{}}}u(Te,"contextOf");function me(e,t,n,r){if(!e){const y=se(n,r),l=Object.values({...y.modelRoutes??{},...y.legacyModelRoutes??{}}),s=l.length?[y.modelLadders.defaultProvider,...y.modelRouting?.familyOrder??[],...l.map(g=>g.family)].find(g=>l.some(E=>E.family===g)):y.modelLadders.defaultProvider??"anthropic";return{kind:"logical",family:s,strength:"strong",spec:`${s}/strong`}}const w=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(e);return w?{kind:"logical",family:w[1],strength:w[2],spec:e}:{kind:t==="exact"?"exact":"concrete",spec:e}}u(me,"requestFor");function Pe(e,t){const{input:n,output:r,cacheRead:w,cacheWrite:y}=e.cost;return n===0&&r===0&&w===0&&y===0?null:Number.isFinite(t)&&t>=0?t:null}u(Pe,"usdEquivalent");function Ue(e,t){return e.compat?.allowedFallbackModels?.find(r=>r.provider===e.provider&&r.model===t)}u(Ue,"allowedFallback");function dt(e){return e?{payer:e.payer??"user",sub:e.sub,grantee:e.grantee,inference_endpoint:e.inference_endpoint??null}:void 0}u(dt,"llmCallerOf");function Je(e){return e instanceof Ee?e.code==="server_error"?new i(503,"upstream_model_unavailable",e.message):new i(404,"model_unavailable",e.message):e}u(Je,"mintFailure");const We=/\bmodel is not supported when using Codex with a ChatGPT account\b/i;function He(e,t,n){if(!(e!=="openai-codex"||t===void 0||!We.test(t)))return new i(404,"model_unavailable",t,void 0,{served_model:n})}u(He,"codexAccountModelRejection");async function lt(e,t,n,r,w,y){const l=y?.payer==="app"?y:void 0;if(e.payer==="app"&&!l)throw new i(403,"payer_not_app","this grant is not app-paid");if(e.payer==="user"&&l)throw new i(400,"invalid_request","this grant is app-paid; payer cannot be user","payer");if(l&&!l.inference_endpoint)throw new i(404,"model_unavailable",`${l.grantee} is app-paid but its grant carries no inference endpoint`);const s=l?.inference_endpoint??void 0;l||Oe(w);const g=await xe(),E=u(c=>c.routeId!=="concrete"&&c.routeId!=="exact","configured"),Y=u(c=>{const h=g.find(c.providerId,c.modelId);return h!==void 0&&(!e.reasoning_effort||h.reasoning)&&(e.seed===void 0&&e.response_format===void 0||h.api==="openai-completions")&&(e.stop===void 0||["openai-completions","anthropic-messages"].includes(h.api))},"matchesFeatures"),pe=u(()=>{const c=me(e.model,e.model_policy,r?.cwd,r?.profileId),h=re(c.kind==="exact"?{kind:"concrete",spec:c.spec}:c,r?.cwd,r?.profileId).filter(E);let a;if(c.kind==="logical")a=e.model_policy==="exact"?h.filter(m=>m.family===c.family&&m.strength===c.strength).slice(0,1):h;else{const m=ie(c.spec),q=h.filter(j=>j.providerId===m?.providerId&&j.modelId===m.modelId&&(e.model_policy!=="exact"||j.thinkingLevel===m.thinkingLevel)&&Y(j));if(e.model_policy==="exact")a=q.slice(0,1);else if(q.length)a=q;else{const j=se(r?.cwd,r?.profileId),x=[...Object.values(j.modelRoutes??{}).flatMap(v=>Object.entries(v.models)),...[j.modelLadders.anthropic,j.modelLadders.openai].flatMap(v=>Object.entries(v))].find(([,v])=>v===c.spec)?.[0],f=me(void 0,"substitute",r?.cwd,r?.profileId);a=x&&f.kind==="logical"?re({...f,strength:x,spec:c.spec},r?.cwd,r?.profileId).filter(v=>E(v)&&v.strength===x):[]}}return a},"ownerCandidates");let $;if(s?$=Ne(s,e.model,e.model_policy==="exact").flatMap((c,h)=>{const a=ie(c.model);return a?[{routeId:"app",credentialSource:"app",family:"app",strength:c.strength,...a,rank:h}]:[]}):$=pe(),$=$.filter(Y),$.length===0)throw e.model_policy==="exact"?new i(400,"substitution_refused","no eligible model route is configured"):new i(404,"model_unavailable","no eligible model route is configured");const ee=`chatcmpl-${be()}`;let I=1/0,b,z;for(const c of $){if(!l&&!we(c,g).authenticated)continue;const h=g.find(c.providerId,c.modelId);if(!h)continue;const a=s?{...h,baseUrl:`${s.base_url}/${h.provider}`}:h,m=c.providerId,q=!l&&H(m)?Me(m).filter(x=>!x.authFailure):[],j=q.filter(x=>de(x,a.id)<=Date.now());if(q.length&&!j.length){const x=Math.min(...q.map(f=>de(f,a.id)));x<I&&(I=x,z=`${a.provider}/${a.id}`);continue}const te=l?[void 0,void 0]:q.length?j:[void 0];for(const[x,f]of te.entries()){const v=Date.now();let oe=f?.access;if(f&&f.expires<=Date.now()&&H(m)){const o=g.getProvider(m)?.auth.oauth;if(!o)throw new i(503,"upstream_model_unavailable",`no OAuth refresh for ${m}`,void 0,{served_model:`${a.provider}/${a.id}`});oe=(await qe(m,f.label,async(k,ne)=>{const W=await o.refresh({type:"oauth",refresh:k,access:"",expires:0,...ne?{clientId:ne}:{}},t??new AbortController().signal),V=W.clientId;return{refresh:W.refresh,access:W.access,expires:W.expires,...typeof V=="string"&&V?{clientId:V}:{}}})).access}const he=Te(e);let N;if(l&&s)try{N=await Se({audience:s.base_url,sub:l.sub,grantee:l.grantee,initiating_app:l.grantee,scope:["crtr:llm"]},{signal:t})}catch(o){throw Je(o)}const C=f||l?void 0:await g.getApiKeyAndHeaders(a);if(C&&!C.ok)continue;const p=`${a.provider}/${a.id}`,O=e.model??p;let A=!1,L,M=3e4,R;const B=[],X=[],D=[];let d,F;const T=u((o,_=null)=>({id:ee,object:"chat.completion.chunk",created:Math.floor(v/1e3),model:p,served_model:p,requested_model:O,substituted:O!==p&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(O),choices:[{index:0,delta:o,finish_reason:_}]}),"chunk"),P=u(o=>{B.push(o),n?.(o)},"emit"),_e=ve(a,he,{apiKey:N?.access_token??oe??(C?.ok?C.apiKey:void 0),...C?.ok?{headers:C.headers}:{},signal:t,reasoning:e.reasoning_effort??c.thinkingLevel,...e.temperature!==void 0?{temperature:e.temperature}:{},...e.max_tokens!==void 0?{maxTokens:e.max_tokens}:{},...e.top_p!==void 0?{samplingParams:{top_p:e.top_p}}:{},...e.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:u(o=>{if(!o||typeof o!="object")return;const _=e.tool_choice,k=_==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof _=="object"&&_!==null?a.api==="anthropic-messages"?{type:"tool",name:_.function.name}:_:void 0;return{...o,...k?{tool_choice:k}:{},...e.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof e.stop=="string"?[e.stop]:e.stop}:{},...e.seed!==void 0?{seed:e.seed}:{},...e.response_format!==void 0?{response_format:e.response_format}:{}}},"onPayload"),fetch:u(async(o,_)=>{const k=await fetch(o,_);return L=k.status,k.status===429&&(A=!0,R=k.headers,M=K(k.headers)??15e3),k},"fetch"),onResponse:u(o=>{o.status===429&&(A=!0,R=o.headers,M=K(o.headers)??15e3)},"onResponse")});try{for await(const o of _e)o.type==="text_delta"&&(X.push(o.delta),P(T({content:o.delta}))),o.type==="thinking_delta"&&(D.push(o.delta),P(T({reasoning_content:o.delta}))),o.type==="done"&&(d=o.message),o.type==="error"&&(d=o.error)}catch(o){const _=Ae(o);R===void 0&&o&&typeof o=="object"&&"headers"in o&&Object.keys(Ie(o.headers)).length&&(R=o.headers);const k=ce({status:o&&typeof o=="object"&&"status"in o?o.status:void 0,message:o instanceof Error?o.message:String(o)});if(l)F=o;else{if(!A&&k.kind!=="rate-limit")throw B.length?new i(503,"upstream_model_unavailable",o instanceof Error?o.message:String(o),void 0,{served_model:p}):o;A=!0,M=_??M}}if(l){if(L===401){if(N&&Le(N.key),x===0)continue;throw new i(404,"model_unavailable",`${s.base_url} refuses this runtime's tokens`,void 0,{served_model:p})}if(A&&K(R)===void 0)throw new i(503,"upstream_model_unavailable",`${p} was rate-limited without a retry-after`,void 0,{served_model:p});if(!A&&(F!==void 0||L!==void 0&&L>=500))throw new i(503,"upstream_model_unavailable",F instanceof Error?F.message:d?.errorMessage??"inference endpoint failed",void 0,{served_model:p})}if(!A&&d?.stopReason==="error"&&ce({message:d.errorMessage}).kind==="rate-limit"&&(A=!0,M=15e3),A){const o=$e(R,a.id);if(K(R)===void 0&&o.resetAt!==void 0&&(M=Math.max(M,o.resetAt-Date.now())),b={provider:m,model:a.id,...f?{account:f.label}:{},observation:o},console.error(`[llm] 429 from ${p}${f?` account "${f.label}"`:""}: scope=${le(o.scope)} retry_after_ms=${M} headers=${JSON.stringify(o.headers)}`),f&&H(m)&&await ke(m,f.label,M,v,Date.now(),o.scope),Date.now()+Math.min(M,ae)<I&&(I=Date.now()+Math.min(M,ae),z=p),X.length||D.length)throw new i(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:p});if(l)break;continue}const U=Ce(d);if(L===400&&e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(d?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(d?.errorMessage??""))throw new i(400,"invalid_request",d.errorMessage,"tool_choice",{served_model:p});if(d?.stopReason==="error"){const o=He(a.provider,d.errorMessage,p);if(o)throw o}if(!d||d.stopReason==="error"&&!U||d.stopReason==="aborted")throw new i(503,"upstream_model_unavailable",d?.errorMessage??"model request failed",void 0,{served_model:p});f&&H(m)&&Re(f,a.id)&&await je(m,f.label,v,a.id);const G=d.content.filter(o=>o.type==="toolCall").map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:JSON.stringify(o.arguments)}})),Z=U?"content_filter":d.stopReason==="toolUse"?"tool_calls":d.stopReason==="length"?"length":"stop",Q=d.responseModel?Ue(a,d.responseModel):void 0,S=Q?`${a.provider}/${Q.model}`:p,J={prompt_tokens:d.usage.input,completion_tokens:d.usage.output,total_tokens:d.usage.totalTokens,cache_read_tokens:d.usage.cacheRead,cache_write_tokens:d.usage.cacheWrite,usd_equivalent:Pe(Q??a,d.usage.cost.total),served_model:S};l?ue(l.grantee,"app",S,d.usage,J.usd_equivalent):w&&ue(w.grantee,"user",S,d.usage,J.usd_equivalent);const ye={id:ee,object:"chat.completion",created:Math.floor(v/1e3),model:S,served_model:S,requested_model:O,substituted:O!==S&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(O),choices:[{index:0,message:{role:"assistant",content:X.join("")||null,...D.length?{reasoning_content:D.join("")}:{},...G.length?{tool_calls:G}:{},...U?{refusal:d.errorMessage??"Content filtered"}:{}},finish_reason:Z}],usage:J,...e.metadata!==void 0?{metadata:e.metadata}:{}};for(const[o,_]of G.entries())P(T({tool_calls:[{index:o,..._}]}));return P({...T({},Z),...U?{choices:[{index:0,delta:{},finish_reason:Z,message:{refusal:d.errorMessage??"Content filtered"}}]}:{},usage:J}),{completion:ye,chunks:B}}}throw I!==1/0?new i(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((I-Date.now())/1e3)),served_model:b?`${b.provider}/${b.model}`:z,...b?{rate_limit:{provider:b.provider,model:b.model,...b.account?{account:b.account}:{},scope:le(b.observation.scope),...b.observation.claim?{claim:b.observation.claim}:{},headers:b.observation.headers}}:{}},void 0,!0):e.model_policy==="exact"?new i(400,"substitution_refused","the requested model is unavailable"):new i(404,"model_unavailable","no usable model route is available")}u(lt,"runLlm");export{dt as llmCallerOf,at as parseLlmRequest,lt as runLlm};
@@ -1,2 +1,2 @@
1
- var W=Object.defineProperty;var d=(e,t)=>W(e,"name",{value:t,configurable:!0});import{setTimeout as j}from"node:timers/promises";import{createAssistantMessageEventStream as J,getCurrentSystemPrompt as Y,getCurrentTools as z}from"@earendil-works/pi-ai";import{socketFetch as X}from"../../../api/node-transport.js";import{apiSocketPath as G}from"../../canvas/paths.js";import{currentNodeToken as H}from"./node-token.js";import{envModelExact as $,envModelIntent as K}from"../../../shared/env.js";import{MODEL_CONTEXT_BODY_MAX_BYTES as v}from"../../llm/body-limits.js";import{modelRequestFromConfig as V}from"../../model-routes.js";import{parseProviderRateLimit as Q,providerFailureDiagnostic as E}from"../../provider-failure.js";import{UPSTREAM_MODEL_UNAVAILABLE as b}from"../../fault-classifier.js";function me(e,t){e.checkAuth=async n=>e.getProvider(n)?{type:"api_key"}:void 0,e.getAvailable=async n=>e.getModels(n),e.getAvailableSnapshot=()=>e.getModels(),e.streamSimple=(n,c,r)=>ne(n,c,r,t)}d(me,"configureBrokerModelRuntime");const Z=300,C="crtr:llm-refusal";class R extends Error{static{d(this,"LlmRefusalError")}retryAt;rateLimit;code;status;constructor(t,n,c,r,o){super(t),this.retryAt=n,this.rateLimit=c,this.code=r,this.status=o}}class S extends Error{static{d(this,"UpstreamModelUnavailableError")}}function N(e,t){const n=e===void 0?"the request body":`the request body (${e} bytes)`;return new Error(`request_too_large: ${n} exceeds the ${t}-byte model request limit; compact the conversation to shrink the context`)}d(N,"contextTooLargeError");function D(e){const t=e.details?.rate_limit;if(!t||typeof t!="object")return;const n=Q(t);if(n===void 0)return;const{provider:c,model:r}=t;return typeof c=="string"&&c!==""&&typeof r=="string"&&r!==""?{...n,model:`${c}/${r}`}:n}d(D,"refusalRateLimit");function he(e){for(const t of e.diagnostics??[]){if(t.type!==C)continue;const n=t.details?.retryAt;if(typeof n=="number"&&Number.isFinite(n)&&n>0)return n}}d(he,"refusalRetryAt");class q extends Error{static{d(this,"ContentFilteredError")}}function B(e){return typeof e=="string"?e:e.map(t=>t.type==="text"?{type:"text",text:t.text}:{type:"image_url",image_url:{url:`data:${t.mimeType};base64,${t.data}`}})}d(B,"wireContent");function ee(e){const t=d(r=>r.role==="assistant"&&(r.stopReason==="error"||r.stopReason==="aborted"),"incomplete"),n=e.some(t)?e.filter(r=>!t(r)):e;let c=n.length;for(;c>0&&n[c-1]?.role==="assistant";)c--;return c===n.length?n:n.slice(0,c)}d(ee,"wireHistory");function te(e,t,n,c){const r=[],o=Y(t.messages),i=z(t.messages);o?r.push({role:"system",content:o}):t.systemPrompt&&r.push({role:"system",content:t.systemPrompt});for(const p of ee(t.messages))p.role==="user"?r.push({role:"user",content:B(p.content)}):p.role==="toolResult"?r.push({role:"tool",content:B(p.content),tool_call_id:p.toolCallId}):p.role==="assistant"&&r.push({role:"assistant",content:p.content.filter(u=>u.type==="text").map(u=>u.text).join("")||null,tool_calls:p.content.filter(u=>u.type==="toolCall").map(u=>({id:u.id,type:"function",function:{name:u.name,arguments:JSON.stringify(u.arguments)}}))});const x=`${e.provider}/${e.id}`,_=c??K();return{model:V(x,_,$())?.kind==="logical"?_:x,model_policy:$()?"exact":"substitute",stream:!0,messages:r,...i.length?{tools:i.map(p=>({type:"function",function:{name:p.name,description:p.description,parameters:p.parameters}}))}:{},...n.reasoning?{reasoning_effort:n.reasoning}:{},...n.temperature!==void 0?{temperature:n.temperature}:{},...n.maxTokens!==void 0?{max_tokens:n.maxTokens}:{}}}d(te,"requestBody");function A(e,t){if(typeof t!="string")return!1;const n=t.indexOf("/");return n<1||n===t.length-1?!1:(e.provider=t.slice(0,n),e.model=t.slice(n+1),!0)}d(A,"applyServedModel");function F(e){if(e.details?.served_model!==void 0)return e.details.served_model;const t=e.details?.rate_limit;return typeof t?.provider=="string"&&typeof t.model=="string"?`${t.provider}/${t.model}`:void 0}d(F,"refusalRoute");function P(e){if(!e||typeof e!="object"||!("error"in e))throw new Error("llm returned an invalid response");const t=e.error;if(!t||typeof t.message!="string")throw new Error("llm returned an invalid refusal");return t}d(P,"parseRefusal");function ne(e,t,n={},c){const r=J(),o={role:"assistant",content:[],api:e.api,provider:e.provider,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"pending",timestamp:Date.now()};return(async()=>{const i=H();if(!i)throw new Error("llm requires a node token");const x=JSON.stringify(te(e,t,n,c)),_=Buffer.byteLength(x);if(_>v)throw N(_,v);let p=0;for(;;){n.signal?.throwIfAborted();const u=await X(G())("http://localhost/v1/llm",{method:"POST",headers:{authorization:`Bearer ${i}`,"content-type":"application/json"},body:x,signal:n.signal});if(!u.ok){const l=P(await u.json());if(u.status===413||l.code==="payload_too_large")throw N(_,v);if(A(o,F(l)),l.retryable!==!0)throw new R(l.message,void 0,D(l),l.code,u.status);const s=l.retry_after_s;if(typeof s!="number"||!Number.isFinite(s)||s<=0)throw l.code===b?new S(l.message):new Error(l.message);const m=Z-p;if(s>m){const f=Date.now()+s*1e3;throw new R(`${l.message} (retry after ${s}s, at ${new Date(f).toISOString()})`,f,D(l))}await j(s*1e3,void 0,{signal:n.signal}),p+=s;continue}if(!u.body)throw new Error("llm returned no stream");r.push({type:"start",partial:o});const U=u.body.getReader(),L=new TextDecoder;let g="",w="stop",I,M=!1,T=!1;const O=d(l=>{const s=JSON.parse(l);if(s.error){const a=P(s);throw A(o,F(a)),a.code===b?new S(a.message):new Error(a.message)}if(s.served_model){if(T=!0,s.served_model.indexOf("/")<1)throw new Error("llm returned an invalid served_model");A(o,s.served_model)}const m=s.choices?.[0],f=m?.delta;if(f?.reasoning_content){const a=o.content.findIndex(k=>k.type==="thinking"),h=a<0?{type:"thinking",thinking:""}:o.content[a],y=a<0?o.content.push(h)-1:a;if(a<0&&r.push({type:"thinking_start",contentIndex:y,partial:o}),h.type!=="thinking")throw new Error("invalid thinking block");h.thinking+=f.reasoning_content,r.push({type:"thinking_delta",contentIndex:y,delta:f.reasoning_content,partial:o})}if(f?.content){const a=o.content.findIndex(k=>k.type==="text"),h=a<0?{type:"text",text:""}:o.content[a],y=a<0?o.content.push(h)-1:a;if(a<0&&r.push({type:"text_start",contentIndex:y,partial:o}),h.type!=="text")throw new Error("invalid text block");h.text+=f.content,r.push({type:"text_delta",contentIndex:y,delta:f.content,partial:o})}for(const a of f?.tool_calls??[]){const h={type:"toolCall",id:a.id,name:a.function.name,arguments:JSON.parse(a.function.arguments)},y=o.content.push(h)-1;r.push({type:"toolcall_start",contentIndex:y,partial:o}),r.push({type:"toolcall_end",contentIndex:y,toolCall:h,partial:o})}m?.finish_reason&&(w=m.finish_reason),m?.message?.refusal&&(I=m.message.refusal),s.usage&&(M=!0,o.usage={input:s.usage.prompt_tokens,output:s.usage.completion_tokens,cacheRead:s.usage.cache_read_tokens,cacheWrite:s.usage.cache_write_tokens,totalTokens:s.usage.total_tokens,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:typeof s.usage.usd_equivalent=="number"&&Number.isFinite(s.usage.usd_equivalent)?s.usage.usd_equivalent:0}})},"apply");for(;;){const{value:l,done:s}=await U.read();if(s)break;g+=L.decode(l,{stream:!0});let m;for(;(m=g.indexOf(`
2
- `))>=0;){const f=g.slice(0,m).trim();g=g.slice(m+1),f&&O(f)}}if(g+=L.decode(),g.trim()&&O(g.trim()),!M||!T)throw new Error("llm stream ended without usage or served_model");for(const[l,s]of o.content.entries())s.type==="text"&&r.push({type:"text_end",contentIndex:l,content:s.text,partial:o}),s.type==="thinking"&&r.push({type:"thinking_end",contentIndex:l,content:s.thinking,partial:o});if(w==="content_filter")throw new q(I??"Content filtered");if(!["stop","tool_calls","length"].includes(w))throw new Error(`llm returned unknown finish reason: ${w}`);o.stopReason=w==="tool_calls"?"toolUse":w==="length"?"length":"stop",r.push({type:"done",reason:o.stopReason,message:o}),r.end();return}})().catch(i=>{o.stopReason=n.signal?.aborted?"aborted":"error",o.errorMessage=i instanceof Error?i.message:String(i),i instanceof R&&(o.diagnostics=[{type:C,timestamp:Date.now(),...i.retryAt!==void 0?{details:{retryAt:i.retryAt}}:{}},...i.rateLimit!==void 0?[E({status:429,ratelimit:i.rateLimit})]:[],...i.rateLimit===void 0&&i.code==="cap_exhausted"?[E({status:i.status??402,code:i.code})]:[]]),i instanceof S&&(o.diagnostics=[E({status:503,code:b})]),i instanceof q&&o.stopReason==="error"&&(o.rawStopReason="content_filter"),r.push({type:"error",reason:o.stopReason,error:o}),r.end()}),r}d(ne,"streamBrokerModel");export{Z as LLM_BROKER_RETRY_WINDOW_S,C as LLM_REFUSAL_DIAGNOSTIC,me as configureBrokerModelRuntime,he as refusalRetryAt,te as requestBody,ne as streamBrokerModel,ee as wireHistory};
1
+ var W=Object.defineProperty;var d=(e,t)=>W(e,"name",{value:t,configurable:!0});import{setTimeout as j}from"node:timers/promises";import{createAssistantMessageEventStream as J,getCurrentSystemPrompt as Y,getCurrentTools as z}from"@earendil-works/pi-ai";import{socketFetch as X}from"../../../api/node-transport.js";import{apiSocketPath as G}from"../../canvas/paths.js";import{currentNodeToken as H}from"./node-token.js";import{envModelExact as C,envModelIntent as K}from"../../../shared/env.js";import{MODEL_CONTEXT_BODY_MAX_BYTES as E}from"../../llm/body-limits.js";import{modelRequestFromConfig as V}from"../../model-routes.js";import{parseProviderRateLimit as Q,providerFailureDiagnostic as b}from"../../provider-failure.js";import{UPSTREAM_MODEL_UNAVAILABLE as R}from"../../fault-classifier.js";function me(e,t){e.checkAuth=async n=>e.getProvider(n)?{type:"api_key"}:void 0,e.getAvailable=async n=>e.getModels(n),e.getAvailableSnapshot=()=>e.getModels(),e.streamSimple=(n,c,r)=>ne(n,c,r,t)}d(me,"configureBrokerModelRuntime");const Z=300,N="crtr:llm-refusal";class k extends Error{static{d(this,"LlmRefusalError")}retryAt;rateLimit;code;status;constructor(t,n,c,r,o){super(t),this.retryAt=n,this.rateLimit=c,this.code=r,this.status=o}}class S extends Error{static{d(this,"UpstreamModelUnavailableError")}}function D(e,t){const n=e===void 0?"the request body":`the request body (${e} bytes)`;return new Error(`request_too_large: ${n} exceeds the ${t}-byte model request limit; compact the conversation to shrink the context`)}d(D,"contextTooLargeError");function A(e){const t=e.details?.rate_limit;if(!t||typeof t!="object")return;const n=Q(t);if(n===void 0)return;const{provider:c,model:r}=t;return typeof c=="string"&&c!==""&&typeof r=="string"&&r!==""?{...n,model:`${c}/${r}`}:n}d(A,"refusalRateLimit");function he(e){for(const t of e.diagnostics??[]){if(t.type!==N)continue;const n=t.details?.retryAt;if(typeof n=="number"&&Number.isFinite(n)&&n>0)return n}}d(he,"refusalRetryAt");class q extends Error{static{d(this,"ContentFilteredError")}}function B(e){return typeof e=="string"?e:e.map(t=>t.type==="text"?{type:"text",text:t.text}:{type:"image_url",image_url:{url:`data:${t.mimeType};base64,${t.data}`}})}d(B,"wireContent");function ee(e){const t=d(r=>r.role==="assistant"&&(r.stopReason==="error"||r.stopReason==="aborted"),"incomplete"),n=e.some(t)?e.filter(r=>!t(r)):e;let c=n.length;for(;c>0&&n[c-1]?.role==="assistant";)c--;return c===n.length?n:n.slice(0,c)}d(ee,"wireHistory");function te(e,t,n,c){const r=[],o=Y(t.messages),a=z(t.messages);o?r.push({role:"system",content:o}):t.systemPrompt&&r.push({role:"system",content:t.systemPrompt});for(const p of ee(t.messages))p.role==="user"?r.push({role:"user",content:B(p.content)}):p.role==="toolResult"?r.push({role:"tool",content:B(p.content),tool_call_id:p.toolCallId}):p.role==="assistant"&&r.push({role:"assistant",content:p.content.filter(u=>u.type==="text").map(u=>u.text).join("")||null,tool_calls:p.content.filter(u=>u.type==="toolCall").map(u=>({id:u.id,type:"function",function:{name:u.name,arguments:JSON.stringify(u.arguments)}}))});const x=`${e.provider}/${e.id}`,_=c??K();return{model:V(x,_,C())?.kind==="logical"?_:x,model_policy:C()?"exact":"substitute",stream:!0,messages:r,...a.length?{tools:a.map(p=>({type:"function",function:{name:p.name,description:p.description,parameters:p.parameters}}))}:{},...n.reasoning?{reasoning_effort:n.reasoning}:{},...n.temperature!==void 0?{temperature:n.temperature}:{},...n.maxTokens!==void 0?{max_tokens:n.maxTokens}:{}}}d(te,"requestBody");function L(e,t){if(typeof t!="string")return!1;const n=t.indexOf("/");return n<1||n===t.length-1?!1:(e.provider=t.slice(0,n),e.model=t.slice(n+1),!0)}d(L,"applyServedModel");function F(e){if(e.details?.served_model!==void 0)return e.details.served_model;const t=e.details?.rate_limit;return typeof t?.provider=="string"&&typeof t.model=="string"?`${t.provider}/${t.model}`:void 0}d(F,"refusalRoute");function P(e){if(!e||typeof e!="object"||!("error"in e))throw new Error("llm returned an invalid response");const t=e.error;if(!t||typeof t.message!="string")throw new Error("llm returned an invalid refusal");return t}d(P,"parseRefusal");function ne(e,t,n={},c){const r=J(),o={role:"assistant",content:[],api:e.api,provider:e.provider,model:e.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"pending",timestamp:Date.now()};return(async()=>{const a=H();if(!a)throw new Error("llm requires a node token");const x=JSON.stringify(te(e,t,n,c)),_=Buffer.byteLength(x);if(_>E)throw D(_,E);let p=0;for(;;){n.signal?.throwIfAborted();const u=await X(G())("http://localhost/v1/llm",{method:"POST",headers:{authorization:`Bearer ${a}`,"content-type":"application/json"},body:x,signal:n.signal});if(!u.ok){const l=P(await u.json());if(u.status===413||l.code==="payload_too_large")throw D(_,E);if(L(o,F(l)),l.retryable!==!0)throw new k(l.message,void 0,A(l),l.code,u.status);const s=l.retry_after_s;if(typeof s!="number"||!Number.isFinite(s)||s<=0)throw l.code===R?new S(l.message):new Error(l.message);const m=Z-p;if(s>m){const f=Date.now()+s*1e3;throw new k(`${l.message} (retry after ${s}s, at ${new Date(f).toISOString()})`,f,A(l))}await j(s*1e3,void 0,{signal:n.signal}),p+=s;continue}if(!u.body)throw new Error("llm returned no stream");r.push({type:"start",partial:o});const U=u.body.getReader(),I=new TextDecoder;let g="",w="stop",M,T=!1,O=!1;const $=d(l=>{const s=JSON.parse(l);if(s.error){const i=P(s);throw L(o,F(i)),i.retryable!==!0?new k(i.message,void 0,A(i),i.code):i.code===R?new S(i.message):new Error(i.message)}if(s.served_model){if(O=!0,s.served_model.indexOf("/")<1)throw new Error("llm returned an invalid served_model");L(o,s.served_model)}const m=s.choices?.[0],f=m?.delta;if(f?.reasoning_content){const i=o.content.findIndex(v=>v.type==="thinking"),h=i<0?{type:"thinking",thinking:""}:o.content[i],y=i<0?o.content.push(h)-1:i;if(i<0&&r.push({type:"thinking_start",contentIndex:y,partial:o}),h.type!=="thinking")throw new Error("invalid thinking block");h.thinking+=f.reasoning_content,r.push({type:"thinking_delta",contentIndex:y,delta:f.reasoning_content,partial:o})}if(f?.content){const i=o.content.findIndex(v=>v.type==="text"),h=i<0?{type:"text",text:""}:o.content[i],y=i<0?o.content.push(h)-1:i;if(i<0&&r.push({type:"text_start",contentIndex:y,partial:o}),h.type!=="text")throw new Error("invalid text block");h.text+=f.content,r.push({type:"text_delta",contentIndex:y,delta:f.content,partial:o})}for(const i of f?.tool_calls??[]){const h={type:"toolCall",id:i.id,name:i.function.name,arguments:JSON.parse(i.function.arguments)},y=o.content.push(h)-1;r.push({type:"toolcall_start",contentIndex:y,partial:o}),r.push({type:"toolcall_end",contentIndex:y,toolCall:h,partial:o})}m?.finish_reason&&(w=m.finish_reason),m?.message?.refusal&&(M=m.message.refusal),s.usage&&(T=!0,o.usage={input:s.usage.prompt_tokens,output:s.usage.completion_tokens,cacheRead:s.usage.cache_read_tokens,cacheWrite:s.usage.cache_write_tokens,totalTokens:s.usage.total_tokens,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:typeof s.usage.usd_equivalent=="number"&&Number.isFinite(s.usage.usd_equivalent)?s.usage.usd_equivalent:0}})},"apply");for(;;){const{value:l,done:s}=await U.read();if(s)break;g+=I.decode(l,{stream:!0});let m;for(;(m=g.indexOf(`
2
+ `))>=0;){const f=g.slice(0,m).trim();g=g.slice(m+1),f&&$(f)}}if(g+=I.decode(),g.trim()&&$(g.trim()),!T||!O)throw new Error("llm stream ended without usage or served_model");for(const[l,s]of o.content.entries())s.type==="text"&&r.push({type:"text_end",contentIndex:l,content:s.text,partial:o}),s.type==="thinking"&&r.push({type:"thinking_end",contentIndex:l,content:s.thinking,partial:o});if(w==="content_filter")throw new q(M??"Content filtered");if(!["stop","tool_calls","length"].includes(w))throw new Error(`llm returned unknown finish reason: ${w}`);o.stopReason=w==="tool_calls"?"toolUse":w==="length"?"length":"stop",r.push({type:"done",reason:o.stopReason,message:o}),r.end();return}})().catch(a=>{o.stopReason=n.signal?.aborted?"aborted":"error",o.errorMessage=a instanceof Error?a.message:String(a),a instanceof k&&(o.diagnostics=[{type:N,timestamp:Date.now(),...a.retryAt!==void 0?{details:{retryAt:a.retryAt}}:{}},...a.rateLimit!==void 0?[b({status:429,ratelimit:a.rateLimit})]:[],...a.rateLimit===void 0&&a.code==="cap_exhausted"?[b({status:a.status??402,code:a.code})]:[]]),a instanceof S&&(o.diagnostics=[b({status:503,code:R})]),a instanceof q&&o.stopReason==="error"&&(o.rawStopReason="content_filter"),r.push({type:"error",reason:o.stopReason,error:o}),r.end()}),r}d(ne,"streamBrokerModel");export{Z as LLM_BROKER_RETRY_WINDOW_S,N as LLM_REFUSAL_DIAGNOSTIC,me as configureBrokerModelRuntime,he as refusalRetryAt,te as requestBody,ne as streamBrokerModel,ee as wireHistory};
@@ -1,2 +1,2 @@
1
- var x=Object.defineProperty;var u=(t,e)=>x(t,"name",{value:e,configurable:!0});import{getNode as S,cancelCronsOnWake as J,transitionStatusIntent as b}from"../canvas/index.js";import{composeNodeOutcomeDocument as A}from"./outcome-document.js";import{withCanvasWrite as E}from"../canvas/db.js";import{nodeObject as D}from"../graph/objects.js";import{endMessageSubscriptions as L}from"../graph/watches.js";import{runStructuredRequestEffects as P,settlePendingStructuredRequestForNodeIn as $}from"../canvas/structured-requests.js";const m="*",j={finish:"finished",park:"parked",cancel:"stranded",crash:"crashed","refresh-launch":null,revive:null},l=["active","idle"],q={finish:{status:"done",intent:"done",from:l},park:{status:"done",intent:"parked",from:l},cancel:{status:"canceled",intent:null,from:m},crash:{status:"dead",from:l},yield:{intent:"refresh",from:l},release:{status:"idle",intent:"idle-release",from:[...l,"done"]},"refresh-launch":{status:"active",intent:"refresh",from:m},revive:{status:"active",intent:null,from:m}};function I(t,e){const r=t.split(`
2
- `)[0]??"";let n=0,c="";for(const d of r){const i=Buffer.byteLength(d,"utf8");if(n+i>e)break;n+=i,c+=d}return c}u(I,"firstLineAtMost");function M(t){if(t===void 0)return null;const e=t.message===void 0?void 0:I(t.message,500),r={...t,...e===void 0?{}:{message:e}},n=JSON.stringify(r);return Buffer.byteLength(n,"utf8")<=4*1024?n:JSON.stringify({schema:"crtr.node-outcome-detail/v1",truncated:!0,...e===void 0?{}:{message:e}})}u(M,"detailJson");function T(t,e){let r,n;return e==="finished"||e==="declined"?(r="done",n="done"):e==="parked"?(r="done",n="parked"):e==="closed"||e==="retired"||e==="stranded"||e==="deadline_exceeded"?(r="canceled",n=null):(r="dead",n=null),{...t,status:r,intent:n,terminal_reason:e}}u(T,"nodeAtOutcome");function v(t,e,r){const n=q[e],c=Object.prototype.hasOwnProperty.call(n,"status"),d=Object.prototype.hasOwnProperty.call(n,"intent"),i=r?.reason??j[e],f={};c&&(f.status=n.status),d&&(f.intent=n.intent??null),i!==void 0&&(f.terminal_reason=i);const a=e==="finish"||e==="park"||e==="cancel"||e==="crash",s=a?S(t):null,p=a&&s?.kind!=="human",h=i,_=a?new Date().toISOString():null,w=M(r?.outcome?.detail),y=p&&s?.run_id!=null&&i!=="parked"&&s.outcome_at!==null&&(l.includes(s.status)||s.outcome_reason==="parked");let k;p&&s!==null&&(s.outcome_at===null||y)&&(k=JSON.stringify(A({node:T(s,h),outcome:{kind:"failure",reason:h,at:_,revision:(s.outcome_revision??0)+1,detailJson:w}})));const N=a?i==="deadline_exceeded"?{intended:"expired",failureReason:"node_deadline_exceeded",failureDetail:r?.outcome?.detail??null}:{intended:"failed",failureReason:i,failureDetail:r?.outcome?.detail??null}:null,g=E(o=>{const O=b(t,f,{fromStatuses:n.from===m?void 0:n.from,requireNoFinalReport:e==="cancel",...p?{outcome:{kind:"failure",reason:h,detailJson:w,at:_,...y?{supersede:!0}:{},...k===void 0?{}:{documentJson:k}}}:{}});a&&O>0&&L(o,D(o,t),"done");const R=O===1&&N!==null?$(o,t,N):null;return{changes:O,settlement:R}});if(g.changes===0){const o=S(t);if(o===null)throw new Error(`transition: unknown node ${t}`);if(e==="cancel"&&o.final_report!==null&&o.final_report!==void 0)return o;throw new Error(`illegal lifecycle transition: '${e}' from status='${o.status}' (node ${t})`)}return g.settlement?.changed&&P(g.settlement.row),(e==="finish"||e==="park"||e==="cancel")&&J(t),S(t)}u(v,"transition");function G(t){return t.status==="done"&&t.intent==="parked"}u(G,"isParked");function H(t){return t?.intent==="idle-release"}u(H,"hasIdleReleaseIntent");export{H as hasIdleReleaseIntent,G as isParked,v as transition};
1
+ var x=Object.defineProperty;var u=(t,e)=>x(t,"name",{value:e,configurable:!0});import{getNode as S,cancelCronsOnWake as J,transitionStatusIntent as b}from"../canvas/index.js";import{composeNodeOutcomeDocument as A}from"./outcome-document.js";import{withCanvasWrite as E}from"../canvas/db.js";import{nodeObject as D}from"../graph/objects.js";import{endMessageSubscriptions as L}from"../graph/watches.js";import{settleNodeObjectWaits as j}from"../canvas/waits.js";import{runStructuredRequestEffects as P,settlePendingStructuredRequestForNodeIn as $}from"../canvas/structured-requests.js";const m="*",q={finish:"finished",park:"parked",cancel:"stranded",crash:"crashed","refresh-launch":null,revive:null},l=["active","idle"],M={finish:{status:"done",intent:"done",from:l},park:{status:"done",intent:"parked",from:l},cancel:{status:"canceled",intent:null,from:m},crash:{status:"dead",from:l},yield:{intent:"refresh",from:l},release:{status:"idle",intent:"idle-release",from:[...l,"done"]},"refresh-launch":{status:"active",intent:"refresh",from:m},revive:{status:"active",intent:null,from:m}};function T(t,e){const r=t.split(`
2
+ `)[0]??"";let n=0,c="";for(const d of r){const s=Buffer.byteLength(d,"utf8");if(n+s>e)break;n+=s,c+=d}return c}u(T,"firstLineAtMost");function W(t){if(t===void 0)return null;const e=t.message===void 0?void 0:T(t.message,500),r={...t,...e===void 0?{}:{message:e}},n=JSON.stringify(r);return Buffer.byteLength(n,"utf8")<=4*1024?n:JSON.stringify({schema:"crtr.node-outcome-detail/v1",truncated:!0,...e===void 0?{}:{message:e}})}u(W,"detailJson");function B(t,e){let r,n;return e==="finished"||e==="declined"?(r="done",n="done"):e==="parked"?(r="done",n="parked"):e==="closed"||e==="retired"||e==="stranded"||e==="deadline_exceeded"?(r="canceled",n=null):(r="dead",n=null),{...t,status:r,intent:n,terminal_reason:e}}u(B,"nodeAtOutcome");function H(t,e,r){const n=M[e],c=Object.prototype.hasOwnProperty.call(n,"status"),d=Object.prototype.hasOwnProperty.call(n,"intent"),s=r?.reason??q[e],f={};c&&(f.status=n.status),d&&(f.intent=n.intent??null),s!==void 0&&(f.terminal_reason=s);const a=e==="finish"||e==="park"||e==="cancel"||e==="crash",o=a?S(t):null,p=a&&o?.kind!=="human",h=s,_=a?new Date().toISOString():null,w=W(r?.outcome?.detail),N=p&&o?.run_id!=null&&s!=="parked"&&o.outcome_at!==null&&(l.includes(o.status)||o.outcome_reason==="parked");let k;p&&o!==null&&(o.outcome_at===null||N)&&(k=JSON.stringify(A({node:B(o,h),outcome:{kind:"failure",reason:h,at:_,revision:(o.outcome_revision??0)+1,detailJson:w}})));const y=a?s==="deadline_exceeded"?{intended:"expired",failureReason:"node_deadline_exceeded",failureDetail:r?.outcome?.detail??null}:{intended:"failed",failureReason:s,failureDetail:r?.outcome?.detail??null}:null,O=E(i=>{const g=b(t,f,{fromStatuses:n.from===m?void 0:n.from,requireNoFinalReport:e==="cancel",...p?{outcome:{kind:"failure",reason:h,detailJson:w,at:_,...N?{supersede:!0}:{},...k===void 0?{}:{documentJson:k}}}:{}});a&&g>0&&(L(i,D(i,t),"done"),j(i,t));const R=g===1&&y!==null?$(i,t,y):null;return{changes:g,settlement:R}});if(O.changes===0){const i=S(t);if(i===null)throw new Error(`transition: unknown node ${t}`);if(e==="cancel"&&i.final_report!==null&&i.final_report!==void 0)return i;throw new Error(`illegal lifecycle transition: '${e}' from status='${i.status}' (node ${t})`)}return O.settlement?.changed&&P(O.settlement.row),(e==="finish"||e==="park"||e==="cancel")&&J(t),S(t)}u(H,"transition");function K(t){return t.status==="done"&&t.intent==="parked"}u(K,"isParked");function Q(t){return t?.intent==="idle-release"}u(Q,"hasIdleReleaseIntent");export{Q as hasIdleReleaseIntent,K as isParked,H as transition};
@@ -1 +1 @@
1
- var J=Object.defineProperty;var a=(e,t)=>J(e,"name",{value:t,configurable:!0});import{envHomeOverride as K,envRuntimeHomeOverride as X,INBOX_BETWEEN_TURNS_ENV as z,SCOPES_FILE_ENV as V,scopeAllowed as C}from"../../shared/env.js";import{isOwnMemoryScope as Y,validateScopes as Q}from"../scopes.js";import{activeGrant as B,terminalGrant as k}from"../grants/mirror.js";import{appendRunEvent as Z}from"../runs/events.js";import{readConfig as S}from"../config.js";import{InputError as T}from"../io.js";import{ApiError as w}from"../../api/errors.js";import{assertGranteeRunsHere as ee}from"./unsandboxed.js";import{randomBytes as ne}from"node:crypto";import{existsSync as P}from"node:fs";import{resolve as re,relative as te,isAbsolute as H,join as ie,sep as oe}from"node:path";import{appSpace as le}from"../layout.js";import{scopesFilePath as ae}from"../canvas/paths.js";import{createNode as se,assertRunNotDeleting as de,conversationOwner as ue,getNode as c,getRow as ce}from"../canvas/index.js";import{ensureAppSpace as fe}from"../spaces/space.js";import{LAUNCHER_SOCKET as L}from"../../launcher/spec.js";import{resolveInstallId as pe}from"../canvas/install-id.js";import{isAppRun as me}from"../canvas/app-run.js";import{canvasDbPath as _e,contextDir as ge}from"../canvas/paths.js";import{conversationIdFromPath as we}from"../conversation-store/index.js";import{usage as U}from"../errors.js";import{assertProfileAvailableForBirth as he}from"../profiles/deletion-reservation.js";import{loadExactProfileManifest as ve}from"../profiles/manifest.js";import{admitNodeCreate as Re}from"../command-hooks/lifecycle-create.js";import{getById as Ee,nodeObject as I,ownerForGrantee as ye}from"../graph/objects.js";import{recordActedOn as Ce}from"../graph/edges.js";import{recordWatch as Ie,copyWatchesForFork as Ne}from"../graph/watches.js";import{publish as ke}from"../graph/events.js";let O;const j=new Map;function cn(e){O=e}a(cn,"setInstallId");function Se(){if(O!==void 0)return O;const e=_e(),t=j.get(e);if(t!==void 0)return t;const o=pe();return j.set(e,o),o}a(Se,"installId");function Te(){return`${Date.now().toString(36)}-${ne(4).toString("hex")}`}a(Te,"newJobId");function Oe(){return`${Se()}-${Te()}`}a(Oe,"newNodeId");const x=128,xe=/^[a-z0-9-]+$/;class q extends Error{static{a(this,"NodeIdConflictError")}nodeId;constructor(t){super(`node already exists: ${t}`),this.nodeId=t,this.name="NodeIdConflictError"}}function D(e){if(e.length===0||e.length>x)throw U(`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(!xe.test(e))throw U(`invalid --node-id '${e}': must contain only lowercase letters, digits, and hyphens (e.g. 'remote42', 'my-node-1')`,{next:"Use only lowercase letters, digits, and hyphens."})}a(D,"validateNodeIdFormat");function fn(e){if(D(e),c(e)!==null)throw new q(e)}a(fn,"preflightNodeId");function De(e){if(D(e),c(e)!==null)throw new q(e);return e}a(De,"validatedExplicitNodeId");function pn(e){let t=e;const o=new Set;for(;;){if(o.has(t))return t;o.add(t);const s=ce(t);if(s===null||s.parent==null)return t;t=s.parent}}a(pn,"rootOfSpine");function mn(){const e=process.env;return{nodeId:e.CRTR_NODE_ID??null,kind:e.CRTR_KIND??null,mode:e.CRTR_MODE??null}}a(mn,"currentNodeContext");function be(e){return me(e)}a(be,"inboxBetweenTurns");function _n(e){const t={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&&(t.CRTR_PARENT_NODE_ID=e.parent),e.run_id&&(t.CRTR_RUN_ID=e.run_id),be(e)&&(t[z]="1"),e.scopes!==null&&e.scopes!==void 0&&(t[V]=ae(e.node_id)),t.CRTR_GRANTEE=e.grantee??"app:terminal";const o=K();o!==void 0&&o!==""&&(t.CRTR_HOME=o);const s=X();s!==void 0&&s!==""&&(t.CRTR_RUNTIME_HOME=s);const f=process.env.CRTR_SUBTREE;return f!==void 0&&f!==""&&(t.CRTR_SUBTREE=f),{...t,...e.launch?.env??{}}}a(_n,"nodeEnv");function Ae(e){const t=we(e),o=t===null?null:ue(t);return o===null?null:c(o)}a(Ae,"forkSourceNode");async function gn(e){const t=e.nodeId!==void 0?(D(e.nodeId),e.nodeId):Oe(),o=e.parent??null,s=o===null,f=e.spawnedBy??o,m=e.creator??null,b=e.mode??"base",N=e.lifecycle??(s?"resident":"terminal"),h=e.run===void 0?k():null,n={node_id:t,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: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?t: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},E=e.scopes===void 0?void 0:Q(e.scopes),v=o===null?null:c(o);if(o!==null&&v===null)throw new Error(`cannot spawn under unknown parent node: ${o}`);if(v?.run_id&&c(v.run_id)?.deleting_at)throw new Error("run is deleting");const u=m===null?null:c(m);if(m!==null&&u===null)throw new Error(`cannot spawn from unknown creator node: ${m}`);if(u?.run_id&&c(u.run_id)?.deleting_at)throw new Error("run is deleting");const A=e.forkSourceFile==null?null:Ae(e.forkSourceFile),$=e.replaces==null?null:c(e.replaces),_=v??A??$??u??(f===null?null:c(f)),F=[v,u,A,$,f===null?null:c(f)].some(r=>r?.isolated===!0||r?.run_id!=null&&c(r.run_id)?.isolated===!0);if(F&&e.isolated===!1)throw new w(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});if(n.isolated=F||e.isolated===!0,_!==null){const r=u??_;if(u&&_.grantee&&u.grantee&&u.grantee!==_.grantee)throw new w(403,"other_app_run","cannot create under another app's node");const i=u&&u!==_?[_,u]:[_],d=[],g=new Set;for(const l of i){l.scopes!==null&&l.scopes!==void 0&&d.push(l.scopes);const p=l.grantee==="app:terminal"?k():l.grant_id?B(l.grant_id):null;if(p){const R=[...p.resolved];g.add(R),d.push(R)}else if(!(l.grantee==="app:terminal"&&!S("user").runtime?.issuer)){const R=[];g.add(R),d.push(R)}}const y=a(l=>Y(l,r.grantee==="user:local"?"app:terminal":r.grantee??"app:terminal"),"ownMemory"),M=a((l,p)=>y(p)&&g.has(l)||C(l,p),"allowed");if(E!==void 0&&!E.every(l=>d.every(p=>M(p,l))))throw new T({error:"unknown_scope",message:"child scopes must be covered by the creating node, its source and their current grants",received:E,field:"scopes",next:"Request only scopes the creating node currently holds."});if(n.scopes=E??(i.every(l=>l.scopes==null)&&i.every(l=>l.grant_id===r.grant_id&&l.sub===r.sub)?null:i.length===1?_.scopes??null:[...new Set(d.flat().filter(l=>d.every(p=>M(p,l))))]),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=r.grantee==="user:local"?"app:terminal":r.grantee,n.sub=r.sub??(n.grantee==="app:terminal"?h?.sub:null)??null,n.grant_id=r.grant_id??(n.grantee==="app:terminal"?h?.id:null)??null,n.client_id=r.client_id,n.run_id=s?t:r.run_id??null}if(ee(n.grantee),e.run===void 0&&n.grantee==="app:terminal"&&S("user").runtime?.issuer&&h===null)throw new w(403,"scope_missing","terminal grant is not active");if(n.isolated){if(process.platform!=="linux"||!P(L))throw new w(400,"isolated_unavailable","isolated nodes require the Linux root launcher");if(e.managedWorktree!==void 0)throw new w(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const r=ie(le(n.grantee??"app:terminal").nodes,t),i=te(r,re(n.cwd));if(!H(n.cwd)||i===".."||i.startsWith(`..${oe}`)||H(i))throw new w(400,"invalid_request","isolated node cwd must be inside its own folder",{param:"cwd"})}if(n.profile_id){const r=ve(n.profile_id).manifest.grantee??"app:terminal";if(r!==n.grantee){const i=`crtr:memory:rw:${r}:profile:${n.profile_id}`,d=n.grantee==="app:terminal"?k():n.grant_id?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(he(n.profile_id??null),!n.grantee)throw new Error("node creation requires an app");const W=await Re({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=a((r,i)=>{const d=I(r,i.node_id),g=m??o??e.actor??f??null,y=g!==null&&Ee(r,g)?.type==="node"?I(r,g):ye(r,e.run===void 0?"user:local":i.grantee);Ce(r,y,d),o!==null&&e.subscribe!==!1&&Ie(r,I(r,o),d,"spawn"),e.forkSourceNode&&Ne(r,I(r,e.forkSourceNode),d),ke(r,{event:"created",object:d,actor:y,attrs:{name:i.name,kind:i.kind,creator:m??null}})},"recordBirth");try{e.nodeId!==void 0&&De(e.nodeId),await e.beforeCreate?.(),(process.platform!=="linux"||P(L))&&await fe(n.grantee??"app:terminal"),se(n,{outcomeDelivery:e.outcomeDelivery,withinCreate:a((r,i)=>{i.run_id&&i.run_id!==i.node_id&&de(r,i.run_id),s&&e.run===void 0&&Z(r,t,t,"run.started",{scopes:i.scopes??null,payer:"user",usage:JSON.parse(i.usage_json)}),e.withinCreate?.(r,i),G(r,i)},"withinCreate")})}finally{W()}return n}a(gn,"spawnNode");export{q as NodeIdConflictError,mn as currentNodeContext,Ae as forkSourceNode,be as inboxBetweenTurns,Te as newJobId,Oe as newNodeId,_n as nodeEnv,fn as preflightNodeId,pn as rootOfSpine,cn as setInstallId,gn as spawnNode};
1
+ var J=Object.defineProperty;var a=(e,t)=>J(e,"name",{value:t,configurable:!0});import{envHomeOverride as K,envRuntimeHomeOverride as X,INBOX_BETWEEN_TURNS_ENV as z,SCOPES_FILE_ENV as V,scopeAllowed as C}from"../../shared/env.js";import{isOwnMemoryScope as Y,validateScopes as Q}from"../scopes.js";import{activeGrant as B,terminalGrant as k}from"../grants/mirror.js";import{grantSnapshotMissing as Z}from"../../daemon/readiness.js";import{appendRunEvent as ee}from"../runs/events.js";import{readConfig as S}from"../config.js";import{InputError as T}from"../io.js";import{ApiError as w}from"../../api/errors.js";import{assertGranteeRunsHere as ne}from"./unsandboxed.js";import{randomBytes as re}from"node:crypto";import{existsSync as P}from"node:fs";import{resolve as te,relative as ie,isAbsolute as H,join as oe,sep as le}from"node:path";import{appSpace as ae}from"../layout.js";import{scopesFilePath as se}from"../canvas/paths.js";import{createNode as de,assertRunNotDeleting as ue,conversationOwner as ce,getNode as c,getRow as fe}from"../canvas/index.js";import{ensureAppSpace as pe}from"../spaces/space.js";import{LAUNCHER_SOCKET as L}from"../../launcher/spec.js";import{resolveInstallId as me}from"../canvas/install-id.js";import{isAppRun as _e}from"../canvas/app-run.js";import{canvasDbPath as ge,contextDir as we}from"../canvas/paths.js";import{conversationIdFromPath as he}from"../conversation-store/index.js";import{usage as U}from"../errors.js";import{assertProfileAvailableForBirth as ve}from"../profiles/deletion-reservation.js";import{loadExactProfileManifest as Re}from"../profiles/manifest.js";import{admitNodeCreate as Ee}from"../command-hooks/lifecycle-create.js";import{getById as ye,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";let O;const j=new Map;function pn(e){O=e}a(pn,"setInstallId");function Te(){if(O!==void 0)return O;const e=ge(),t=j.get(e);if(t!==void 0)return t;const o=me();return j.set(e,o),o}a(Te,"installId");function Oe(){return`${Date.now().toString(36)}-${re(4).toString("hex")}`}a(Oe,"newJobId");function xe(){return`${Te()}-${Oe()}`}a(xe,"newNodeId");const x=128,De=/^[a-z0-9-]+$/;class q extends Error{static{a(this,"NodeIdConflictError")}nodeId;constructor(t){super(`node already exists: ${t}`),this.nodeId=t,this.name="NodeIdConflictError"}}function D(e){if(e.length===0||e.length>x)throw U(`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(!De.test(e))throw U(`invalid --node-id '${e}': must contain only lowercase letters, digits, and hyphens (e.g. 'remote42', 'my-node-1')`,{next:"Use only lowercase letters, digits, and hyphens."})}a(D,"validateNodeIdFormat");function mn(e){if(D(e),c(e)!==null)throw new q(e)}a(mn,"preflightNodeId");function be(e){if(D(e),c(e)!==null)throw new q(e);return e}a(be,"validatedExplicitNodeId");function _n(e){let t=e;const o=new Set;for(;;){if(o.has(t))return t;o.add(t);const s=fe(t);if(s===null||s.parent==null)return t;t=s.parent}}a(_n,"rootOfSpine");function gn(){const e=process.env;return{nodeId:e.CRTR_NODE_ID??null,kind:e.CRTR_KIND??null,mode:e.CRTR_MODE??null}}a(gn,"currentNodeContext");function Ae(e){return _e(e)}a(Ae,"inboxBetweenTurns");function wn(e){const t={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:we(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&&(t.CRTR_PARENT_NODE_ID=e.parent),e.run_id&&(t.CRTR_RUN_ID=e.run_id),Ae(e)&&(t[z]="1"),e.scopes!==null&&e.scopes!==void 0&&(t[V]=se(e.node_id)),t.CRTR_GRANTEE=e.grantee??"app:terminal";const o=K();o!==void 0&&o!==""&&(t.CRTR_HOME=o);const s=X();s!==void 0&&s!==""&&(t.CRTR_RUNTIME_HOME=s);const f=process.env.CRTR_SUBTREE;return f!==void 0&&f!==""&&(t.CRTR_SUBTREE=f),{...t,...e.launch?.env??{}}}a(wn,"nodeEnv");function Me(e){const t=he(e),o=t===null?null:ce(t);return o===null?null:c(o)}a(Me,"forkSourceNode");async function hn(e){const t=e.nodeId!==void 0?(D(e.nodeId),e.nodeId):xe(),o=e.parent??null,s=o===null,f=e.spawnedBy??o,m=e.creator??null,b=e.mode??"base",N=e.lifecycle??(s?"resident":"terminal"),h=e.run===void 0?k():null,n={node_id:t,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: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?t: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},E=e.scopes===void 0?void 0:Q(e.scopes),v=o===null?null:c(o);if(o!==null&&v===null)throw new Error(`cannot spawn under unknown parent node: ${o}`);if(v?.run_id&&c(v.run_id)?.deleting_at)throw new Error("run is deleting");const u=m===null?null:c(m);if(m!==null&&u===null)throw new Error(`cannot spawn from unknown creator node: ${m}`);if(u?.run_id&&c(u.run_id)?.deleting_at)throw new Error("run is deleting");const A=e.forkSourceFile==null?null:Me(e.forkSourceFile),M=e.replaces==null?null:c(e.replaces),_=v??A??M??u??(f===null?null:c(f)),$=[v,u,A,M,f===null?null:c(f)].some(r=>r?.isolated===!0||r?.run_id!=null&&c(r.run_id)?.isolated===!0);if($&&e.isolated===!1)throw new w(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});if(n.isolated=$||e.isolated===!0,_!==null){const r=u??_;if(u&&_.grantee&&u.grantee&&u.grantee!==_.grantee)throw new w(403,"other_app_run","cannot create under another app's node");const i=u&&u!==_?[_,u]:[_],d=[],g=new Set;for(const l of i){l.scopes!==null&&l.scopes!==void 0&&d.push(l.scopes);const p=l.grantee==="app:terminal"?k():l.grant_id?B(l.grant_id):null;if(p){const R=[...p.resolved];g.add(R),d.push(R)}else if(!(l.grantee==="app:terminal"&&!S("user").runtime?.issuer)){const R=[];g.add(R),d.push(R)}}const y=a(l=>Y(l,r.grantee==="user:local"?"app:terminal":r.grantee??"app:terminal"),"ownMemory"),F=a((l,p)=>y(p)&&g.has(l)||C(l,p),"allowed");if(E!==void 0&&!E.every(l=>d.every(p=>F(p,l))))throw new T({error:"unknown_scope",message:"child scopes must be covered by the creating node, its source and their current grants",received:E,field:"scopes",next:"Request only scopes the creating node currently holds."});if(n.scopes=E??(i.every(l=>l.scopes==null)&&i.every(l=>l.grant_id===r.grant_id&&l.sub===r.sub)?null:i.length===1?_.scopes??null:[...new Set(d.flat().filter(l=>d.every(p=>F(p,l))))]),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=r.grantee==="user:local"?"app:terminal":r.grantee,n.sub=r.sub??(n.grantee==="app:terminal"?h?.sub:null)??null,n.grant_id=r.grant_id??(n.grantee==="app:terminal"?h?.id:null)??null,n.client_id=r.client_id,n.run_id=s?t:r.run_id??null}if(ne(n.grantee),e.run===void 0&&n.grantee==="app:terminal"&&S("user").runtime?.issuer&&h===null)throw Z()?new w(503,"runtime_starting","the first grant snapshot from the directory has not committed"):new w(403,"scope_missing","terminal grant is not active");if(n.isolated){if(process.platform!=="linux"||!P(L))throw new w(400,"isolated_unavailable","isolated nodes require the Linux root launcher");if(e.managedWorktree!==void 0)throw new w(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const r=oe(ae(n.grantee??"app:terminal").nodes,t),i=ie(r,te(n.cwd));if(!H(n.cwd)||i===".."||i.startsWith(`..${le}`)||H(i))throw new w(400,"invalid_request","isolated node cwd must be inside its own folder",{param:"cwd"})}if(n.profile_id){const r=Re(n.profile_id).manifest.grantee??"app:terminal";if(r!==n.grantee){const i=`crtr:memory:rw:${r}:profile:${n.profile_id}`,d=n.grantee==="app:terminal"?k():n.grant_id?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 W=await Ee({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=a((r,i)=>{const d=I(r,i.node_id),g=m??o??e.actor??f??null,y=g!==null&&ye(r,g)?.type==="node"?I(r,g):Ce(r,e.run===void 0?"user:local":i.grantee);Ie(r,y,d),o!==null&&e.subscribe!==!1&&Ne(r,I(r,o),d,"spawn"),e.forkSourceNode&&ke(r,I(r,e.forkSourceNode),d),Se(r,{event:"created",object:d,actor:y,attrs:{name:i.name,kind:i.kind,creator:m??null}})},"recordBirth");try{e.nodeId!==void 0&&be(e.nodeId),await e.beforeCreate?.(),(process.platform!=="linux"||P(L))&&await pe(n.grantee??"app:terminal"),de(n,{outcomeDelivery:e.outcomeDelivery,withinCreate:a((r,i)=>{i.run_id&&i.run_id!==i.node_id&&ue(r,i.run_id),s&&e.run===void 0&&ee(r,t,t,"run.started",{scopes:i.scopes??null,payer:"user",usage:JSON.parse(i.usage_json)}),e.withinCreate?.(r,i),G(r,i)},"withinCreate")})}finally{W()}return n}a(hn,"spawnNode");export{q as NodeIdConflictError,gn as currentNodeContext,Me as forkSourceNode,Ae as inboxBetweenTurns,Oe as newJobId,xe as newNodeId,wn as nodeEnv,mn as preflightNodeId,_n as rootOfSpine,pn as setInstallId,hn as spawnNode};