@north-light/cli 0.3.534 → 0.3.536

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 (67) hide show
  1. package/dist/api/client.d.ts +3 -0
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/contract.d.ts +1 -1
  4. package/dist/api/contract.js +1 -1
  5. package/dist/api/dto/nodes.d.ts +2 -0
  6. package/dist/api/dto/recovery.d.ts +2 -1
  7. package/dist/builtin-memory/crouter-sdk/release-notes.md +1 -1
  8. package/dist/clients/attach/viewer.js +406 -406
  9. package/dist/commands/api-client.d.ts +1 -0
  10. package/dist/commands/api-client.js +3 -3
  11. package/dist/commands/node/inspect.js +9 -9
  12. package/dist/core/bash-jobs.d.ts +4 -0
  13. package/dist/core/bash-jobs.js +8 -8
  14. package/dist/core/canvas/canvas.d.ts +26 -2
  15. package/dist/core/canvas/canvas.js +19 -19
  16. package/dist/core/canvas/crons.d.ts +4 -2
  17. package/dist/core/canvas/crons.js +1 -1
  18. package/dist/core/canvas/paths.d.ts +5 -0
  19. package/dist/core/canvas/paths.js +1 -1
  20. package/dist/core/canvas/recovery.d.ts +7 -0
  21. package/dist/core/canvas/recovery.js +2 -2
  22. package/dist/core/canvas/remote-canvas-source.d.ts +1 -0
  23. package/dist/core/canvas/remote-canvas-source.js +1 -1
  24. package/dist/core/canvas/source.d.ts +4 -0
  25. package/dist/core/canvas/source.js +1 -1
  26. package/dist/core/canvas/tree.d.ts +2 -0
  27. package/dist/core/canvas/tree.js +12 -5
  28. package/dist/core/feed/feed.js +3 -3
  29. package/dist/core/feed/messages.d.ts +6 -0
  30. package/dist/core/feed/messages.js +5 -5
  31. package/dist/core/graph/kinds/node.js +5 -5
  32. package/dist/core/graph/watches.d.ts +0 -2
  33. package/dist/core/graph/watches.js +7 -12
  34. package/dist/core/providers/mint.d.ts +2 -0
  35. package/dist/core/providers/mint.js +1 -1
  36. package/dist/core/runtime/broker/cli-relay.js +1 -1
  37. package/dist/core/runtime/broker/fault-retry.d.ts +18 -0
  38. package/dist/core/runtime/broker/fault-retry.js +1 -1
  39. package/dist/core/runtime/broker/run-turn-end.d.ts +3 -2
  40. package/dist/core/runtime/broker/run-turn-end.js +1 -1
  41. package/dist/core/runtime/launch.js +2 -2
  42. package/dist/core/runtime/lifecycle.d.ts +4 -0
  43. package/dist/core/runtime/lifecycle.js +2 -2
  44. package/dist/core/runtime/nodes.js +1 -1
  45. package/dist/core/runtime/revive.d.ts +1 -1
  46. package/dist/core/runtime/revive.js +1 -1
  47. package/dist/core/worktree-mutation-async.d.ts +10 -0
  48. package/dist/core/worktree-mutation-async.js +5 -5
  49. package/dist/core/worktree-sweep.js +6 -6
  50. package/dist/core/worktree.d.ts +1 -0
  51. package/dist/core/worktree.js +1 -1
  52. package/dist/daemon/api/handlers/broker-recovery.js +1 -1
  53. package/dist/daemon/api/handlers/messages.js +2 -2
  54. package/dist/daemon/api/handlers/nodes.js +1 -1
  55. package/dist/daemon/api/handlers/objects.js +3 -3
  56. package/dist/daemon/messaging/node-message.js +1 -1
  57. package/dist/daemon/peer/forward.js +1 -1
  58. package/dist/daemon/reconcilers/bash-deadline.d.ts +6 -0
  59. package/dist/daemon/reconcilers/bash-deadline.js +3 -2
  60. package/dist/daemon/runs/observer.d.ts +2 -0
  61. package/dist/daemon/runs/observer.js +2 -2
  62. package/docs/sdk/release-notes.md +1 -1
  63. package/package.json +2 -2
  64. package/packages/crouter-identity/package.json +1 -1
  65. package/runtime.lock.json +12 -12
  66. package/dist/core/canvas/nav-model.d.ts +0 -44
  67. package/dist/core/canvas/nav-model.js +0 -1
@@ -1 +1 @@
1
- var P=Object.defineProperty;var e=(t,n)=>P(t,"name",{value:n,configurable:!0});import{join as r}from"node:path";import{mkdirSync as c}from"node:fs";import{appSpace as f,runtimeRoot as k,socketDir as m}from"../layout.js";import{envGrantee as R,envNodeId as j}from"../../shared/env.js";import{withDirBeneath as h}from"../spaces/open-beneath.js";function i(){return k()}e(i,"crtrHome");function H(){return r(i(),"canvas.db")}e(H,"canvasDbPath");function B(){return r(i(),"on-disk-migration.lock")}e(B,"onDiskMigrationLockPath");function L(){return r(i(),"on-disk-migrations.json")}e(L,"onDiskMigrationMarkerPath");function O(t){return r(s(t),"view.sock")}e(O,"viewSocketPath");function D(){return r(i(),"nodes")}e(D,"nodesRoot");function w(){return r(i(),"reviews")}e(w,"reviewsRoot");function l(t){return p(t),r(w(),t)}e(l,"reviewDir");function $(t){return r(l(t),"result.json")}e($,"reviewResultPath");function F(t){c(l(t),{recursive:!0,mode:448})}e(F,"ensureReviewDir");function X(){return r(m(),"api.sock")}e(X,"apiSocketPath");function G(){return r(i(),"session-list-cache.json")}e(G,"sessionListCachePath");const b=128;function g(t){return typeof t=="string"&&t!==""&&t!=="."&&t!==".."&&!/[\\/\0]/u.test(t)&&Buffer.byteLength(t,"utf8")<=b}e(g,"isSafeNodeId");function p(t){if(!g(t))throw new TypeError("node_id must be a non-empty path segment of at most 128 UTF-8 bytes")}e(p,"assertSafeNodeId");function a(t){return p(t),r(D(),t)}e(a,"nodeRecordDir");const u=a;function x(t,n){p(t);const o=n??(j()===t?R():void 0);if(!o)throw new Error(`node ${t} has no app assignment`);return r(f(o).nodes,t)}e(x,"nodeAgentDir");function N(t,n){return r(x(t,n),"home")}e(N,"isolatedNodeHome");function J(t,n,o){return o?N(t,n):f(n).home}e(J,"nodeHome");function s(t){return p(t),process.platform==="linux"?r(m(),"nodes",t):r(a(t),"run")}e(s,"nodeRunDir");function U(t){return r(s(t),"scopes.json")}e(U,"scopesFilePath");function _(t,n){return r(x(t,n),"context")}e(_,"contextDir");function Y(t,n,o){return t.replaceAll("$CRTR_CONTEXT_DIR",_(n,o)).replaceAll("$CRTR_NODE_ID",n)}e(Y,"interpolateNodePaths");function S(t){return r(a(t),"job")}e(S,"jobDir");function T(t){return r(s(t),"job")}e(T,"nodeRunJobDir");function q(t){return r(u(t),"messages")}e(q,"messagesDir");function z(t){return r(u(t),"meta.json")}e(z,"nodeMetaPath");function K(t){return r(u(t),"inbox.jsonl")}e(K,"inboxPath");function Q(t){return r(s(t),"situational-context.md")}e(Q,"situationalContextPath");function V(t){return r(u(t),"passive.jsonl")}e(V,"passivePath");function W(t){return r(u(t),"transcript.jsonl")}e(W,"transcriptPath");function Z(t){return r(u(t),"session.ptr")}e(Z,"sessionPtrPath");function I(t,n){const o=x(t,n);c(a(t),{recursive:!0}),c(S(t),{recursive:!0});for(const v of["context","reports","attachments","worktree"])h(f(n).nodes,r(o,v),()=>{},!0);h(s(t),T(t),()=>{},!0)}e(I,"ensureNodeDirs");function tt(){c(D(),{recursive:!0})}e(tt,"ensureHome");export{X as apiSocketPath,H as canvasDbPath,_ as contextDir,i as crtrHome,tt as ensureHome,I as ensureNodeDirs,F as ensureReviewDir,K as inboxPath,Y as interpolateNodePaths,g as isSafeNodeId,N as isolatedNodeHome,S as jobDir,q as messagesDir,x as nodeAgentDir,u as nodeDir,J as nodeHome,z as nodeMetaPath,a as nodeRecordDir,s as nodeRunDir,T as nodeRunJobDir,D as nodesRoot,B as onDiskMigrationLockPath,L as onDiskMigrationMarkerPath,V as passivePath,l as reviewDir,$ as reviewResultPath,w as reviewsRoot,U as scopesFilePath,G as sessionListCachePath,Z as sessionPtrPath,Q as situationalContextPath,W as transcriptPath,O as viewSocketPath};
1
+ var v=Object.defineProperty;var e=(t,n)=>v(t,"name",{value:n,configurable:!0});import{join as r}from"node:path";import{mkdirSync as c}from"node:fs";import{appSpace as f,runtimeRoot as P,socketDir as m}from"../layout.js";import{envGrantee as k,envNodeId as E}from"../../shared/env.js";import{withDirBeneath as h}from"../spaces/open-beneath.js";function i(){return P()}e(i,"crtrHome");function B(){return r(i(),"canvas.db")}e(B,"canvasDbPath");function C(){return r(i(),"on-disk-migration.lock")}e(C,"onDiskMigrationLockPath");function L(){return r(i(),"on-disk-migrations.json")}e(L,"onDiskMigrationMarkerPath");function H(t){return r(u(t),"view.sock")}e(H,"viewSocketPath");function l(){return r(i(),"nodes")}e(l,"nodesRoot");function b(){return r(i(),"reviews")}e(b,"reviewsRoot");function D(t){return p(t),r(b(),t)}e(D,"reviewDir");function K(t){return r(D(t),"result.json")}e(K,"reviewResultPath");function $(t){c(D(t),{recursive:!0,mode:448})}e($,"ensureReviewDir");function F(){return r(m(),"api.sock")}e(F,"apiSocketPath");function G(){return r(i(),"session-list-cache.json")}e(G,"sessionListCachePath");const j=128;function w(t){return typeof t=="string"&&t!==""&&t!=="."&&t!==".."&&!/[\\/\0]/u.test(t)&&Buffer.byteLength(t,"utf8")<=j}e(w,"isSafeNodeId");function p(t){if(!w(t))throw new TypeError("node_id must be a non-empty path segment of at most 128 UTF-8 bytes")}e(p,"assertSafeNodeId");function a(t){return p(t),r(l(),t)}e(a,"nodeRecordDir");const s=a;function x(t,n){p(t);const o=n??(E()===t?k():void 0);if(!o)throw new Error(`node ${t} has no app assignment`);return r(f(o).nodes,t)}e(x,"nodeAgentDir");function _(t,n){return r(x(t,n),"home")}e(_,"isolatedNodeHome");function X(t,n,o){return o?_(t,n):f(n).home}e(X,"nodeHome");function u(t){return p(t),process.platform==="linux"?r(m(),"nodes",t):r(a(t),"run")}e(u,"nodeRunDir");const Y="broker-released",J="cli-relay-generation";function U(t){return r(u(t),"scopes.json")}e(U,"scopesFilePath");function g(t,n){return r(x(t,n),"context")}e(g,"contextDir");function q(t,n,o){return t.replaceAll("$CRTR_CONTEXT_DIR",g(n,o)).replaceAll("$CRTR_NODE_ID",n)}e(q,"interpolateNodePaths");function N(t){return r(a(t),"job")}e(N,"jobDir");function A(t){return r(u(t),"job")}e(A,"nodeRunJobDir");function z(t){return r(s(t),"messages")}e(z,"messagesDir");function Q(t){return r(s(t),"meta.json")}e(Q,"nodeMetaPath");function V(t){return r(s(t),"inbox.jsonl")}e(V,"inboxPath");function W(t){return r(u(t),"situational-context.md")}e(W,"situationalContextPath");function Z(t){return r(s(t),"passive.jsonl")}e(Z,"passivePath");function I(t){return r(s(t),"transcript.jsonl")}e(I,"transcriptPath");function tt(t){return r(s(t),"session.ptr")}e(tt,"sessionPtrPath");function et(t,n){const o=x(t,n);c(a(t),{recursive:!0}),c(N(t),{recursive:!0});for(const R of["context","reports","attachments","worktree"])h(f(n).nodes,r(o,R),()=>{},!0);h(u(t),A(t),()=>{},!0)}e(et,"ensureNodeDirs");function rt(){c(l(),{recursive:!0})}e(rt,"ensureHome");export{J as BROKER_RELAY_GENERATION,Y as BROKER_RELEASED_MARKER,F as apiSocketPath,B as canvasDbPath,g as contextDir,i as crtrHome,rt as ensureHome,et as ensureNodeDirs,$ as ensureReviewDir,V as inboxPath,q as interpolateNodePaths,w as isSafeNodeId,_ as isolatedNodeHome,N as jobDir,z as messagesDir,x as nodeAgentDir,s as nodeDir,X as nodeHome,Q as nodeMetaPath,a as nodeRecordDir,u as nodeRunDir,A as nodeRunJobDir,l as nodesRoot,C as onDiskMigrationLockPath,L as onDiskMigrationMarkerPath,Z as passivePath,D as reviewDir,K as reviewResultPath,b as reviewsRoot,U as scopesFilePath,G as sessionListCachePath,tt as sessionPtrPath,W as situationalContextPath,I as transcriptPath,H as viewSocketPath};
@@ -80,6 +80,13 @@ export declare function admitEpisode(nodeId: string, input: {
80
80
  faultId: string;
81
81
  retry: FaultRetry;
82
82
  }): Promise<boolean>;
83
+ /** Atomically settle only the admitted attempt that rejected its recovery prompt. A newer turn or episode must never be overwritten by its late result. */
84
+ export declare function settleRejectedEpisode(nodeId: string, input: {
85
+ sessionId: string;
86
+ faultId: string;
87
+ fault: FaultRecordInput;
88
+ reopen: boolean;
89
+ }): Promise<boolean>;
83
90
  export declare function reopenEpisode(nodeId: string, input: {
84
91
  sessionId: string;
85
92
  }): Promise<boolean>;
@@ -1,4 +1,4 @@
1
- var se=Object.defineProperty;var o=(e,t)=>se(e,"name",{value:t,configurable:!0});import{prepared as _}from"../sqlite-statements.js";import{randomUUID as I}from"node:crypto";import{existsSync as f,readdirSync as ae,readFileSync as de,realpathSync as ue,statSync as ce}from"node:fs";import{join as N,resolve as _e}from"node:path";import{withCanvasWrite as l,openDb as p}from"./db.js";import{jobDir as pe,nodesRoot as F}from"./paths.js";import{errorClassFromFault as X}from"../fault-classifier.js";import{parseProviderRateLimit as le}from"../provider-failure.js";import{emitEvent as $}from"../events/emit.js";import{operationIdContext as j,validateOperationId as B}from"../events/operation-id.js";const Me=5*6e4,Ee=500,J=["pi\u2192provider","viewer\u2194broker","relay\u2194broker","viewer\u2194crtrd","daemon\u2192node","crtr\u2192pi"],fe=[...J,"broker\u2194crtrd"],ye=["rate-limit","overloaded","connection","auth","protocol","context-overflow","other","wedged","model-not-found"],Te=["auto","manual","fatal"],Ne=["sdk","daemon","client"],ge=["pending","admitted","invalidated"];function u(){return new Date().toISOString()}o(u,"now");function C(e,t){return new Set(e.prepare(`PRAGMA table_info(${t})`).all().map(({name:n})=>n))}o(C,"tableColumns");function Le(e){const t=C(e,"executions"),n=[["turn_open_at","TEXT"],["turn_progress_at","TEXT"],["turn_closed_at","TEXT"],["turn_end_reason","TEXT CHECK (turn_end_reason IS NULL OR turn_end_reason IN ('settled', 'teardown-aborted'))"],["exit_diagnostic_json","TEXT"]];for(const[r,i]of n)t.has(r)||e.exec(`ALTER TABLE executions ADD COLUMN ${r} ${i}`)}o(Le,"addExecutionColumns");function me(e){e.exec(`
1
+ var ae=Object.defineProperty;var o=(e,t)=>ae(e,"name",{value:t,configurable:!0});import{prepared as _}from"../sqlite-statements.js";import{randomUUID as I}from"node:crypto";import{existsSync as f,readdirSync as se,readFileSync as de,realpathSync as ue,statSync as ce}from"node:fs";import{join as N,resolve as _e}from"node:path";import{withCanvasWrite as l,openDb as p}from"./db.js";import{jobDir as pe,nodesRoot as C}from"./paths.js";import{errorClassFromFault as B}from"../fault-classifier.js";import{parseProviderRateLimit as le}from"../provider-failure.js";import{emitEvent as J}from"../events/emit.js";import{operationIdContext as K,validateOperationId as Y}from"../events/operation-id.js";const Me=5*6e4,Ee=500,G=["pi\u2192provider","viewer\u2194broker","relay\u2194broker","viewer\u2194crtrd","daemon\u2192node","crtr\u2192pi"],fe=[...G,"broker\u2194crtrd"],ye=["rate-limit","overloaded","connection","auth","protocol","context-overflow","other","wedged","model-not-found"],Te=["auto","manual","fatal"],Ne=["sdk","daemon","client"],ge=["pending","admitted","invalidated"];function u(){return new Date().toISOString()}o(u,"now");function H(e,t){return new Set(e.prepare(`PRAGMA table_info(${t})`).all().map(({name:n})=>n))}o(H,"tableColumns");function Le(e){const t=H(e,"executions"),n=[["turn_open_at","TEXT"],["turn_progress_at","TEXT"],["turn_closed_at","TEXT"],["turn_end_reason","TEXT CHECK (turn_end_reason IS NULL OR turn_end_reason IN ('settled', 'teardown-aborted'))"],["exit_diagnostic_json","TEXT"]];for(const[r,i]of n)t.has(r)||e.exec(`ALTER TABLE executions ADD COLUMN ${r} ${i}`)}o(Le,"addExecutionColumns");function me(e){e.exec(`
2
2
  CREATE TABLE IF NOT EXISTS node_faults (
3
3
  fault_id TEXT PRIMARY KEY,
4
4
  node_id TEXT NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
@@ -23,4 +23,4 @@ CREATE TABLE IF NOT EXISTS node_faults (
23
23
  );
24
24
  CREATE UNIQUE INDEX IF NOT EXISTS idx_node_faults_active ON node_faults(node_id) WHERE active = 1;
25
25
  CREATE UNIQUE INDEX IF NOT EXISTS idx_node_faults_episode ON node_faults(node_id) WHERE episode_state IS NOT NULL;
26
- `)}o(me,"createFaultTable");function g(e,t){return typeof t=="string"&&e.includes(t)}o(g,"contains");function b(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(b,"isRecord");function K(e){if(!b(e)||typeof e.provider!="string"||e.provider===""||typeof e.model!="string"||e.model==="")return;const t=typeof e.status=="number"&&Number.isFinite(e.status)?e.status:void 0,n=typeof e.code=="string"||typeof e.code=="number"?e.code:void 0,r=typeof e.errno=="string"?e.errno:void 0,i=typeof e.body=="string"?e.body:void 0,s=le(e.ratelimit),d=Array.isArray(e.causes)?e.causes.flatMap(a=>{if(!b(a))return[];const E={...typeof a.message=="string"?{message:a.message}:{},...typeof a.status=="number"&&Number.isFinite(a.status)?{status:a.status}:{},...typeof a.code=="string"||typeof a.code=="number"?{code:a.code}:{},...typeof a.errno=="string"?{errno:a.errno}:{},...typeof a.body=="string"?{body:a.body}:{}};return Object.keys(E).length===0?[]:[E]}):void 0;return{provider:e.provider,model:e.model,...t===void 0?{}:{status:t},...n===void 0?{}:{code:n},...r===void 0?{}:{errno:r},...i===void 0?{}:{body:i},...d===void 0||d.length===0?{}:{causes:d},...s===void 0?{}:{ratelimit:s}}}o(K,"parseProviderError");function Y(e){try{const t=JSON.parse(e);if(!g(fe,t.link)||typeof t.op!="string"||t.op===""||!g(ye,t.kind)||typeof t.message!="string"||typeof t.since!="string")return null;const n=B(t.operation_id);if(!b(t.retry)||!g(Te,t.retry.disposition)||t.retry.by!==void 0&&!g(Ne,t.retry.by)||t.retry.attempt!==void 0&&typeof t.retry.attempt!="number"||t.retry.max!==void 0&&typeof t.retry.max!="number"||t.retry.nextAt!==void 0&&typeof t.retry.nextAt!="string"||t.anchorEntryId!==void 0&&(typeof t.anchorEntryId!="string"||t.anchorEntryId===""))return null;const r=t.providerError===void 0?void 0:K(t.providerError);return t.providerError!==void 0&&r===void 0?null:{link:t.link,op:t.op,kind:t.kind,retry:{disposition:t.retry.disposition,...t.retry.by===void 0?{}:{by:t.retry.by},...t.retry.attempt===void 0?{}:{attempt:t.retry.attempt},...t.retry.max===void 0?{}:{max:t.retry.max},...t.retry.nextAt===void 0?{}:{nextAt:t.retry.nextAt}},message:t.message,since:t.since,operation_id:n,...t.anchorEntryId===void 0?{}:{anchorEntryId:t.anchorEntryId},...r===void 0?{}:{providerError:r}}}catch{return null}}o(Y,"parseLegacyFault");function Ae(e){const t=_e(e);try{return ue(t)}catch{return t}}o(Ae,"canonicalPath");function Se(e){try{const t=JSON.parse(e);if(!g(ge,t.state)||typeof t.sessionFile!="string"||t.sessionFile==="")return null;const n=JSON.stringify(t.fault),r=Y(n);if(r===null||r.link!=="pi\u2192provider"||r.retry.disposition!=="auto"||r.retry.by!=="daemon")return null;const i=t.anchorEntryId;return i!==null&&typeof i!="string"||(r.anchorEntryId??null)!==i?null:{state:t.state,sessionFile:Ae(t.sessionFile),fault:r}}catch{return null}}o(Se,"parseLegacyEpisode");function G(e){return e!==void 0?B(e):j.current()??j.fresh(n=>n)}o(G,"resolveOperationId");function xe(e){return e.slice(0,Ee)}o(xe,"clampMessage");function w(e){return e.link==="pi\u2192provider"&&e.retry.disposition==="auto"&&e.retry.by==="daemon"?3:e.retry.disposition==="fatal"?2:1}o(w,"faultPriority");function q(e){return{link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operationId:e.operation_id,anchorEntryId:e.anchorEntryId??null,retryDisposition:e.retry.disposition,retryBy:e.retry.by??null,retryAttempt:e.retry.attempt??null,retryMax:e.retry.max??null,retryNextAt:e.retry.nextAt??null,providerError:e.providerError===void 0?null:JSON.stringify(e.providerError)}}o(q,"encodeFault");function H(e){const t=e.provider_error_json,n=typeof t=="string"?K(JSON.parse(t)):void 0;if(typeof t=="string"&&n===void 0)throw new Error(`invalid provider error on stored fault ${e.fault_id}`);const r={disposition:e.retry_disposition,...e.retry_by===null?{}:{by:e.retry_by},...e.retry_attempt===null?{}:{attempt:e.retry_attempt},...e.retry_max===null?{}:{max:e.retry_max},...e.retry_next_at===null?{}:{nextAt:e.retry_next_at}};return{faultId:e.fault_id,link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operation_id:e.operation_id,retry:r,...e.anchor_entry_id===null?{}:{anchorEntryId:e.anchor_entry_id},...n===void 0?{}:{providerError:n},...typeof e.notified_since=="string"?{notifiedSince:e.notified_since}:{}}}o(H,"hydrateFault");async function L(e,t){const n=await e.prepare("SELECT * FROM node_faults WHERE node_id = ? AND active = 1").get(t);return n===void 0?null:H(n)}o(L,"activeFault");function V(e){if(!g(J,e.link))throw new Error(`fault link is not durable: ${e.link}`);return{link:e.link,op:e.op,kind:e.kind,retry:e.retry,message:xe(e.message),since:e.since??u(),operation_id:G(e.operation_id),...typeof e.anchorEntryId=="string"&&e.anchorEntryId!==""?{anchorEntryId:e.anchorEntryId}:{},...e.providerError===void 0?{}:{providerError:e.providerError}}}o(V,"storedFault");function Q(e,t){$({level:t.retry.disposition==="fatal"?"error":"warn",event:"fault.recorded",operation_id:t.operation_id,node_id:e,error_class:X(t),fields:{link:t.link,op:t.op,kind:t.kind,retry:t.retry,message:t.message,...t.anchorEntryId===void 0?{}:{anchor_entry_id:t.anchorEntryId},...t.providerError===void 0?{}:{provider_error:t.providerError}}})}o(Q,"emitRecordedFault");const z="INSERT INTO node_faults (fault_id, node_id, link, op, kind, message, since, operation_id, anchor_entry_id, retry_disposition, retry_by, retry_attempt, retry_max, retry_next_at, provider_error_json, active, episode_state, episode_binding_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";function Z(e,t,n,r,i){const s=q(n),d=u();return[e,t,s.link,s.op,s.kind,s.message,s.since,s.operationId,s.anchorEntryId,s.retryDisposition,s.retryBy,s.retryAttempt,s.retryMax,s.retryNextAt,s.providerError,r?1:0,i?.state??null,i?.bindingId??null,d,d]}o(Z,"faultInsertParams");async function ee(e,t,n,r,i){const s=I();return await e.prepare(z).run(...Z(s,t,n,r,i)),{...n,faultId:s}}o(ee,"insertFault");async function te(e,t,n,r){const i=q(n);return await e.prepare("UPDATE node_faults SET link = ?, op = ?, kind = ?, message = ?, since = ?, operation_id = ?, anchor_entry_id = ?, retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, provider_error_json = ?, episode_state = COALESCE(?, episode_state), episode_binding_id = COALESCE(?, episode_binding_id), updated_at = ? WHERE fault_id = ?").run(i.link,i.op,i.kind,i.message,i.since,i.operationId,i.anchorEntryId,i.retryDisposition,i.retryBy,i.retryAttempt,i.retryMax,i.retryNextAt,i.providerError,r?.state??null,r?.bindingId??null,u(),t),{...n,faultId:t}}o(te,"updateFault");async function D(e,t){await e.prepare("DELETE FROM node_faults WHERE fault_id = ? AND active = 0 AND episode_state IS NULL").run(t)}o(D,"deleteIfUnretained");async function ne(e,t,n,r){const i=await L(e,t);if(i===null){const s=await e.prepare("SELECT fault_id, link, kind, since, operation_id FROM node_faults WHERE node_id = ? AND active = 0 AND episode_state IS NOT NULL").get(t);return s!==void 0&&s.link===n.link&&s.kind===n.kind&&s.since===n.since&&s.operation_id===n.operation_id?(await e.prepare("UPDATE node_faults SET active = 1, updated_at = ? WHERE fault_id = ?").run(u(),s.fault_id),await te(e,s.fault_id,n)):await ee(e,t,n,!0,r)}if(i.link===n.link&&i.kind===n.kind){const s=n.retry.disposition==="auto"?{...i.retry,...n.retry}:n.retry,d=i.retry.disposition===n.retry.disposition&&(n.retry.disposition!=="fatal"||i.message===n.message),a=d?i.since:n.since,E=await te(e,i.faultId,{...n,since:a,anchorEntryId:i.anchorEntryId??n.anchorEntryId,retry:s},r);return d||await e.prepare("UPDATE node_faults SET notified_since = NULL WHERE fault_id = ?").run(i.faultId),E}return i.link!==n.link&&w(i)>w(n)?null:(await e.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),await D(e,i.faultId),await ee(e,t,n,!0,r))}o(ne,"replaceActiveFault");async function m(e,t,n){return(await e.prepare("SELECT binding_id FROM sessions WHERE node_id = ? AND session_id = ? AND unbound_at IS NULL").get(t,n))?.binding_id??null}o(m,"liveBinding");async function Xe(e,t){return await l(async n=>{const r=u();return(await n.prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(r,e)).changes>0?!0:(await n.prepare("UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ? AND state != 'exited'").run(r,r,e)).changes>0})}o(Xe,"openTurn");async function $e(e){return(await(await p()).prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(u(),e)).changes>0}o($e,"recordTurnProgress");async function je(e,t){return await l(async n=>{const r=u(),i=(await n.prepare("UPDATE executions SET turn_closed_at = ?, turn_end_reason = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(r,t.endReason,e)).changes>0;if(i&&t.endReason==="settled"){const s="(SELECT node_id FROM executions WHERE execution_id = ?)";t.viewerPresent===!0?await n.prepare(`UPDATE nodes SET reply_at = ?, read_at = ? WHERE node_id = ${s}`).run(r,r,e):await n.prepare(`UPDATE nodes SET reply_at = ? WHERE node_id = ${s}`).run(r,e)}return i})}o(je,"closeTurn");async function Be(e){return(await(await p()).prepare("UPDATE nodes SET read_at = ? WHERE node_id = ?").run(u(),e)).changes>0}o(Be,"markNodeRead");async function Je(e,t){if(!Number.isInteger(t.consecutivePasses)||t.consecutivePasses<1||t.routes.length===0||!t.routes.every(n=>n!==""))throw new Error("invalid transport-poison verdict");return(await(await p()).prepare("UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ? AND state != 'exited'").run(JSON.stringify({schema:"crtr.execution-exit-diagnostic/v1",transport_poison:{consecutive_passes:t.consecutivePasses,routes:t.routes}}),e)).changes>0}o(Je,"recordTransportPoisonVerdict");async function Ke(e){const t=await(await p()).prepare("SELECT exit_diagnostic_json FROM executions WHERE node_id = ? ORDER BY seq DESC LIMIT 1").get(e);if(t===void 0||t.exit_diagnostic_json===null)return null;try{const n=JSON.parse(t.exit_diagnostic_json),r=n.transport_poison;return n.schema!=="crtr.execution-exit-diagnostic/v1"||r===void 0||!Number.isInteger(r.consecutive_passes)||r.consecutive_passes<1||!Array.isArray(r.routes)||r.routes.length===0||!r.routes.every(i=>typeof i=="string"&&i!=="")?null:{consecutivePasses:r.consecutive_passes,routes:r.routes}}catch{return null}}o(Ke,"readTransportPoisonVerdict");async function Ye(e){const t=await(await p()).prepare("SELECT turn_open_at, turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND state != 'exited'").get(e);return t===void 0?null:{busy:t.turn_open_at!==null&&t.turn_closed_at===null,progressAt:t.turn_progress_at}}o(Ye,"readLiveTurn");async function Ge(e){const t=await p(),n=await t.prepare("SELECT turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND turn_open_at IS NOT NULL ORDER BY seq DESC LIMIT 1").get(e);if(n===void 0)return null;const r=await t.prepare("SELECT turn_end_reason FROM executions WHERE node_id = ? ORDER BY seq DESC LIMIT 1").get(e);return{interrupted:n.turn_closed_at===null||r?.turn_end_reason==="teardown-aborted",progressAt:n.turn_progress_at}}o(Ge,"readInterruptedTurn");async function qe(e){return await(await p()).prepare("SELECT 1 FROM executions WHERE node_id = ? AND turn_open_at IS NOT NULL LIMIT 1").get(e)!==void 0}o(qe,"hasAdmittedTurn");async function Ve(e,t){const n=V(t);return Q(e,n),await l(async r=>await ne(r,e,n))}o(Ve,"recordFault");async function Qe(e){return await l(async t=>(await t.prepare("UPDATE node_faults SET notified_since = since, updated_at = ? WHERE fault_id = ?").run(u(),e)).changes>0)}o(Qe,"markFaultNotified");function Re(e){return e.link==="pi\u2192provider"&&e.retry.disposition==="fatal"&&e.notifiedSince!==e.since}o(Re,"isUnnotifiedFatalProviderFault");async function ze(){return(await(await p()).prepare("SELECT f.node_id FROM node_faults f JOIN nodes n ON n.node_id = f.node_id WHERE f.active = 1 AND f.link = 'pi\u2192provider' AND f.retry_disposition = 'fatal' AND (f.notified_since IS NULL OR f.notified_since != f.since) AND n.status IN ('active', 'idle')").all()).map(e=>e.node_id)}o(ze,"listUnnotifiedFatalFaultNodes");let W=null;function Ze(e){W=e}o(Ze,"setFatalFaultAnnouncer");async function et(e,t){const n=await L(await p(),e);return n===null||t?.link!==void 0&&n.link!==t.link?!1:await l(async r=>{const i=await L(r,e);if(i===null||t?.link!==void 0&&i.link!==t.link)return!1;W!==null&&Re(i)&&await W(e);const s=await r.prepare("SELECT episode_state FROM node_faults WHERE fault_id = ?").get(i.faultId);await r.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),s.episode_state==="pending"&&t?.preserveEpisode!==!0&&await r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),await D(r,i.faultId);const d=G();return $({level:"info",event:"fault.cleared",operation_id:d,node_id:e,error_class:X(i),fields:{cleared_fault_operation_id:i.operation_id,link:i.link,op:i.op,kind:i.kind,retry:i.retry,message:i.message,since:i.since,...i.anchorEntryId===void 0?{}:{anchor_entry_id:i.anchorEntryId}}}),!0})}o(et,"clearFault");async function re(e){return await L(await p(),e)}o(re,"readFault");async function tt(e,t){const n=V(t.input);if(n.link!=="pi\u2192provider"||n.retry.disposition!=="auto"||n.retry.by!=="daemon")throw new Error("provider retry episode requires an automatic daemon provider fault");return Q(e,n),await l(async r=>{const i=await m(r,e,t.sessionId);if(i===null)return null;const s=await L(r,e);if(s!==null&&(s.link!==n.link||s.kind!==n.kind)&&w(s)>w(n))return null;const d=await r.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_state IS NOT NULL").all(e);await r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_state IS NOT NULL").run(u(),e);for(const E of d)await D(r,E.fault_id);const a=await ne(r,e,n,{state:"pending",bindingId:i});return a===null?null:(await r.prepare("UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run("pending",i,u(),a.faultId),a)})}o(tt,"recordPendingEpisode");async function nt(e,t){return await l(async n=>{if(await n.prepare("SELECT 1 FROM nodes WHERE node_id = ? AND intent = 'refresh'").get(e)!==void 0)return!1;const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'admitted', retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, updated_at = ? WHERE node_id = ? AND fault_id = ? AND episode_state = 'pending' AND episode_binding_id = ?").run(t.retry.disposition,t.retry.by??null,t.retry.attempt??null,t.retry.max??null,t.retry.nextAt??null,u(),e,t.faultId,r)).changes>0})}o(nt,"admitEpisode");async function rt(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'pending', updated_at = ? WHERE node_id = ? AND episode_state = 'admitted' AND episode_binding_id = ?").run(u(),e,r)).changes>0})}o(rt,"reopenEpisode");async function it(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'invalidated', updated_at = ? WHERE node_id = ? AND episode_state != 'invalidated' AND episode_state IS NOT NULL AND episode_binding_id = ?").run(u(),e,r)).changes>0})}o(it,"invalidateEpisode");async function ot(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);if(r===null)return!1;const i=await n.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").all(e,r);if(i.length===0)return!1;await n.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").run(u(),e,r);for(const s of i)await D(n,s.fault_id);return!0})}o(ot,"clearEpisode");async function st(e){const t=await(await p()).prepare("SELECT f.*, s.session_id FROM node_faults f JOIN sessions s ON s.binding_id = f.episode_binding_id WHERE f.node_id = ? AND f.episode_state IS NOT NULL").get(e);return t===void 0?null:{state:t.episode_state,sessionId:t.session_id,fault:H(t)}}o(st,"readEpisode");async function at(){return(await(await p()).prepare("SELECT f.*, s.session_id AS bound_session_id FROM node_faults f JOIN sessions s ON s.binding_id = f.episode_binding_id WHERE f.active = 1 AND f.episode_state = 'pending' AND s.unbound_at IS NULL AND s.session_id IS NOT NULL AND f.link = 'pi\u2192provider' AND f.retry_disposition = 'auto' AND f.retry_by = 'daemon' AND f.kind IN ('connection', 'rate-limit', 'overloaded')").all()).map(t=>({nodeId:t.node_id,sessionId:t.bound_session_id,fault:H(t),updatedAt:t.updated_at}))}o(at,"listPendingProviderRetries");async function dt(e){const t=await re(e)===null;return{async explanation(){if(!t)return null;const n=await re(e);if(n===null||n.link!=="crtr\u2192pi"||n.retry.disposition!=="fatal")return null;const r=n.op.trim(),i=n.message.trim();return r===""||i===""?null:`${r}: ${i}`}}}o(dt,"beginBootFaultAttempt");function ie(e,t){try{return ce(e).mtime.toISOString()}catch{return t}}o(ie,"markerMtime");function P(e,t){if(!f(e))return null;try{return de(e,"utf8")}catch(n){if(!t)return null;throw new Error(`cannot import recovery input ${e}: ${n.message}`)}}o(P,"legacyFile");function Ie(e,t){return _(e,"SELECT execution_id, started_at FROM executions WHERE node_id = ? ORDER BY rowid DESC LIMIT 1").get(t)??null}o(Ie,"latestExecution");function we(e,t){const r=C(e,"nodes").has("launched_at")?_(e,"SELECT COALESCE(launched_at, created) AS started_at FROM nodes WHERE node_id = ?").get(t):_(e,"SELECT created AS started_at FROM nodes WHERE node_id = ?").get(t);if(r===void 0)throw new Error(`cannot import recovery witness for unknown node ${t}`);const i=I();return _(e,"INSERT INTO executions (execution_id, node_id, daemon_epoch, pid, process_identity, state, started_at, ended_at, exit_reason) VALUES (?, ?, NULL, NULL, NULL, 'exited', ?, ?, 'pre-consolidation')").run(i,t,r.started_at,r.started_at),{execution_id:i,started_at:r.started_at}}o(we,"syntheticExecution");function oe(e,t,n,r,i){const s=I();return _(e,z).run(...Z(s,t,n,r,i)),{...n,faultId:s}}o(oe,"insertMigratedFault");function De(e,t,n,r){const i=_(e,"SELECT binding_id, node_id FROM sessions WHERE transcript_path = ? AND unbound_at IS NULL").get(n);if(i!==void 0){if(i.node_id!==t)throw new Error(`cannot import provider retry ${r}: transcript is live-owned by ${i.node_id}`);return i.binding_id}const s=_(e,"SELECT binding_id FROM sessions WHERE node_id = ? AND transcript_path = ? ORDER BY rowid DESC LIMIT 1").get(t,n);if(s!==void 0)return s.binding_id;const d=_(e,"SELECT created FROM nodes WHERE node_id = ?").get(t);if(d===void 0)throw new Error(`cannot import provider retry ${r}: unknown node ${t}`);const a=I();return _(e,"INSERT INTO sessions (binding_id, session_id, node_id, transcript_path, source_session_id, fork_entry_id, created_at, bound_at, unbound_at) VALUES (?, NULL, ?, ?, NULL, NULL, ?, ?, ?)").run(a,t,n,d.created,d.created,u()),a}o(De,"migrationBinding");function ut(e){e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'node_faults'").get()!==void 0&&(C(e,"node_faults").has("notified_since")||e.exec("ALTER TABLE node_faults ADD COLUMN notified_since TEXT"))}o(ut,"addFaultNotifiedSince");function ct(e){if(Le(e),me(e),!f(F()))return;const t=ae(F(),{withFileTypes:!0}).filter(r=>r.isDirectory()).sort((r,i)=>r.name.localeCompare(i.name)),n=_(e,"SELECT 1 FROM nodes WHERE node_id = ?");for(const r of t){const i=r.name,s=pe(i),d=N(s,"busy"),a=N(s,"clean-abort"),E=N(s,"transport-poison.json"),U=N(s,"fault"),A=N(s,"provider-retry"),O=f(d),h=f(a);if(!O&&!h&&!f(E)&&!f(U)&&!f(A)||!f(N(F(),i,"meta.json"))&&n.get(i)===void 0)continue;const k=P(E,!1),S=P(U,!0),x=P(A,!0);if(!O&&!h&&k===null&&S===null&&x===null)continue;let y=Ie(e,i);if(y===null&&(y=we(e,i)),O){const c=ie(d,y.started_at);_(e,"UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ?").run(c,c,y.execution_id)}else if(h){const c=ie(a,y.started_at);_(e,"UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = ?, turn_end_reason = 'teardown-aborted' WHERE execution_id = ?").run(c,c,c,y.execution_id)}if(k!==null)try{const c=JSON.parse(k);Number.isInteger(c.consecutivePasses)&&c.consecutivePasses>=1&&Array.isArray(c.routes)&&c.routes.length>0&&c.routes.every(M=>typeof M=="string"&&M!=="")&&_(e,"UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ?").run(JSON.stringify({schema:"crtr.execution-exit-diagnostic/v1",transport_poison:{consecutive_passes:c.consecutivePasses,routes:c.routes}}),y.execution_id)}catch{}const R=S===null?null:Y(S);if(S!==null&&R===null)throw new Error(`cannot import recovery fault ${U}`);const T=x===null?null:Se(x);if(x!==null&&T===null)throw new Error(`cannot import provider retry ${A}`);let v=R===null||R.link==="broker\u2194crtrd"?null:oe(e,i,R,!0);if(T!==null){const c=De(e,i,T.sessionFile,A);v!==null&&v.since===T.fault.since?_(e,"UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run(T.state,c,u(),v.faultId):oe(e,i,T.fault,!1,{state:T.state,bindingId:c})}}}o(ct,"migrateRecoveryWitnesses");export{Me as FAULT_QUIET_MS,ut as addFaultNotifiedSince,nt as admitEpisode,dt as beginBootFaultAttempt,ot as clearEpisode,et as clearFault,je as closeTurn,qe as hasAdmittedTurn,H as hydrateFault,it as invalidateEpisode,at as listPendingProviderRetries,ze as listUnnotifiedFatalFaultNodes,Qe as markFaultNotified,Be as markNodeRead,ct as migrateRecoveryWitnesses,Xe as openTurn,K as parseProviderError,st as readEpisode,re as readFault,Ge as readInterruptedTurn,Ye as readLiveTurn,Ke as readTransportPoisonVerdict,Ve as recordFault,tt as recordPendingEpisode,Je as recordTransportPoisonVerdict,$e as recordTurnProgress,rt as reopenEpisode,Ze as setFatalFaultAnnouncer};
26
+ `)}o(me,"createFaultTable");function g(e,t){return typeof t=="string"&&e.includes(t)}o(g,"contains");function W(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(W,"isRecord");function q(e){if(!W(e)||typeof e.provider!="string"||e.provider===""||typeof e.model!="string"||e.model==="")return;const t=typeof e.status=="number"&&Number.isFinite(e.status)?e.status:void 0,n=typeof e.code=="string"||typeof e.code=="number"?e.code:void 0,r=typeof e.errno=="string"?e.errno:void 0,i=typeof e.body=="string"?e.body:void 0,a=le(e.ratelimit),d=Array.isArray(e.causes)?e.causes.flatMap(s=>{if(!W(s))return[];const E={...typeof s.message=="string"?{message:s.message}:{},...typeof s.status=="number"&&Number.isFinite(s.status)?{status:s.status}:{},...typeof s.code=="string"||typeof s.code=="number"?{code:s.code}:{},...typeof s.errno=="string"?{errno:s.errno}:{},...typeof s.body=="string"?{body:s.body}:{}};return Object.keys(E).length===0?[]:[E]}):void 0;return{provider:e.provider,model:e.model,...t===void 0?{}:{status:t},...n===void 0?{}:{code:n},...r===void 0?{}:{errno:r},...i===void 0?{}:{body:i},...d===void 0||d.length===0?{}:{causes:d},...a===void 0?{}:{ratelimit:a}}}o(q,"parseProviderError");function V(e){try{const t=JSON.parse(e);if(!g(fe,t.link)||typeof t.op!="string"||t.op===""||!g(ye,t.kind)||typeof t.message!="string"||typeof t.since!="string")return null;const n=Y(t.operation_id);if(!W(t.retry)||!g(Te,t.retry.disposition)||t.retry.by!==void 0&&!g(Ne,t.retry.by)||t.retry.attempt!==void 0&&typeof t.retry.attempt!="number"||t.retry.max!==void 0&&typeof t.retry.max!="number"||t.retry.nextAt!==void 0&&typeof t.retry.nextAt!="string"||t.anchorEntryId!==void 0&&(typeof t.anchorEntryId!="string"||t.anchorEntryId===""))return null;const r=t.providerError===void 0?void 0:q(t.providerError);return t.providerError!==void 0&&r===void 0?null:{link:t.link,op:t.op,kind:t.kind,retry:{disposition:t.retry.disposition,...t.retry.by===void 0?{}:{by:t.retry.by},...t.retry.attempt===void 0?{}:{attempt:t.retry.attempt},...t.retry.max===void 0?{}:{max:t.retry.max},...t.retry.nextAt===void 0?{}:{nextAt:t.retry.nextAt}},message:t.message,since:t.since,operation_id:n,...t.anchorEntryId===void 0?{}:{anchorEntryId:t.anchorEntryId},...r===void 0?{}:{providerError:r}}}catch{return null}}o(V,"parseLegacyFault");function Ae(e){const t=_e(e);try{return ue(t)}catch{return t}}o(Ae,"canonicalPath");function Se(e){try{const t=JSON.parse(e);if(!g(ge,t.state)||typeof t.sessionFile!="string"||t.sessionFile==="")return null;const n=JSON.stringify(t.fault),r=V(n);if(r===null||r.link!=="pi\u2192provider"||r.retry.disposition!=="auto"||r.retry.by!=="daemon")return null;const i=t.anchorEntryId;return i!==null&&typeof i!="string"||(r.anchorEntryId??null)!==i?null:{state:t.state,sessionFile:Ae(t.sessionFile),fault:r}}catch{return null}}o(Se,"parseLegacyEpisode");function Q(e){return e!==void 0?Y(e):K.current()??K.fresh(n=>n)}o(Q,"resolveOperationId");function xe(e){return e.slice(0,Ee)}o(xe,"clampMessage");function w(e){return e.link==="pi\u2192provider"&&e.retry.disposition==="auto"&&e.retry.by==="daemon"?3:e.retry.disposition==="fatal"?2:1}o(w,"faultPriority");function z(e){return{link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operationId:e.operation_id,anchorEntryId:e.anchorEntryId??null,retryDisposition:e.retry.disposition,retryBy:e.retry.by??null,retryAttempt:e.retry.attempt??null,retryMax:e.retry.max??null,retryNextAt:e.retry.nextAt??null,providerError:e.providerError===void 0?null:JSON.stringify(e.providerError)}}o(z,"encodeFault");function b(e){const t=e.provider_error_json,n=typeof t=="string"?q(JSON.parse(t)):void 0;if(typeof t=="string"&&n===void 0)throw new Error(`invalid provider error on stored fault ${e.fault_id}`);const r={disposition:e.retry_disposition,...e.retry_by===null?{}:{by:e.retry_by},...e.retry_attempt===null?{}:{attempt:e.retry_attempt},...e.retry_max===null?{}:{max:e.retry_max},...e.retry_next_at===null?{}:{nextAt:e.retry_next_at}};return{faultId:e.fault_id,link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operation_id:e.operation_id,retry:r,...e.anchor_entry_id===null?{}:{anchorEntryId:e.anchor_entry_id},...n===void 0?{}:{providerError:n},...typeof e.notified_since=="string"?{notifiedSince:e.notified_since}:{}}}o(b,"hydrateFault");async function L(e,t){const n=await e.prepare("SELECT * FROM node_faults WHERE node_id = ? AND active = 1").get(t);return n===void 0?null:b(n)}o(L,"activeFault");function P(e){if(!g(G,e.link))throw new Error(`fault link is not durable: ${e.link}`);return{link:e.link,op:e.op,kind:e.kind,retry:e.retry,message:xe(e.message),since:e.since??u(),operation_id:Q(e.operation_id),...typeof e.anchorEntryId=="string"&&e.anchorEntryId!==""?{anchorEntryId:e.anchorEntryId}:{},...e.providerError===void 0?{}:{providerError:e.providerError}}}o(P,"storedFault");function M(e,t){J({level:t.retry.disposition==="fatal"?"error":"warn",event:"fault.recorded",operation_id:t.operation_id,node_id:e,error_class:B(t),fields:{link:t.link,op:t.op,kind:t.kind,retry:t.retry,message:t.message,...t.anchorEntryId===void 0?{}:{anchor_entry_id:t.anchorEntryId},...t.providerError===void 0?{}:{provider_error:t.providerError}}})}o(M,"emitRecordedFault");const Z="INSERT INTO node_faults (fault_id, node_id, link, op, kind, message, since, operation_id, anchor_entry_id, retry_disposition, retry_by, retry_attempt, retry_max, retry_next_at, provider_error_json, active, episode_state, episode_binding_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";function ee(e,t,n,r,i){const a=z(n),d=u();return[e,t,a.link,a.op,a.kind,a.message,a.since,a.operationId,a.anchorEntryId,a.retryDisposition,a.retryBy,a.retryAttempt,a.retryMax,a.retryNextAt,a.providerError,r?1:0,i?.state??null,i?.bindingId??null,d,d]}o(ee,"faultInsertParams");async function te(e,t,n,r,i){const a=I();return await e.prepare(Z).run(...ee(a,t,n,r,i)),{...n,faultId:a}}o(te,"insertFault");async function D(e,t,n,r){const i=z(n);return await e.prepare("UPDATE node_faults SET link = ?, op = ?, kind = ?, message = ?, since = ?, operation_id = ?, anchor_entry_id = ?, retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, provider_error_json = ?, episode_state = COALESCE(?, episode_state), episode_binding_id = COALESCE(?, episode_binding_id), updated_at = ? WHERE fault_id = ?").run(i.link,i.op,i.kind,i.message,i.since,i.operationId,i.anchorEntryId,i.retryDisposition,i.retryBy,i.retryAttempt,i.retryMax,i.retryNextAt,i.providerError,r?.state??null,r?.bindingId??null,u(),t),{...n,faultId:t}}o(D,"updateFault");async function U(e,t){await e.prepare("DELETE FROM node_faults WHERE fault_id = ? AND active = 0 AND episode_state IS NULL").run(t)}o(U,"deleteIfUnretained");async function ne(e,t,n,r){const i=await L(e,t);if(i===null){const a=await e.prepare("SELECT fault_id, link, kind, since, operation_id FROM node_faults WHERE node_id = ? AND active = 0 AND episode_state IS NOT NULL").get(t);return a!==void 0&&a.link===n.link&&a.kind===n.kind&&a.since===n.since&&a.operation_id===n.operation_id?(await e.prepare("UPDATE node_faults SET active = 1, updated_at = ? WHERE fault_id = ?").run(u(),a.fault_id),await D(e,a.fault_id,n)):await te(e,t,n,!0,r)}if(i.link===n.link&&i.kind===n.kind){const a=n.retry.disposition==="auto"?{...i.retry,...n.retry}:n.retry,d=i.retry.disposition===n.retry.disposition&&(n.retry.disposition!=="fatal"||i.message===n.message),s=d?i.since:n.since,E=await D(e,i.faultId,{...n,since:s,anchorEntryId:n.anchorEntryId??i.anchorEntryId,retry:a},r);return d||await e.prepare("UPDATE node_faults SET notified_since = NULL WHERE fault_id = ?").run(i.faultId),E}return i.link!==n.link&&w(i)>w(n)?null:(await e.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),await U(e,i.faultId),await te(e,t,n,!0,r))}o(ne,"replaceActiveFault");async function m(e,t,n){return(await e.prepare("SELECT binding_id FROM sessions WHERE node_id = ? AND session_id = ? AND unbound_at IS NULL").get(t,n))?.binding_id??null}o(m,"liveBinding");async function Xe(e,t){return await l(async n=>{const r=u();return(await n.prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(r,e)).changes>0?!0:(await n.prepare("UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ? AND state != 'exited'").run(r,r,e)).changes>0})}o(Xe,"openTurn");async function $e(e){return(await(await p()).prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(u(),e)).changes>0}o($e,"recordTurnProgress");async function je(e,t){return await l(async n=>{const r=u(),i=(await n.prepare("UPDATE executions SET turn_closed_at = ?, turn_end_reason = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(r,t.endReason,e)).changes>0;if(i&&t.endReason==="settled"){const a="(SELECT node_id FROM executions WHERE execution_id = ?)";t.viewerPresent===!0?await n.prepare(`UPDATE nodes SET reply_at = ?, read_at = ? WHERE node_id = ${a}`).run(r,r,e):await n.prepare(`UPDATE nodes SET reply_at = ? WHERE node_id = ${a}`).run(r,e)}return i})}o(je,"closeTurn");async function Be(e){return(await(await p()).prepare("UPDATE nodes SET read_at = ? WHERE node_id = ?").run(u(),e)).changes>0}o(Be,"markNodeRead");async function Je(e,t){if(!Number.isInteger(t.consecutivePasses)||t.consecutivePasses<1||t.routes.length===0||!t.routes.every(n=>n!==""))throw new Error("invalid transport-poison verdict");return(await(await p()).prepare("UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ? AND state != 'exited'").run(JSON.stringify({schema:"crtr.execution-exit-diagnostic/v1",transport_poison:{consecutive_passes:t.consecutivePasses,routes:t.routes}}),e)).changes>0}o(Je,"recordTransportPoisonVerdict");async function Ke(e){const t=await(await p()).prepare("SELECT exit_diagnostic_json FROM executions WHERE node_id = ? ORDER BY seq DESC LIMIT 1").get(e);if(t===void 0||t.exit_diagnostic_json===null)return null;try{const n=JSON.parse(t.exit_diagnostic_json),r=n.transport_poison;return n.schema!=="crtr.execution-exit-diagnostic/v1"||r===void 0||!Number.isInteger(r.consecutive_passes)||r.consecutive_passes<1||!Array.isArray(r.routes)||r.routes.length===0||!r.routes.every(i=>typeof i=="string"&&i!=="")?null:{consecutivePasses:r.consecutive_passes,routes:r.routes}}catch{return null}}o(Ke,"readTransportPoisonVerdict");async function Ye(e){const t=await(await p()).prepare("SELECT turn_open_at, turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND state != 'exited'").get(e);return t===void 0?null:{busy:t.turn_open_at!==null&&t.turn_closed_at===null,progressAt:t.turn_progress_at}}o(Ye,"readLiveTurn");async function Ge(e){const t=await p(),n=await t.prepare("SELECT turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND turn_open_at IS NOT NULL ORDER BY seq DESC LIMIT 1").get(e);if(n===void 0)return null;const r=await t.prepare("SELECT turn_end_reason FROM executions WHERE node_id = ? ORDER BY seq DESC LIMIT 1").get(e);return{interrupted:n.turn_closed_at===null||r?.turn_end_reason==="teardown-aborted",progressAt:n.turn_progress_at}}o(Ge,"readInterruptedTurn");async function qe(e){return await(await p()).prepare("SELECT 1 FROM executions WHERE node_id = ? AND turn_open_at IS NOT NULL LIMIT 1").get(e)!==void 0}o(qe,"hasAdmittedTurn");async function Ve(e,t){const n=P(t);return M(e,n),await l(async r=>await ne(r,e,n))}o(Ve,"recordFault");async function Qe(e){return await l(async t=>(await t.prepare("UPDATE node_faults SET notified_since = since, updated_at = ? WHERE fault_id = ?").run(u(),e)).changes>0)}o(Qe,"markFaultNotified");function Re(e){return e.link==="pi\u2192provider"&&e.retry.disposition==="fatal"&&e.notifiedSince!==e.since}o(Re,"isUnnotifiedFatalProviderFault");async function ze(){return(await(await p()).prepare("SELECT f.node_id FROM node_faults f JOIN nodes n ON n.node_id = f.node_id WHERE f.active = 1 AND f.link = 'pi\u2192provider' AND f.retry_disposition = 'fatal' AND (f.notified_since IS NULL OR f.notified_since != f.since) AND n.status IN ('active', 'idle')").all()).map(e=>e.node_id)}o(ze,"listUnnotifiedFatalFaultNodes");let X=null;function Ze(e){X=e}o(Ze,"setFatalFaultAnnouncer");async function et(e,t){const n=await L(await p(),e);return n===null||t?.link!==void 0&&n.link!==t.link?!1:await l(async r=>{const i=await L(r,e);if(i===null||t?.link!==void 0&&i.link!==t.link)return!1;X!==null&&Re(i)&&await X(e);const a=await r.prepare("SELECT episode_state FROM node_faults WHERE fault_id = ?").get(i.faultId);await r.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),a.episode_state==="pending"&&t?.preserveEpisode!==!0&&await r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),await U(r,i.faultId);const d=Q();return J({level:"info",event:"fault.cleared",operation_id:d,node_id:e,error_class:B(i),fields:{cleared_fault_operation_id:i.operation_id,link:i.link,op:i.op,kind:i.kind,retry:i.retry,message:i.message,since:i.since,...i.anchorEntryId===void 0?{}:{anchor_entry_id:i.anchorEntryId}}}),!0})}o(et,"clearFault");async function re(e){return await L(await p(),e)}o(re,"readFault");async function tt(e,t){const n=P(t.input);if(n.link!=="pi\u2192provider"||n.retry.disposition!=="auto"||n.retry.by!=="daemon")throw new Error("provider retry episode requires an automatic daemon provider fault");return M(e,n),await l(async r=>{const i=await m(r,e,t.sessionId);if(i===null)return null;const a=await L(r,e);if(a!==null&&(a.link!==n.link||a.kind!==n.kind)&&w(a)>w(n))return null;const d=await r.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_state IS NOT NULL").all(e);await r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_state IS NOT NULL").run(u(),e);for(const E of d)await U(r,E.fault_id);const s=await ne(r,e,n,{state:"pending",bindingId:i});return s===null?null:(await r.prepare("UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run("pending",i,u(),s.faultId),s)})}o(tt,"recordPendingEpisode");async function nt(e,t){return await l(async n=>{if(await n.prepare("SELECT 1 FROM nodes WHERE node_id = ? AND intent = 'refresh'").get(e)!==void 0)return!1;const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'admitted', retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, updated_at = ? WHERE node_id = ? AND fault_id = ? AND episode_state = 'pending' AND episode_binding_id = ?").run(t.retry.disposition,t.retry.by??null,t.retry.attempt??null,t.retry.max??null,t.retry.nextAt??null,u(),e,t.faultId,r)).changes>0})}o(nt,"admitEpisode");async function rt(e,t){const n=P(t.fault);return await l(async r=>{const i=await m(r,e,t.sessionId);return i===null||(await L(r,e))?.faultId!==t.faultId||await r.prepare("SELECT 1 FROM node_faults WHERE node_id = ? AND fault_id = ? AND episode_state = 'admitted' AND episode_binding_id = ?").get(e,t.faultId,i)===void 0?!1:(M(e,n),t.reopen?await D(r,t.faultId,n,{state:"pending",bindingId:i}):(await D(r,t.faultId,n),await r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE fault_id = ?").run(u(),t.faultId),await r.prepare("UPDATE node_faults SET notified_since = NULL WHERE fault_id = ?").run(t.faultId)),!0)})}o(rt,"settleRejectedEpisode");async function it(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'pending', updated_at = ? WHERE node_id = ? AND episode_state = 'admitted' AND episode_binding_id = ?").run(u(),e,r)).changes>0})}o(it,"reopenEpisode");async function ot(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'invalidated', updated_at = ? WHERE node_id = ? AND episode_state != 'invalidated' AND episode_state IS NOT NULL AND episode_binding_id = ?").run(u(),e,r)).changes>0})}o(ot,"invalidateEpisode");async function at(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);if(r===null)return!1;const i=await n.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").all(e,r);if(i.length===0)return!1;await n.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").run(u(),e,r);for(const a of i)await U(n,a.fault_id);return!0})}o(at,"clearEpisode");async function st(e){const t=await(await p()).prepare("SELECT f.*, s.session_id FROM node_faults f JOIN sessions s ON s.binding_id = f.episode_binding_id WHERE f.node_id = ? AND f.episode_state IS NOT NULL").get(e);return t===void 0?null:{state:t.episode_state,sessionId:t.session_id,fault:b(t)}}o(st,"readEpisode");async function dt(){return(await(await p()).prepare("SELECT f.*, s.session_id AS bound_session_id FROM node_faults f JOIN sessions s ON s.binding_id = f.episode_binding_id WHERE f.active = 1 AND f.episode_state = 'pending' AND s.unbound_at IS NULL AND s.session_id IS NOT NULL AND f.link = 'pi\u2192provider' AND f.retry_disposition = 'auto' AND f.retry_by = 'daemon' AND f.kind IN ('connection', 'rate-limit', 'overloaded')").all()).map(t=>({nodeId:t.node_id,sessionId:t.bound_session_id,fault:b(t),updatedAt:t.updated_at}))}o(dt,"listPendingProviderRetries");async function ut(e){const t=await re(e)===null;return{async explanation(){if(!t)return null;const n=await re(e);if(n===null||n.link!=="crtr\u2192pi"||n.retry.disposition!=="fatal")return null;const r=n.op.trim(),i=n.message.trim();return r===""||i===""?null:`${r}: ${i}`}}}o(ut,"beginBootFaultAttempt");function ie(e,t){try{return ce(e).mtime.toISOString()}catch{return t}}o(ie,"markerMtime");function $(e,t){if(!f(e))return null;try{return de(e,"utf8")}catch(n){if(!t)return null;throw new Error(`cannot import recovery input ${e}: ${n.message}`)}}o($,"legacyFile");function Ie(e,t){return _(e,"SELECT execution_id, started_at FROM executions WHERE node_id = ? ORDER BY rowid DESC LIMIT 1").get(t)??null}o(Ie,"latestExecution");function we(e,t){const r=H(e,"nodes").has("launched_at")?_(e,"SELECT COALESCE(launched_at, created) AS started_at FROM nodes WHERE node_id = ?").get(t):_(e,"SELECT created AS started_at FROM nodes WHERE node_id = ?").get(t);if(r===void 0)throw new Error(`cannot import recovery witness for unknown node ${t}`);const i=I();return _(e,"INSERT INTO executions (execution_id, node_id, daemon_epoch, pid, process_identity, state, started_at, ended_at, exit_reason) VALUES (?, ?, NULL, NULL, NULL, 'exited', ?, ?, 'pre-consolidation')").run(i,t,r.started_at,r.started_at),{execution_id:i,started_at:r.started_at}}o(we,"syntheticExecution");function oe(e,t,n,r,i){const a=I();return _(e,Z).run(...ee(a,t,n,r,i)),{...n,faultId:a}}o(oe,"insertMigratedFault");function De(e,t,n,r){const i=_(e,"SELECT binding_id, node_id FROM sessions WHERE transcript_path = ? AND unbound_at IS NULL").get(n);if(i!==void 0){if(i.node_id!==t)throw new Error(`cannot import provider retry ${r}: transcript is live-owned by ${i.node_id}`);return i.binding_id}const a=_(e,"SELECT binding_id FROM sessions WHERE node_id = ? AND transcript_path = ? ORDER BY rowid DESC LIMIT 1").get(t,n);if(a!==void 0)return a.binding_id;const d=_(e,"SELECT created FROM nodes WHERE node_id = ?").get(t);if(d===void 0)throw new Error(`cannot import provider retry ${r}: unknown node ${t}`);const s=I();return _(e,"INSERT INTO sessions (binding_id, session_id, node_id, transcript_path, source_session_id, fork_entry_id, created_at, bound_at, unbound_at) VALUES (?, NULL, ?, ?, NULL, NULL, ?, ?, ?)").run(s,t,n,d.created,d.created,u()),s}o(De,"migrationBinding");function ct(e){e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'node_faults'").get()!==void 0&&(H(e,"node_faults").has("notified_since")||e.exec("ALTER TABLE node_faults ADD COLUMN notified_since TEXT"))}o(ct,"addFaultNotifiedSince");function _t(e){if(Le(e),me(e),!f(C()))return;const t=se(C(),{withFileTypes:!0}).filter(r=>r.isDirectory()).sort((r,i)=>r.name.localeCompare(i.name)),n=_(e,"SELECT 1 FROM nodes WHERE node_id = ?");for(const r of t){const i=r.name,a=pe(i),d=N(a,"busy"),s=N(a,"clean-abort"),E=N(a,"transport-poison.json"),O=N(a,"fault"),A=N(a,"provider-retry"),h=f(d),k=f(s);if(!h&&!k&&!f(E)&&!f(O)&&!f(A)||!f(N(C(),i,"meta.json"))&&n.get(i)===void 0)continue;const v=$(E,!1),S=$(O,!0),x=$(A,!0);if(!h&&!k&&v===null&&S===null&&x===null)continue;let y=Ie(e,i);if(y===null&&(y=we(e,i)),h){const c=ie(d,y.started_at);_(e,"UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ?").run(c,c,y.execution_id)}else if(k){const c=ie(s,y.started_at);_(e,"UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = ?, turn_end_reason = 'teardown-aborted' WHERE execution_id = ?").run(c,c,c,y.execution_id)}if(v!==null)try{const c=JSON.parse(v);Number.isInteger(c.consecutivePasses)&&c.consecutivePasses>=1&&Array.isArray(c.routes)&&c.routes.length>0&&c.routes.every(j=>typeof j=="string"&&j!=="")&&_(e,"UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ?").run(JSON.stringify({schema:"crtr.execution-exit-diagnostic/v1",transport_poison:{consecutive_passes:c.consecutivePasses,routes:c.routes}}),y.execution_id)}catch{}const R=S===null?null:V(S);if(S!==null&&R===null)throw new Error(`cannot import recovery fault ${O}`);const T=x===null?null:Se(x);if(x!==null&&T===null)throw new Error(`cannot import provider retry ${A}`);let F=R===null||R.link==="broker\u2194crtrd"?null:oe(e,i,R,!0);if(T!==null){const c=De(e,i,T.sessionFile,A);F!==null&&F.since===T.fault.since?_(e,"UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run(T.state,c,u(),F.faultId):oe(e,i,T.fault,!1,{state:T.state,bindingId:c})}}}o(_t,"migrateRecoveryWitnesses");export{Me as FAULT_QUIET_MS,ct as addFaultNotifiedSince,nt as admitEpisode,ut as beginBootFaultAttempt,at as clearEpisode,et as clearFault,je as closeTurn,qe as hasAdmittedTurn,b as hydrateFault,ot as invalidateEpisode,dt as listPendingProviderRetries,ze as listUnnotifiedFatalFaultNodes,Qe as markFaultNotified,Be as markNodeRead,_t as migrateRecoveryWitnesses,Xe as openTurn,q as parseProviderError,st as readEpisode,re as readFault,Ge as readInterruptedTurn,Ye as readLiveTurn,Ke as readTransportPoisonVerdict,Ve as recordFault,tt as recordPendingEpisode,Je as recordTransportPoisonVerdict,$e as recordTurnProgress,it as reopenEpisode,Ze as setFatalFaultAnnouncer,rt as settleRejectedEpisode};
@@ -60,6 +60,7 @@ export declare class RemoteCanvasSource implements CanvasSource {
60
60
  getRow(id: string): Promise<NodeRow | null>;
61
61
  listNodes(filter?: {
62
62
  status?: NodeStatus | NodeStatus[];
63
+ under?: string;
63
64
  }): Promise<NodeRow[]>;
64
65
  subscriptionsOf(id: string): Promise<SubscriptionRef[]>;
65
66
  subscribersOf(id: string): Promise<SubscriptionRef[]>;
@@ -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,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,e?.under??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(",")),e?.under&&n.push("--under",e.under);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};
@@ -4,8 +4,11 @@ import type { RosterDTO } from '../../api/dto/canvas.js';
4
4
  export interface CanvasSource {
5
5
  getNode(nodeId: string): Promise<NodeMeta | null>;
6
6
  getRow(nodeId: string): Promise<NodeRow | null>;
7
+ /** `under` restricts to the nodes below that node, excluding it. */
8
+ /** `under` scopes to that node plus every node below it (human ask nodes included), one query. */
7
9
  listNodes(filter?: {
8
10
  status?: NodeStatus | NodeStatus[];
11
+ under?: string;
9
12
  }): Promise<NodeRow[]>;
10
13
  /** Optional one-request browse projection. Other listNodes callers keep their full rows. */
11
14
  browseSnapshot?(): Promise<NodeRow[]>;
@@ -29,6 +32,7 @@ export declare class LocalCanvasSource implements CanvasSource {
29
32
  getRow(nodeId: string): Promise<NodeRow | null>;
30
33
  listNodes(filter?: {
31
34
  status?: NodeStatus | NodeStatus[];
35
+ under?: string;
32
36
  }): Promise<NodeRow[]>;
33
37
  subscriptionsOf(subscriber: string): Promise<SubscriptionRef[]>;
34
38
  subscribersOf(publisher: string): Promise<SubscriptionRef[]>;
@@ -1 +1 @@
1
- var u=Object.defineProperty;var a=(t,e)=>u(t,"name",{value:e,configurable:!0});import{getNode as m,getRow as v,listNodes as w,subscriptionsOf as p,subscribersOf as d,view as n,hasActiveLiveSubscription as f}from"./canvas.js";import{ticketCountsForNodes as l}from"./attention.js";import{readState as y}from"../config.js";import{resolveRemoteCanvasTarget as C}from"../remote-canvas-target.js";import{RemoteCanvasSource as N}from"./remote-canvas-source.js";import{isSandboxedNode as b}from"../../shared/env.js";import{ownerOnly as h}from"../errors.js";class S{static{a(this,"LocalCanvasSource")}async getNode(e){return await m(e)}async getRow(e){return await v(e)}async listNodes(e){return await w(e)}async subscriptionsOf(e){return await p(e)}async subscribersOf(e){return await d(e)}async view(e){return await n(e)}async ticketCountsForNodes(e){return await l([...e])}async ticketCountsForView(e){return this.ticketCountsForNodes([e,...await n(e)])}async hasActiveLiveSubscription(e){return await f(e)}}const o=new S;function T(t,e=b()){const r=t?.scope??"user";if(e){if(t?.canvasName!==void 0)throw h("--canvas selects the person's remote canvas targets, which an app's node cannot reach","Omit --canvas; this node reads its own runtime's canvas.");return o}const s=t?.canvasName??y(r).activeCanvas??null;if(!s)return o;const{previewEndpoint:i,relayToken:c}=C(s,r);return new N(i,c)}a(T,"resolveCanvasSource");export{S as LocalCanvasSource,o as localCanvasSource,T as resolveCanvasSource};
1
+ var u=Object.defineProperty;var s=(t,e)=>u(t,"name",{value:e,configurable:!0});import{getNode as v,getRow as m,listNodes as d,subscriptionsOf as w,subscribersOf as p,view as a,hasActiveLiveSubscription as f}from"./canvas.js";import{ticketCountsForNodes as l}from"./attention.js";import{readState as y}from"../config.js";import{resolveRemoteCanvasTarget as C}from"../remote-canvas-target.js";import{RemoteCanvasSource as N}from"./remote-canvas-source.js";import{isSandboxedNode as b}from"../../shared/env.js";import{ownerOnly as h}from"../errors.js";class S{static{s(this,"LocalCanvasSource")}async getNode(e){return await v(e)}async getRow(e){return await m(e)}async listNodes(e){const{under:r,...n}=e??{};return await d(r===void 0?n:{...n,under:r,underInclusive:!0})}async subscriptionsOf(e){return await w(e)}async subscribersOf(e){return await p(e)}async view(e){return await a(e)}async ticketCountsForNodes(e){return await l([...e])}async ticketCountsForView(e){return this.ticketCountsForNodes([e,...await a(e)])}async hasActiveLiveSubscription(e){return await f(e)}}const o=new S;function T(t,e=b()){const r=t?.scope??"user";if(e){if(t?.canvasName!==void 0)throw h("--canvas selects the person's remote canvas targets, which an app's node cannot reach","Omit --canvas; this node reads its own runtime's canvas.");return o}const n=t?.canvasName??y(r).activeCanvas??null;if(!n)return o;const{previewEndpoint:i,relayToken:c}=C(n,r);return new N(i,c)}s(T,"resolveCanvasSource");export{S as LocalCanvasSource,o as localCanvasSource,T as resolveCanvasSource};
@@ -20,6 +20,8 @@ export declare function setSubscriptionActive(subscriber: string, publisher: str
20
20
  export declare function subscribersOf(publisher: string): Promise<SubscriptionRef[]>;
21
21
  /** The nodes `subscriber` watches in the tree — its reports. */
22
22
  export declare function subscriptionsOf(subscriber: string): Promise<SubscriptionRef[]>;
23
+ export declare const SUBTREE_IDS_SQL: string;
24
+ export declare const SUBTREE_ALL_IDS_SQL: string;
23
25
  /** Every node below `root` in the tree, excluding root. */
24
26
  export declare function view(root: string): Promise<string[]>;
25
27
  /** Stop-guard primitive: does this node hold an active tree subscription to a live node —
@@ -1,12 +1,19 @@
1
- var N=Object.defineProperty;var i=(o,t)=>N(o,"name",{value:t,configurable:!0});import{openDb as c,withCanvasWrite as d}from"./db.js";import{getById as A,nodeObject as r}from"../graph/objects.js";import{recordWatch as S,unwatch as v,descendants as b,configFromJson as y,configToJson as O}from"../graph/watches.js";import{publish as R}from"../graph/events.js";import{DEFAULT_LISTEN as T}from"../graph/types.js";import{filterAsync as j}from"../async-array.js";const D=["spawn","job-start","ask"],E="('spawn','job-start','ask')",I=[{threshold:3,delivery:"next-turn"}];function h(o){const t=o.find(a=>a.threshold<=7);return t!==void 0&&t.delivery!=="next-turn"}i(h,"wakesAtSeven");async function w(o,t,...a){const n=await o.prepareUncached(`SELECT wo.id AS from_id, oo.id AS to_id, ws.config_json, ws.created_at,
1
+ var O=Object.defineProperty;var i=(o,t)=>O(o,"name",{value:t,configurable:!0});import{openDb as c,withCanvasWrite as d}from"./db.js";import{getById as m,nodeObject as r}from"../graph/objects.js";import{recordWatch as y,unwatch as I,configFromJson as N,configToJson as b}from"../graph/watches.js";import{publish as T}from"../graph/events.js";import{DEFAULT_LISTEN as v}from"../graph/types.js";const D=["spawn","job-start","ask"],S="('spawn','job-start','ask')",R=[{threshold:3,delivery:"next-turn"}];function l(o){const t=o.find(a=>a.threshold<=7);return t!==void 0&&t.delivery!=="next-turn"}i(l,"wakesAtSeven");async function w(o,t,...a){const e=await o.prepareUncached(`SELECT wo.id AS from_id, oo.id AS to_id, ws.config_json, ws.created_at,
2
2
  wo.listen_json, wn.status AS from_status, onode.status AS to_status
3
3
  FROM watches w
4
4
  JOIN objects wo ON wo.id = w.watcher AND wo.type = 'node'
5
5
  JOIN objects oo ON oo.id = w.object AND oo.type = 'node'
6
- JOIN watch_subscriptions ws ON ws.watch = w.watch_id AND ws.started_by IN ${E}
6
+ JOIN watch_subscriptions ws ON ws.watch = w.watch_id AND ws.started_by IN ${S}
7
7
  JOIN nodes wn ON wn.node_id = wo.id
8
8
  JOIN nodes onode ON onode.node_id = oo.id
9
9
  WHERE w.in_tree = 1 ${t}
10
- ORDER BY ws.created_at, ws.seq`).all(...a),s=new Map;for(const e of n){const u=e.config_json,f=e.listen_json,_=u!=null?y(u):f!=null?y(f):T,p=`${e.from_id}\0${e.to_id}`,m=s.get(p);if(m){m.active||=h(_);continue}s.set(p,{from_id:e.from_id,to_id:e.to_id,active:h(_),created:e.created_at,from_status:e.from_status??null,to_status:e.to_status??null})}return[...s.values()]}i(w,"treeEdges");async function C(){return(await w(await c(),"")).map(({from_id:o,to_id:t,active:a,created:n})=>({from_id:o,to_id:t,active:a,created:n}))}i(C,"rosterEdges");async function H(o,t,a=!0){await d(async n=>{const s=await r(n,o),e=await r(n,t);await S(n,s,e,"ask"),await g(o,t,a),await R(n,{event:"watched",object:e,actor:s,attrs:{}})})}i(H,"subscribe");async function M(o,t){await d(async a=>{const n=await r(a,o),s=await r(a,t);for(const e of D)await v(a,n,s,{startedBy:e})})}i(M,"unsubscribe");async function g(o,t,a){await d(async n=>{await n.prepareUncached(`UPDATE watch_subscriptions SET config_json = ?
11
- WHERE started_by IN ${E}
12
- AND watch IN (SELECT watch_id FROM watches WHERE watcher = ? AND object = ?)`).run(a?null:O(I),(await r(n,o)).id,(await r(n,t)).id)})}i(g,"setSubscriptionActive");async function U(o){return(await w(await c(),"AND oo.id = ?",o)).map(t=>({node_id:t.from_id,active:t.active,created:t.created}))}i(U,"subscribersOf");async function B(o){return(await w(await c(),"AND wo.id = ?",o)).map(t=>({node_id:t.to_id,active:t.active,created:t.created}))}i(B,"subscriptionsOf");async function P(o){const t=await c(),a=await A(t,o);if(a?.type!=="node")return[];const n=t.prepare("SELECT 1 FROM nodes WHERE node_id = ?");return await j((await b(t,a)).map(s=>s.id),async s=>await n.get(s)!==void 0)}i(P,"view");const l=new Set(["active","idle"]);async function V(o){return(await w(await c(),"AND wo.id = ?",o)).some(t=>t.active&&l.has(t.to_status??""))}i(V,"hasActiveLiveSubscription");async function q(o){return(await w(await c(),"AND oo.id = ?",o)).some(t=>t.active&&l.has(t.from_status??""))}i(q,"hasLiveSubscriber");export{V as hasActiveLiveSubscription,q as hasLiveSubscriber,C as rosterEdges,g as setSubscriptionActive,H as subscribe,U as subscribersOf,B as subscriptionsOf,M as unsubscribe,P as view};
10
+ ORDER BY ws.created_at, ws.seq`).all(...a),s=new Map;for(const n of e){const u=n.config_json,_=n.listen_json,f=u!=null?N(u):_!=null?N(_):v,p=`${n.from_id}\0${n.to_id}`,E=s.get(p);if(E){E.active||=l(f);continue}s.set(p,{from_id:n.from_id,to_id:n.to_id,active:l(f),created:n.created_at,from_status:n.from_status??null,to_status:n.to_status??null})}return[...s.values()]}i(w,"treeEdges");async function F(){return(await w(await c(),"")).map(({from_id:o,to_id:t,active:a,created:e})=>({from_id:o,to_id:t,active:a,created:e}))}i(F,"rosterEdges");async function W(o,t,a=!0){await d(async e=>{const s=await r(e,o),n=await r(e,t);await y(e,s,n,"ask"),await j(o,t,a),await T(e,{event:"watched",object:n,actor:s,attrs:{}})})}i(W,"subscribe");async function M(o,t){await d(async a=>{const e=await r(a,o),s=await r(a,t);for(const n of D)await I(a,e,s,{startedBy:n})})}i(M,"unsubscribe");async function j(o,t,a){await d(async e=>{await e.prepareUncached(`UPDATE watch_subscriptions SET config_json = ?
11
+ WHERE started_by IN ${S}
12
+ AND watch IN (SELECT watch_id FROM watches WHERE watcher = ? AND object = ?)`).run(a?null:b(R),(await r(e,o)).id,(await r(e,t)).id)})}i(j,"setSubscriptionActive");async function $(o){return(await w(await c(),"AND oo.id = ?",o)).map(t=>({node_id:t.from_id,active:t.active,created:t.created}))}i($,"subscribersOf");async function B(o){return(await w(await c(),"AND wo.id = ?",o)).map(t=>({node_id:t.to_id,active:t.active,created:t.created}))}i(B,"subscriptionsOf");function h(o){return`WITH RECURSIVE walk(id) AS (
13
+ SELECT CAST(? AS text)
14
+ UNION
15
+ SELECT w.object FROM walk
16
+ JOIN watches w ON w.watcher = walk.id AND w.in_tree = 1
17
+ ${o?`JOIN objects oo ON oo.id = w.object AND oo.type = 'node' AND oo.kind IS DISTINCT FROM 'human'
18
+ JOIN nodes onode ON onode.node_id = oo.id`:"JOIN nodes onode ON onode.node_id = w.object"}
19
+ ) SELECT id FROM walk WHERE id != ?`}i(h,"subtreeSql");const H=h(!0),L=h(!1);async function P(o){const t=await c(),a=await m(t,o);return a?.type!=="node"?[]:(await t.prepareUncached(L).all(a.id,a.id)).map(e=>e.id)}i(P,"view");const A=new Set(["active","idle"]);async function Q(o){return(await w(await c(),"AND wo.id = ?",o)).some(t=>t.active&&A.has(t.to_status??""))}i(Q,"hasActiveLiveSubscription");async function V(o){return(await w(await c(),"AND oo.id = ?",o)).some(t=>t.active&&A.has(t.from_status??""))}i(V,"hasLiveSubscriber");export{L as SUBTREE_ALL_IDS_SQL,H as SUBTREE_IDS_SQL,Q as hasActiveLiveSubscription,V as hasLiveSubscriber,F as rosterEdges,j as setSubscriptionActive,W as subscribe,$ as subscribersOf,B as subscriptionsOf,M as unsubscribe,P as view};
@@ -1,4 +1,4 @@
1
- var N=Object.defineProperty;var i=(e,t)=>N(e,"name",{value:t,configurable:!0});import{cancelCronsOnWake as O,getNode as E}from"../canvas/index.js";import{runStructuredRequestEffects as y,settleStructuredRequestIn as R}from"../canvas/structured-requests.js";import{openDb as q,withCanvasWrite as T}from"../canvas/db.js";import{randomUUID as x}from"node:crypto";import{composeNodeOutcomeDocument as F}from"../runtime/outcome-document.js";import{closeNode as M}from"../runtime/close.js";import{put as W}from"../graph/bodies.js";import{writeDocument as z}from"../graph/documents.js";import{asRuntimeWriter as A}from"../graph/spaces/own-memory.js";import{publish as k}from"../graph/events.js";import{nodeObject as v}from"../graph/objects.js";import{readerForNode as L}from"../graph/reader.js";import{endMessageSubscriptions as C}from"../graph/watches.js";import{operationIdContext as D}from"../events/operation-id.js";class H extends Error{static{i(this,"StructuredRequestFinalizationError")}state;constructor(t){super(t==="expired"?"structured_request_expired":"structured_request_settled"),this.state=t}}class h extends Error{static{i(this,"FinalizationError")}code;report;constructor(t,r){super(t),this.code=t,this.report=r}}function j(e){const t=i((r,n=2)=>String(r).padStart(n,"0"),"pad");return`${e.getUTCFullYear()}${t(e.getUTCMonth()+1)}${t(e.getUTCDate())}T${t(e.getUTCHours())}${t(e.getUTCMinutes())}${t(e.getUTCSeconds())}${t(e.getUTCMilliseconds(),3)}`}i(j,"compactTs");function S(e,t=80){const r=e.split(`
2
- `)[0]??"";return r.length>t?`${r.slice(0,t-1)}\u2026`:r}i(S,"firstLine");function B(e){return e.kind==="final"?{event:"report.final"}:e.result!==void 0?{event:"report.result"}:e.kind==="urgent"?{event:"report.update",level:7}:{event:"report.update",level:e.deliveryTier==="deferred"?3:e.deliveryTier==="urgent"?7:5}}i(B,"eventFor");async function U(e,t,r){const n=r.from??t,o=`reports/${j(new Date)}-${x().slice(-8)}-${r.kind}`,u=await v(e,t),c=await v(e,n),{object:d}=await z(e,A(await L(e,n,async()=>[])),{owner:u,kind:"report",name:o,preview:S(r.body)||r.kind,privacy:"public"},r.body),s=`${t}/${o}`,{event:p,level:m}=B(r);await k(e,{event:p,object:u,actor:c,...m===void 0?{}:{level:m},attrs:{},pointer:d.id,messageBody:{kind:r.kind,label:S(r.body),bodyRef:s,...r.disposition===void 0?{}:{disposition:r.disposition}}});const l=(await e.prepare(`SELECT d.destination_id FROM deliveries d JOIN messages m ON m.message_id = d.message_id
1
+ var O=Object.defineProperty;var i=(e,t)=>O(e,"name",{value:t,configurable:!0});import{cancelCronsOnWake as q,getNode as E}from"../canvas/index.js";import{runStructuredRequestEffects as g,settleStructuredRequestIn as R}from"../canvas/structured-requests.js";import{afterCommit as x,openDb as F,withCanvasWrite as k}from"../canvas/db.js";import{brokerRelayGeneration as M,recordBrokerRelease as W}from"../runtime/lifecycle.js";import{randomUUID as z}from"node:crypto";import{composeNodeOutcomeDocument as A}from"../runtime/outcome-document.js";import{closeNode as L}from"../runtime/close.js";import{put as H}from"../graph/bodies.js";import{writeDocument as j}from"../graph/documents.js";import{asRuntimeWriter as B}from"../graph/spaces/own-memory.js";import{publish as T}from"../graph/events.js";import{nodeObject as v}from"../graph/objects.js";import{readerForNode as J}from"../graph/reader.js";import{endMessageSubscriptions as C}from"../graph/watches.js";import{operationIdContext as D}from"../events/operation-id.js";class b extends Error{static{i(this,"StructuredRequestFinalizationError")}state;constructor(t){super(t==="expired"?"structured_request_expired":"structured_request_settled"),this.state=t}}class h extends Error{static{i(this,"FinalizationError")}code;report;constructor(t,r){super(t),this.code=t,this.report=r}}function Y(e){const t=i((r,n=2)=>String(r).padStart(n,"0"),"pad");return`${e.getUTCFullYear()}${t(e.getUTCMonth()+1)}${t(e.getUTCDate())}T${t(e.getUTCHours())}${t(e.getUTCMinutes())}${t(e.getUTCSeconds())}${t(e.getUTCMilliseconds(),3)}`}i(Y,"compactTs");function S(e,t=80){const r=e.split(`
2
+ `)[0]??"";return r.length>t?`${r.slice(0,t-1)}\u2026`:r}i(S,"firstLine");function P(e){return e.kind==="final"?{event:"report.final"}:e.result!==void 0?{event:"report.result"}:e.kind==="urgent"?{event:"report.update",level:7}:{event:"report.update",level:e.deliveryTier==="deferred"?3:e.deliveryTier==="urgent"?7:5}}i(P,"eventFor");async function U(e,t,r){const n=r.from??t,o=`reports/${Y(new Date)}-${z().slice(-8)}-${r.kind}`,u=await v(e,t),c=await v(e,n),{object:d}=await j(e,B(await J(e,n,async()=>[])),{owner:u,kind:"report",name:o,preview:S(r.body)||r.kind,privacy:"public"},r.body),s=`${t}/${o}`,{event:p,level:m}=P(r);await T(e,{event:p,object:u,actor:c,...m===void 0?{}:{level:m},attrs:{},pointer:d.id,messageBody:{kind:r.kind,label:S(r.body),bodyRef:s,...r.disposition===void 0?{}:{disposition:r.disposition}}});const l=(await e.prepare(`SELECT d.destination_id FROM deliveries d JOIN messages m ON m.message_id = d.message_id
3
3
  JOIN objects receiver ON receiver.id = d.destination_id
4
- WHERE m.body_ref = ? AND receiver.type = 'node' ORDER BY d.destination_id`).all(s)).map(w=>w.destination_id);return r.kind==="final"?await C(e,u,"final"):r.result?.requestMessageId&&await C(e,u,{result:r.result.requestMessageId}),{name:o,ref:s,doc:d,createdAt:d.created_at,deliveredTo:l}}i(U,"writeReport");async function J(e,t){return T(async r=>{const n=await r.prepare("SELECT status, final_report FROM nodes WHERE node_id = ?").get(e);if(n===void 0)throw new h("unknown_node");const o=t.outcome;let u;if(o?.structured!==void 0){const a=await R(r,o.structured.requestId,o.structured.declined===void 0?{intended:"completed",result:o.structured.value}:{intended:"declined",declined:o.structured.declined});if(!a.changed||a.row===null||a.row.state!=="completed"&&a.row.state!=="declined")return{written:void 0,...a.row===null?{}:{structured:a.row},structuredRefused:!0};u=a.row}else{const a=await r.prepareUncached("SELECT request_id FROM structured_requests WHERE node_id = ? AND state = 'pending'").get(e);if(a!==void 0){const f=await R(r,a.request_id,{intended:"failed",failureReason:"finalized"});if(!f.changed||f.row===null||f.row.state!=="failed")return{written:void 0,...f.row===null?{}:{structured:f.row},structuredRefused:!0};u=f.row}}if(n.final_report!==null)throw new h("already_finalized",`${e}/${n.final_report}`);if(n.status!=="active"&&n.status!=="idle"&&n.status!=="done")throw new h("finalization_forbidden");const c=await U(r,e,t),d=await E(e),s=d?.kind!=="human",p=(d?.outcome_revision??0)+1,m=t.outcome?.kind??"result",l=t.outcome?.reason??"finalized",w=t.outcome?.detailJson??null,_=c.createdAt;if(s){const a=d===null?null:await F({node:{...d,status:"done",intent:"done",terminal_reason:l},outcome:{kind:m,reason:l,at:_,revision:p,detailJson:w},finalReport:c.name,...u?.state==="completed"?{structuredResult:u.result}:{}});await r.prepare("UPDATE nodes SET status = 'done', intent = 'done', terminal_reason = ?, final_report = ?, finalized_at = ?, outcome_kind = ?, outcome_reason = ?, outcome_at = ?, outcome_revision = outcome_revision + 1, outcome_detail_json = ? WHERE node_id = ? AND final_report IS NULL").run(l,c.name,_,m,l,_,w,e),a!==null&&await k(r,{event:"outcome",object:await v(r,e),actor:null,attrs:{revision:p},messageBody:a})}else await r.prepare("UPDATE nodes SET status = 'done', intent = 'done', terminal_reason = 'finalized', final_report = ?, finalized_at = ? WHERE node_id = ? AND final_report IS NULL").run(c.name,_,e);return{written:c,...u===void 0?{}:{structured:u}}})}i(J,"commitCanonicalFinal");async function $(e,t){if(!e)throw new Error(`push: empty nodeId (kind=${t.kind}) \u2014 a report must belong to a node`);if(t.kind==="final"&&t.deliveryTier!==void 0)throw new Error("push final does not accept a delivery tier");if(t.kind!=="final"&&t.outcome!==void 0)throw new Error("push outcome is final-only");if(t.kind!=="update"&&t.result!==void 0)throw new Error("a one-off result is an update report");if(W(t.body),t.kind==="final"){const n=await J(e,t);if(n.structuredRefused===!0)throw n.structured!==void 0&&y(n.structured),new H(n.structured?.state??"failed");n.structured!==void 0&&t.outcome?.structured?.skipEffects!==!0&&y(n.structured);const{written:o}=n;if(await O(e),(await E(e))?.run_id===e){const c=await(await q()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ? ORDER BY created DESC").all(e,e);for(const d of c){const s=await E(d.node_id);s&&s.status!=="done"&&s.status!=="canceled"&&s.status!=="dead"&&await M(d.node_id,{preserveEmpty:!0})}}return{report:o.ref,reportId:o.doc.id,deliveredTo:o.deliveredTo}}const r=await T(async n=>await U(n,e,t));return{report:r.ref,reportId:r.doc.id,deliveredTo:r.deliveredTo}}i($,"pushWithContext");function g(e,t){return D.current()===void 0?D.fresh(()=>$(e,t)):$(e,t)}i(g,"push");async function oe(e,t,r){return g(e,{kind:"update",body:t,...r})}i(oe,"pushUpdate");async function ae(e,t,r){return g(e,{kind:"urgent",body:t,...r})}i(ae,"pushUrgent");async function ue(e,t,r){return g(e,{kind:"final",body:t,...r})}i(ue,"pushFinal");export{h as FinalizationError,H as StructuredRequestFinalizationError,g as push,ue as pushFinal,oe as pushUpdate,ae as pushUrgent};
4
+ WHERE m.body_ref = ? AND receiver.type = 'node' ORDER BY d.destination_id`).all(s)).map(w=>w.destination_id);return r.kind==="final"?await C(e,u,"final"):r.result?.requestMessageId&&await C(e,u,{result:r.result.requestMessageId}),{name:o,ref:s,doc:d,createdAt:d.created_at,deliveredTo:l}}i(U,"writeReport");async function G(e,t){return k(async r=>{const n=await r.prepare("SELECT status, final_report FROM nodes WHERE node_id = ?").get(e);if(n===void 0)throw new h("unknown_node");const o=t.outcome;let u;if(o?.structured!==void 0){const a=await R(r,o.structured.requestId,o.structured.declined===void 0?{intended:"completed",result:o.structured.value}:{intended:"declined",declined:o.structured.declined});if(!a.changed||a.row===null||a.row.state!=="completed"&&a.row.state!=="declined")return{written:void 0,...a.row===null?{}:{structured:a.row},structuredRefused:!0};u=a.row}else{const a=await r.prepareUncached("SELECT request_id FROM structured_requests WHERE node_id = ? AND state = 'pending'").get(e);if(a!==void 0){const f=await R(r,a.request_id,{intended:"failed",failureReason:"finalized"});if(!f.changed||f.row===null||f.row.state!=="failed")return{written:void 0,...f.row===null?{}:{structured:f.row},structuredRefused:!0};u=f.row}}if(n.final_report!==null)throw new h("already_finalized",`${e}/${n.final_report}`);if(n.status!=="active"&&n.status!=="idle"&&n.status!=="done")throw new h("finalization_forbidden");const c=await U(r,e,t),d=await E(e),s=d?.kind!=="human",p=(d?.outcome_revision??0)+1,m=t.outcome?.kind??"result",l=t.outcome?.reason??"finalized",w=t.outcome?.detailJson??null,_=c.createdAt;if(s){const a=d===null?null:await A({node:{...d,status:"done",intent:"done",terminal_reason:l},outcome:{kind:m,reason:l,at:_,revision:p,detailJson:w},finalReport:c.name,...u?.state==="completed"?{structuredResult:u.result}:{}});await r.prepare("UPDATE nodes SET status = 'done', intent = 'done', terminal_reason = ?, final_report = ?, finalized_at = ?, outcome_kind = ?, outcome_reason = ?, outcome_at = ?, outcome_revision = outcome_revision + 1, outcome_detail_json = ? WHERE node_id = ? AND final_report IS NULL").run(l,c.name,_,m,l,_,w,e),a!==null&&await T(r,{event:"outcome",object:await v(r,e),actor:null,attrs:{revision:p},messageBody:a})}else await r.prepare("UPDATE nodes SET status = 'done', intent = 'done', terminal_reason = 'finalized', final_report = ?, finalized_at = ? WHERE node_id = ? AND final_report IS NULL").run(c.name,_,e);const N=M(e);return x(()=>W(e,N)),{written:c,...u===void 0?{}:{structured:u}}})}i(G,"commitCanonicalFinal");async function $(e,t){if(!e)throw new Error(`push: empty nodeId (kind=${t.kind}) \u2014 a report must belong to a node`);if(t.kind==="final"&&t.deliveryTier!==void 0)throw new Error("push final does not accept a delivery tier");if(t.kind!=="final"&&t.outcome!==void 0)throw new Error("push outcome is final-only");if(t.kind!=="update"&&t.result!==void 0)throw new Error("a one-off result is an update report");if(H(t.body),t.kind==="final"){const n=await G(e,t);if(n.structuredRefused===!0)throw n.structured!==void 0&&g(n.structured),new b(n.structured?.state??"failed");n.structured!==void 0&&t.outcome?.structured?.skipEffects!==!0&&g(n.structured);const{written:o}=n;if(await q(e),(await E(e))?.run_id===e){const c=await(await F()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ? ORDER BY created DESC").all(e,e);for(const d of c){const s=await E(d.node_id);s&&s.status!=="done"&&s.status!=="canceled"&&s.status!=="dead"&&await L(d.node_id,{preserveEmpty:!0})}}return{report:o.ref,reportId:o.doc.id,deliveredTo:o.deliveredTo}}const r=await k(async n=>await U(n,e,t));return{report:r.ref,reportId:r.doc.id,deliveredTo:r.deliveredTo}}i($,"pushWithContext");function y(e,t){return D.current()===void 0?D.fresh(()=>$(e,t)):$(e,t)}i(y,"push");async function ce(e,t,r){return y(e,{kind:"update",body:t,...r})}i(ce,"pushUpdate");async function le(e,t,r){return y(e,{kind:"urgent",body:t,...r})}i(le,"pushUrgent");async function fe(e,t,r){return y(e,{kind:"final",body:t,...r})}i(fe,"pushFinal");export{h as FinalizationError,b as StructuredRequestFinalizationError,y as push,fe as pushFinal,ce as pushUpdate,le as pushUrgent};
@@ -65,6 +65,12 @@ export declare function listenDeliveryTier(delivery: ListenDelivery): {
65
65
  urgency: InboxTier;
66
66
  wake: 0 | 1;
67
67
  };
68
+ /** A wake reached `nodeId`: delete its cancel-on-wake deadline and, in the same write, queue a
69
+ * notice naming each deleted deadline so the node knows its timer is gone. The notice is inserted
70
+ * directly rather than through `queueNodeNotice`, so it cancels nothing and a deadline the node
71
+ * arms afterwards survives it. A deadline that fires on its own deletes its row first, so finds
72
+ * nothing here and no notice is queued. */
73
+ export declare function consumeDeadlineOnWake(nodeId: string): Promise<void>;
68
74
  /** Queue one runtime notice to a node's inbox inside the caller's write transaction (no sender). */
69
75
  export declare function queueNodeNotice(db: CanvasDb, nodeId: string, notice: {
70
76
  body: string;
@@ -1,8 +1,8 @@
1
- var p=Object.defineProperty;var s=(r,e)=>p(r,"name",{value:e,configurable:!0});import{existsSync as _,mkdirSync as E,renameSync as N,writeFileSync as I}from"node:fs";import{randomBytes as v,randomUUID as b}from"node:crypto";import{join as k}from"node:path";import{cancelCronsOnWake as u}from"../canvas/crons.js";import{withCanvasWrite as c,openDb as h}from"../canvas/db.js";import{getRow as S}from"../canvas/canvas.js";import{messagesDir as R}from"../canvas/paths.js";import{syncDirectory as D,syncFile as T}from"../fs-utils.js";import{settleDeadControllerWait as A,settleMatchingControllerWait as O}from"../canvas/waits.js";import{generateOperationId as x,operationIdContext as L,validateOperationId as C}from"../events/operation-id.js";import{clipBody as J}from"../../shared/inbox-entry-body.js";function y(){let r;do r=v(16).toString("hex");while(r==="00000000000000000000000000000000");return r}s(y,"newId");function M(r,e){const t=JSON.stringify(r);if(t===void 0)throw new TypeError(`${e} must be JSON-serializable`);return t}s(M,"stringify");async function U(r,e){return e!=="normal"&&e!=="deferred"?e:(await S(r))?.lifecycle==="terminal"?"urgent":e}s(U,"effectiveTier");function W(r,e,t){const n=R(r);_(n)||E(n,{recursive:!0});const a=e.replace(/[^0-9]/g,""),d=k(n,`${a}-${b().slice(0,6)}.md`),i=`${d}.tmp`;return I(i,t,"utf8"),T(i),N(i,d),D(n),d}s(W,"writeMessageBody");function q(r){if(typeof r!="object"||r===null)return"";const e=r.body;return typeof e=="string"?e.trim():""}s(q,"inlineBody");async function m(r,e){const t=e.messageId??y(),n=Number((await r.prepare("UPDATE message_sequence SET seq = seq + 1 WHERE id = 1 RETURNING seq").get()).seq);return await r.prepare(`INSERT INTO messages
1
+ var _=Object.defineProperty;var o=(r,e)=>_(r,"name",{value:e,configurable:!0});import{existsSync as N,mkdirSync as E,renameSync as I,writeFileSync as b}from"node:fs";import{randomBytes as v,randomUUID as k}from"node:crypto";import{join as h}from"node:path";import{cancelCronsOnWake as R}from"../canvas/crons.js";import{withCanvasWrite as l,openDb as S}from"../canvas/db.js";import{getRow as D}from"../canvas/canvas.js";import{messagesDir as T}from"../canvas/paths.js";import{syncDirectory as A,syncFile as O}from"../fs-utils.js";import{settleDeadControllerWait as x,settleMatchingControllerWait as L}from"../canvas/waits.js";import{generateOperationId as C,operationIdContext as J,validateOperationId as M}from"../events/operation-id.js";import{clipBody as U}from"../../shared/inbox-entry-body.js";function u(){let r;do r=v(16).toString("hex");while(r==="00000000000000000000000000000000");return r}o(u,"newId");function W(r,e){const t=JSON.stringify(r);if(t===void 0)throw new TypeError(`${e} must be JSON-serializable`);return t}o(W,"stringify");async function q(r,e){return e!=="normal"&&e!=="deferred"?e:(await D(r))?.lifecycle==="terminal"?"urgent":e}o(q,"effectiveTier");function $(r,e,t){const n=T(r);N(n)||E(n,{recursive:!0});const a=e.replace(/[^0-9]/g,""),d=h(n,`${a}-${k().slice(0,6)}.md`),i=`${d}.tmp`;return b(i,t,"utf8"),O(i),I(i,d),A(n),d}o($,"writeMessageBody");function F(r){if(typeof r!="object"||r===null)return"";const e=r.body;return typeof e=="string"?e.trim():""}o(F,"inlineBody");async function y(r,e){const t=e.messageId??u(),n=Number((await r.prepare("UPDATE message_sequence SET seq = seq + 1 WHERE id = 1 RETURNING seq").get()).seq);return await r.prepare(`INSERT INTO messages
2
2
  (message_id, seq, operation_id, created_at, sender_node_id, sender_name, kind, label, body_ref, body_json, disposition,
3
3
  event, object_id, level, actor_id, cause_message_id, depth)
4
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(t,n,e.operationId,e.createdAt,e.senderNodeId,e.senderName,e.kind,e.label,e.bodyRef,e.bodyJson===void 0?null:M(e.bodyJson,"message body"),e.disposition,e.event?.event??null,e.event?.objectId??null,e.event?.level??null,e.event?.actorId??null,e.causeMessageId??null,e.depth??0),t}s(m,"insertMessage");async function f(r,e,t,n,a,d,i){const o=y();return await r.prepare(`INSERT INTO deliveries
4
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(t,n,e.operationId,e.createdAt,e.senderNodeId,e.senderName,e.kind,e.label,e.bodyRef,e.bodyJson===void 0?null:W(e.bodyJson,"message body"),e.disposition,e.event?.event??null,e.event?.objectId??null,e.event?.level??null,e.event?.actorId??null,e.causeMessageId??null,e.depth??0),t}o(y,"insertMessage");async function m(r,e,t,n,a,d,i){const s=u();return await r.prepare(`INSERT INTO deliveries
5
5
  (delivery_id, message_id, destination_id, urgency, wake, state, idempotency_key, created_at, updated_at)
6
- VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?)`).run(o,e,t,n,a,i??null,d,d),o}s(f,"insertNodeDelivery");function g(r,e){const t=new Date().toISOString(),n=r.operationId===void 0?L.current()??x():C(r.operationId);let a=r.bodyRef??null,d=r.bodyJson;if(a===null&&e!==null&&r.senderNodeId!=="human"){const i=q(d),o=J(i);o.clipped&&(a=W(e,t,i),d={...d,body:o.text})}return{createdAt:t,operationId:n,bodyRef:a,bodyJson:d}}s(g,"finalizeNodeMail");async function F(r,e){const t=g(e,r);return await c(async n=>{const a=await n.prepare("SELECT run_id FROM nodes WHERE node_id = ?").get(r);if(a?.run_id&&(await n.prepare("SELECT deleting_at FROM nodes WHERE node_id = ?").get(a.run_id))?.deleting_at)throw new Error("run is deleting");if(e.idempotencyKey!==void 0){const l=await n.prepare(`SELECT d.delivery_id, d.message_id, d.urgency, m.created_at
7
- FROM deliveries d JOIN messages m ON m.message_id = d.message_id WHERE d.idempotency_key = ?`).get(e.idempotencyKey);if(l!==void 0)return{messageId:l.message_id,deliveryId:l.delivery_id,urgency:l.urgency,createdAt:l.created_at}}const d=Date.now(),i=await U(r,e.urgency),o=await m(n,{messageId:e.messageId,operationId:t.operationId,createdAt:t.createdAt,senderNodeId:e.senderNodeId??null,senderName:e.senderName??null,kind:e.kind,label:e.label,bodyRef:t.bodyRef,bodyJson:t.bodyJson,disposition:e.disposition??null}),w=await f(n,o,r,i,e.wake,d,e.idempotencyKey);return await O(r,e.senderNodeId??null,d,n),await e.withinEnqueue?.(n,o),e.wake===1&&i!=="deferred"&&await u(r),{messageId:o,deliveryId:w,urgency:i,createdAt:t.createdAt}})}s(F,"enqueueNodeMail");function Z(r){switch(r){case"interrupt":return{urgency:"critical",wake:1};case"steer":return{urgency:"urgent",wake:1};case"after-turn":return{urgency:"normal",wake:1};case"next-turn":return{urgency:"deferred",wake:0}}}s(Z,"listenDeliveryTier");async function ee(r,e,t){const n={kind:"update",label:t.label??"notice",bodyJson:{body:t.body},urgency:t.tier,wake:t.wake},a=g(n,e),d=Date.now(),i=await m(r,{operationId:a.operationId,createdAt:a.createdAt,senderNodeId:null,senderName:null,kind:n.kind,label:n.label,bodyRef:a.bodyRef,bodyJson:a.bodyJson,disposition:null}),o=await f(r,i,e,t.tier,t.wake,d);return t.wake===1&&t.tier!=="deferred"&&await u(e),{messageId:i,deliveryId:o,urgency:t.tier,createdAt:a.createdAt}}s(ee,"queueNodeNotice");async function re(r,e=Date.now()){return Number((await(await h()).prepare("UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE delivery_id = ? AND canceled_at IS NULL").run(e,e,r)).changes)===1}s(re,"cancelDelivery");async function te(r,e={}){return await c(async t=>{const n=["d.destination_id = ?","d.canceled_at IS NULL","d.state IN ('pending', 'handed_off')"],a=[r];e.senderNodeId!==void 0&&(n.push("m.sender_node_id IS NOT DISTINCT FROM ?"),a.push(e.senderNodeId)),e.kind!==void 0&&(n.push("m.kind = ?"),a.push(e.kind));const d=await t.prepareUncached(`SELECT d.delivery_id FROM deliveries d JOIN messages m ON m.message_id = d.message_id WHERE ${n.join(" AND ")}`).all(...a);if(d.length===0)return[];const i=Date.now();return await t.prepareUncached(`UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE delivery_id IN (${d.map(()=>"?").join(",")})`).run(i,i,...d.map(o=>o.delivery_id)),d.map(o=>o.delivery_id)})}s(te,"cancelPendingNodeMail");async function ne(r,e){return await c(async t=>{const n=Date.now(),a=await t.prepare(`SELECT target.status AS controller_status FROM waits w LEFT JOIN nodes target ON target.node_id = w.target_id
8
- WHERE w.node_id = ? AND w.kind = 'controller' AND w.target_id = ? AND w.satisfied_at IS NULL`).get(r,e);if(a===void 0||a.controller_status==="active"||a.controller_status==="idle")return null;const d=await F(r,{kind:"urgent",label:"declared controller is no longer live",bodyJson:{body:`Controller ${e} is no longer live; the declared message wait was cleared.`},urgency:"urgent",wake:1});if(!await A(r,e,n,t))throw new Error(`controller wait changed while notifying ${r}`);return d})}s(ne,"enqueueControllerDeathNotice");export{re as cancelDelivery,te as cancelPendingNodeMail,U as effectiveTier,ne as enqueueControllerDeathNotice,F as enqueueNodeMail,g as finalizeNodeMail,m as insertMessage,Z as listenDeliveryTier,y as newId,ee as queueNodeNotice};
6
+ VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?)`).run(s,e,t,n,a,i??null,d,d),s}o(m,"insertNodeDelivery");function f(r,e){const t=new Date().toISOString(),n=r.operationId===void 0?J.current()??C():M(r.operationId);let a=r.bodyRef??null,d=r.bodyJson;if(a===null&&e!==null&&r.senderNodeId!=="human"){const i=F(d),s=U(i);s.clipped&&(a=$(e,t,i),d={...d,body:s.text})}return{createdAt:t,operationId:n,bodyRef:a,bodyJson:d}}o(f,"finalizeNodeMail");async function j(r,e){const t=f(e,r);return await l(async n=>{const a=await n.prepare("SELECT run_id FROM nodes WHERE node_id = ?").get(r);if(a?.run_id&&(await n.prepare("SELECT deleting_at FROM nodes WHERE node_id = ?").get(a.run_id))?.deleting_at)throw new Error("run is deleting");if(e.idempotencyKey!==void 0){const c=await n.prepare(`SELECT d.delivery_id, d.message_id, d.urgency, m.created_at
7
+ FROM deliveries d JOIN messages m ON m.message_id = d.message_id WHERE d.idempotency_key = ?`).get(e.idempotencyKey);if(c!==void 0)return{messageId:c.message_id,deliveryId:c.delivery_id,urgency:c.urgency,createdAt:c.created_at}}const d=Date.now(),i=await q(r,e.urgency),s=await y(n,{messageId:e.messageId,operationId:t.operationId,createdAt:t.createdAt,senderNodeId:e.senderNodeId??null,senderName:e.senderName??null,kind:e.kind,label:e.label,bodyRef:t.bodyRef,bodyJson:t.bodyJson,disposition:e.disposition??null}),p=await m(n,s,r,i,e.wake,d,e.idempotencyKey);return await L(r,e.senderNodeId??null,d,n),await e.withinEnqueue?.(n,s),e.wake===1&&i!=="deferred"&&await g(r),{messageId:s,deliveryId:p,urgency:i,createdAt:t.createdAt}})}o(j,"enqueueNodeMail");function re(r){switch(r){case"interrupt":return{urgency:"critical",wake:1};case"steer":return{urgency:"urgent",wake:1};case"after-turn":return{urgency:"normal",wake:1};case"next-turn":return{urgency:"deferred",wake:0}}}o(re,"listenDeliveryTier");async function w(r,e,t){const n={kind:"update",label:t.label??"notice",bodyJson:{body:t.body},urgency:t.tier,wake:t.wake},a=f(n,e),d=Date.now(),i=await y(r,{operationId:a.operationId,createdAt:a.createdAt,senderNodeId:null,senderName:null,kind:n.kind,label:n.label,bodyRef:a.bodyRef,bodyJson:a.bodyJson,disposition:null}),s=await m(r,i,e,t.tier,t.wake,d);return{messageId:i,deliveryId:s,urgency:t.tier,createdAt:a.createdAt}}o(w,"insertNodeNotice");async function g(r){await l(async e=>{for(const t of await R(r))await w(e,r,{body:`Your cron ${t.name}, next scheduled for ${t.fire_at}, was canceled by this wake. Re-create it if you still need it.`,label:"cron canceled",tier:"normal",wake:1})})}o(g,"consumeDeadlineOnWake");async function te(r,e,t){const n=await w(r,e,t);return t.wake===1&&t.tier!=="deferred"&&await g(e),n}o(te,"queueNodeNotice");async function ne(r,e=Date.now()){return Number((await(await S()).prepare("UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE delivery_id = ? AND canceled_at IS NULL").run(e,e,r)).changes)===1}o(ne,"cancelDelivery");async function ae(r,e={}){return await l(async t=>{const n=["d.destination_id = ?","d.canceled_at IS NULL","d.state IN ('pending', 'handed_off')"],a=[r];e.senderNodeId!==void 0&&(n.push("m.sender_node_id IS NOT DISTINCT FROM ?"),a.push(e.senderNodeId)),e.kind!==void 0&&(n.push("m.kind = ?"),a.push(e.kind));const d=await t.prepareUncached(`SELECT d.delivery_id FROM deliveries d JOIN messages m ON m.message_id = d.message_id WHERE ${n.join(" AND ")}`).all(...a);if(d.length===0)return[];const i=Date.now();return await t.prepareUncached(`UPDATE deliveries SET canceled_at = ?, updated_at = ? WHERE delivery_id IN (${d.map(()=>"?").join(",")})`).run(i,i,...d.map(s=>s.delivery_id)),d.map(s=>s.delivery_id)})}o(ae,"cancelPendingNodeMail");async function de(r,e){return await l(async t=>{const n=Date.now(),a=await t.prepare(`SELECT target.status AS controller_status FROM waits w LEFT JOIN nodes target ON target.node_id = w.target_id
8
+ WHERE w.node_id = ? AND w.kind = 'controller' AND w.target_id = ? AND w.satisfied_at IS NULL`).get(r,e);if(a===void 0||a.controller_status==="active"||a.controller_status==="idle")return null;const d=await j(r,{kind:"urgent",label:"declared controller is no longer live",bodyJson:{body:`Controller ${e} is no longer live; the declared message wait was cleared.`},urgency:"urgent",wake:1});if(!await x(r,e,n,t))throw new Error(`controller wait changed while notifying ${r}`);return d})}o(de,"enqueueControllerDeathNotice");export{ne as cancelDelivery,ae as cancelPendingNodeMail,g as consumeDeadlineOnWake,q as effectiveTier,de as enqueueControllerDeathNotice,j as enqueueNodeMail,f as finalizeNodeMail,y as insertMessage,re as listenDeliveryTier,u as newId,te as queueNodeNotice};
@@ -1,5 +1,5 @@
1
- var E=Object.defineProperty;var l=(t,r)=>E(t,"name",{value:r,configurable:!0});import{cancelCronsOnWake as _}from"../../canvas/crons.js";import{settleMatchingControllerWait as T}from"../../canvas/waits.js";import{effectiveTier as b,listenDeliveryTier as R,newId as I}from"../../feed/messages.js";import{readGoal as N}from"../../runtime/kickoff.js";import{canRead as h}from"../access.js";import{objectDTO as y}from"./dto.js";import{withBase as S}from"./kind.js";import{recordLiveRead as L}from"./live-read.js";import{attr as O,header as D,section as m}from"./render.js";import{filterAsync as j,mapAsync as C}from"../../async-array.js";const p=[3,5,7];function g(t){return t?.split(`
2
- `).map(r=>r.trim()).find(r=>r!=="")??""}l(g,"firstLine");const q={bodyOwner:l(t=>t[0]?.id??null,"bodyOwner"),async enqueue(t,r,a){const d=Date.now(),s=r.senderNodeId,n=a.map(e=>e.id),c=`(${n.map(()=>"?").join(",")})`,u=s===null?new Set:new Set((await t.prepareUncached(`SELECT DISTINCT node_id FROM waits WHERE kind = 'controller' AND target_id = ? AND satisfied_at IS NULL AND node_id IN ${c}`).all(s,...n)).map(e=>e.node_id)),i=new Set((await t.prepareUncached(`SELECT DISTINCT anchor_node FROM crons WHERE cancel_on_wake = 1 AND anchor_node IN ${c}`).all(...n)).map(e=>e.anchor_node)),w=t.prepare(`INSERT INTO deliveries (delivery_id, message_id, destination_id, urgency, wake, state, created_at, updated_at, idempotency_key)
3
- VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?)`);for(const e of a){const o=R(e.delivery),f=e.reception===2?o.wake:0,v=r.elevatesTier?await b(e.id,o.urgency):o.urgency;await w.run(I(),r.messageId,e.id,v,f,d,d,r.idempotencyKey===void 0?null:`${r.idempotencyKey}:${e.id}`),u.has(e.id)&&await T(e.id,s,d,t),f===1&&v!=="deferred"&&i.has(e.id)&&await _(e.id)}}},J={name:"node",addressable:"id",receiver:"inbox",receiverFor:l(async(t,r)=>await t.prepare("SELECT 1 FROM peer_stubs WHERE object = ?").get(r.id)===void 0?"inbox":"peer","receiverFor"),events:S({configured:{level:1,durable:!0},messaged:{level:2,durable:!0},sent:{level:1,durable:!0},"report.update":{level:5,levels:p,durable:!0,carriesBody:!0,elevatesTier:!0},"report.final":{level:7,durable:!0,carriesBody:!0,elevatesTier:!0,inboxKind:"final"},outcome:{level:1,durable:!0,supersedes:!0,carriesBody:!0},"report.result":{level:5,durable:!0,carriesBody:!0,elevatesTier:!0,inboxKind:"completed"},"notice.crash":{level:5,levels:p,durable:!0,carriesBody:!0,elevatesTier:!0},"notice.deadline":{level:5,levels:p,durable:!0,carriesBody:!0,elevatesTier:!0},"notice.close":{level:5,levels:p,durable:!0,carriesBody:!0,elevatesTier:!0}}),async read(t,r,a,d){const s=await t.prepare("SELECT status FROM nodes WHERE node_id = ?").get(a.id),n=l(async e=>await C(await j(e,async o=>await h(t,r,o)),async o=>await y(t,o)),"visible"),c=await t.prepare(`SELECT o.* FROM nodes n JOIN objects o ON o.id = n.node_id
4
- WHERE n.parent = ? AND o.deleted_at IS NULL ORDER BY o.seq`).all(a.id),u=await t.prepare("SELECT * FROM objects WHERE owner = ? AND deleted_at IS NULL ORDER BY seq").all(a.id);await L(t,r,a,d.reposFn);const i={status:s?.status??"deleted",mandate_first_line:g(await N(a.id)),children:await n(c),owned:await n(u.filter(e=>e.type!=="job")),jobs:await n(u.filter(e=>e.type==="job"))},w=[D("object",await y(t,a),O("status",i.status)),`Mandate: ${i.mandate_first_line}`,...m("Children",i.children),...m("Owns",i.owned),...m("Jobs",i.jobs)].join(`
5
- `);return{data:i,text:w}}};export{q as nodeInbox,J as nodeKind};
1
+ var E=Object.defineProperty;var l=(t,r)=>E(t,"name",{value:r,configurable:!0});import{settleMatchingControllerWait as _}from"../../canvas/waits.js";import{consumeDeadlineOnWake as T,effectiveTier as b,listenDeliveryTier as R,newId as I}from"../../feed/messages.js";import{readGoal as N}from"../../runtime/kickoff.js";import{canRead as h}from"../access.js";import{objectDTO as f}from"./dto.js";import{withBase as S}from"./kind.js";import{recordLiveRead as D}from"./live-read.js";import{attr as L,header as O,section as m}from"./render.js";import{filterAsync as j,mapAsync as g}from"../../async-array.js";const p=[3,5,7];function A(t){return t?.split(`
2
+ `).map(r=>r.trim()).find(r=>r!=="")??""}l(A,"firstLine");const K={bodyOwner:l(t=>t[0]?.id??null,"bodyOwner"),async enqueue(t,r,a){const d=Date.now(),s=r.senderNodeId,n=a.map(e=>e.id),c=`(${n.map(()=>"?").join(",")})`,u=s===null?new Set:new Set((await t.prepareUncached(`SELECT DISTINCT node_id FROM waits WHERE kind = 'controller' AND target_id = ? AND satisfied_at IS NULL AND node_id IN ${c}`).all(s,...n)).map(e=>e.node_id)),i=new Set((await t.prepareUncached(`SELECT DISTINCT anchor_node FROM crons WHERE cancel_on_wake = 1 AND anchor_node IN ${c}`).all(...n)).map(e=>e.anchor_node)),w=t.prepare(`INSERT INTO deliveries (delivery_id, message_id, destination_id, urgency, wake, state, created_at, updated_at, idempotency_key)
3
+ VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?)`);for(const e of a){const o=R(e.delivery),v=e.reception===2?o.wake:0,y=r.elevatesTier?await b(e.id,o.urgency):o.urgency;await w.run(I(),r.messageId,e.id,y,v,d,d,r.idempotencyKey===void 0?null:`${r.idempotencyKey}:${e.id}`),u.has(e.id)&&await _(e.id,s,d,t),v===1&&y!=="deferred"&&i.has(e.id)&&await T(e.id)}}},q={name:"node",addressable:"id",receiver:"inbox",receiverFor:l(async(t,r)=>await t.prepare("SELECT 1 FROM peer_stubs WHERE object = ?").get(r.id)===void 0?"inbox":"peer","receiverFor"),events:S({configured:{level:1,durable:!0},messaged:{level:2,durable:!0},sent:{level:1,durable:!0},"report.update":{level:5,levels:p,durable:!0,carriesBody:!0,elevatesTier:!0},"report.final":{level:7,durable:!0,carriesBody:!0,elevatesTier:!0,inboxKind:"final"},outcome:{level:1,durable:!0,supersedes:!0,carriesBody:!0},"report.result":{level:5,durable:!0,carriesBody:!0,elevatesTier:!0,inboxKind:"completed"},"notice.crash":{level:5,levels:p,durable:!0,carriesBody:!0,elevatesTier:!0},"notice.deadline":{level:5,levels:p,durable:!0,carriesBody:!0,elevatesTier:!0},"notice.close":{level:5,levels:p,durable:!0,carriesBody:!0,elevatesTier:!0}}),async read(t,r,a,d){const s=await t.prepare("SELECT status FROM nodes WHERE node_id = ?").get(a.id),n=l(async e=>await g(await j(e,async o=>await h(t,r,o)),async o=>await f(t,o)),"visible"),c=await t.prepare(`SELECT o.* FROM nodes n JOIN objects o ON o.id = n.node_id
4
+ WHERE n.parent = ? AND o.deleted_at IS NULL ORDER BY o.seq`).all(a.id),u=await t.prepare("SELECT * FROM objects WHERE owner = ? AND deleted_at IS NULL ORDER BY seq").all(a.id);await D(t,r,a,d.reposFn);const i={status:s?.status??"deleted",mandate_first_line:A(await N(a.id)),children:await n(c),owned:await n(u.filter(e=>e.type!=="job")),jobs:await n(u.filter(e=>e.type==="job"))},w=[O("object",await f(t,a),L("status",i.status)),`Mandate: ${i.mandate_first_line}`,...m("Children",i.children),...m("Owns",i.owned),...m("Jobs",i.jobs)].join(`
5
+ `);return{data:i,text:w}}};export{K as nodeInbox,q as nodeKind};
@@ -57,8 +57,6 @@ export declare function watchesOf(db: CanvasDb, reader: Reader, watcher: ObjectR
57
57
  export declare function watchersOf(db: CanvasDb, reader: Reader, object: ObjectRow): Promise<Watch[]>;
58
58
  /** The ids of every object that watches `object`, whatever started the watch. */
59
59
  export declare function watcherIdsOf(db: CanvasDb, object: ObjectRow): Promise<string[]>;
60
- /** Every node below `root` in the tree (in-tree watches, watcher → object), each once. */
61
- export declare function descendants(db: CanvasDb, root: ObjectRow): Promise<ObjectRow[]>;
62
60
  /** Every node above `leaf` in the tree (in-tree watches, object → watcher), each once. */
63
61
  export declare function ancestors(db: CanvasDb, leaf: ObjectRow): Promise<ObjectRow[]>;
64
62
  /** The objects whose in-tree watch covers `object` (its spawner, job starter, explicit watchers). */
@@ -1,19 +1,14 @@
1
- var T=Object.defineProperty;var i=(t,e)=>T(t,"name",{value:e,configurable:!0});import{canRead as R}from"./access.js";import{receiverForObject as j}from"./receivers.js";import{getById as N}from"./objects.js";import{randomUUID as h}from"node:crypto";import{filterAsync as A,mapAsync as D}from"../async-array.js";const f=["spawn","job-start","ask"],b=["report.final","report.result","notice.>"];function S(t){return JSON.stringify(Object.fromEntries(t.map(e=>[String(e.threshold),e.delivery])))}i(S,"configToJson");function I(t){const e=JSON.parse(t);return Object.entries(e).map(([n,r])=>({threshold:Number(n),delivery:r})).sort((n,r)=>r.threshold-n.threshold)}i(I,"configFromJson");async function _(t,e,n){return await t.prepare("SELECT * FROM watches WHERE watcher = ? AND object = ?").get(e.id,n.id)}i(_,"watchRow");async function E(t,e){return await t.prepare("SELECT * FROM watch_subscriptions WHERE watch = ? ORDER BY seq").all(e)}i(E,"subsOf");async function u(t,e){const n=await E(t,e);if(n.length===0){await t.prepare("DELETE FROM watches WHERE watch_id = ?").run(e);return}const r=n.some(a=>f.includes(a.started_by))?1:0;await t.prepare("UPDATE watches SET in_tree = ? WHERE watch_id = ? AND in_tree != ?").run(r,e,r)}i(u,"settle");function m(t,e){return t==="message"?b:e.patterns?.length?e.patterns:[">"]}i(m,"defaultPatterns");async function x(t,e,n,r,a={}){if(r==="fork")throw new Error("recordWatch: fork watches are made by copyWatchesForFork");if(e.type.includes(".")&&!await j(t,e))throw new Error(`custom object ${e.id} has no receiver`);const o=new Date().toISOString();await t.prepare(`INSERT INTO watches (watch_id, watcher, object, in_tree, read_heads_key, created_at, link_app) VALUES (?, ?, ?, 0, ?, ?, ?)
1
+ var S=Object.defineProperty;var i=(t,e)=>S(t,"name",{value:e,configurable:!0});import{canRead as O}from"./access.js";import{receiverForObject as j}from"./receivers.js";import{getById as y}from"./objects.js";import{randomUUID as h}from"node:crypto";import{filterAsync as D,mapAsync as m}from"../async-array.js";const f=["spawn","job-start","ask"],A=["report.final","report.result","notice.>"];function N(t){return JSON.stringify(Object.fromEntries(t.map(e=>[String(e.threshold),e.delivery])))}i(N,"configToJson");function b(t){const e=JSON.parse(t);return Object.entries(e).map(([n,r])=>({threshold:Number(n),delivery:r})).sort((n,r)=>r.threshold-n.threshold)}i(b,"configFromJson");async function _(t,e,n){return await t.prepare("SELECT * FROM watches WHERE watcher = ? AND object = ?").get(e.id,n.id)}i(_,"watchRow");async function E(t,e){return await t.prepare("SELECT * FROM watch_subscriptions WHERE watch = ? ORDER BY seq").all(e)}i(E,"subsOf");async function u(t,e){const n=await E(t,e);if(n.length===0){await t.prepare("DELETE FROM watches WHERE watch_id = ?").run(e);return}const r=n.some(a=>f.includes(a.started_by))?1:0;await t.prepare("UPDATE watches SET in_tree = ? WHERE watch_id = ? AND in_tree != ?").run(r,e,r)}i(u,"settle");function g(t,e){return t==="message"?A:e.patterns?.length?e.patterns:[">"]}i(g,"defaultPatterns");async function x(t,e,n,r,a={}){if(r==="fork")throw new Error("recordWatch: fork watches are made by copyWatchesForFork");if(e.type.includes(".")&&!await j(t,e))throw new Error(`custom object ${e.id} has no receiver`);const s=new Date().toISOString();await t.prepare(`INSERT INTO watches (watch_id, watcher, object, in_tree, read_heads_key, created_at, link_app) VALUES (?, ?, ?, 0, ?, ?, ?)
2
2
  ON CONFLICT (watcher, object) DO UPDATE SET read_heads_key = COALESCE(excluded.read_heads_key, watches.read_heads_key),
3
- link_app = COALESCE(excluded.link_app, watches.link_app)`).run(h(),e.id,n.id,a.readHeadsKey??null,o,a.linkApp?.id??null);const c=await _(t,e,n),w=r==="message"?a.endsOn??"final":null,l=f.includes(r),s=await E(t,c.watch_id),d=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
4
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);for(const O of m(r,a))s.some(p=>p.pattern===O&&(p.ends_on===null||p.ends_on===w)&&(!l||f.includes(p.started_by)))||await d.run(h(),c.watch_id,O,a.filter===void 0?null:JSON.stringify(a.filter),a.config?S(a.config):null,r,w,o);await u(t,c.watch_id)}i(x,"recordWatch");async function U(t,e,n,r){await t.prepare("UPDATE watches SET link_app = ? WHERE watcher = ? AND object = ?").run(r.id,e.id,n.id)}i(U,"markLinkWatch");async function J(t,e,n,r){const a=await _(t,e,n);a&&(r===void 0?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ?").run(a.watch_id):typeof r=="string"?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND pattern = ?").run(a.watch_id,r):await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND started_by = ?").run(a.watch_id,r.startedBy),await u(t,a.watch_id))}i(J,"unwatch");async function v(t,e,n,r,a){const o=await _(t,e,n),c=o?(await t.prepare("UPDATE watch_subscriptions SET config_json = ? WHERE watch = ? AND pattern = ?").run(a?S(a):null,o.watch_id,r)).changes:0;if(Number(c)===0)throw new Error(`${e.id} has no subscription ${r} on ${n.id}`)}i(v,"setSubscriptionConfig");async function B(t,e,n){const r=typeof n=="string"?await t.prepare(`DELETE FROM watch_subscriptions WHERE started_by = 'message'
3
+ link_app = COALESCE(excluded.link_app, watches.link_app)`).run(h(),e.id,n.id,a.readHeadsKey??null,s,a.linkApp?.id??null);const c=await _(t,e,n),w=r==="message"?a.endsOn??"final":null,l=f.includes(r),o=await E(t,c.watch_id),d=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
4
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);for(const R of g(r,a))o.some(p=>p.pattern===R&&(p.ends_on===null||p.ends_on===w)&&(!l||f.includes(p.started_by)))||await d.run(h(),c.watch_id,R,a.filter===void 0?null:JSON.stringify(a.filter),a.config?N(a.config):null,r,w,s);await u(t,c.watch_id)}i(x,"recordWatch");async function U(t,e,n,r){await t.prepare("UPDATE watches SET link_app = ? WHERE watcher = ? AND object = ?").run(r.id,e.id,n.id)}i(U,"markLinkWatch");async function J(t,e,n,r){const a=await _(t,e,n);a&&(r===void 0?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ?").run(a.watch_id):typeof r=="string"?await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND pattern = ?").run(a.watch_id,r):await t.prepare("DELETE FROM watch_subscriptions WHERE watch = ? AND started_by = ?").run(a.watch_id,r.startedBy),await u(t,a.watch_id))}i(J,"unwatch");async function v(t,e,n,r,a){const s=await _(t,e,n),c=s?(await t.prepare("UPDATE watch_subscriptions SET config_json = ? WHERE watch = ? AND pattern = ?").run(a?N(a):null,s.watch_id,r)).changes:0;if(Number(c)===0)throw new Error(`${e.id} has no subscription ${r} on ${n.id}`)}i(v,"setSubscriptionConfig");async function B(t,e,n){const r=typeof n=="string"?await t.prepare(`DELETE FROM watch_subscriptions WHERE started_by = 'message'
5
5
  AND watch IN (SELECT watch_id FROM watches WHERE object = ?) RETURNING watch`).all(e.id):await t.prepare(`DELETE FROM watch_subscriptions WHERE started_by = 'message' AND ends_on = ?
6
- AND watch IN (SELECT watch_id FROM watches WHERE object = ?) RETURNING watch`).all(`result:${n.result}`,e.id);for(const a of new Set(r.map(o=>o.watch)))await u(t,a)}i(B,"endMessageSubscriptions");async function g(t,e){const n=await N(t,e.watcher),r=await N(t,e.object);return!n||!r?null:{watch_id:e.watch_id,watcher:n,object:r,in_tree:e.in_tree===1,read_heads_key:e.read_heads_key,created_at:e.created_at,subscriptions:(await E(t,e.watch_id)).map(a=>({subscription_id:a.subscription_id,pattern:a.pattern,started_by:a.started_by,ends_on:a.ends_on,created_at:a.created_at,filter:a.filter_json===null?null:JSON.parse(a.filter_json),config:a.config_json===null?null:I(a.config_json)}))}}i(g,"hydrate");async function y(t,e,n){const r=(await D(n,async a=>await g(t,a))).filter(a=>a!==null);return await A(r,async a=>await R(t,e,a.watcher)&&await R(t,e,a.object))}i(y,"visible");async function P(t,e,n){return await y(t,e,await t.prepare("SELECT * FROM watches WHERE watcher = ? ORDER BY seq").all(n.id))}i(P,"watchesOf");async function V(t,e,n){return await y(t,e,await t.prepare("SELECT * FROM watches WHERE object = ? ORDER BY seq").all(n.id))}i(V,"watchersOf");async function q(t,e){return(await t.prepare("SELECT o.id FROM watches w JOIN objects o ON o.id = w.watcher WHERE w.object = ? ORDER BY w.seq").all(e.id)).map(n=>n.id)}i(q,"watcherIdsOf");async function Y(t,e){return await t.prepare(`WITH RECURSIVE walk(id) AS (
7
- SELECT CAST(? AS text)
8
- UNION
9
- SELECT w.object FROM walk JOIN watches w ON w.watcher = walk.id AND w.in_tree = 1
10
- ) SELECT o.* FROM walk JOIN objects o ON o.id = walk.id
11
- WHERE o.type = 'node' AND o.id != ?`).all(e.id,e.id)}i(Y,"descendants");async function $(t,e){return await t.prepare(`WITH RECURSIVE walk(id) AS (
6
+ AND watch IN (SELECT watch_id FROM watches WHERE object = ?) RETURNING watch`).all(`result:${n.result}`,e.id);for(const a of new Set(r.map(s=>s.watch)))await u(t,a)}i(B,"endMessageSubscriptions");async function I(t,e){const n=await y(t,e.watcher),r=await y(t,e.object);return!n||!r?null:{watch_id:e.watch_id,watcher:n,object:r,in_tree:e.in_tree===1,read_heads_key:e.read_heads_key,created_at:e.created_at,subscriptions:(await E(t,e.watch_id)).map(a=>({subscription_id:a.subscription_id,pattern:a.pattern,started_by:a.started_by,ends_on:a.ends_on,created_at:a.created_at,filter:a.filter_json===null?null:JSON.parse(a.filter_json),config:a.config_json===null?null:b(a.config_json)}))}}i(I,"hydrate");async function T(t,e,n){const r=(await m(n,async a=>await I(t,a))).filter(a=>a!==null);return await D(r,async a=>await O(t,e,a.watcher)&&await O(t,e,a.object))}i(T,"visible");async function P(t,e,n){return await T(t,e,await t.prepare("SELECT * FROM watches WHERE watcher = ? ORDER BY seq").all(n.id))}i(P,"watchesOf");async function q(t,e,n){return await T(t,e,await t.prepare("SELECT * FROM watches WHERE object = ? ORDER BY seq").all(n.id))}i(q,"watchersOf");async function V(t,e){return(await t.prepare("SELECT o.id FROM watches w JOIN objects o ON o.id = w.watcher WHERE w.object = ? ORDER BY w.seq").all(e.id)).map(n=>n.id)}i(V,"watcherIdsOf");async function Y(t,e){return await t.prepare(`WITH RECURSIVE walk(id) AS (
12
7
  SELECT CAST(? AS text)
13
8
  UNION
14
9
  SELECT w.watcher FROM walk JOIN watches w ON w.object = walk.id AND w.in_tree = 1
15
10
  ) SELECT o.* FROM walk JOIN objects o ON o.id = walk.id
16
- WHERE o.type = 'node' AND o.id != ?`).all(e.id,e.id)}i($,"ancestors");async function G(t,e){return await t.prepare(`SELECT o.* FROM watches w JOIN objects o ON o.id = w.watcher
17
- WHERE w.object = ? AND w.in_tree = 1 ORDER BY w.seq`).all(e.id)}i(G,"treeWatchersOf");async function K(t,e,n){const r=new Date().toISOString(),a=await t.prepare("SELECT * FROM watches WHERE watcher = ?").all(e.id),o=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
11
+ WHERE o.type = 'node' AND o.id != ?`).all(e.id,e.id)}i(Y,"ancestors");async function $(t,e){return await t.prepare(`SELECT o.* FROM watches w JOIN objects o ON o.id = w.watcher
12
+ WHERE w.object = ? AND w.in_tree = 1 ORDER BY w.seq`).all(e.id)}i($,"treeWatchersOf");async function G(t,e,n){const r=new Date().toISOString(),a=await t.prepare("SELECT * FROM watches WHERE watcher = ?").all(e.id),s=t.prepare(`INSERT INTO watch_subscriptions (subscription_id, watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
18
13
  VALUES (?, ?, ?, ?, ?, 'fork', ?, ?)`);for(const c of a){if(c.object===n.id)continue;await t.prepare(`INSERT INTO watches (watch_id, watcher, object, in_tree, read_heads_key, created_at) VALUES (?, ?, ?, 0, ?, ?)
19
- ON CONFLICT (watcher, object) DO NOTHING`).run(h(),n.id,c.object,c.read_heads_key,r);const w=await _(t,n,{id:c.object}),l=await E(t,w.watch_id);for(const s of await E(t,c.watch_id))l.some(d=>d.pattern===s.pattern&&d.ends_on===s.ends_on)||await o.run(h(),w.watch_id,s.pattern,s.filter_json,s.config_json,s.ends_on,r);await u(t,w.watch_id)}}i(K,"copyWatchesForFork");export{$ as ancestors,I as configFromJson,S as configToJson,K as copyWatchesForFork,Y as descendants,B as endMessageSubscriptions,U as markLinkWatch,x as recordWatch,v as setSubscriptionConfig,G as treeWatchersOf,J as unwatch,q as watcherIdsOf,V as watchersOf,P as watchesOf};
14
+ ON CONFLICT (watcher, object) DO NOTHING`).run(h(),n.id,c.object,c.read_heads_key,r);const w=await _(t,n,{id:c.object}),l=await E(t,w.watch_id);for(const o of await E(t,c.watch_id))l.some(d=>d.pattern===o.pattern&&d.ends_on===o.ends_on)||await s.run(h(),w.watch_id,o.pattern,o.filter_json,o.config_json,o.ends_on,r);await u(t,w.watch_id)}}i(G,"copyWatchesForFork");export{Y as ancestors,b as configFromJson,N as configToJson,G as copyWatchesForFork,B as endMessageSubscriptions,U as markLinkWatch,x as recordWatch,v as setSubscriptionConfig,$ as treeWatchersOf,J as unwatch,V as watcherIdsOf,q as watchersOf,P as watchesOf};
@@ -4,6 +4,8 @@ export type { OutboundTokenRequest } from './token-cache.js';
4
4
  export { discardFile } from './token-cache.js';
5
5
  /** How long the token endpoint has to answer. */
6
6
  export declare const MINT_TIMEOUT_MS = 10000;
7
+ /** Waits before the second and third attempt at a token endpoint that answered 502, 503 or 504. */
8
+ export declare const MINT_RETRY_DELAYS_MS: number[];
7
9
  export interface MintOptions {
8
10
  /** The directory issuer; default the configured `runtime.issuer`. */
9
11
  issuer?: string;
@@ -1 +1 @@
1
- var T=Object.defineProperty;var a=(r,e)=>T(r,"name",{value:e,configurable:!0});import{createHash as M}from"node:crypto";import{readConfig as E}from"../config.js";import{emitEvent as $}from"../events/emit.js";import{grantSyncStateFor as A}from"../grants/mirror.js";import{assertion as N,machineRevokedMessage as O,requestGrantSync as P,runtimeClient as I}from"../grants/sync.js";import{isUnsandboxedMachine as F}from"../runtime/unsandboxed.js";import{ProviderError as h}from"./errors.js";import{cacheKey as G,cachedToken as L,discardEpoch as U,storeToken as C}from"./token-cache.js";import{discardFile as ce}from"./token-cache.js";const D=1e4;function J(r){const e=r.issuer??E("user").runtime?.issuer;if(!e)throw new h("not_found","provider calls need a directory-connected runtime");return e.replace(/\/$/,"")}a(J,"issuerOf");function f(r,e,t){return new h(r,e,{origin:"directory",details:t})}a(f,"directoryError");function B(r,e,t,i){const n=typeof e.error=="string"?e.error:null,o=typeof e.error_description=="string"?e.error_description:void 0,c={directory_status:r,...n?{directory_error:n}:{},...o?{directory_description:o}:{}},u=o?`: ${o}`:"";switch(n){case"invalid_client":return F()?{error:f("machine_revoked",O(i),c),grantRefusal:!1}:{error:f("server_error",`the directory refused the outbound token request (${n})${u}`,c),grantRefusal:!1};case"invalid_request":return{error:f("server_error",`the directory refused the outbound token request (${n})${u}`,c),grantRefusal:!1};case"invalid_target":return{error:f("not_found",`the directory knows no provider at ${t.audience}${u}`,c),grantRefusal:!1};case"invalid_grant":return{error:f("grant_revoked",`the directory has no active grant for ${t.grantee}${u}`,c),grantRefusal:!0};case"provider_not_connected":return{error:f("provider_not_connected",o??"The user has not connected this provider.",c),grantRefusal:!1};case"invalid_scope":{const s=typeof e.scope=="string"&&e.scope?e.scope:t.scope.length===1?t.scope[0]:void 0,m=s?`missing scope ${s}`:`the grant does not cover ${t.scope.join(" ")}`;return{error:new h("scope_missing",m,{origin:"directory",details:c,...s?{scope:s}:{}}),grantRefusal:!0}}default:return{error:f("server_error",`the directory token endpoint answered ${r}${n?` ${n}`:""}${u}`,c),grantRefusal:!1}}}a(B,"refusal");function H(r){try{return JSON.parse(Buffer.from(r.split(".")[1]??"","base64url").toString("utf8"))}catch{return{}}}a(H,"claims");function K(r){const e=r.grant_rev,t=r.grant_status;return typeof e!="number"||!Number.isInteger(e)||e<0||typeof t!="string"?null:{rev:e,status:t}}a(K,"refusedGrantState");function z(r,e){const t=a(i=>i.status==="active"?0:1,"phase");return r.rev>e.rev||r.rev===e.rev&&t(r)>t(e)}a(z,"newer");async function Q(r,e,t){if(!e)return;const i=await A(r.grantee,r.sub)??{rev:0,status:"removed"};if(!z(e,i))return;const n=t.syncGrants??P;Promise.resolve().then(()=>n({fresh:!0})).catch(()=>{})}a(Q,"syncIfNewer");async function V(r,e={}){const t=J(e),i=`${t}/token`,n=I(),o=new URLSearchParams({grant_type:"client_credentials",client_id:n.client_id,client_assertion_type:"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",client_assertion:N(n,i),audience:r.audience,sub:r.sub,grantee:r.grantee,scope:[...new Set(r.scope)].sort().join(" "),initiating_app:r.initiating_app}),c=AbortSignal.timeout(D),u=e.signal?AbortSignal.any([e.signal,c]):c;let s,m;try{s=await(e.fetch??fetch)(i,{method:"POST",body:o,signal:u,headers:{Accept:"application/json"}}),m=await s.text()}catch(l){throw e.signal?.aborted?e.signal.reason:new h("server_error",`the directory token endpoint could not be reached: ${l instanceof Error?l.message:String(l)}`,{origin:"directory"})}let p=null;try{p=m?JSON.parse(m):null}catch{p=null}const g=p&&typeof p=="object"&&!Array.isArray(p)?p:{};if(!s.ok){const{error:l,grantRefusal:j}=B(s.status,g,r,t);throw l.code==="machine_revoked"&&$({level:"error",event:"daemon.grants.machine_revoked",fields:{audience:r.audience}}),$({level:"info",event:"daemon.providers.mint_refused",fields:{audience:r.audience,grantee:r.grantee,code:l.code,directory_error:typeof g.error=="string"?g.error:null,status:s.status}}),j&&await Q(r,K(g),e),l}const y=g.access_token;if(typeof y!="string"||!y)throw new h("server_error","the directory token response has no access_token",{origin:"directory"});const d=H(y),S=Math.floor((e.now?.()??Date.now())/1e3),_=g.expires_in,v=typeof _=="number"&&Number.isFinite(_)&&_>0?S+Math.floor(_):null,w=typeof d.exp=="number"?d.exp:null,b=v!==null&&w!==null?Math.min(v,w):v??w;if(b===null)throw new h("server_error","the directory token response names no expiry",{origin:"directory"});const x=typeof d.jti=="string"&&d.jti?d.jti:M("sha256").update(y).digest("hex"),R=typeof d.payer=="string"&&d.payer?d.payer:r.sub;return{access_token:y,jti:x,exp:b,payer:R}}a(V,"mintOutboundToken");const k=new Map;async function ie(r,e={}){const t=G(r),i=e.now??Date.now,n=L(t,i());if(n)return{...n,key:t};let o=k.get(t);if(!o){const u=U(t);o=V(r,{...e,signal:void 0}).then(s=>(C(t,s,u),s)).finally(()=>{k.delete(t)}),o.catch(()=>{}),k.set(t,o)}return{...e.signal?await W(o,e.signal):await o,key:t}}a(ie,"outboundToken");function W(r,e){return e.aborted?Promise.reject(e.reason):new Promise((t,i)=>{const n=a(()=>i(e.reason),"onAbort");e.addEventListener("abort",n,{once:!0}),r.then(t,i).finally(()=>e.removeEventListener("abort",n))})}a(W,"abortable");export{D as MINT_TIMEOUT_MS,ce as discardFile,V as mintOutboundToken,ie as outboundToken};
1
+ var M=Object.defineProperty;var a=(r,e)=>M(r,"name",{value:e,configurable:!0});import{createHash as j}from"node:crypto";import{readConfig as E}from"../config.js";import{emitEvent as S}from"../events/emit.js";import{grantSyncStateFor as A}from"../grants/mirror.js";import{assertion as N,machineRevokedMessage as P,requestGrantSync as I,runtimeClient as O}from"../grants/sync.js";import{isUnsandboxedMachine as L}from"../runtime/unsandboxed.js";import{ProviderError as h}from"./errors.js";import{cacheKey as D,cachedToken as F,discardEpoch as G,storeToken as U}from"./token-cache.js";import{discardFile as ue}from"./token-cache.js";const C=1e4,J=[300,900];function Y(r){const e=r.issuer??E("user").runtime?.issuer;if(!e)throw new h("not_found","provider calls need a directory-connected runtime");return e.replace(/\/$/,"")}a(Y,"issuerOf");function g(r,e,t){return new h(r,e,{origin:"directory",details:t})}a(g,"directoryError");function B(r,e,t,c){const n=typeof e.error=="string"?e.error:null,i=typeof e.error_description=="string"?e.error_description:void 0,o={directory_status:r,...n?{directory_error:n}:{},...i?{directory_description:i}:{}},d=i?`: ${i}`:"";switch(n){case"invalid_client":return L()?{error:g("machine_revoked",P(c),o),grantRefusal:!1}:{error:g("server_error",`the directory refused the outbound token request (${n})${d}`,o),grantRefusal:!1};case"invalid_request":return{error:g("server_error",`the directory refused the outbound token request (${n})${d}`,o),grantRefusal:!1};case"invalid_target":return{error:g("not_found",`the directory knows no provider at ${t.audience}${d}`,o),grantRefusal:!1};case"invalid_grant":return{error:g("grant_revoked",`the directory has no active grant for ${t.grantee}${d}`,o),grantRefusal:!0};case"provider_not_connected":return{error:g("provider_not_connected",i??"The user has not connected this provider.",o),grantRefusal:!1};case"invalid_scope":{const s=typeof e.scope=="string"&&e.scope?e.scope:t.scope.length===1?t.scope[0]:void 0,l=s?`missing scope ${s}`:`the grant does not cover ${t.scope.join(" ")}`;return{error:new h("scope_missing",l,{origin:"directory",details:o,...s?{scope:s}:{}}),grantRefusal:!0}}default:return{error:g("server_error",`the directory token endpoint answered ${r}${n?` ${n}`:""}${d}`,o),grantRefusal:!1}}}a(B,"refusal");function H(r){try{return JSON.parse(Buffer.from(r.split(".")[1]??"","base64url").toString("utf8"))}catch{return{}}}a(H,"claims");function K(r){const e=r.grant_rev,t=r.grant_status;return typeof e!="number"||!Number.isInteger(e)||e<0||typeof t!="string"?null:{rev:e,status:t}}a(K,"refusedGrantState");function z(r,e){const t=a(c=>c.status==="active"?0:1,"phase");return r.rev>e.rev||r.rev===e.rev&&t(r)>t(e)}a(z,"newer");async function Q(r,e,t){if(!e)return;const c=await A(r.grantee,r.sub)??{rev:0,status:"removed"};if(!z(e,c))return;const n=t.syncGrants??I;Promise.resolve().then(()=>n({fresh:!0})).catch(()=>{})}a(Q,"syncIfNewer");async function V(r,e={}){const t=Y(e),c=`${t}/token`,n=O(),i=a(async()=>{const u=new URLSearchParams({grant_type:"client_credentials",client_id:n.client_id,client_assertion_type:"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",client_assertion:N(n,c),audience:r.audience,sub:r.sub,grantee:r.grantee,scope:[...new Set(r.scope)].sort().join(" "),initiating_app:r.initiating_app}),p=AbortSignal.timeout(C),w=e.signal?AbortSignal.any([e.signal,p]):p,$=await(e.fetch??fetch)(c,{method:"POST",body:u,signal:w,headers:{Accept:"application/json"}});return{response:$,text:await $.text()}},"attempt");let o,d;try{for(let u=0;;u++){({response:o,text:d}=await i());const p=J[u];if(p===void 0||![502,503,504].includes(o.status))break;await new Promise(w=>setTimeout(w,p))}}catch(u){throw e.signal?.aborted?e.signal.reason:new h("server_error",`the directory token endpoint could not be reached: ${u instanceof Error?u.message:String(u)}`,{origin:"directory"})}let s=null;try{s=d?JSON.parse(d):null}catch{s=null}const l=s&&typeof s=="object"&&!Array.isArray(s)?s:{};if(!o.ok){const{error:u,grantRefusal:p}=B(o.status,l,r,t);throw u.code==="machine_revoked"&&S({level:"error",event:"daemon.grants.machine_revoked",fields:{audience:r.audience}}),S({level:"info",event:"daemon.providers.mint_refused",fields:{audience:r.audience,grantee:r.grantee,code:u.code,directory_error:typeof l.error=="string"?l.error:null,status:o.status}}),p&&await Q(r,K(l),e),u}const m=l.access_token;if(typeof m!="string"||!m)throw new h("server_error","the directory token response has no access_token",{origin:"directory"});const f=H(m),x=Math.floor((e.now?.()??Date.now())/1e3),y=l.expires_in,_=typeof y=="number"&&Number.isFinite(y)&&y>0?x+Math.floor(y):null,v=typeof f.exp=="number"?f.exp:null,b=_!==null&&v!==null?Math.min(_,v):_??v;if(b===null)throw new h("server_error","the directory token response names no expiry",{origin:"directory"});const R=typeof f.jti=="string"&&f.jti?f.jti:j("sha256").update(m).digest("hex"),T=typeof f.payer=="string"&&f.payer?f.payer:r.sub;return{access_token:m,jti:R,exp:b,payer:T}}a(V,"mintOutboundToken");const k=new Map;async function se(r,e={}){const t=D(r),c=e.now??Date.now,n=F(t,c());if(n)return{...n,key:t};let i=k.get(t);if(!i){const d=G(t);i=V(r,{...e,signal:void 0}).then(s=>(U(t,s,d),s)).finally(()=>{k.delete(t)}),i.catch(()=>{}),k.set(t,i)}return{...e.signal?await W(i,e.signal):await i,key:t}}a(se,"outboundToken");function W(r,e){return e.aborted?Promise.reject(e.reason):new Promise((t,c)=>{const n=a(()=>c(e.reason),"onAbort");e.addEventListener("abort",n,{once:!0}),r.then(t,c).finally(()=>e.removeEventListener("abort",n))})}a(W,"abortable");export{J as MINT_RETRY_DELAYS_MS,C as MINT_TIMEOUT_MS,ue as discardFile,V as mintOutboundToken,se as outboundToken};
@@ -1 +1 @@
1
- var m=Object.defineProperty;var d=(s,n)=>m(s,"name",{value:n,configurable:!0});import{createServer as h,request as f}from"node:http";import{chmodSync as y,existsSync as l,unlinkSync as p}from"node:fs";import{join as S}from"node:path";import{apiSocketPath as u,nodeRunDir as k}from"../../canvas/paths.js";async function H(s,n){const r=S(k(s),"cli.sock"),o=h((t,e)=>{const i={...t.headers,authorization:`Bearer ${n}`};delete i.connection;const c=f({socketPath:u(),path:t.url,method:t.method,headers:i},a=>{e.writeHead(a.statusCode??502,a.headers),a.pipe(e)});c.on("error",a=>{e.headersSent||e.writeHead(503,{"content-type":"application/json"}),e.end(JSON.stringify({error:{code:"daemon_unavailable",message:a.message}}))}),t.pipe(c),e.on("close",()=>c.destroy())});return l(r)&&p(r),await new Promise((t,e)=>{o.once("error",e),o.listen(r,()=>{o.off("error",e),t()})}),y(r,384),{path:r,close(){o.closeAllConnections(),o.close(),l(r)&&p(r)}}}d(H,"serveCliRelay");export{H as serveCliRelay};
1
+ var E=Object.defineProperty;var p=(a,n)=>E(a,"name",{value:n,configurable:!0});import{createServer as y,request as u}from"node:http";import{chmodSync as S,existsSync as h,rmSync as w,unlinkSync as f}from"node:fs";import{join as l}from"node:path";import{apiSocketPath as _,BROKER_RELEASED_MARKER as k,BROKER_RELAY_GENERATION as v,nodeRunDir as R}from"../../canvas/paths.js";import{writeFileBeneath as A}from"../../spaces/open-beneath.js";async function I(a,n){const r=l(R(a),"cli.sock"),t=y((o,e)=>{const m={...o.headers,authorization:`Bearer ${n}`};delete m.connection;const i=u({socketPath:_(),path:o.url,method:o.method,headers:m},s=>{e.writeHead(s.statusCode??502,s.headers),s.pipe(e)});i.on("error",s=>{e.headersSent||e.writeHead(503,{"content-type":"application/json"}),e.end(JSON.stringify({error:{code:"daemon_unavailable",message:s.message}}))}),o.pipe(i),e.on("close",()=>i.destroy())});h(r)&&f(r),await new Promise((o,e)=>{t.once("error",e),t.listen(r,()=>{t.off("error",e),o()})}),S(r,384);const c=R(a),d=process.env.CRTR_EXECUTION_ID;if(!d)throw new Error("broker relay has no execution id");return A(c,l(c,v),d,{atomic:!0}),w(l(c,k),{force:!0}),{path:r,close(){t.closeAllConnections(),t.close(),h(r)&&f(r)}}}p(I,"serveCliRelay");export{I as serveCliRelay};