@north-light/cli 0.3.446 → 0.3.447

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 (40) hide show
  1. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crtr-commands/tree-cache.json +728 -0
  2. package/dist/clients/attach/viewer.js +560 -559
  3. package/dist/core/canvas/migrations.js +65 -31
  4. package/dist/core/graph/events.js +6 -6
  5. package/dist/core/graph/kinds/node.js +5 -5
  6. package/dist/core/graph/objects.js +8 -8
  7. package/dist/core/graph/reader.js +3 -3
  8. package/dist/core/graph/receivers.d.ts +2 -1
  9. package/dist/core/graph/receivers.js +5 -5
  10. package/dist/core/graph/spaces/app.js +1 -1
  11. package/dist/core/graph/types.d.ts +9 -1
  12. package/dist/core/peer/ingest.d.ts +26 -0
  13. package/dist/core/peer/ingest.js +1 -0
  14. package/dist/core/peer/outbox-store.d.ts +70 -0
  15. package/dist/core/peer/outbox-store.js +4 -0
  16. package/dist/core/peer/receiver.d.ts +8 -0
  17. package/dist/core/peer/receiver.js +1 -0
  18. package/dist/core/peer/stubs.d.ts +51 -0
  19. package/dist/core/peer/stubs.js +1 -0
  20. package/dist/daemon/api/app-listener.js +9 -9
  21. package/dist/daemon/api/principal.d.ts +4 -4
  22. package/dist/daemon/api/principal.js +1 -1
  23. package/dist/daemon/api/sync-notice.d.ts +17 -0
  24. package/dist/daemon/api/sync-notice.js +1 -0
  25. package/dist/daemon/crtrd.js +8 -8
  26. package/dist/daemon/peer/handlers.d.ts +4 -1
  27. package/dist/daemon/peer/handlers.js +1 -1
  28. package/dist/daemon/peer/local-ingress.d.ts +8 -0
  29. package/dist/daemon/peer/local-ingress.js +1 -0
  30. package/dist/daemon/peer/outbox.d.ts +38 -0
  31. package/dist/daemon/peer/outbox.js +1 -0
  32. package/dist/daemon/peer/relay-client.d.ts +35 -0
  33. package/dist/daemon/peer/relay-client.js +1 -0
  34. package/dist/daemon/peer/relay-protocol.d.ts +52 -0
  35. package/dist/daemon/peer/relay-protocol.js +1 -0
  36. package/dist/daemon/peer/relay-service.d.ts +26 -0
  37. package/dist/daemon/peer/relay-service.js +1 -0
  38. package/package.json +2 -2
  39. package/packages/crouter-identity/package.json +1 -1
  40. package/runtime.lock.json +12 -12
@@ -1,4 +1,4 @@
1
- var ae=Object.defineProperty;var o=(e,t)=>ae(e,"name",{value:t,configurable:!0});import{randomBytes as P}from"node:crypto";import{parseScope as Te}from"@north-light/identity";import{importNodeSqlAuthority as de,recreateNodeRowsView as _e}from"./node-sql-migration.js";import{migrateRecoveryWitnesses as ce}from"./recovery.js";import{migrateMessagingState as Ne}from"./deliveries.js";import{migrateHumanRequestsState as Le}from"../human/requests.js";import{existsSync as U,readFileSync as M,readdirSync as k,realpathSync as le,statSync as ue}from"node:fs";import{basename as pe,join as j,resolve as Y}from"node:path";import{appSpace as Re}from"../layout.js";import{emitEvent as Ae}from"../events/emit.js";import{isSafeNodeId as Oe,nodesRoot as h,nodeMetaPath as x}from"./paths.js";import{convertLegacyEdges as me}from"../../migrations/004-canvas-documents/legacy-edges.js";import{customExtensionPaths as Ie}from"./extensions.js";import{readMetaObject as K,updateJsonFileDurably as q,withMetaLock as fe,writeMetaObjectLocked as Xe}from"./meta-file.js";function Ue(e){e.exec(`
1
+ var Te=Object.defineProperty;var o=(e,t)=>Te(e,"name",{value:t,configurable:!0});import{randomBytes as P}from"node:crypto";import{parseScope as ae}from"@north-light/identity";import{importNodeSqlAuthority as de,recreateNodeRowsView as _e}from"./node-sql-migration.js";import{migrateRecoveryWitnesses as ce}from"./recovery.js";import{migrateMessagingState as Ne}from"./deliveries.js";import{migrateHumanRequestsState as Le}from"../human/requests.js";import{existsSync as U,readFileSync as M,readdirSync as k,realpathSync as le,statSync as ue}from"node:fs";import{basename as pe,join as j,resolve as Y}from"node:path";import{appSpace as Re}from"../layout.js";import{emitEvent as Ae}from"../events/emit.js";import{isSafeNodeId as Oe,nodesRoot as h,nodeMetaPath as x}from"./paths.js";import{convertLegacyEdges as me}from"../../migrations/004-canvas-documents/legacy-edges.js";import{customExtensionPaths as Ie}from"./extensions.js";import{readMetaObject as K,updateJsonFileDurably as q,withMetaLock as fe,writeMetaObjectLocked as Xe}from"./meta-file.js";function Ue(e){e.exec(`
2
2
  CREATE TABLE IF NOT EXISTS nodes (
3
3
  node_id TEXT PRIMARY KEY,
4
4
  name TEXT NOT NULL,
@@ -23,7 +23,7 @@ CREATE TABLE IF NOT EXISTS edges (
23
23
  CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(type, to_id);
24
24
  CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(type, from_id);
25
25
  CREATE INDEX IF NOT EXISTS idx_nodes_status ON nodes(status);
26
- `)}o(Ue,"baselineSchema");function b(e,t){return new Set(e.prepare(`PRAGMA table_info(${t})`).all().map(n=>n.name))}o(b,"tableColumns");function $(e,t,n,E){t.has(n)||(e.exec(E),t.add(n))}o($,"addColumnIfMissing");function m(e){return b(e,"nodes")}o(m,"nodeColumns");const N=$;function Ce(e){const t=m(e);N(e,t,"intent","ALTER TABLE nodes ADD COLUMN intent TEXT;"),N(e,t,"pi_pid","ALTER TABLE nodes ADD COLUMN pi_pid INTEGER;"),N(e,t,"window","ALTER TABLE nodes ADD COLUMN window TEXT;"),N(e,t,"tmux_session","ALTER TABLE nodes ADD COLUMN tmux_session TEXT;")}o(Ce,"addRuntimeColumns");function Se(e){const t=h();if(!U(t))return;const n=e.prepare('UPDATE nodes SET intent = ?, pi_pid = ?, "window" = ?, tmux_session = ? WHERE node_id = ?');for(const E of k(t)){const a=x(E);if(!U(a))continue;let i;try{i=JSON.parse(M(a,"utf8"))}catch{continue}n.run(i.intent??null,i.pi_pid??null,i.window??null,i.tmux_session??null,E)}}o(Se,"backfillRuntime");function De(e){B(e,3,()=>{if(!(e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'edges'").get()!==void 0)){e.exec(`
26
+ `)}o(Ue,"baselineSchema");function b(e,t){return new Set(e.prepare(`PRAGMA table_info(${t})`).all().map(n=>n.name))}o(b,"tableColumns");function $(e,t,n,E){t.has(n)||(e.exec(E),t.add(n))}o($,"addColumnIfMissing");function m(e){return b(e,"nodes")}o(m,"nodeColumns");const N=$;function Ce(e){const t=m(e);N(e,t,"intent","ALTER TABLE nodes ADD COLUMN intent TEXT;"),N(e,t,"pi_pid","ALTER TABLE nodes ADD COLUMN pi_pid INTEGER;"),N(e,t,"window","ALTER TABLE nodes ADD COLUMN window TEXT;"),N(e,t,"tmux_session","ALTER TABLE nodes ADD COLUMN tmux_session TEXT;")}o(Ce,"addRuntimeColumns");function Se(e){const t=h();if(!U(t))return;const n=e.prepare('UPDATE nodes SET intent = ?, pi_pid = ?, "window" = ?, tmux_session = ? WHERE node_id = ?');for(const E of k(t)){const T=x(E);if(!U(T))continue;let i;try{i=JSON.parse(M(T,"utf8"))}catch{continue}n.run(i.intent??null,i.pi_pid??null,i.window??null,i.tmux_session??null,E)}}o(Se,"backfillRuntime");function De(e){B(e,3,()=>{if(!(e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'edges'").get()!==void 0)){e.exec(`
27
27
  CREATE TABLE edges (
28
28
  type TEXT NOT NULL,
29
29
  from_id TEXT NOT NULL,
@@ -57,14 +57,14 @@ DROP TABLE edges;
57
57
  ALTER TABLE edges_new RENAME TO edges;
58
58
  CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(type, to_id);
59
59
  CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(type, from_id);
60
- `);const E=e.prepare("SELECT COUNT(*) AS n FROM edges").get().n;if(E!==n)throw new Error(`edges FK rebuild lost rows: expected ${n} non-orphan edge(s), got ${E}`);const a=e.prepare("PRAGMA foreign_key_check").all();if(a.length>0)throw new Error(`edges FK rebuild left ${a.length} foreign-key violation(s)`)})}o(De,"edgesForeignKeyCascade");function ge(e){const t=m(e);N(e,t,"pane","ALTER TABLE nodes ADD COLUMN pane TEXT;")}o(ge,"addPaneColumn");function ve(e){e.exec(`
60
+ `);const E=e.prepare("SELECT COUNT(*) AS n FROM edges").get().n;if(E!==n)throw new Error(`edges FK rebuild lost rows: expected ${n} non-orphan edge(s), got ${E}`);const T=e.prepare("PRAGMA foreign_key_check").all();if(T.length>0)throw new Error(`edges FK rebuild left ${T.length} foreign-key violation(s)`)})}o(De,"edgesForeignKeyCascade");function ge(e){const t=m(e);N(e,t,"pane","ALTER TABLE nodes ADD COLUMN pane TEXT;")}o(ge,"addPaneColumn");function we(e){e.exec(`
61
61
  CREATE TABLE IF NOT EXISTS focuses (
62
62
  focus_id TEXT PRIMARY KEY, -- stable internal id for the viewport
63
63
  pane TEXT, -- the durable %pane_id realizing the focus
64
64
  session TEXT, -- derived cache of the user session (reconciled from pane)
65
65
  node_id TEXT NOT NULL UNIQUE -- the node shown; UNIQUE \u2192 a node occupies <=1 focus
66
66
  );
67
- `)}o(ve,"addFocusesTable");function we(e){e.exec(`
67
+ `)}o(we,"addFocusesTable");function ye(e){e.exec(`
68
68
  CREATE TABLE IF NOT EXISTS wakeups (
69
69
  wakeup_id TEXT PRIMARY KEY,
70
70
  node_id TEXT, -- TARGET + node-anchored cancel anchor; NULL = canvas-detached
@@ -80,7 +80,7 @@ CREATE INDEX IF NOT EXISTS idx_wakeups_fire ON wakeups(fire_at);
80
80
  CREATE INDEX IF NOT EXISTS idx_wakeups_node ON wakeups(node_id); -- by-node cancel / list
81
81
  CREATE INDEX IF NOT EXISTS idx_wakeups_owner ON wakeups(owner_id); -- by-owner reap (ruling A)
82
82
  CREATE UNIQUE INDEX IF NOT EXISTS idx_wakeups_deadline ON wakeups(node_id) WHERE kind = 'deadline';
83
- `)}o(we,"appendWakeupsTable");function ye(e){e.exec(`
83
+ `)}o(ye,"appendWakeupsTable");function ve(e){e.exec(`
84
84
  CREATE TABLE IF NOT EXISTS triggers (
85
85
  trigger_id TEXT PRIMARY KEY,
86
86
  owner_id TEXT NOT NULL,
@@ -131,12 +131,12 @@ SELECT
131
131
  FROM wakeups;
132
132
 
133
133
  DROP TABLE wakeups;
134
- `)}o(ye,"migrateWakeupsToTriggers");function he(e){const t=m(e);N(e,t,"host_kind","ALTER TABLE nodes ADD COLUMN host_kind TEXT;")}o(he,"addHostKindColumn");function xe(e){e.exec(`
134
+ `)}o(ve,"migrateWakeupsToTriggers");function he(e){const t=m(e);N(e,t,"host_kind","ALTER TABLE nodes ADD COLUMN host_kind TEXT;")}o(he,"addHostKindColumn");function xe(e){e.exec(`
135
135
  CREATE TABLE IF NOT EXISTS canvas_meta (
136
136
  key TEXT PRIMARY KEY,
137
137
  value TEXT NOT NULL
138
138
  );
139
- `)}o(xe,"addCanvasMetaTable");function Fe(e){const t=m(e);N(e,t,"profile_id","ALTER TABLE nodes ADD COLUMN profile_id TEXT;")}o(Fe,"addProfileIdColumn");function Me(e){const t=m(e);N(e,t,"pi_pid_identity","ALTER TABLE nodes ADD COLUMN pi_pid_identity TEXT;")}o(Me,"addPidIdentityColumn");function ke(e){const t=m(e);N(e,t,"waiting_for","ALTER TABLE nodes ADD COLUMN waiting_for TEXT;")}o(ke,"addMessageWaitColumn");function J(e){const t=m(e);if(t.size===0)return;N(e,t,"final_report","ALTER TABLE nodes ADD COLUMN final_report TEXT;"),N(e,t,"finalized_at","ALTER TABLE nodes ADD COLUMN finalized_at TEXT;");const n=e.prepare("SELECT node_id FROM nodes WHERE status = 'done' AND final_report IS NULL").all(),E=e.prepare("UPDATE nodes SET final_report = ?, finalized_at = ? WHERE node_id = ? AND final_report IS NULL");for(const{node_id:a}of n){const i=j(h(),a,"reports");if(!U(i))continue;let T=null;for(const c of k(i))if(c.endsWith(".md"))try{const L=M(j(i,c),"utf8"),l=/^---\nnode: [^\n]+\nkind: final\nts: ([^\n]+)\n---\n/.exec(L);if(l===null||Number.isNaN(Date.parse(l[1])))continue;const r={name:c,ts:l[1]};(T===null||r.ts>T.ts||r.ts===T.ts&&r.name>T.name)&&(T=r)}catch{}T!==null&&E.run(T.name,T.ts,a)}}o(J,"addCanonicalFinalColumns");function je(e){const t=h();if(U(t))for(const n of k(t)){const E=x(n);U(E)&&q(E,a=>{const i=a.launch;if(typeof i!="object"||i===null||Array.isArray(i))return!1;const T=i.extensions;if(!Array.isArray(T)||!T.every(L=>typeof L=="string"))return!1;const c=Ie(T);return c.length===T.length?!1:(i.extensions=c,!0)})}}o(je,"normalizePersistedLaunchExtensions");function He(e){const t=m(e);N(e,t,"waiting_for_generation","ALTER TABLE nodes ADD COLUMN waiting_for_generation INTEGER NOT NULL DEFAULT 0;")}o(He,"addMessageWaitGenerationColumn");function Ge(e){e.exec(`
139
+ `)}o(xe,"addCanvasMetaTable");function Fe(e){const t=m(e);N(e,t,"profile_id","ALTER TABLE nodes ADD COLUMN profile_id TEXT;")}o(Fe,"addProfileIdColumn");function Me(e){const t=m(e);N(e,t,"pi_pid_identity","ALTER TABLE nodes ADD COLUMN pi_pid_identity TEXT;")}o(Me,"addPidIdentityColumn");function ke(e){const t=m(e);N(e,t,"waiting_for","ALTER TABLE nodes ADD COLUMN waiting_for TEXT;")}o(ke,"addMessageWaitColumn");function J(e){const t=m(e);if(t.size===0)return;N(e,t,"final_report","ALTER TABLE nodes ADD COLUMN final_report TEXT;"),N(e,t,"finalized_at","ALTER TABLE nodes ADD COLUMN finalized_at TEXT;");const n=e.prepare("SELECT node_id FROM nodes WHERE status = 'done' AND final_report IS NULL").all(),E=e.prepare("UPDATE nodes SET final_report = ?, finalized_at = ? WHERE node_id = ? AND final_report IS NULL");for(const{node_id:T}of n){const i=j(h(),T,"reports");if(!U(i))continue;let a=null;for(const c of k(i))if(c.endsWith(".md"))try{const L=M(j(i,c),"utf8"),l=/^---\nnode: [^\n]+\nkind: final\nts: ([^\n]+)\n---\n/.exec(L);if(l===null||Number.isNaN(Date.parse(l[1])))continue;const r={name:c,ts:l[1]};(a===null||r.ts>a.ts||r.ts===a.ts&&r.name>a.name)&&(a=r)}catch{}a!==null&&E.run(a.name,a.ts,T)}}o(J,"addCanonicalFinalColumns");function je(e){const t=h();if(U(t))for(const n of k(t)){const E=x(n);U(E)&&q(E,T=>{const i=T.launch;if(typeof i!="object"||i===null||Array.isArray(i))return!1;const a=i.extensions;if(!Array.isArray(a)||!a.every(L=>typeof L=="string"))return!1;const c=Ie(a);return c.length===a.length?!1:(i.extensions=c,!0)})}}o(je,"normalizePersistedLaunchExtensions");function He(e){const t=m(e);N(e,t,"waiting_for_generation","ALTER TABLE nodes ADD COLUMN waiting_for_generation INTEGER NOT NULL DEFAULT 0;")}o(He,"addMessageWaitGenerationColumn");function Ge(e){e.exec(`
140
140
  CREATE TABLE IF NOT EXISTS consult_outbox (
141
141
  node_id TEXT PRIMARY KEY, -- legacy work node id
142
142
  dir TEXT NOT NULL, -- legacy ticket directory
@@ -150,7 +150,7 @@ CREATE INDEX IF NOT EXISTS idx_consult_outbox_status ON consult_outbox(status);
150
150
  CREATE INDEX IF NOT EXISTS idx_consult_outbox_dir ON consult_outbox(dir);
151
151
  CREATE INDEX IF NOT EXISTS idx_consult_outbox_dir_request_status ON consult_outbox(dir, request_id, status);
152
152
  CREATE INDEX IF NOT EXISTS idx_consult_outbox_dir_status ON consult_outbox(dir, status);
153
- `)}o(Ge,"addHumanWorkOutboxTable");function be(e){const t=b(e,"consult_outbox"),n=o((E,a)=>$(e,t,E,a),"add");n("work_kind","ALTER TABLE consult_outbox ADD COLUMN work_kind TEXT NOT NULL DEFAULT 'follow_up';"),n("interaction_id","ALTER TABLE consult_outbox ADD COLUMN interaction_id TEXT;"),n("generation_id","ALTER TABLE consult_outbox ADD COLUMN generation_id TEXT;"),n("interaction_snapshot","ALTER TABLE consult_outbox ADD COLUMN interaction_snapshot TEXT;"),n("handler_command","ALTER TABLE consult_outbox ADD COLUMN handler_command TEXT;"),n("handler_args_json","ALTER TABLE consult_outbox ADD COLUMN handler_args_json TEXT;"),n("claim_token","ALTER TABLE consult_outbox ADD COLUMN claim_token TEXT;"),e.exec(`
153
+ `)}o(Ge,"addHumanWorkOutboxTable");function be(e){const t=b(e,"consult_outbox"),n=o((E,T)=>$(e,t,E,T),"add");n("work_kind","ALTER TABLE consult_outbox ADD COLUMN work_kind TEXT NOT NULL DEFAULT 'follow_up';"),n("interaction_id","ALTER TABLE consult_outbox ADD COLUMN interaction_id TEXT;"),n("generation_id","ALTER TABLE consult_outbox ADD COLUMN generation_id TEXT;"),n("interaction_snapshot","ALTER TABLE consult_outbox ADD COLUMN interaction_snapshot TEXT;"),n("handler_command","ALTER TABLE consult_outbox ADD COLUMN handler_command TEXT;"),n("handler_args_json","ALTER TABLE consult_outbox ADD COLUMN handler_args_json TEXT;"),n("claim_token","ALTER TABLE consult_outbox ADD COLUMN claim_token TEXT;"),e.exec(`
154
154
  CREATE INDEX IF NOT EXISTS idx_consult_outbox_human_work_status ON consult_outbox(work_kind, status);
155
155
  CREATE INDEX IF NOT EXISTS idx_consult_outbox_human_work_dir_status ON consult_outbox(dir, work_kind, status);
156
156
  CREATE INDEX IF NOT EXISTS idx_consult_outbox_human_work_request_status ON consult_outbox(dir, request_id, work_kind, status);
@@ -216,19 +216,19 @@ CREATE INDEX IF NOT EXISTS idx_cron_runs_cron ON cron_runs(cron_id, started);
216
216
  `)}o(We,"addCronsTables");function g(e){return`'${e.replace(/'/g,`'"'"'`)}'`}o(g,"shellSingleQuote");function Pe(e,t){let n=0;for(;;){const E=`${t}_${n}`;if(!e.split(`
217
217
  `).includes(E))return E;n++}}o(Pe,"uniqueHeredocDelimiter");function Q(e,t,n){const E=Pe(t,n);return`${e} <<'${E}'
218
218
  ${t}
219
- ${E}`}o(Q,"heredocCommand");function Ye(e){if(typeof e!="string")return{};try{const t=JSON.parse(e);return typeof t!="object"||t===null||Array.isArray(t)?{}:t}catch{return{}}}o(Ye,"migrationPayload");function X(e,t,n){const E=e[t];if(typeof E!="string"||E.trim()==="")throw new Error(`trigger ${n} payload requires non-empty ${t}`);return E}o(X,"requiredString");function H(e,t){const n=e[t];return typeof n=="string"&&n.trim()!==""?n:void 0}o(H,"optionalString");function v(e,t){const n=e[t];return typeof n=="string"&&n.trim()!==""?n:void 0}o(v,"optionalColumn");function z(e,t,n,E){return Q(`crtr node message send --to ${g(e)} --tier ${g(t)}`,n,`CRTR_MIGRATED_${E.replace(/[^A-Za-z0-9]/g,"_")}`)}o(z,"migratedMessageCommand");function Ke(e,t){return`printf '%s\\n' "$CRTR_GATE_STDOUT" | crtr node message send --to ${g(e)} --tier ${g(t)}`}o(Ke,"migratedGateStdoutMessageCommand");function qe(e,t){const n=[`crtr node new --kind ${g(X(e,"kind",t))}`],E=[["mode","mode"],["cwd","cwd"],["name","name"],["parent","parent"],["worktreeBase","worktree"],["forkFrom","fork-from"],["model","model"],["profile","profile"],["situationalContext","situational-context"]];for(const[i,T]of E){const c=H(e,i);c!==void 0&&n.push(`--${T} ${g(c)}`)}e.root===!0&&n.push("--root");const a=X(e,"prompt",t);return Q(n.join(" "),a,`CRTR_MIGRATED_${t.replace(/[^A-Za-z0-9]/g,"_")}`)}o(qe,"migratedSpawnCommand");const V="migration.v22.skipped";function $e(e){const t=e.prepare("SELECT cwd FROM nodes WHERE parent IS NULL ORDER BY created LIMIT 1").get();return(t!==void 0?v(t,"cwd"):void 0)??process.cwd()}o($e,"ambientMigrationCwd");function Je(e){let t;try{t=e.prepare("SELECT eval_pid FROM triggers WHERE eval_state = 'running' AND eval_pid IS NOT NULL").all()}catch{return}for(const n of t){const E=n.eval_pid;if(!(typeof E!="number"||!Number.isInteger(E)||E<=1))try{process.kill(-E,"SIGTERM")}catch{}}}o(Je,"reapRunningPredicateEvaluations");function Qe(e){const t=[],n=e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'triggers'").get()!==void 0;n&&Je(e);let E=[];if(n)try{E=e.prepare("SELECT * FROM triggers ORDER BY created, trigger_id").all()}catch(T){t.push({trigger_id:"<unreadable table>",reason:`triggers table could not be read: ${T instanceof Error?T.message:String(T)}`})}const a=$e(e),i=e.prepare(`
219
+ ${E}`}o(Q,"heredocCommand");function Ye(e){if(typeof e!="string")return{};try{const t=JSON.parse(e);return typeof t!="object"||t===null||Array.isArray(t)?{}:t}catch{return{}}}o(Ye,"migrationPayload");function X(e,t,n){const E=e[t];if(typeof E!="string"||E.trim()==="")throw new Error(`trigger ${n} payload requires non-empty ${t}`);return E}o(X,"requiredString");function H(e,t){const n=e[t];return typeof n=="string"&&n.trim()!==""?n:void 0}o(H,"optionalString");function w(e,t){const n=e[t];return typeof n=="string"&&n.trim()!==""?n:void 0}o(w,"optionalColumn");function z(e,t,n,E){return Q(`crtr node message send --to ${g(e)} --tier ${g(t)}`,n,`CRTR_MIGRATED_${E.replace(/[^A-Za-z0-9]/g,"_")}`)}o(z,"migratedMessageCommand");function Ke(e,t){return`printf '%s\\n' "$CRTR_GATE_STDOUT" | crtr node message send --to ${g(e)} --tier ${g(t)}`}o(Ke,"migratedGateStdoutMessageCommand");function qe(e,t){const n=[`crtr node new --kind ${g(X(e,"kind",t))}`],E=[["mode","mode"],["cwd","cwd"],["name","name"],["parent","parent"],["worktreeBase","worktree"],["forkFrom","fork-from"],["model","model"],["profile","profile"],["situationalContext","situational-context"]];for(const[i,a]of E){const c=H(e,i);c!==void 0&&n.push(`--${a} ${g(c)}`)}e.root===!0&&n.push("--root");const T=X(e,"prompt",t);return Q(n.join(" "),T,`CRTR_MIGRATED_${t.replace(/[^A-Za-z0-9]/g,"_")}`)}o(qe,"migratedSpawnCommand");const V="migration.v22.skipped";function $e(e){const t=e.prepare("SELECT cwd FROM nodes WHERE parent IS NULL ORDER BY created LIMIT 1").get();return(t!==void 0?w(t,"cwd"):void 0)??process.cwd()}o($e,"ambientMigrationCwd");function Je(e){let t;try{t=e.prepare("SELECT eval_pid FROM triggers WHERE eval_state = 'running' AND eval_pid IS NOT NULL").all()}catch{return}for(const n of t){const E=n.eval_pid;if(!(typeof E!="number"||!Number.isInteger(E)||E<=1))try{process.kill(-E,"SIGTERM")}catch{}}}o(Je,"reapRunningPredicateEvaluations");function Qe(e){const t=[],n=e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'triggers'").get()!==void 0;n&&Je(e);let E=[];if(n)try{E=e.prepare("SELECT * FROM triggers ORDER BY created, trigger_id").all()}catch(a){t.push({trigger_id:"<unreadable table>",reason:`triggers table could not be read: ${a instanceof Error?a.message:String(a)}`})}const T=$e(e),i=e.prepare(`
220
220
  INSERT OR REPLACE INTO crons (
221
221
  cron_id, name, created_by, command, fire_at, recur, tz, expires_at,
222
222
  anchor_node, cancel_on_wake, cwd, env_json, profile, scope,
223
223
  run_timeout_s, overlap, on_output, sink, tier, created, updated
224
- ) VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, NULL, ?, 'profile', ?, 'skip', 'on-failure', '{}', ?, ?, ?)`);for(const T of E){const c=v(T,"trigger_id");if(c===void 0){t.push({trigger_id:"<unidentified row>",reason:"row carries no trigger_id"});continue}try{ze(e,i,T,c,a)}catch(L){t.push({trigger_id:c,reason:L instanceof Error?L.message:String(L)})}}t.length>0&&(e.prepare("INSERT INTO canvas_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(V,JSON.stringify(t)),console.warn(`[canvas migration v22] ${t.length} retired schedule(s) could not be converted to crons and were dropped; see canvas_meta['${V}']: `+t.map(T=>`${T.trigger_id} (${T.reason})`).join("; "))),e.exec(`
224
+ ) VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, NULL, ?, 'profile', ?, 'skip', 'on-failure', '{}', ?, ?, ?)`);for(const a of E){const c=w(a,"trigger_id");if(c===void 0){t.push({trigger_id:"<unidentified row>",reason:"row carries no trigger_id"});continue}try{ze(e,i,a,c,T)}catch(L){t.push({trigger_id:c,reason:L instanceof Error?L.message:String(L)})}}t.length>0&&(e.prepare("INSERT INTO canvas_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(V,JSON.stringify(t)),console.warn(`[canvas migration v22] ${t.length} retired schedule(s) could not be converted to crons and were dropped; see canvas_meta['${V}']: `+t.map(a=>`${a.trigger_id} (${a.reason})`).join("; "))),e.exec(`
225
225
  DROP INDEX IF EXISTS idx_triggers_fire;
226
226
  DROP INDEX IF EXISTS idx_triggers_node;
227
227
  DROP INDEX IF EXISTS idx_triggers_owner;
228
228
  DROP INDEX IF EXISTS idx_triggers_eval;
229
229
  DROP INDEX IF EXISTS idx_triggers_deadline;
230
230
  DROP TABLE IF EXISTS triggers;
231
- `)}o(Qe,"migrateTriggersToCrons");function ze(e,t,n,E,a){const i=X(n,"owner_id",E),T=e.prepare("SELECT cwd, profile_id FROM nodes WHERE node_id = ?").get(i),c=(T===void 0?void 0:v(T,"cwd"))??a,L=T===void 0?null:v(T,"profile_id")??null,l=v(n,"node_id")??null,r=X(n,"action",E),_=X(n,"action_mode",E),d=X(n,"trigger_type",E),u=Ye(n.payload),p=v(n,"created")??new Date().toISOString(),O=_==="deadline_message"?"urgent":H(u,"tier")??"normal",R=H(u,"label");let s,A,I=!1;if(r==="msg"&&(_==="inbox_message"||_==="deadline_message")){if(l===null)throw new Error("has no message target");const f=H(u,"body");f!==void 0?s=z(l,O,f,E):d==="when"?(I=!0,s=Ke(l,O)):s=z(l,O,R??"Scheduled message (no body was recorded when this schedule was armed).",E),A=R??(_==="deadline_message"?"deadline":"scheduled message")}else if(r==="msg"&&_==="fresh_revive"){if(l===null)throw new Error("has no revive target");s=`crtr node lifecycle revive ${g(l)} --fresh`,A=R??"scheduled fresh revive"}else if(r==="new"&&_==="node_birth")s=qe(u,E),A=H(u,"name")??`scheduled ${X(u,"kind",E)} node`;else throw new Error(`has unsupported action ${r}/${_}`);let D,w,G=null;if(d==="when"){D=X(n,"next_eval_at",E),w=X(n,"eval_every",E),G=v(n,"timeout_at")??null;const f=X(n,"when_script",E);s=`${I?`if ! CRTR_GATE_STDOUT="$(
231
+ `)}o(Qe,"migrateTriggersToCrons");function ze(e,t,n,E,T){const i=X(n,"owner_id",E),a=e.prepare("SELECT cwd, profile_id FROM nodes WHERE node_id = ?").get(i),c=(a===void 0?void 0:w(a,"cwd"))??T,L=a===void 0?null:w(a,"profile_id")??null,l=w(n,"node_id")??null,r=X(n,"action",E),_=X(n,"action_mode",E),d=X(n,"trigger_type",E),u=Ye(n.payload),p=w(n,"created")??new Date().toISOString(),O=_==="deadline_message"?"urgent":H(u,"tier")??"normal",R=H(u,"label");let s,A,I=!1;if(r==="msg"&&(_==="inbox_message"||_==="deadline_message")){if(l===null)throw new Error("has no message target");const f=H(u,"body");f!==void 0?s=z(l,O,f,E):d==="when"?(I=!0,s=Ke(l,O)):s=z(l,O,R??"Scheduled message (no body was recorded when this schedule was armed).",E),A=R??(_==="deadline_message"?"deadline":"scheduled message")}else if(r==="msg"&&_==="fresh_revive"){if(l===null)throw new Error("has no revive target");s=`crtr node lifecycle revive ${g(l)} --fresh`,A=R??"scheduled fresh revive"}else if(r==="new"&&_==="node_birth")s=qe(u,E),A=H(u,"name")??`scheduled ${X(u,"kind",E)} node`;else throw new Error(`has unsupported action ${r}/${_}`);let D,y,G=null;if(d==="when"){D=X(n,"next_eval_at",E),y=X(n,"eval_every",E),G=w(n,"timeout_at")??null;const f=X(n,"when_script",E);s=`${I?`if ! CRTR_GATE_STDOUT="$(
232
232
  ${f}
233
233
  )"; then`:`if ! (
234
234
  ${f}
@@ -237,7 +237,7 @@ ${f}
237
237
  fi
238
238
  {
239
239
  ${s}
240
- } && crtr cron cancel "$CRTR_CRON_ID"`}else if(d==="at"||d==="every"||d==="until")D=X(n,"fire_at",E),w=d==="every"?X(n,"recur",E):null;else throw new Error(`has unknown timing type ${d}`);if(_==="deadline_message"&&l!==null){const f=e.prepare("SELECT cron_id FROM crons WHERE anchor_node = ? AND cancel_on_wake = 1 AND cron_id <> ?").get(l,E);if(f!==void 0)throw new Error(`node ${l} already has a migrated deadline (${String(f.cron_id)}); only one is allowed`)}t.run(E,A,i,s,D,w,G,l,_==="deadline_message"?1:0,c,L,300,O,p,p)}o(ze,"convertTriggerRow");function Ve(e){const t=b(e,"crons");t.size===0||t.has("run_pid_identity")||e.exec("ALTER TABLE crons ADD COLUMN run_pid_identity TEXT;")}o(Ve,"addCronRunPidIdentityColumn");function Ze(e){const t=b(e,"crons");t.size!==0&&(t.has("run_out_of_band")||e.exec("ALTER TABLE crons ADD COLUMN run_out_of_band INTEGER NOT NULL DEFAULT 0;"),t.has("queued")&&e.exec("ALTER TABLE crons DROP COLUMN queued;"))}o(Ze,"addCronOutOfBandColumn");function et(e){e.exec(`
240
+ } && crtr cron cancel "$CRTR_CRON_ID"`}else if(d==="at"||d==="every"||d==="until")D=X(n,"fire_at",E),y=d==="every"?X(n,"recur",E):null;else throw new Error(`has unknown timing type ${d}`);if(_==="deadline_message"&&l!==null){const f=e.prepare("SELECT cron_id FROM crons WHERE anchor_node = ? AND cancel_on_wake = 1 AND cron_id <> ?").get(l,E);if(f!==void 0)throw new Error(`node ${l} already has a migrated deadline (${String(f.cron_id)}); only one is allowed`)}t.run(E,A,i,s,D,y,G,l,_==="deadline_message"?1:0,c,L,300,O,p,p)}o(ze,"convertTriggerRow");function Ve(e){const t=b(e,"crons");t.size===0||t.has("run_pid_identity")||e.exec("ALTER TABLE crons ADD COLUMN run_pid_identity TEXT;")}o(Ve,"addCronRunPidIdentityColumn");function Ze(e){const t=b(e,"crons");t.size!==0&&(t.has("run_out_of_band")||e.exec("ALTER TABLE crons ADD COLUMN run_out_of_band INTEGER NOT NULL DEFAULT 0;"),t.has("queued")&&e.exec("ALTER TABLE crons DROP COLUMN queued;"))}o(Ze,"addCronOutOfBandColumn");function et(e){e.exec(`
241
241
  CREATE TABLE IF NOT EXISTS warm_pool (
242
242
  node_id TEXT PRIMARY KEY REFERENCES nodes(node_id) ON DELETE CASCADE,
243
243
  recipe_key TEXT NOT NULL, -- frozen launch tuple this spare was minted for
@@ -256,7 +256,7 @@ CREATE TABLE consult_outbox_new (
256
256
  );
257
257
  `);const n=e.prepare(`
258
258
  INSERT INTO consult_outbox_new (node_id, ticket_node_id, request_id, work_kind, status, final_report, updated_at)
259
- VALUES (?, ?, ?, ?, ?, ?, ?)`);let E=0;for(const a of t){const i=pe(a.dir);if(!(a.root===Y(a.root)&&a.dir===j(a.root,i))||!Oe(i)){E+=1;continue}n.run(a.node_id,i,a.request_id,a.work_kind,a.status,a.final_report,a.updated_at)}E>0&&console.warn(`[canvas migration v26] dropped ${E} consult_outbox row(s) whose legacy ticket path cannot safely map to a ticket node id`),e.exec(`
259
+ VALUES (?, ?, ?, ?, ?, ?, ?)`);let E=0;for(const T of t){const i=pe(T.dir);if(!(T.root===Y(T.root)&&T.dir===j(T.root,i))||!Oe(i)){E+=1;continue}n.run(T.node_id,i,T.request_id,T.work_kind,T.status,T.final_report,T.updated_at)}E>0&&console.warn(`[canvas migration v26] dropped ${E} consult_outbox row(s) whose legacy ticket path cannot safely map to a ticket node id`),e.exec(`
260
260
  DROP TABLE consult_outbox;
261
261
  ALTER TABLE consult_outbox_new RENAME TO consult_outbox;
262
262
 
@@ -360,7 +360,7 @@ ALTER TABLE review_comments
360
360
  `)}o(Et,"addReviewCommentAnchorState");function it(e){e.exec("DROP TABLE IF EXISTS consult_outbox")}o(it,"dropConsultOutbox");function st(e){e.exec("ALTER TABLE reviews ADD COLUMN submit_requested_at TEXT")}o(st,"addReviewSubmitRequested");function rt(e){e.exec(`
361
361
  ALTER TABLE crons ADD COLUMN held INTEGER NOT NULL DEFAULT 0;
362
362
  ALTER TABLE crons ADD COLUMN last_poke_at TEXT;
363
- `)}o(rt,"addCronHeldColumns");function at(e){e.exec(`
363
+ `)}o(rt,"addCronHeldColumns");function Tt(e){e.exec(`
364
364
  CREATE TABLE human_action_deliveries (
365
365
  request_id TEXT PRIMARY KEY,
366
366
  state TEXT NOT NULL,
@@ -378,18 +378,18 @@ CREATE TABLE human_action_deliveries (
378
378
  updated_at INTEGER NOT NULL
379
379
  );
380
380
  CREATE INDEX idx_human_action_deliveries_due ON human_action_deliveries(state, next_attempt_at);
381
- `)}o(at,"addHumanActionDeliveries");function Tt(e){const t=m(e);N(e,t,"pi_session_file","ALTER TABLE nodes ADD COLUMN pi_session_file TEXT;");for(const n of k(h(),{withFileTypes:!0}))if(n.isDirectory())try{const E=JSON.parse(M(x(n.name),"utf8"));typeof E.pi_session_file=="string"&&e.prepare("UPDATE nodes SET pi_session_file = ? WHERE node_id = ?").run(E.pi_session_file,n.name)}catch{}e.exec("CREATE INDEX IF NOT EXISTS idx_nodes_pi_session_file ON nodes(pi_session_file);")}o(Tt,"addNodeSessionFileIndex");function Z(e,t){const n=o(c=>{const L=Y(c);return U(L)?le(L):L},"canonical"),E=e.prepare("UPDATE nodes SET pi_session_file = ? WHERE node_id = ?");e.exec("DROP INDEX IF EXISTS idx_nodes_pi_session_file;");const a=e.prepare("SELECT node_id FROM nodes ORDER BY created ASC, node_id ASC").all();for(const{node_id:c}of a){const L=x(c);if(K(L)===null){E.run(null,c);continue}const l=fe(L,()=>{const r=K(L);if(r===null)throw new Error(`node ${c} session metadata changed during ownership migration`);if(typeof r.pi_session_file!="string")return null;const _=r.pi_session_file,d=n(_);return d!==_&&(r.pi_session_file=d,Xe(L,r)),d});E.run(l,c)}const i=e.prepare(`
381
+ `)}o(Tt,"addHumanActionDeliveries");function at(e){const t=m(e);N(e,t,"pi_session_file","ALTER TABLE nodes ADD COLUMN pi_session_file TEXT;");for(const n of k(h(),{withFileTypes:!0}))if(n.isDirectory())try{const E=JSON.parse(M(x(n.name),"utf8"));typeof E.pi_session_file=="string"&&e.prepare("UPDATE nodes SET pi_session_file = ? WHERE node_id = ?").run(E.pi_session_file,n.name)}catch{}e.exec("CREATE INDEX IF NOT EXISTS idx_nodes_pi_session_file ON nodes(pi_session_file);")}o(at,"addNodeSessionFileIndex");function Z(e,t){const n=o(c=>{const L=Y(c);return U(L)?le(L):L},"canonical"),E=e.prepare("UPDATE nodes SET pi_session_file = ? WHERE node_id = ?");e.exec("DROP INDEX IF EXISTS idx_nodes_pi_session_file;");const T=e.prepare("SELECT node_id FROM nodes ORDER BY created ASC, node_id ASC").all();for(const{node_id:c}of T){const L=x(c);if(K(L)===null){E.run(null,c);continue}const l=fe(L,()=>{const r=K(L);if(r===null)throw new Error(`node ${c} session metadata changed during ownership migration`);if(typeof r.pi_session_file!="string")return null;const _=r.pi_session_file,d=n(_);return d!==_&&(r.pi_session_file=d,Xe(L,r)),d});E.run(l,c)}const i=e.prepare(`
382
382
  SELECT pi_session_file
383
383
  FROM nodes
384
384
  WHERE pi_session_file IS NOT NULL
385
385
  GROUP BY pi_session_file
386
386
  HAVING COUNT(*) > 1
387
- `).all();if(!t&&i.length>0){const c=i.map(({pi_session_file:L})=>L).join(", ");throw new Error(`cannot restore unique Pi session ownership: authoritative metadata has concurrent claimants for ${c}`)}const T=e.prepare("UPDATE nodes SET pi_session_file = NULL WHERE node_id = ?");for(const{pi_session_file:c}of i){const L=e.prepare(`
387
+ `).all();if(!t&&i.length>0){const c=i.map(({pi_session_file:L})=>L).join(", ");throw new Error(`cannot restore unique Pi session ownership: authoritative metadata has concurrent claimants for ${c}`)}const a=e.prepare("UPDATE nodes SET pi_session_file = NULL WHERE node_id = ?");for(const{pi_session_file:c}of i){const L=e.prepare(`
388
388
  SELECT node_id
389
389
  FROM nodes
390
390
  WHERE pi_session_file = ?
391
391
  ORDER BY created ASC, node_id ASC
392
- `).all(c);for(const{node_id:l}of L.slice(1)){if(!q(x(l),_=>{const d=_.pi_session_file;if(typeof d!="string"||n(d)!==c)throw new Error(`node ${l} session metadata changed during ownership migration`);return _.pi_session_id=null,_.pi_session_file=null,!0}))throw new Error(`node ${l} session metadata changed during ownership migration`);T.run(l)}}e.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_nodes_pi_session_file ON nodes(pi_session_file);")}o(Z,"reconcileNodeSessionFileOwners");function dt(e){Z(e,!0)}o(dt,"makeNodeSessionFileUnique");function _t(e){Z(e,!1)}o(_t,"repairNodeSessionFileUniqueIndex");function ct(e){const t=m(e);N(e,t,"frozen_at","ALTER TABLE nodes ADD COLUMN frozen_at TEXT;"),N(e,t,"respawn_failures","ALTER TABLE nodes ADD COLUMN respawn_failures INTEGER NOT NULL DEFAULT 0;"),N(e,t,"respawn_not_before","ALTER TABLE nodes ADD COLUMN respawn_not_before TEXT;"),N(e,t,"launched_at","ALTER TABLE nodes ADD COLUMN launched_at TEXT;"),N(e,t,"last_settled_at","ALTER TABLE nodes ADD COLUMN last_settled_at TEXT;"),N(e,t,"terminal_reason","ALTER TABLE nodes ADD COLUMN terminal_reason TEXT;"),e.exec(`
392
+ `).all(c);for(const{node_id:l}of L.slice(1)){if(!q(x(l),_=>{const d=_.pi_session_file;if(typeof d!="string"||n(d)!==c)throw new Error(`node ${l} session metadata changed during ownership migration`);return _.pi_session_id=null,_.pi_session_file=null,!0}))throw new Error(`node ${l} session metadata changed during ownership migration`);a.run(l)}}e.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_nodes_pi_session_file ON nodes(pi_session_file);")}o(Z,"reconcileNodeSessionFileOwners");function dt(e){Z(e,!0)}o(dt,"makeNodeSessionFileUnique");function _t(e){Z(e,!1)}o(_t,"repairNodeSessionFileUniqueIndex");function ct(e){const t=m(e);N(e,t,"frozen_at","ALTER TABLE nodes ADD COLUMN frozen_at TEXT;"),N(e,t,"respawn_failures","ALTER TABLE nodes ADD COLUMN respawn_failures INTEGER NOT NULL DEFAULT 0;"),N(e,t,"respawn_not_before","ALTER TABLE nodes ADD COLUMN respawn_not_before TEXT;"),N(e,t,"launched_at","ALTER TABLE nodes ADD COLUMN launched_at TEXT;"),N(e,t,"last_settled_at","ALTER TABLE nodes ADD COLUMN last_settled_at TEXT;"),N(e,t,"terminal_reason","ALTER TABLE nodes ADD COLUMN terminal_reason TEXT;"),e.exec(`
393
393
  UPDATE nodes SET terminal_reason = CASE
394
394
  WHEN status = 'done' AND final_report IS NOT NULL THEN 'finalized'
395
395
  WHEN status = 'done' AND intent = 'parked' THEN 'parked'
@@ -398,7 +398,7 @@ UPDATE nodes SET terminal_reason = CASE
398
398
  WHEN status = 'dead' THEN 'crashed'
399
399
  END
400
400
  WHERE status IN ('done','canceled','dead') AND terminal_reason IS NULL;
401
- `)}o(ct,"addNodeLifecycleColumns");function Nt(e){const t=m(e);N(e,t,"description","ALTER TABLE nodes ADD COLUMN description TEXT;"),N(e,t,"cycles","ALTER TABLE nodes ADD COLUMN cycles INTEGER;");const n=h();if(!U(n))return;const E=e.prepare("UPDATE nodes SET description = ?, cycles = ? WHERE node_id = ?");for(const a of k(n)){const i=x(a);if(!U(i))continue;let T;try{T=JSON.parse(M(i,"utf8"))}catch{continue}const c=typeof T.description=="string"?T.description:null,L=typeof T.cycles=="number"?T.cycles:null;E.run(c,L,a)}}o(Nt,"addNodeLabelColumns");function Lt(e){const t=m(e);N(e,t,"outcome_kind","ALTER TABLE nodes ADD COLUMN outcome_kind TEXT;"),N(e,t,"outcome_reason","ALTER TABLE nodes ADD COLUMN outcome_reason TEXT;"),N(e,t,"outcome_at","ALTER TABLE nodes ADD COLUMN outcome_at TEXT;"),N(e,t,"outcome_revision","ALTER TABLE nodes ADD COLUMN outcome_revision INTEGER NOT NULL DEFAULT 0;"),N(e,t,"outcome_detail_json","ALTER TABLE nodes ADD COLUMN outcome_detail_json TEXT;"),N(e,t,"deadline_at","ALTER TABLE nodes ADD COLUMN deadline_at TEXT;"),e.exec(`
401
+ `)}o(ct,"addNodeLifecycleColumns");function Nt(e){const t=m(e);N(e,t,"description","ALTER TABLE nodes ADD COLUMN description TEXT;"),N(e,t,"cycles","ALTER TABLE nodes ADD COLUMN cycles INTEGER;");const n=h();if(!U(n))return;const E=e.prepare("UPDATE nodes SET description = ?, cycles = ? WHERE node_id = ?");for(const T of k(n)){const i=x(T);if(!U(i))continue;let a;try{a=JSON.parse(M(i,"utf8"))}catch{continue}const c=typeof a.description=="string"?a.description:null,L=typeof a.cycles=="number"?a.cycles:null;E.run(c,L,T)}}o(Nt,"addNodeLabelColumns");function Lt(e){const t=m(e);N(e,t,"outcome_kind","ALTER TABLE nodes ADD COLUMN outcome_kind TEXT;"),N(e,t,"outcome_reason","ALTER TABLE nodes ADD COLUMN outcome_reason TEXT;"),N(e,t,"outcome_at","ALTER TABLE nodes ADD COLUMN outcome_at TEXT;"),N(e,t,"outcome_revision","ALTER TABLE nodes ADD COLUMN outcome_revision INTEGER NOT NULL DEFAULT 0;"),N(e,t,"outcome_detail_json","ALTER TABLE nodes ADD COLUMN outcome_detail_json TEXT;"),N(e,t,"deadline_at","ALTER TABLE nodes ADD COLUMN deadline_at TEXT;"),e.exec(`
402
402
  CREATE TABLE IF NOT EXISTS node_outcome_deliveries (
403
403
  node_id TEXT PRIMARY KEY,
404
404
  state TEXT NOT NULL,
@@ -434,7 +434,7 @@ UPDATE nodes SET outcome_kind='failure',
434
434
  `)}o(Lt,"addNodeOutcomeAndDeadline");function lt(e){const t=m(e);N(e,t,"telemetry_tokens_in","ALTER TABLE nodes ADD COLUMN telemetry_tokens_in INTEGER;"),N(e,t,"telemetry_context_tokens","ALTER TABLE nodes ADD COLUMN telemetry_context_tokens INTEGER;"),N(e,t,"telemetry_last_activity","ALTER TABLE nodes ADD COLUMN telemetry_last_activity TEXT;"),N(e,t,"telemetry_updated_at","ALTER TABLE nodes ADD COLUMN telemetry_updated_at TEXT;");const n=e.prepare(`UPDATE nodes
435
435
  SET telemetry_tokens_in = ?, telemetry_context_tokens = ?,
436
436
  telemetry_last_activity = ?, telemetry_updated_at = ?
437
- WHERE node_id = ?`);for(const{node_id:E}of e.prepare("SELECT node_id FROM nodes").all()){const a=j(h(),E,"job","telemetry.json");if(U(a))try{const i=JSON.parse(M(a,"utf8")),T=typeof i.tokens_in=="number"&&Number.isFinite(i.tokens_in)?i.tokens_in:null,c=typeof i.context_tokens=="number"&&Number.isFinite(i.context_tokens)?i.context_tokens:null,L=typeof i.last_activity=="string"?i.last_activity:null,l=ue(a).mtime.toISOString();n.run(T,c,L,l,E)}catch{}}}o(lt,"addNodeTelemetryColumns");function ut(e){e.exec(`
437
+ WHERE node_id = ?`);for(const{node_id:E}of e.prepare("SELECT node_id FROM nodes").all()){const T=j(h(),E,"job","telemetry.json");if(U(T))try{const i=JSON.parse(M(T,"utf8")),a=typeof i.tokens_in=="number"&&Number.isFinite(i.tokens_in)?i.tokens_in:null,c=typeof i.context_tokens=="number"&&Number.isFinite(i.context_tokens)?i.context_tokens:null,L=typeof i.last_activity=="string"?i.last_activity:null,l=ue(T).mtime.toISOString();n.run(a,c,L,l,E)}catch{}}}o(lt,"addNodeTelemetryColumns");function ut(e){e.exec(`
438
438
  CREATE INDEX IF NOT EXISTS deliveries_external_due_order
439
439
  ON deliveries(state, next_attempt_at, created_at, delivery_id)
440
440
  WHERE destination_kind = 'external_action' AND canceled_at IS NULL;
@@ -503,7 +503,7 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
503
503
  );
504
504
  CREATE UNIQUE INDEX idx_grants_grantee_sub ON grants(grantee, sub);
505
505
  CREATE TABLE runtime_state (key TEXT PRIMARY KEY, value TEXT NOT NULL);
506
- `)}o(It,"addGrantMirror");function ft(e){e.exec("UPDATE nodes SET grantee = 'app:terminal' WHERE grantee = 'user:local';")}o(ft,"migrateTerminalGrantees");function Xt(e){const t={llm:"crtr:llm",act:"crtr:act",schedule:"crtr:schedule","memory:read":"crtr:memory:read:user","memory:write":"crtr:memory:rw:user"},n=e.prepare("SELECT node_id, scopes_json FROM nodes WHERE scopes_json IS NOT NULL").all(),E=e.prepare("UPDATE nodes SET scopes_json = ? WHERE node_id = ?");for(const a of n){const i=JSON.parse(a.scopes_json);if(!Array.isArray(i))throw new Error(`invalid launched scopes for node ${a.node_id}`);const T=[];for(const c of i){if(typeof c!="string")continue;const L=t[c]??c;try{Te(L),T.push(L)}catch(l){if(!(l instanceof TypeError))throw l}}E.run(JSON.stringify([...new Set(T)]),a.node_id)}}o(Xt,"migrateLaunchedScopes");function Ut(e){e.exec(`CREATE TABLE node_tokens (
506
+ `)}o(It,"addGrantMirror");function ft(e){e.exec("UPDATE nodes SET grantee = 'app:terminal' WHERE grantee = 'user:local';")}o(ft,"migrateTerminalGrantees");function Xt(e){const t={llm:"crtr:llm",act:"crtr:act",schedule:"crtr:schedule","memory:read":"crtr:memory:read:user","memory:write":"crtr:memory:rw:user"},n=e.prepare("SELECT node_id, scopes_json FROM nodes WHERE scopes_json IS NOT NULL").all(),E=e.prepare("UPDATE nodes SET scopes_json = ? WHERE node_id = ?");for(const T of n){const i=JSON.parse(T.scopes_json);if(!Array.isArray(i))throw new Error(`invalid launched scopes for node ${T.node_id}`);const a=[];for(const c of i){if(typeof c!="string")continue;const L=t[c]??c;try{ae(L),a.push(L)}catch(l){if(!(l instanceof TypeError))throw l}}E.run(JSON.stringify([...new Set(a)]),T.node_id)}}o(Xt,"migrateLaunchedScopes");function Ut(e){e.exec(`CREATE TABLE node_tokens (
507
507
  broker_process_id TEXT PRIMARY KEY REFERENCES executions(execution_id) ON DELETE CASCADE,
508
508
  node_id TEXT NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
509
509
  token_hash TEXT NOT NULL UNIQUE, grantee TEXT NOT NULL, uid INTEGER NOT NULL,
@@ -655,8 +655,8 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
655
655
  ALTER TABLE messages ADD COLUMN object_id TEXT;
656
656
  ALTER TABLE messages ADD COLUMN level INTEGER;
657
657
  ALTER TABLE messages ADD COLUMN actor_id TEXT;
658
- `),ie(e),e.exec("ANALYZE"))}o(Ee,"canvasObjectsSchema");function ie(e){const t=new Date().toISOString(),n=e.prepare("INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at) VALUES (?, ?, NULL, NULL, 'public', ?, '', ?) RETURNING rowid"),E=e.prepare("INSERT INTO identities (object, scheme, external_id) VALUES (?, ?, ?)"),a=e.prepare(`SELECT DISTINCT n.grantee FROM nodes n
659
- WHERE NOT EXISTS (SELECT 1 FROM identities i WHERE i.external_id = n.grantee AND i.scheme IN ('app', 'local-user'))`).all();for(const{grantee:i}of a){let T;if(i==="user:local")T={type:"person",scheme:"local-user",handle:"user"};else if(/^app:[a-z0-9][a-z0-9._-]*$/.test(i))T={type:"app",scheme:"app",handle:i.slice(4)};else throw new Error(`canvas objects migration: node grantee ${JSON.stringify(i)} is neither app:<name> nor user:local`);const{rowid:c}=n.get(`${Date.now().toString(36)}-${P(4).toString("hex")}`,T.type,T.handle,t);E.run(c,T.scheme,i)}e.exec(`INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at)
658
+ `),ie(e),e.exec("ANALYZE"))}o(Ee,"canvasObjectsSchema");function ie(e){const t=new Date().toISOString(),n=e.prepare("INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at) VALUES (?, ?, NULL, NULL, 'public', ?, '', ?) RETURNING rowid"),E=e.prepare("INSERT INTO identities (object, scheme, external_id) VALUES (?, ?, ?)"),T=e.prepare(`SELECT DISTINCT n.grantee FROM nodes n
659
+ WHERE NOT EXISTS (SELECT 1 FROM identities i WHERE i.external_id = n.grantee AND i.scheme IN ('app', 'local-user'))`).all();for(const{grantee:i}of T){let a;if(i==="user:local")a={type:"person",scheme:"local-user",handle:"user"};else if(/^app:[a-z0-9][a-z0-9._-]*$/.test(i))a={type:"app",scheme:"app",handle:i.slice(4)};else throw new Error(`canvas objects migration: node grantee ${JSON.stringify(i)} is neither app:<name> nor user:local`);const{rowid:c}=n.get(`${Date.now().toString(36)}-${P(4).toString("hex")}`,a.type,a.handle,t);E.run(c,a.scheme,i)}e.exec(`INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at)
660
660
  SELECT n.node_id, 'node', n.kind, i.object, 'public', n.name, '', n.created
661
661
  FROM nodes n JOIN identities i ON i.external_id = n.grantee AND i.scheme IN ('app', 'local-user')
662
662
  WHERE NOT EXISTS (SELECT 1 FROM objects o WHERE o.id = n.node_id)`)}o(ie,"copyNodesIntoObjects");function Dt(e){if(Ee(e),!S(e,"nodes","name"))return;ie(e);const t=e.prepare("SELECT COUNT(*) AS n FROM nodes WHERE node_id NOT IN (SELECT id FROM objects)").get();if(t.n>0)throw new Error(`canvasObjectsCut: ${t.n} node(s) have no objects row`);e.exec(`
@@ -723,7 +723,7 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
723
723
  url TEXT NOT NULL, created_by TEXT NOT NULL, created_at TEXT NOT NULL, expires_at TEXT,
724
724
  status TEXT NOT NULL CHECK (status IN ('on', 'off')), object_deleted_at TEXT
725
725
  );
726
- CREATE INDEX IF NOT EXISTS idx_shares_created_by ON shares(created_by);`)}o(gt,"addFileTransfer");function vt(e){e.exec(`CREATE TABLE IF NOT EXISTS daemon_actions (
726
+ CREATE INDEX IF NOT EXISTS idx_shares_created_by ON shares(created_by);`)}o(gt,"addFileTransfer");function wt(e){e.exec(`CREATE TABLE IF NOT EXISTS daemon_actions (
727
727
  action_id TEXT PRIMARY KEY,
728
728
  adapter_json TEXT NOT NULL,
729
729
  body_json TEXT,
@@ -762,7 +762,7 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
762
762
  DELETE FROM messages
763
763
  WHERE message_id IN (SELECT message_id FROM moved_actions WHERE message_id IS NOT NULL)
764
764
  AND NOT EXISTS (SELECT 1 FROM deliveries d WHERE d.message_id = messages.message_id);
765
- DROP TABLE moved_actions;`)}o(vt,"moveDaemonActions");function wt(e){B(e,61,()=>{if(!C(e,"objects"))return;const t=e.prepare("SELECT COUNT(*) AS n FROM objects").get().n,n=e.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'view' AND sql LIKE '%objects%'").all();for(const i of n)e.exec(`DROP VIEW "${i.name}"`);e.exec(`
765
+ DROP TABLE moved_actions;`)}o(wt,"moveDaemonActions");function yt(e){B(e,61,()=>{if(!C(e,"objects"))return;const t=e.prepare("SELECT COUNT(*) AS n FROM objects").get().n,n=e.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'view' AND sql LIKE '%objects%'").all();for(const i of n)e.exec(`DROP VIEW "${i.name}"`);e.exec(`
766
766
  CREATE TABLE objects_new (
767
767
  rowid INTEGER PRIMARY KEY,
768
768
  id TEXT NOT NULL UNIQUE,
@@ -783,10 +783,10 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
783
783
  CREATE UNIQUE INDEX objects_handle ON objects(type, name) WHERE type IN ('person','app','repo','profile');
784
784
  CREATE INDEX objects_owner_type ON objects(owner, type, kind);
785
785
  CREATE INDEX objects_type_kind ON objects(type, kind);
786
- `);for(const i of n)e.exec(i.sql);const E=e.prepare("SELECT COUNT(*) AS n FROM objects").get().n;if(E!==t)throw new Error(`objects rebuild lost rows: expected ${t}, got ${E}`);const a=e.prepare("PRAGMA foreign_key_check").all();if(a.length>0)throw new Error(`objects rebuild left ${a.length} foreign-key violation(s)`)})}o(wt,"objectsWithoutTypeCheck");function yt(e){C(e,"deliveries")&&e.exec(`
786
+ `);for(const i of n)e.exec(i.sql);const E=e.prepare("SELECT COUNT(*) AS n FROM objects").get().n;if(E!==t)throw new Error(`objects rebuild lost rows: expected ${t}, got ${E}`);const T=e.prepare("PRAGMA foreign_key_check").all();if(T.length>0)throw new Error(`objects rebuild left ${T.length} foreign-key violation(s)`)})}o(yt,"objectsWithoutTypeCheck");function vt(e){C(e,"deliveries")&&e.exec(`
787
787
  CREATE INDEX IF NOT EXISTS deliveries_destination ON deliveries(destination_id, wake, state, created_at);
788
788
  DROP INDEX IF EXISTS deliveries_node_inbox;
789
- `)}o(yt,"addDeliveriesDestinationIndex");function ht(e){e.exec(`
789
+ `)}o(vt,"addDeliveriesDestinationIndex");function ht(e){e.exec(`
790
790
  CREATE TABLE object_kinds (
791
791
  name TEXT PRIMARY KEY, owner INTEGER NOT NULL REFERENCES objects(rowid),
792
792
  description TEXT NOT NULL DEFAULT '', events_json TEXT NOT NULL,
@@ -870,18 +870,18 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
870
870
  FROM deliveries d
871
871
  WHERE d.adapter_json IS NOT NULL
872
872
  AND NOT EXISTS (SELECT 1 FROM nodes n JOIN objects o ON o.id = n.node_id AND o.type = 'node'
873
- WHERE d.idempotency_key = 'node-outcome:' || n.node_id)`).all(),E=n.filter(s=>s.idempotency_key?.startsWith("node-outcome:")!==!0||s.state!=="acknowledged"&&s.state!=="permanent_failed");if(E.length)throw new Error(`v67 cannot migrate ${E.length} unmappable external-action deliver${E.length===1?"y":"ies"}: ${E.slice(0,5).map(s=>`${s.delivery_id} (${s.state}, ${s.idempotency_key??"no key"})`).join(", ")}; pending work requires a live outcome node`);const a=e.prepare("DELETE FROM deliveries WHERE delivery_id = ?");for(const s of n)a.run(s.delivery_id);const i=e.prepare(`SELECT d.delivery_id, d.message_id, d.destination_id,
873
+ WHERE d.idempotency_key = 'node-outcome:' || n.node_id)`).all(),E=n.filter(s=>s.idempotency_key?.startsWith("node-outcome:")!==!0||s.state!=="acknowledged"&&s.state!=="permanent_failed");if(E.length)throw new Error(`v67 cannot migrate ${E.length} unmappable external-action deliver${E.length===1?"y":"ies"}: ${E.slice(0,5).map(s=>`${s.delivery_id} (${s.state}, ${s.idempotency_key??"no key"})`).join(", ")}; pending work requires a live outcome node`);const T=e.prepare("DELETE FROM deliveries WHERE delivery_id = ?");for(const s of n)T.run(s.delivery_id);const i=e.prepare(`SELECT d.delivery_id, d.message_id, d.destination_id,
874
874
  d.adapter_json, d.state, d.created_at, n.node_id, o.owner, o.privacy
875
875
  FROM deliveries d
876
876
  JOIN nodes n ON d.idempotency_key = 'node-outcome:' || n.node_id
877
877
  JOIN objects o ON o.id = n.node_id AND o.type = 'node'
878
- WHERE d.idempotency_key LIKE 'node-outcome:%'`).all(),T=e.prepare(`INSERT INTO objects
878
+ WHERE d.idempotency_key LIKE 'node-outcome:%'`).all(),a=e.prepare(`INSERT INTO objects
879
879
  (id, type, kind, owner, privacy, name, preview, created_at, reception)
880
880
  VALUES (?, 'action', NULL, ?, ?, ?, '', ?, 2) RETURNING rowid`),c=e.prepare("INSERT INTO custom_objects (object, receiver_json, payload_json) VALUES (?, ?, ?)"),L=e.prepare(`INSERT INTO watches (watcher, object, in_tree, read_heads_key, created_at)
881
881
  VALUES (?, ?, 1, NULL, ?) RETURNING rowid`),l=e.prepare(`INSERT INTO watch_subscriptions
882
882
  (watch, pattern, filter_json, config_json, started_by, ends_on, created_at)
883
883
  VALUES (?, 'outcome', NULL, '{"1":"after-turn"}', 'spawn', NULL, ?)`),r=e.prepare("UPDATE deliveries SET destination_id = ?, idempotency_key = NULL WHERE delivery_id = ?"),_=e.prepare("SELECT body_json FROM messages WHERE message_id = ?"),d=e.prepare(`UPDATE messages SET body_json = ?, event = 'outcome', object_id = ?, level = 1
884
- WHERE message_id = ?`);for(const s of i){if(s.owner===null)throw new Error(`v67 outcome node ${s.node_id} has no owner`);const A=JSON.parse(s.adapter_json);if(A.adapter!=="subprocess"||!Array.isArray(A.argv)||typeof A.cwd!="string"||typeof A.config_origin!="string")throw new Error(`v67 invalid outcome adapter on ${s.delivery_id}`);const I=P(16).toString("hex"),D=new Date(s.created_at).toISOString(),w=T.get(I,s.owner,s.privacy,`outcome/${s.node_id}`,D);c.run(w.rowid,JSON.stringify({argv:A.argv,cwd:A.cwd,config_origin:A.config_origin,name:s.destination_id}),JSON.stringify(A.payload??null));const G=L.get(w.rowid,e.prepare("SELECT rowid FROM objects WHERE id = ?").get(s.node_id).rowid,D);if(l.run(G.rowid,D),s.message_id===null){if(s.state!=="armed")throw new Error(`v67 delivery ${s.delivery_id} has no message but is ${s.state}`);continue}const f=_.get(s.message_id);if(!f||f.body_json===null)throw new Error(`v67 delivery ${s.delivery_id} has no frozen outcome document`);if(e.prepare("SELECT COUNT(*) AS n FROM deliveries WHERE message_id = ?").get(s.message_id).n!==1)throw new Error(`v67 outcome message ${s.message_id} is shared`);const y=JSON.parse(f.body_json);if(y===null||Array.isArray(y)||typeof y!="object")throw new Error(`v67 invalid outcome body on ${s.delivery_id}`);delete y.action,delete y.delivery,d.run(JSON.stringify(y),s.node_id,s.message_id),r.run(I,s.delivery_id)}const u=e.prepare("SELECT COUNT(*) AS n FROM deliveries WHERE state = 'armed'").get();if(u.n!==i.filter(s=>s.state==="armed").length)throw new Error(`v67 found ${u.n} armed deliveries but only ${i.length} mapped outcome rows`);const p=e.prepare("SELECT COUNT(*) AS n FROM deliveries").get().n-u.n,O=e.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'view' AND sql LIKE '%deliveries%'").all();for(const s of O)e.exec(`DROP VIEW "${s.name.replaceAll('"','""')}"`);e.exec(`
884
+ WHERE message_id = ?`);for(const s of i){if(s.owner===null)throw new Error(`v67 outcome node ${s.node_id} has no owner`);const A=JSON.parse(s.adapter_json);if(A.adapter!=="subprocess"||!Array.isArray(A.argv)||typeof A.cwd!="string"||typeof A.config_origin!="string")throw new Error(`v67 invalid outcome adapter on ${s.delivery_id}`);const I=P(16).toString("hex"),D=new Date(s.created_at).toISOString(),y=a.get(I,s.owner,s.privacy,`outcome/${s.node_id}`,D);c.run(y.rowid,JSON.stringify({argv:A.argv,cwd:A.cwd,config_origin:A.config_origin,name:s.destination_id}),JSON.stringify(A.payload??null));const G=L.get(y.rowid,e.prepare("SELECT rowid FROM objects WHERE id = ?").get(s.node_id).rowid,D);if(l.run(G.rowid,D),s.message_id===null){if(s.state!=="armed")throw new Error(`v67 delivery ${s.delivery_id} has no message but is ${s.state}`);continue}const f=_.get(s.message_id);if(!f||f.body_json===null)throw new Error(`v67 delivery ${s.delivery_id} has no frozen outcome document`);if(e.prepare("SELECT COUNT(*) AS n FROM deliveries WHERE message_id = ?").get(s.message_id).n!==1)throw new Error(`v67 outcome message ${s.message_id} is shared`);const v=JSON.parse(f.body_json);if(v===null||Array.isArray(v)||typeof v!="object")throw new Error(`v67 invalid outcome body on ${s.delivery_id}`);delete v.action,delete v.delivery,d.run(JSON.stringify(v),s.node_id,s.message_id),r.run(I,s.delivery_id)}const u=e.prepare("SELECT COUNT(*) AS n FROM deliveries WHERE state = 'armed'").get();if(u.n!==i.filter(s=>s.state==="armed").length)throw new Error(`v67 found ${u.n} armed deliveries but only ${i.length} mapped outcome rows`);const p=e.prepare("SELECT COUNT(*) AS n FROM deliveries").get().n-u.n,O=e.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'view' AND sql LIKE '%deliveries%'").all();for(const s of O)e.exec(`DROP VIEW "${s.name.replaceAll('"','""')}"`);e.exec(`
885
885
  CREATE TABLE deliveries_new (
886
886
  delivery_id TEXT PRIMARY KEY,
887
887
  message_id TEXT NOT NULL REFERENCES messages(message_id),
@@ -924,7 +924,7 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
924
924
  state TEXT NOT NULL DEFAULT 'reserved' CHECK (state IN ('reserved', 'warm_claimed', 'complete'))
925
925
  );
926
926
  CREATE INDEX IF NOT EXISTS idx_create_requests_expires ON create_requests(expires_at);
927
- `)}o(Ht,"addCreateRequests");function Gt(e){for(const[r,_]of[["sub","TEXT"],["grant_id","TEXT"],["scopes_json","TEXT"],["origin_run_id","TEXT"],["run_execution_id","TEXT"]])S(e,"crons",r)||e.exec(`ALTER TABLE crons ADD COLUMN ${r} ${_}`);const t=[],n=o(r=>e.prepare("SELECT id, sub, resolved_scopes_json FROM grants WHERE grantee = ? ORDER BY CASE status WHEN 'active' THEN 0 ELSE 1 END, rev DESC LIMIT 1").get(r),"grantRow"),E=o(r=>e.prepare("SELECT grantee, sub, resolved_scopes_json FROM grants WHERE id = ?").get(r),"grantById"),a=o(r=>{try{const _=JSON.parse(r);return Array.isArray(_)?_.flatMap(d=>d&&d.mode==="always"&&typeof d.scope=="string"?[d.scope]:[]):[]}catch{return[]}},"snapshot"),i=o((r,_)=>{if(_===null)return null;const d=e.prepare("SELECT grantee, sub, grant_id, scopes_json, run_id FROM node_rows WHERE node_id = ?").get(_);if(d===void 0||d.grantee!==r)return null;const u=r==="app:terminal";let p=null;if(d.scopes_json!==null){let A;try{A=JSON.parse(d.scopes_json)}catch{return{pause:"bad_scopes"}}if(!Array.isArray(A)||!A.every(I=>typeof I=="string"))return{pause:"bad_scopes"};p=A}const O=u?n(r):void 0;let R=d.grant_id??O?.id??null;R===null&&!u&&d.sub!==null&&(R=e.prepare("SELECT id FROM grants WHERE grantee = ? AND sub = ? ORDER BY CASE status WHEN 'active' THEN 0 ELSE 1 END, rev DESC LIMIT 1").get(r,d.sub)?.id??null);const s=R===null?void 0:E(R);return R!==null&&(s===void 0||s.grantee!==r||d.sub!==null&&s.sub!==d.sub)?{pause:"no_grant"}:R===null&&!u?{pause:"no_grant"}:(p===null&&!u&&(p=a(s.resolved_scopes_json)),{principal:{sub:d.sub??s?.sub??O?.sub??"user:local",grant_id:R,scopes:p,origin_run_id:d.run_id}})},"fromCreator"),T=o(r=>{const _=n(r);return r==="app:terminal"?{sub:_?.sub??"user:local",grant_id:_?.id??null,scopes:null,origin_run_id:null}:_===void 0?null:{sub:_.sub,grant_id:_.id,scopes:a(_.resolved_scopes_json),origin_run_id:null}},"fromGrantee"),c=e.prepare("SELECT cron_id, grantee, created_by, isolated, cwd FROM crons WHERE sub IS NULL").all(),L=e.prepare("UPDATE crons SET sub = ?, grant_id = ?, scopes_json = ?, origin_run_id = ? WHERE cron_id = ?"),l=new Map;for(const r of c){const _=i(r.grantee,r.created_by),d=_===null?T(r.grantee):"principal"in _?_.principal:null;if(d===null){const u=_!==null&&"pause"in _?_.pause:"no_grant";L.run("user:local",null,null,null,r.cron_id),e.prepare("UPDATE crons SET state = 'paused' WHERE cron_id = ?").run(r.cron_id),t.push({event:`cron.migrated.${u}`,fields:{cron_id:r.cron_id,grantee:r.grantee}})}else l.set(r.cron_id,d),L.run(d.sub,d.grant_id,d.scopes===null?null:JSON.stringify(d.scopes),d.origin_run_id,r.cron_id);if(r.isolated!==0){const u=j(Re(r.grantee).root,"crons",r.cron_id);e.prepare("UPDATE crons SET cwd = ? WHERE cron_id = ?").run(u,r.cron_id),t.push({event:"cron.migrated.isolated_cwd",fields:{cron_id:r.cron_id,old_cwd:r.cwd,cwd:u}})}}if(C(e,"daemon_actions")){const r=e.prepare("SELECT action_id, adapter_json FROM daemon_actions WHERE state IN ('pending', 'handed_off') AND json_extract(adapter_json, '$.adapter') = 'cron_spawn' AND json_extract(adapter_json, '$.principal') IS NULL").all();for(const _ of r){const d=JSON.parse(_.adapter_json),u=typeof d.cronId=="string"?d.cronId:null,p=u===null?void 0:e.prepare("SELECT grantee, sub, grant_id, scopes_json, origin_run_id, isolated, created_by FROM crons WHERE cron_id = ?").get(u);let O=null;if(p!==void 0&&!(p.grant_id===null&&p.grantee!=="app:terminal"))O={cron_id:u,grantee:p.grantee,sub:p.sub,grant_id:p.grant_id,scopes:p.scopes_json===null?null:JSON.parse(p.scopes_json),origin_run_id:p.origin_run_id,isolated:p.isolated!==0,creator:p.created_by};else if(p===void 0){const R=typeof d.parent=="string"?d.parent:null,s=R===null?void 0:e.prepare("SELECT grantee FROM node_rows WHERE node_id = ?").get(R),A=s===void 0?null:i(s.grantee,R),I=A===null||!("principal"in A)?null:A.principal;s!==void 0&&I!==null&&(O={cron_id:u,grantee:s.grantee,sub:I.sub,grant_id:I.grant_id,scopes:I.scopes,origin_run_id:I.origin_run_id,isolated:d.isolated===!0,creator:R})}if(O!==null)e.prepare("UPDATE daemon_actions SET adapter_json = ? WHERE action_id = ?").run(JSON.stringify({...d,principal:O}),_.action_id);else{const R=Date.now();e.prepare("UPDATE daemon_actions SET state = 'permanent_failed', failed_at = ?, updated_at = ?, claimed_at = NULL, claim_owner = NULL, next_attempt_at = NULL, last_failure_json = ? WHERE action_id = ?").run(R,R,JSON.stringify({kind:"grant_refused",message:"the cron has no derivable principal to authorize this birth"}),_.action_id),t.push({event:"cron.birth.refused",fields:{cron_id:u,action_id:_.action_id,reason:"grant_missing"}})}}}for(const{event:r,fields:_}of t)Ae({level:"warn",event:r,fields:_})}o(Gt,"addCronPrincipal");const F=[Ue,Ce,Se,De,ge,ve,we,he,ye,xe,Fe,Me,ke,J,je,J,He,Ge,be,Be,We,Qe,Ve,Ze,et,tt,nt,ot,Et,it,st,rt,at,Tt,ct,dt,_t,Nt,Lt,e=>{de(e),ce(e),Ne(e),Le(e)},ut,pt,lt,Rt,At,Ot,It,Ut,ft,Xt,ee,te,ne,oe,Ct,St,Ee,Dt,me,gt,vt,wt,yt,kt,Mt,Ft,jt,xt,e=>e.exec(`
927
+ `)}o(Ht,"addCreateRequests");function Gt(e){for(const[r,_]of[["sub","TEXT"],["grant_id","TEXT"],["scopes_json","TEXT"],["origin_run_id","TEXT"],["run_execution_id","TEXT"]])S(e,"crons",r)||e.exec(`ALTER TABLE crons ADD COLUMN ${r} ${_}`);const t=[],n=o(r=>e.prepare("SELECT id, sub, resolved_scopes_json FROM grants WHERE grantee = ? ORDER BY CASE status WHEN 'active' THEN 0 ELSE 1 END, rev DESC LIMIT 1").get(r),"grantRow"),E=o(r=>e.prepare("SELECT grantee, sub, resolved_scopes_json FROM grants WHERE id = ?").get(r),"grantById"),T=o(r=>{try{const _=JSON.parse(r);return Array.isArray(_)?_.flatMap(d=>d&&d.mode==="always"&&typeof d.scope=="string"?[d.scope]:[]):[]}catch{return[]}},"snapshot"),i=o((r,_)=>{if(_===null)return null;const d=e.prepare("SELECT grantee, sub, grant_id, scopes_json, run_id FROM node_rows WHERE node_id = ?").get(_);if(d===void 0||d.grantee!==r)return null;const u=r==="app:terminal";let p=null;if(d.scopes_json!==null){let A;try{A=JSON.parse(d.scopes_json)}catch{return{pause:"bad_scopes"}}if(!Array.isArray(A)||!A.every(I=>typeof I=="string"))return{pause:"bad_scopes"};p=A}const O=u?n(r):void 0;let R=d.grant_id??O?.id??null;R===null&&!u&&d.sub!==null&&(R=e.prepare("SELECT id FROM grants WHERE grantee = ? AND sub = ? ORDER BY CASE status WHEN 'active' THEN 0 ELSE 1 END, rev DESC LIMIT 1").get(r,d.sub)?.id??null);const s=R===null?void 0:E(R);return R!==null&&(s===void 0||s.grantee!==r||d.sub!==null&&s.sub!==d.sub)?{pause:"no_grant"}:R===null&&!u?{pause:"no_grant"}:(p===null&&!u&&(p=T(s.resolved_scopes_json)),{principal:{sub:d.sub??s?.sub??O?.sub??"user:local",grant_id:R,scopes:p,origin_run_id:d.run_id}})},"fromCreator"),a=o(r=>{const _=n(r);return r==="app:terminal"?{sub:_?.sub??"user:local",grant_id:_?.id??null,scopes:null,origin_run_id:null}:_===void 0?null:{sub:_.sub,grant_id:_.id,scopes:T(_.resolved_scopes_json),origin_run_id:null}},"fromGrantee"),c=e.prepare("SELECT cron_id, grantee, created_by, isolated, cwd FROM crons WHERE sub IS NULL").all(),L=e.prepare("UPDATE crons SET sub = ?, grant_id = ?, scopes_json = ?, origin_run_id = ? WHERE cron_id = ?"),l=new Map;for(const r of c){const _=i(r.grantee,r.created_by),d=_===null?a(r.grantee):"principal"in _?_.principal:null;if(d===null){const u=_!==null&&"pause"in _?_.pause:"no_grant";L.run("user:local",null,null,null,r.cron_id),e.prepare("UPDATE crons SET state = 'paused' WHERE cron_id = ?").run(r.cron_id),t.push({event:`cron.migrated.${u}`,fields:{cron_id:r.cron_id,grantee:r.grantee}})}else l.set(r.cron_id,d),L.run(d.sub,d.grant_id,d.scopes===null?null:JSON.stringify(d.scopes),d.origin_run_id,r.cron_id);if(r.isolated!==0){const u=j(Re(r.grantee).root,"crons",r.cron_id);e.prepare("UPDATE crons SET cwd = ? WHERE cron_id = ?").run(u,r.cron_id),t.push({event:"cron.migrated.isolated_cwd",fields:{cron_id:r.cron_id,old_cwd:r.cwd,cwd:u}})}}if(C(e,"daemon_actions")){const r=e.prepare("SELECT action_id, adapter_json FROM daemon_actions WHERE state IN ('pending', 'handed_off') AND json_extract(adapter_json, '$.adapter') = 'cron_spawn' AND json_extract(adapter_json, '$.principal') IS NULL").all();for(const _ of r){const d=JSON.parse(_.adapter_json),u=typeof d.cronId=="string"?d.cronId:null,p=u===null?void 0:e.prepare("SELECT grantee, sub, grant_id, scopes_json, origin_run_id, isolated, created_by FROM crons WHERE cron_id = ?").get(u);let O=null;if(p!==void 0&&!(p.grant_id===null&&p.grantee!=="app:terminal"))O={cron_id:u,grantee:p.grantee,sub:p.sub,grant_id:p.grant_id,scopes:p.scopes_json===null?null:JSON.parse(p.scopes_json),origin_run_id:p.origin_run_id,isolated:p.isolated!==0,creator:p.created_by};else if(p===void 0){const R=typeof d.parent=="string"?d.parent:null,s=R===null?void 0:e.prepare("SELECT grantee FROM node_rows WHERE node_id = ?").get(R),A=s===void 0?null:i(s.grantee,R),I=A===null||!("principal"in A)?null:A.principal;s!==void 0&&I!==null&&(O={cron_id:u,grantee:s.grantee,sub:I.sub,grant_id:I.grant_id,scopes:I.scopes,origin_run_id:I.origin_run_id,isolated:d.isolated===!0,creator:R})}if(O!==null)e.prepare("UPDATE daemon_actions SET adapter_json = ? WHERE action_id = ?").run(JSON.stringify({...d,principal:O}),_.action_id);else{const R=Date.now();e.prepare("UPDATE daemon_actions SET state = 'permanent_failed', failed_at = ?, updated_at = ?, claimed_at = NULL, claim_owner = NULL, next_attempt_at = NULL, last_failure_json = ? WHERE action_id = ?").run(R,R,JSON.stringify({kind:"grant_refused",message:"the cron has no derivable principal to authorize this birth"}),_.action_id),t.push({event:"cron.birth.refused",fields:{cron_id:u,action_id:_.action_id,reason:"grant_missing"}})}}}for(const{event:r,fields:_}of t)Ae({level:"warn",event:r,fields:_})}o(Gt,"addCronPrincipal");const F=[Ue,Ce,Se,De,ge,we,ye,he,ve,xe,Fe,Me,ke,J,je,J,He,Ge,be,Be,We,Qe,Ve,Ze,et,tt,nt,ot,Et,it,st,rt,Tt,at,ct,dt,_t,Nt,Lt,e=>{de(e),ce(e),Ne(e),Le(e)},ut,pt,lt,Rt,At,Ot,It,Ut,ft,Xt,ee,te,ne,oe,Ct,St,Ee,Dt,me,gt,wt,yt,vt,kt,Mt,Ft,jt,xt,e=>e.exec(`
928
928
  CREATE TABLE audit_rows (
929
929
  id INTEGER PRIMARY KEY, request_id TEXT NOT NULL, time TEXT NOT NULL, grantee TEXT, sub TEXT,
930
930
  client_id TEXT, class TEXT, operation TEXT NOT NULL, scope_checked TEXT,
@@ -993,4 +993,38 @@ CREATE INDEX idx_structured_requests_effects ON structured_requests(effects_done
993
993
  usd_equivalent REAL,
994
994
  PRIMARY KEY (grantee, month, payer, source, served_model)
995
995
  );
996
- `),Gt,e=>e.exec("ALTER TABLE audit_rows ADD COLUMN via TEXT")];function B(e,t,n){const E=e.prepare("PRAGMA foreign_keys").get().foreign_keys;e.exec("PRAGMA foreign_keys = OFF;");try{e.exec("BEGIN IMMEDIATE");try{W(e)===t&&(n(),e.exec(`PRAGMA user_version = ${t+1}`)),e.exec("COMMIT")}catch(a){try{e.exec("ROLLBACK")}catch{}throw a}}finally{e.exec(`PRAGMA foreign_keys = ${E};`)}}o(B,"selfTransactionalMigration");const bt=new Set([3,61,63,66]);function W(e){return e.prepare("PRAGMA user_version").get().user_version}o(W,"currentUserVersion");function se(e,t){F[t](e),e.exec(`PRAGMA user_version = ${t+1}`)}o(se,"applyMigration");function En(e){const t=W(e);if(t>F.length)throw new Error(`unsupported canvas schema version ${t} (latest supported ${F.length})`);if(t===F.length)return;const n=e.prepare("PRAGMA busy_timeout").get().timeout;e.exec("PRAGMA busy_timeout = 60000;");try{Bt(e)}finally{e.exec(`PRAGMA busy_timeout = ${n};`)}}o(En,"migrate");function Bt(e){for(;;){const t=W(e);if(t>=F.length)return;if(bt.has(t)){if(e.isTransaction)throw new Error(`canvas migration v${t+1} requires autocommit`);F[t](e);continue}if(e.isTransaction){se(e,t);continue}e.exec("BEGIN IMMEDIATE");try{W(e)===t&&se(e,t),e.exec("COMMIT")}catch(n){try{e.exec("ROLLBACK")}catch{}throw n}}}o(Bt,"runPendingMigrations");export{F as MIGRATIONS,En as migrate};
996
+ `),Gt,e=>e.exec("ALTER TABLE audit_rows ADD COLUMN via TEXT"),e=>e.exec(`
997
+ ALTER TABLE objects ADD COLUMN home_machine TEXT;
998
+ CREATE TABLE peer_stubs (
999
+ object INTEGER PRIMARY KEY REFERENCES objects(rowid) ON DELETE CASCADE,
1000
+ home_machine TEXT NOT NULL,
1001
+ envelope_json TEXT,
1002
+ created_at TEXT NOT NULL
1003
+ );
1004
+ CREATE TABLE peer_outbox (
1005
+ id INTEGER PRIMARY KEY,
1006
+ peer TEXT NOT NULL,
1007
+ kind TEXT NOT NULL,
1008
+ payload_json TEXT NOT NULL,
1009
+ idempotency_key TEXT NOT NULL UNIQUE,
1010
+ state TEXT NOT NULL CHECK (state IN ('pending','waiting','claimed','done','expired')),
1011
+ attempts INTEGER NOT NULL DEFAULT 0,
1012
+ next_at INTEGER,
1013
+ claimed_by TEXT,
1014
+ claimed_at INTEGER,
1015
+ first_failed_at INTEGER,
1016
+ created_at INTEGER NOT NULL
1017
+ );
1018
+ CREATE INDEX peer_outbox_due ON peer_outbox(next_at) WHERE state = 'pending';
1019
+ CREATE INDEX peer_outbox_peer ON peer_outbox(peer, state);
1020
+ CREATE TABLE peer_locations (
1021
+ id TEXT PRIMARY KEY,
1022
+ machine_id TEXT NOT NULL,
1023
+ learned_at INTEGER NOT NULL
1024
+ );
1025
+ `),e=>e.exec(`
1026
+ CREATE TABLE peer_ingested (
1027
+ key TEXT PRIMARY KEY,
1028
+ ingested_at INTEGER NOT NULL
1029
+ );
1030
+ `)];function B(e,t,n){const E=e.prepare("PRAGMA foreign_keys").get().foreign_keys;e.exec("PRAGMA foreign_keys = OFF;");try{e.exec("BEGIN IMMEDIATE");try{W(e)===t&&(n(),e.exec(`PRAGMA user_version = ${t+1}`)),e.exec("COMMIT")}catch(T){try{e.exec("ROLLBACK")}catch{}throw T}}finally{e.exec(`PRAGMA foreign_keys = ${E};`)}}o(B,"selfTransactionalMigration");const bt=new Set([3,61,63,66]);function W(e){return e.prepare("PRAGMA user_version").get().user_version}o(W,"currentUserVersion");function se(e,t){F[t](e),e.exec(`PRAGMA user_version = ${t+1}`)}o(se,"applyMigration");function En(e){const t=W(e);if(t>F.length)throw new Error(`unsupported canvas schema version ${t} (latest supported ${F.length})`);if(t===F.length)return;const n=e.prepare("PRAGMA busy_timeout").get().timeout;e.exec("PRAGMA busy_timeout = 60000;");try{Bt(e)}finally{e.exec(`PRAGMA busy_timeout = ${n};`)}}o(En,"migrate");function Bt(e){for(;;){const t=W(e);if(t>=F.length)return;if(bt.has(t)){if(e.isTransaction)throw new Error(`canvas migration v${t+1} requires autocommit`);F[t](e);continue}if(e.isTransaction){se(e,t);continue}e.exec("BEGIN IMMEDIATE");try{W(e)===t&&se(e,t),e.exec("COMMIT")}catch(n){try{e.exec("ROLLBACK")}catch{}throw n}}}o(Bt,"runPendingMigrations");export{F as MIGRATIONS,En as migrate};
@@ -1,11 +1,11 @@
1
- var k=Object.defineProperty;var c=(n,e)=>k(n,"name",{value:e,configurable:!0});import{prepared as m}from"../sqlite-statements.js";import{evalCondition as x}from"../predicate.js";import{finalizeNodeMail as W,insertMessage as $}from"../feed/messages.js";import{ownerChain as B,readCheck as J}from"./access.js";import{publishLookups as U,readerForObject as G}from"./reader.js";import{configFromJson as T}from"./watches.js";import{CrtrError as L}from"../errors.js";import{ExitCode as A}from"../../types.js";import{eventSpec as K,kindOf as C}from"./kinds/registry.js";import{RECEIVERS as D}from"./receivers.js";import{DEFAULT_LISTEN as F}from"./types.js";function q(n,e){if(n.level===void 0||n.level===e.level||e.levels?.includes(n.level))return n.level??e.level;const o=[e.level,...e.levels??[]].filter((s,t,d)=>d.indexOf(s)===t).sort((s,t)=>s-t);throw new L("invalid_level",`${n.object.type} ${n.event} publishes at level ${o.join(", ")}, not ${n.level}`,A.USAGE,{kind:n.object.type,event:n.event,level:n.level,allowed:o})}c(q,"resolveLevel");function X(n,e){const o=n.split("."),s=e.split(".");for(let t=0;t<o.length;t++){if(o[t]===">")return t===o.length-1&&s.length>t;if(t>=s.length||o[t]!=="*"&&o[t]!==s[t])return!1}return o.length===s.length}c(X,"matchSubject");const H={"next-turn":1,"after-turn":2,steer:3,interrupt:4};function Y(n,e){let o=null;for(const s of n){let t=null;for(const d of s)d.threshold<=e&&(!t||d.threshold>t.threshold)&&(t=d);t&&(!o||H[t.delivery]>H[o])&&(o=t.delivery)}return o}c(Y,"resolveDelivery");function fe(n,e,o){if(e.config)return e.config;const s=m(n,"SELECT listen_json FROM objects WHERE rowid = ?").get(o.rowid);return s?.listen_json?T(s.listen_json):F}c(fe,"listenConfigFor");function z(n,e){return e.inboxKind??(n.level>=7?"urgent":"update")}c(z,"inboxKind");function P(n,e){return e.type==="node"?e.id:m(n,`SELECT 1 FROM objects WHERE type IN ('person','app','repo','profile')
2
- AND name = ? AND rowid != ? LIMIT 1`).get(e.name,e.rowid)?`${e.type}:${e.name}`:e.name}c(P,"ownerHandleOf");function V(n,e,o){const s=e.object.owner===null?null:m(n,"SELECT * FROM objects WHERE rowid = ?").get(e.object.owner),t=s?.id??null,d=s?P(n,s):null;return{event:e.event,level:e.level,object:{id:e.object.id,type:e.object.type,kind:e.object.kind,owner:t,owner_handle:d,name:e.object.name},actor:e.actor?.id??null,attrs:o,...e.pointer===void 0?{}:{pointer:e.pointer}}}c(V,"pushFields");function Q(n,e,o,s){const t=V(n,e,s),d=e.messageBody!==void 0&&o.carriesBody===!0,E=d?typeof e.messageBody=="object"&&e.messageBody!==null&&!Array.isArray(e.messageBody)?{...e.messageBody,push:t}:{body:e.messageBody,push:t}:{push:t},a=d?e.messageBody:null,l=a?.bodyRef===void 0?null:a;return{kind:z(e,o),label:l?.label??(e.object.type.includes(".")&&typeof e.messageBody?.summary=="string"?e.messageBody.summary.split(`
3
- `)[0]:`${e.event} ${e.object.type} ${e.object.name}`),...l?.bodyRef===void 0?{}:{bodyRef:l.bodyRef},...l?.disposition===void 0?{}:{disposition:l.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor?.name??null,bodyJson:E}}c(Q,"messageInput");function Z(n,e,o,s,t){if(!o.supersedes)return[{attrs:e.attrs,destinations:[...s]}];const d=m(n,`SELECT d.delivery_id, d.message_id, d.state, m.body_json FROM deliveries d
1
+ var k=Object.defineProperty;var a=(n,e)=>k(n,"name",{value:e,configurable:!0});import{prepared as m}from"../sqlite-statements.js";import{evalCondition as x}from"../predicate.js";import{finalizeNodeMail as W,insertMessage as $}from"../feed/messages.js";import{ownerChain as B,readCheck as J}from"./access.js";import{publishLookups as K,readerForObject as U}from"./reader.js";import{configFromJson as T}from"./watches.js";import{CrtrError as L}from"../errors.js";import{ExitCode as A}from"../../types.js";import{eventSpec as G,kindOf as C}from"./kinds/registry.js";import{RECEIVERS as D}from"./receivers.js";import{DEFAULT_LISTEN as F}from"./types.js";function q(n,e){if(n.level===void 0||n.level===e.level||e.levels?.includes(n.level))return n.level??e.level;const o=[e.level,...e.levels??[]].filter((s,t,d)=>d.indexOf(s)===t).sort((s,t)=>s-t);throw new L("invalid_level",`${n.object.type} ${n.event} publishes at level ${o.join(", ")}, not ${n.level}`,A.USAGE,{kind:n.object.type,event:n.event,level:n.level,allowed:o})}a(q,"resolveLevel");function X(n,e){const o=n.split("."),s=e.split(".");for(let t=0;t<o.length;t++){if(o[t]===">")return t===o.length-1&&s.length>t;if(t>=s.length||o[t]!=="*"&&o[t]!==s[t])return!1}return o.length===s.length}a(X,"matchSubject");const H={"next-turn":1,"after-turn":2,steer:3,interrupt:4};function Y(n,e){let o=null;for(const s of n){let t=null;for(const d of s)d.threshold<=e&&(!t||d.threshold>t.threshold)&&(t=d);t&&(!o||H[t.delivery]>H[o])&&(o=t.delivery)}return o}a(Y,"resolveDelivery");function fe(n,e,o){if(e.config)return e.config;const s=m(n,"SELECT listen_json FROM objects WHERE rowid = ?").get(o.rowid);return s?.listen_json?T(s.listen_json):F}a(fe,"listenConfigFor");function z(n,e){return e.inboxKind??(n.level>=7?"urgent":"update")}a(z,"inboxKind");function P(n,e){return e.type==="node"?e.id:m(n,`SELECT 1 FROM objects WHERE type IN ('person','app','repo','profile')
2
+ AND name = ? AND rowid != ? LIMIT 1`).get(e.name,e.rowid)?`${e.type}:${e.name}`:e.name}a(P,"ownerHandleOf");function V(n,e,o){const s=e.object.owner===null?null:m(n,"SELECT * FROM objects WHERE rowid = ?").get(e.object.owner),t=s?.id??null,d=s?P(n,s):null;return{event:e.event,level:e.level,object:{id:e.object.id,type:e.object.type,kind:e.object.kind,owner:t,owner_handle:d,name:e.object.name},actor:e.actor?.id??null,attrs:o,...e.pointer===void 0?{}:{pointer:e.pointer}}}a(V,"pushFields");function Q(n,e,o,s){const t=V(n,e,s),d=e.messageBody!==void 0&&o.carriesBody===!0,g=d?typeof e.messageBody=="object"&&e.messageBody!==null&&!Array.isArray(e.messageBody)?{...e.messageBody,push:t}:{body:e.messageBody,push:t}:{push:t},l=d?e.messageBody:null,c=l?.bodyRef===void 0?null:l;return{kind:z(e,o),label:c?.label??(typeof l?.label=="string"?l.label:e.object.type.includes(".")&&typeof e.messageBody?.summary=="string"?e.messageBody.summary.split(`
3
+ `)[0]:`${e.event} ${e.object.type} ${e.object.name}`),...c?.bodyRef===void 0?{}:{bodyRef:c.bodyRef},...c?.disposition===void 0?{}:{disposition:c.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor?.name??null,bodyJson:g}}a(Q,"messageInput");function Z(n,e,o,s,t){if(!o.supersedes)return[{attrs:e.attrs,destinations:[...s]}];const d=m(n,`SELECT d.delivery_id, d.message_id, d.state, m.body_json FROM deliveries d
4
4
  JOIN messages m ON m.message_id = d.message_id
5
5
  WHERE d.destination_id = ? AND d.state IN ('pending', 'handed_off')
6
6
  AND d.canceled_at IS NULL AND m.event = ? AND m.object_id = ?
7
- ORDER BY m.seq`),E=m(n,"DELETE FROM deliveries WHERE delivery_id = ?"),a=m(n,"SELECT * FROM objects WHERE id = ?"),l=new Map,j=new Set;for(const p of s){const w=t(p),R=a.get(w),S=R===void 0?void 0:C(n,R.type),N=d.all(w,e.event,e.object.id),v=new Set;for(const r of N)r.state==="handed_off"?S?.supersedeClaimed?.(n,r.delivery_id)&&v.add(r.delivery_id):(E.run(r.delivery_id),j.add(r.message_id));const g=N.filter(r=>r.state==="pending"||v.has(r.delivery_id)).at(-1),b=g?.message_id??null,i=l.get(b)??{prior:g?.body_json?JSON.parse(g.body_json).push?.attrs??null:null,destinations:[]};i.destinations.push(p),l.set(b,i)}const _=m(n,"DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)");for(const p of j)_.run(p,p);return[...l.values()].map(p=>({attrs:o.merge?o.merge(p.prior,e):e.attrs,destinations:p.destinations}))}c(Z,"supersede");const M=8;class ee extends L{static{c(this,"HopLimitError")}constructor(e){super("hop_limit",`event depth ${e} exceeds ${M}`,A.GENERAL,{depth:e,limit:M})}}function me(n,e,o=()=>[]){const s=K(n,e.object.type,e.event),t={...e,level:q(e,s)},d=e.cause??null,E=d===null?null:m(n,`SELECT depth FROM messages WHERE message_id = ?
8
- UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(d,d);if(d!==null&&E===void 0)throw new L("unknown_cause",`no message ${d}`,A.USAGE,{cause:d});const a=E===null?0:E.depth+1;if(a>M)throw new ee(a);const l=B(n,t.object),j=`w.watcher, o.id AS watcher_id, o.type AS watcher_type, o.reception, s.pattern, s.filter_json,
7
+ ORDER BY m.seq`),g=m(n,"DELETE FROM deliveries WHERE delivery_id = ?"),l=m(n,"SELECT * FROM objects WHERE id = ?"),c=new Map,j=new Set;for(const u of s){const w=t(u),R=l.get(w),S=R===void 0?void 0:C(n,R.type),N=d.all(w,e.event,e.object.id),v=new Set;for(const r of N)r.state==="handed_off"?S?.supersedeClaimed?.(n,r.delivery_id)&&v.add(r.delivery_id):(g.run(r.delivery_id),j.add(r.message_id));const y=N.filter(r=>r.state==="pending"||v.has(r.delivery_id)).at(-1),b=y?.message_id??null,i=c.get(b)??{prior:y?.body_json?JSON.parse(y.body_json).push?.attrs??null:null,destinations:[]};i.destinations.push(u),c.set(b,i)}const _=m(n,"DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)");for(const u of j)_.run(u,u);return[...c.values()].map(u=>({attrs:o.merge?o.merge(u.prior,e):e.attrs,destinations:u.destinations}))}a(Z,"supersede");const M=8;class ee extends L{static{a(this,"HopLimitError")}constructor(e){super("hop_limit",`event depth ${e} exceeds ${M}`,A.GENERAL,{depth:e,limit:M})}}function me(n,e,o=()=>[]){const s=G(n,e.object.type,e.event),t={...e,level:q(e,s)},d=e.cause??null,g=d===null?null:m(n,`SELECT depth FROM messages WHERE message_id = ?
8
+ UNION ALL SELECT depth FROM message_causes WHERE message_id = ? LIMIT 1`).get(d,d);if(d!==null&&g===void 0)throw new L("unknown_cause",`no message ${d}`,A.USAGE,{cause:d});const l=g===null?0:g.depth+1;if(l>M)throw new ee(l);const c=B(n,t.object),j=`w.watcher, o.id AS watcher_id, o.type AS watcher_type, o.reception, s.pattern, s.filter_json,
9
9
  COALESCE(s.config_json, o.listen_json) AS config_json`,_=n.prepare(s.durable?`SELECT ${j} FROM watches w JOIN watch_subscriptions s ON s.watch = w.rowid
10
10
  JOIN objects o ON o.rowid = w.watcher WHERE w.object = ? AND w.watcher != ? AND o.reception >= 1`:`SELECT ${j} FROM objects o INDEXED BY objects_receiving CROSS JOIN watches w CROSS JOIN watch_subscriptions s
11
- WHERE o.reception = 2 AND w.watcher = o.rowid AND s.watch = w.rowid AND w.object = ? AND w.watcher != ?`).all(t.object.rowid,t.actor?.rowid??-1);if(_.length===0)return{delivered:0,message_id:null,depth:a};const p={event:t.event,level:t.level,id:t.object.id,type:t.object.type,kind:t.object.kind,actor:t.actor?.id??null,...t.attrs},w=new Map;for(const i of _){if(!X(i.pattern,t.event)||i.filter_json!==null&&!x(JSON.parse(i.filter_json),p))continue;const r=w.get(i.watcher)??{id:i.watcher_id,type:i.watcher_type,reception:i.reception,configs:[]};r.configs.push(i.config_json?T(i.config_json):F),w.set(i.watcher,r)}const R=(l.length?l[l.length-1]:t.object).type==="repo",S=c(()=>[],"noRepos"),N=J(n,t.object),v=U(n),g=[];for(const i of w.values()){const r=m(n,"SELECT * FROM objects WHERE id = ?").get(i.id),u=C(n,r.type),O=u.receiverFor?u.receiverFor(n,r):u.receiver;if(O===void 0)continue;const y=Y(i.configs,t.level);if(y===null)continue;const h=G(n,r,R?o:S,v);h.scopes===null&&(h.class==="node"||h.owner.type!=="person")||N(h)&&g.push({id:i.id,reception:i.reception,delivery:y,receiver:O})}if(g.length===0)return{delivered:0,message_id:null,depth:a};let b=null;for(const i of Z(n,t,s,g,r=>r.id)){const r=new Map;for(const f of i.destinations)r.set(f.receiver,[...r.get(f.receiver)??[],f]);const u=Q(n,t,s,i.attrs),O=[...r].map(([f,I])=>D[f].bodyOwner(I)).find(f=>f!==null)??null,y=W({...u,urgency:"normal"},O),h=$(n,{operationId:y.operationId,createdAt:y.createdAt,senderNodeId:u.senderNodeId,senderName:u.senderName,kind:u.kind,label:u.label,bodyRef:y.bodyRef,bodyJson:y.bodyJson,disposition:u.disposition??null,event:{event:t.event,objectId:t.object.id,level:t.level,actorId:t.actor?.id??null},causeMessageId:d,depth:a});b??=h;for(const[f,I]of r)D[f].enqueue(n,{messageId:h,event:t.event,level:t.level,senderNodeId:u.senderNodeId,elevatesTier:s.elevatesTier===!0},I)}return{delivered:g.length,message_id:b,depth:a}}c(me,"publish");export{M as HOP_LIMIT,ee as HopLimitError,fe as listenConfigFor,X as matchSubject,me as publish,Y as resolveDelivery,q as resolveLevel,Z as supersede};
11
+ WHERE o.reception = 2 AND w.watcher = o.rowid AND s.watch = w.rowid AND w.object = ? AND w.watcher != ?`).all(t.object.rowid,t.actor?.rowid??-1);if(_.length===0)return{delivered:0,message_id:null,depth:l};const u={event:t.event,level:t.level,id:t.object.id,type:t.object.type,kind:t.object.kind,actor:t.actor?.id??null,...t.attrs},w=new Map;for(const i of _){if(!X(i.pattern,t.event)||i.filter_json!==null&&!x(JSON.parse(i.filter_json),u))continue;const r=w.get(i.watcher)??{id:i.watcher_id,type:i.watcher_type,reception:i.reception,configs:[]};r.configs.push(i.config_json?T(i.config_json):F),w.set(i.watcher,r)}const R=(c.length?c[c.length-1]:t.object).type==="repo",S=a(()=>[],"noRepos"),N=J(n,t.object),v=K(n),y=[];for(const i of w.values()){const r=m(n,"SELECT * FROM objects WHERE id = ?").get(i.id),p=C(n,r.type),O=p.receiverFor?p.receiverFor(n,r):p.receiver;if(O===void 0)continue;const E=Y(i.configs,t.level);if(E===null)continue;const h=U(n,r,R?o:S,v);h.scopes===null&&(h.class==="node"||h.owner.type!=="person")||N(h)&&y.push({id:i.id,reception:i.reception,delivery:E,receiver:O})}if(y.length===0)return{delivered:0,message_id:null,depth:l};let b=null;for(const i of Z(n,t,s,y,r=>r.id)){const r=new Map;for(const f of i.destinations)r.set(f.receiver,[...r.get(f.receiver)??[],f]);const p=Q(n,t,s,i.attrs),O=[...r].map(([f,I])=>D[f].bodyOwner(I)).find(f=>f!==null)??null,E=W({...p,urgency:"normal"},O),h=$(n,{operationId:E.operationId,createdAt:E.createdAt,senderNodeId:p.senderNodeId,senderName:p.senderName,kind:p.kind,label:p.label,bodyRef:E.bodyRef,bodyJson:E.bodyJson,disposition:p.disposition??null,event:{event:t.event,objectId:t.object.id,level:t.level,actorId:t.actor?.id??null},causeMessageId:d,depth:l});b??=h;for(const[f,I]of r)D[f].enqueue(n,{messageId:h,event:t.event,level:t.level,senderNodeId:p.senderNodeId,elevatesTier:s.elevatesTier===!0,...t.idempotencyKey===void 0?{}:{idempotencyKey:t.idempotencyKey}},I)}return{delivered:y.length,message_id:b,depth:l}}a(me,"publish");export{M as HOP_LIMIT,ee as HopLimitError,fe as listenConfigFor,X as matchSubject,me as publish,Y as resolveDelivery,q as resolveLevel,Z as supersede};
@@ -1,5 +1,5 @@
1
- var T=Object.defineProperty;var u=(r,t)=>T(r,"name",{value:t,configurable:!0});import{prepared as p}from"../../sqlite-statements.js";import{cancelCronsOnWake as I}from"../../canvas/crons.js";import{settleMatchingControllerWait as N}from"../../canvas/waits.js";import{effectiveTier as R,listenDeliveryTier as b,newId as y}from"../../feed/messages.js";import{readGoal as S}from"../../runtime/kickoff.js";import{canRead as h}from"../access.js";import{objectDTO as _}from"./dto.js";import{recordLiveRead as D,withBase as L}from"./kind.js";import{attr as O,header as g,section as f}from"./render.js";const m=[3,5,7];function j(r){return r?.split(`
2
- `).map(t=>t.trim()).find(t=>t!=="")??""}u(j,"firstLine");const $={bodyOwner:u(r=>r[0]?.id??null,"bodyOwner"),enqueue(r,t,o){const d=Date.now(),s=t.senderNodeId,i=o.map(e=>e.id),l=`(${i.map(()=>"?").join(",")})`,c=s===null?new Set:new Set(r.prepare(`SELECT DISTINCT node_id FROM waits WHERE kind = 'controller' AND target_id = ? AND satisfied_at IS NULL AND node_id IN ${l}`).all(s,...i).map(e=>e.node_id)),n=new Set(r.prepare(`SELECT DISTINCT anchor_node FROM crons WHERE cancel_on_wake = 1 AND anchor_node IN ${l}`).all(...i).map(e=>e.anchor_node)),v=p(r,`INSERT INTO deliveries (delivery_id, message_id, destination_id, urgency, wake, state, created_at, updated_at)
3
- VALUES (?, ?, ?, ?, ?, 'pending', ?, ?)`);for(const e of o){const a=b(e.delivery),E=e.reception===2?a.wake:0,w=t.elevatesTier?R(e.id,a.urgency):a.urgency;v.run(y(),t.messageId,e.id,w,E,d,d),c.has(e.id)&&N(e.id,s,d,r),E===1&&w!=="deferred"&&n.has(e.id)&&I(e.id)}}},K={name:"node",addressable:"id",receiver:"inbox",events:L({configured:{level:1,durable:!0},messaged:{level:2,durable:!0},sent:{level:1,durable:!0},"report.update":{level:5,levels:m,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:m,durable:!0,carriesBody:!0,elevatesTier:!0},"notice.deadline":{level:5,levels:m,durable:!0,carriesBody:!0,elevatesTier:!0},"notice.close":{level:5,levels:m,durable:!0,carriesBody:!0,elevatesTier:!0}}),read(r,t,o,d){const s=p(r,"SELECT status FROM nodes WHERE node_id = ?").get(o.id),i=u(e=>e.filter(a=>h(r,t,a)).map(a=>_(r,a)),"visible"),l=p(r,`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.rowid`).all(o.id),c=p(r,"SELECT * FROM objects WHERE owner = ? AND deleted_at IS NULL ORDER BY rowid").all(o.rowid);D(r,t,o,d.reposFn);const n={status:s?.status??"deleted",mandate_first_line:j(S(o.id)),children:i(l),owned:i(c.filter(e=>e.type!=="job")),jobs:i(c.filter(e=>e.type==="job"))},v=[g("object",_(r,o),O("status",n.status)),`Mandate: ${n.mandate_first_line}`,...f("Children",n.children),...f("Owns",n.owned),...f("Jobs",n.jobs)].join(`
5
- `);return{data:n,text:v}}};export{$ as nodeInbox,K as nodeKind};
1
+ var y=Object.defineProperty;var s=(r,t)=>y(r,"name",{value:t,configurable:!0});import{prepared as c}from"../../sqlite-statements.js";import{cancelCronsOnWake as T}from"../../canvas/crons.js";import{settleMatchingControllerWait as b}from"../../canvas/waits.js";import{effectiveTier as R,listenDeliveryTier as I,newId as N}from"../../feed/messages.js";import{readGoal as S}from"../../runtime/kickoff.js";import{canRead as h}from"../access.js";import{objectDTO as w}from"./dto.js";import{recordLiveRead as L,withBase as O}from"./kind.js";import{attr as D,header as j,section as f}from"./render.js";const m=[3,5,7];function C(r){return r?.split(`
2
+ `).map(t=>t.trim()).find(t=>t!=="")??""}s(C,"firstLine");const K={bodyOwner:s(r=>r[0]?.id??null,"bodyOwner"),enqueue(r,t,n){const a=Date.now(),l=t.senderNodeId,i=n.map(e=>e.id),u=`(${i.map(()=>"?").join(",")})`,p=l===null?new Set:new Set(r.prepare(`SELECT DISTINCT node_id FROM waits WHERE kind = 'controller' AND target_id = ? AND satisfied_at IS NULL AND node_id IN ${u}`).all(l,...i).map(e=>e.node_id)),o=new Set(r.prepare(`SELECT DISTINCT anchor_node FROM crons WHERE cancel_on_wake = 1 AND anchor_node IN ${u}`).all(...i).map(e=>e.anchor_node)),v=c(r,`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 n){const d=I(e.delivery),E=e.reception===2?d.wake:0,_=t.elevatesTier?R(e.id,d.urgency):d.urgency;v.run(N(),t.messageId,e.id,_,E,a,a,t.idempotencyKey===void 0?null:`${t.idempotencyKey}:${e.id}`),p.has(e.id)&&b(e.id,l,a,r),E===1&&_!=="deferred"&&o.has(e.id)&&T(e.id)}}},U={name:"node",addressable:"id",receiver:"inbox",receiverFor:s((r,t)=>c(r,"SELECT 1 FROM peer_stubs WHERE object = ?").get(t.rowid)===void 0?"inbox":"peer","receiverFor"),events:O({configured:{level:1,durable:!0},messaged:{level:2,durable:!0},sent:{level:1,durable:!0},"report.update":{level:5,levels:m,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:m,durable:!0,carriesBody:!0,elevatesTier:!0},"notice.deadline":{level:5,levels:m,durable:!0,carriesBody:!0,elevatesTier:!0},"notice.close":{level:5,levels:m,durable:!0,carriesBody:!0,elevatesTier:!0}}),read(r,t,n,a){const l=c(r,"SELECT status FROM nodes WHERE node_id = ?").get(n.id),i=s(e=>e.filter(d=>h(r,t,d)).map(d=>w(r,d)),"visible"),u=c(r,`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.rowid`).all(n.id),p=c(r,"SELECT * FROM objects WHERE owner = ? AND deleted_at IS NULL ORDER BY rowid").all(n.rowid);L(r,t,n,a.reposFn);const o={status:l?.status??"deleted",mandate_first_line:C(S(n.id)),children:i(u),owned:i(p.filter(e=>e.type!=="job")),jobs:i(p.filter(e=>e.type==="job"))},v=[j("object",w(r,n),D("status",o.status)),`Mandate: ${o.mandate_first_line}`,...f("Children",o.children),...f("Owns",o.owned),...f("Jobs",o.jobs)].join(`
5
+ `);return{data:o,text:v}}};export{K as nodeInbox,U as nodeKind};
@@ -1,11 +1,11 @@
1
- var b=Object.defineProperty;var s=(t,e)=>b(t,"name",{value:e,configurable:!0});import{prepared as d}from"../sqlite-statements.js";import{CrtrError as h,notFound as T}from"../errors.js";import{ExitCode as L}from"../../types.js";import{newNodeId as y}from"../runtime/nodes.js";import{kindOf as x}from"./kinds/registry.js";import{canRead as N}from"./access.js";import{bodyOf as A}from"./bodies.js";const _=new Set(["person","app","repo","profile"]);function O(t){return t||null}s(O,"rowFrom");function I(t,e){return O(d(t,"SELECT * FROM objects WHERE id = ?").get(e))}s(I,"getById");function P(t,e){return O(d(t,"SELECT * FROM objects WHERE rowid = ?").get(e))}s(P,"getByRowid");function D(t,e){return x(t,e.type),d(t,`INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at)
1
+ var b=Object.defineProperty;var s=(t,e)=>b(t,"name",{value:e,configurable:!0});import{prepared as d}from"../sqlite-statements.js";import{CrtrError as R,notFound as T}from"../errors.js";import{ExitCode as L}from"../../types.js";import{newNodeId as y}from"../runtime/nodes.js";import{kindOf as x}from"./kinds/registry.js";import{canRead as N}from"./access.js";import{bodyOf as A}from"./bodies.js";const _=new Set(["person","app","repo","profile"]);function h(t){return t||null}s(h,"rowFrom");function I(t,e){return h(d(t,"SELECT * FROM objects WHERE id = ?").get(e))}s(I,"getById");function P(t,e){return h(d(t,"SELECT * FROM objects WHERE rowid = ?").get(e))}s(P,"getByRowid");function D(t,e){return x(t,e.type),d(t,`INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at)
2
2
  VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(e.id??y(),e.type,e.kind,e.owner,e.privacy,e.name,e.preview,new Date().toISOString())}s(D,"createObject");function S(t,e,o,i,n,r){if(!_.has(e))throw new Error(`ensureOwner: ${e} is not an owner type`);const p=d(t,`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
3
- WHERE i.scheme = ? AND i.external_id = ?`).get(o,i);if(p)return p;const E=d(t,`SELECT o.id, o.type, i.scheme, i.external_id FROM objects o
3
+ WHERE i.scheme = ? AND i.external_id = ?`).get(o,i);if(p)return p;const a=d(t,`SELECT o.id, o.type, i.scheme, i.external_id FROM objects o
4
4
  LEFT JOIN identities i ON i.object = o.rowid
5
- WHERE o.name = ? AND o.type = ?`).get(n,e);if(E)throw new h("conflict",`handle "${n}" is held by ${E.type} ${E.id} (${E.scheme}:${E.external_id}); cannot give it to ${e} ${o}:${i}`,L.GENERAL,{handle:n,holder:E.id});const a=D(t,{type:e,kind:null,owner:e==="profile"?r?.rowid??null:null,privacy:"public",name:n,preview:""});return d(t,"INSERT INTO identities (object, scheme, external_id) VALUES (?, ?, ?)").run(a.rowid,o,i),a}s(S,"ensureOwner");function J(t,e,o){const i=d(t,`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
5
+ WHERE o.name = ? AND o.type = ?`).get(n,e);if(a)throw new R("conflict",`handle "${n}" is held by ${a.type} ${a.id} (${a.scheme}:${a.external_id}); cannot give it to ${e} ${o}:${i}`,L.GENERAL,{handle:n,holder:a.id});const E=D(t,{type:e,kind:null,owner:e==="profile"?r?.rowid??null:null,privacy:"public",name:n,preview:""});return d(t,"INSERT INTO identities (object, scheme, external_id) VALUES (?, ?, ?)").run(E.rowid,o,i),E}s(S,"ensureOwner");function J(t,e,o){const i=d(t,`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
6
6
  WHERE i.scheme = 'profile' AND i.external_id = ? AND o.type = 'profile' AND o.deleted_at IS NULL`).get(e),n=d(t,`SELECT o.id FROM objects o
7
- WHERE o.name = ? AND o.type = 'profile' AND o.deleted_at IS NULL AND o.rowid <> ?`).get(o,i?i.rowid:-1);if(n)throw new h("conflict",`handle "${o}" is already held by profile object ${n.id}; cannot rename profile ${e} to it`,L.GENERAL,{handle:o,holder:n.id});if(!i)return!1;const r=i;return r.name===o?!1:(g(t,r,o),!0)}s(J,"renameProfileObject");function B(t,e){if(e==="user:local")return S(t,"person","local-user",e,"user");if(/^app:[a-z0-9][a-z0-9._-]*$/.test(e))return S(t,"app","app",e,e.slice(4));throw new Error(`node grantee ${JSON.stringify(e)} is neither app:<name> nor user:local`)}s(B,"ownerForGrantee");function G(t,e,o){return O(d(t,"SELECT * FROM objects WHERE (type = 'document' OR instr(type, '.') > 0) AND owner = ? AND name = ? AND deleted_at IS NULL").get(e,o))}s(G,"liveDocumentNamed");function K(t,e){const o=I(t,e);if(!o||o.type!=="node")throw T(`no node object ${e}`);return o}s(K,"nodeObject");function g(t,e,o){d(t,"UPDATE objects SET name = ? WHERE rowid = ?").run(o,e.rowid)}s(g,"setObjectName");function Y(t,e,o){d(t,"UPDATE objects SET kind = ? WHERE rowid = ?").run(o,e.rowid)}s(Y,"setObjectKind");function z(t,e,o,i={}){const n=["rowid > ?"],r=[i.after_rowid??0];return o.type!==void 0&&(n.push("type = ?"),r.push(o.type)),o.kind!==void 0&&(n.push("kind = ?"),r.push(o.kind)),o.owner!==void 0&&(n.push("owner = ?"),r.push(o.owner.rowid)),o.privacy!==void 0&&(n.push("privacy = ?"),r.push(o.privacy)),o.name!==void 0&&(n.push("name = ?"),r.push(o.name)),n.push(o.deleted?"deleted_at IS NOT NULL":"deleted_at IS NULL"),C(t,e,`SELECT * FROM objects WHERE ${n.join(" AND ")}`,r,Math.min(i.limit??100,100))}s(z,"listObjects");function V(t,e,o){const n=[],r=`%${o.text.replace(/[\\%_]/g,c=>`\\${c}`)}%`;if(!o.grep){const c=d(t,`SELECT o.*, d.summary AS _summary FROM objects o LEFT JOIN documents d ON d.object = o.rowid
8
- WHERE o.deleted_at IS NULL AND (o.name LIKE ?1 ESCAPE '\\' OR o.preview LIKE ?1 ESCAPE '\\' OR d.summary LIKE ?1 ESCAPE '\\')
9
- ORDER BY o.rowid`).all(r),u=o.text.toLowerCase();for(const l of c){const{_summary:f,...w}=l,m=w;if(!N(t,e,m))continue;const R=m.name.toLowerCase().includes(u)?"name":m.preview.toLowerCase().includes(u)?"preview":"summary";if(n.push({object:m,field:R,...R==="summary"?{text:String(f)}:{}}),n.length>=100)return n}}if(!o.body&&!o.grep)return n;const p=new Set(n.map(c=>c.object.rowid)),E=o.grep?(c=>u=>c.test(u))(new RegExp(o.text)):c=>c.includes(o.text),a=d(t,"SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL ORDER BY rowid").all(),j=d(t,`SELECT r.body_hash, d.source_path FROM revisions r JOIN documents d ON d.object = r.document WHERE r.document = ?
10
- AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rowid)`);for(const c of a)if(!(p.has(c.rowid)||!N(t,e,c)))for(const{body_hash:u,source_path:l}of j.all(c.rowid)){const f=A(u,l).split(`
11
- `),w=f.findIndex(E);if(!(w<0)){if(n.push({object:c,field:"body",line:w+1,text:f[w]}),n.length>=100)return n;break}}return n}s(V,"searchObjects");function C(t,e,o,i,n){const r=[],p=t.prepare(`${o} ORDER BY rowid LIMIT ${n*4}`);for(;;){const E=p.all(...i);for(const a of E)if(N(t,e,a)&&r.push(a),r.length===n)return r;if(E.length<n*4)return r;i[0]=E[E.length-1].rowid}}s(C,"readablePages");export{D as createObject,S as ensureOwner,I as getById,P as getByRowid,z as listObjects,G as liveDocumentNamed,K as nodeObject,B as ownerForGrantee,J as renameProfileObject,V as searchObjects,Y as setObjectKind,g as setObjectName};
7
+ WHERE o.name = ? AND o.type = 'profile' AND o.deleted_at IS NULL AND o.rowid <> ?`).get(o,i?i.rowid:-1);if(n)throw new R("conflict",`handle "${o}" is already held by profile object ${n.id}; cannot rename profile ${e} to it`,L.GENERAL,{handle:o,holder:n.id});if(!i)return!1;const r=i;return r.name===o?!1:(g(t,r,o),!0)}s(J,"renameProfileObject");function B(t,e){if(e==="user:local")return S(t,"person","local-user",e,"user");if(/^app:[a-z0-9][a-z0-9._-]*$/.test(e))return S(t,"app","app",e,e.slice(4));throw new Error(`node grantee ${JSON.stringify(e)} is neither app:<name> nor user:local`)}s(B,"ownerForGrantee");function G(t,e,o){return h(d(t,"SELECT * FROM objects WHERE (type = 'document' OR instr(type, '.') > 0) AND owner = ? AND name = ? AND deleted_at IS NULL").get(e,o))}s(G,"liveDocumentNamed");function K(t,e){const o=I(t,e);if(!o||o.type!=="node")throw T(`no node object ${e}`);return o}s(K,"nodeObject");function g(t,e,o){d(t,"UPDATE objects SET name = ? WHERE rowid = ?").run(o,e.rowid)}s(g,"setObjectName");function Y(t,e,o){d(t,"UPDATE objects SET kind = ? WHERE rowid = ?").run(o,e.rowid)}s(Y,"setObjectKind");function z(t,e,o,i={}){const n=["rowid > ?"],r=[i.after_rowid??0];return o.type!==void 0&&(n.push("type = ?"),r.push(o.type)),o.kind!==void 0&&(n.push("kind = ?"),r.push(o.kind)),o.owner!==void 0&&(n.push("owner = ?"),r.push(o.owner.rowid)),o.privacy!==void 0&&(n.push("privacy = ?"),r.push(o.privacy)),o.name!==void 0&&(n.push("name = ?"),r.push(o.name)),n.push("home_machine IS NULL"),n.push(o.deleted?"deleted_at IS NOT NULL":"deleted_at IS NULL"),C(t,e,`SELECT * FROM objects WHERE ${n.join(" AND ")}`,r,Math.min(i.limit??100,100))}s(z,"listObjects");function V(t,e,o){const n=[],r=`%${o.text.replace(/[\\%_]/g,c=>`\\${c}`)}%`;if(!o.grep){const c=d(t,`SELECT o.*, d.summary AS _summary FROM objects o LEFT JOIN documents d ON d.object = o.rowid
8
+ WHERE o.deleted_at IS NULL AND o.home_machine IS NULL AND (o.name LIKE ?1 ESCAPE '\\' OR o.preview LIKE ?1 ESCAPE '\\' OR d.summary LIKE ?1 ESCAPE '\\')
9
+ ORDER BY o.rowid`).all(r),u=o.text.toLowerCase();for(const l of c){const{_summary:f,...w}=l,m=w;if(!N(t,e,m))continue;const O=m.name.toLowerCase().includes(u)?"name":m.preview.toLowerCase().includes(u)?"preview":"summary";if(n.push({object:m,field:O,...O==="summary"?{text:String(f)}:{}}),n.length>=100)return n}}if(!o.body&&!o.grep)return n;const p=new Set(n.map(c=>c.object.rowid)),a=o.grep?(c=>u=>c.test(u))(new RegExp(o.text)):c=>c.includes(o.text),E=d(t,"SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL ORDER BY rowid").all(),j=d(t,`SELECT r.body_hash, d.source_path FROM revisions r JOIN documents d ON d.object = r.document WHERE r.document = ?
10
+ AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rowid)`);for(const c of E)if(!(p.has(c.rowid)||!N(t,e,c)))for(const{body_hash:u,source_path:l}of j.all(c.rowid)){const f=A(u,l).split(`
11
+ `),w=f.findIndex(a);if(!(w<0)){if(n.push({object:c,field:"body",line:w+1,text:f[w]}),n.length>=100)return n;break}}return n}s(V,"searchObjects");function C(t,e,o,i,n){const r=[],p=t.prepare(`${o} ORDER BY rowid LIMIT ${n*4}`);for(;;){const a=p.all(...i);for(const E of a)if(N(t,e,E)&&r.push(E),r.length===n)return r;if(a.length<n*4)return r;i[0]=a[a.length-1].rowid}}s(C,"readablePages");export{D as createObject,S as ensureOwner,I as getById,P as getByRowid,z as listObjects,G as liveDocumentNamed,K as nodeObject,B as ownerForGrantee,J as renameProfileObject,V as searchObjects,Y as setObjectKind,g as setObjectName};
@@ -1,3 +1,3 @@
1
- var N=Object.defineProperty;var s=(o,e)=>N(o,"name",{value:e,configurable:!0});import{prepared as f}from"../sqlite-statements.js";import{coveredByAny as w}from"../../shared/env.js";import{parseScope as $}from"@north-light/identity";import{activeGrant as R,grantFor as F}from"../grants/mirror.js";import{prepared as S}from"../canvas/db.js";import{ApiError as b}from"../../api/errors.js";import{readConfig as x}from"../config.js";import{SCOPE_VOCABULARY as E}from"../scopes.js";import{loadExactProfileManifest as A}from"../profiles/manifest.js";import{ensureOwner as g,getByRowid as C,nodeObject as L}from"./objects.js";function v(o){return{repos:o.map(e=>e.repo),checkouts:o}}s(v,"inRepos");function z(o,e,a){if(e.class==="anonymous")throw new Error("a publish token cannot read as a principal");if(e.class==="person")return{class:"person",person:j(o)};if(e.class==="app")return{class:"app",app:_(o,e.app),scopes:e.scopes};if(e.class==="machine")throw new Error("a machine token cannot read as a principal");if(e.class==="forwarded"){if(e.actor.kind==="person")return{class:"person",person:j(o)};const c=F(e.actor.app,e.sub);if(!c)throw new b(403,"app_not_granted",`${e.actor.app} holds no active grant for ${e.sub} on this machine`);const p=e.actor.node_scopes;return{class:"app",app:_(o,e.actor.app),place:e.actor.place,scopes:p===null?c.resolved:p.filter(i=>w(c.resolved,i))}}if(e.class==="cron"){const c=e.grant?R(e.grant)?.resolved??[]:E,p=e.scopes;return{class:"app",app:_(o,e.app),scopes:p===null?c:p.filter(i=>w(c,i)),place:{run:e.origin_run??"",isolated:e.isolated}}}const n=d(o),r=L(o,e.node),t=r.owner===null?null:n.object(r.owner);if(!t)throw new Error(`node ${e.node} has no owner`);const l=f(o,"SELECT cwd, profile_id FROM nodes WHERE node_id = ?").get(e.node);return{class:"node",node:r,app:t,scopes:y(e.app,e.sub,e.grant,e.scopes,n),cwd:l.cwd,...v(a(l.cwd)),profile:l.profile_id===null?null:n.profile(l.profile_id,t)}}s(z,"readerFor");function M(o,e,a,n=d(o),r=!1){const t=S(o,"SELECT o.* FROM objects o WHERE o.id = ? AND o.type = 'node'").get(e),l=S(o,`SELECT (SELECT external_id FROM identities WHERE object = ?) AS grantee,
2
- sub, grant_id, scopes_json, cwd, profile_id FROM nodes WHERE node_id = ?`).get(t?.owner??null,e);if(!t||!l)throw new Error(`no node ${e}`);const{grantee:c,sub:p,grant_id:i,scopes_json:u,cwd:O,profile_id:h}=l,m=t.owner===null?null:n.object(t.owner);if(!m)throw new Error(`node ${e} has no owner`);const H=u===null?null:JSON.parse(u);return{class:"node",node:t,app:m,scopes:y(c,p??"",i,H,n,r),cwd:O,...v(a(O)),profile:h===null?null:n.profile(h,m)}}s(M,"readerForNode");function K(o,e,a=()=>[],n=d(o)){if(e.type==="node")return M(o,e.id,a,n,!0);const r=e.owner===null?null:n.object(e.owner);if(!r)throw new Error(`watcher ${e.id} has no owner`);const l=(r.type==="app"?r:r.type==="profile"&&r.owner!==null?n.object(r.owner):null)??f(o,"SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object WHERE i.external_id = 'app:terminal' AND i.scheme = 'app'").get()??f(o,"SELECT * FROM objects WHERE type = 'person' AND name = 'user'").get();if(!l)throw new Error("no terminal app or person object");const c=f(o,"SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(l.rowid)?.external_id??"app:terminal",p=f(o,"SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(c),i=r.type==="person"?null:p.length===0?c==="app:terminal"&&!n.terminalHasIssuer()?E:null:p.length===1?JSON.parse(p[0].resolved_scopes_json).filter(u=>u.mode==="always").map(u=>$(u.scope)):null;return{class:"object",object:e,node:e,owner:r,app:l,scopes:i,repos:r.type==="repo"?[r]:[]}}s(K,"readerForObject");function d(o){return{grantById:R,grantFor:F,terminalHasIssuer:s(()=>!!x("user").runtime?.issuer,"terminalHasIssuer"),object:s(e=>C(o,e),"object"),profile:s((e,a)=>T(o,e,a),"profile")}}s(d,"liveLookups");function Q(o){const e=d(o),a=new Map,n=s((r,t)=>(a.has(r)||a.set(r,t()),a.get(r)),"memo");return{grantById:s(r=>n(`g\0${r}`,()=>e.grantById(r)),"grantById"),grantFor:s((r,t)=>n(`a\0${r}\0${t}`,()=>e.grantFor(r,t)),"grantFor"),terminalHasIssuer:s(()=>n("issuer",()=>e.terminalHasIssuer()),"terminalHasIssuer"),object:s(r=>n(`o\0${r}`,()=>e.object(r)),"object"),profile:s((r,t)=>n(`p\0${r}\0${t.rowid}`,()=>e.profile(r,t)),"profile")}}s(Q,"publishLookups");function y(o,e,a,n,r,t=!1){const l=a?r.grantById(a):r.grantFor(o,e),c=l&&l.grantee===o?l.resolved:o==="app:terminal"&&!r.terminalHasIssuer()?E:null;if(c===null){if(t)return null;throw new Error(`the grant of ${o} is not active`)}return n===null?c:n.filter(p=>w(c,p))}s(y,"nodeScopes");function j(o){return g(o,"person","local-user","user:local","user")}s(j,"personObject");function _(o,e){return e==="user:local"?j(o):g(o,"app","app",e,e.replace(/^app:/,""))}s(_,"appObject");function T(o,e,a){return f(o,`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
3
- WHERE i.scheme = 'profile' AND i.external_id = ?`).get(e)??g(o,"profile","profile",e,A(e).manifest.name,a)}s(T,"profileObject");export{d as liveLookups,T as profileObject,Q as publishLookups,z as readerFor,M as readerForNode,K as readerForObject};
1
+ var N=Object.defineProperty;var l=(o,e)=>N(o,"name",{value:e,configurable:!0});import{prepared as f}from"../sqlite-statements.js";import{coveredByAny as E}from"../../shared/env.js";import{parseScope as C}from"@north-light/identity";import{activeGrant as h,grantFor as v}from"../grants/mirror.js";import{prepared as F}from"../canvas/db.js";import{ApiError as L}from"../../api/errors.js";import{readConfig as $}from"../config.js";import{SCOPE_VOCABULARY as _}from"../scopes.js";import{loadExactProfileManifest as x}from"../profiles/manifest.js";import{ensureOwner as O,getByRowid as A,nodeObject as M}from"./objects.js";function S(o){return{repos:o.map(e=>e.repo),checkouts:o}}l(S,"inRepos");function K(o,e,s){if(e.class==="anonymous")throw new Error("a publish token cannot read as a principal");if(e.class==="person")return{class:"person",person:m(o)};if(e.class==="app")return{class:"app",app:g(o,e.app),scopes:e.scopes};if(e.class==="machine")throw new Error("a machine token cannot read as a principal");if(e.class==="forwarded"){if(e.actor.kind==="person")return{class:"person",person:m(o)};const a=v(e.actor.app,e.sub);if(!a)throw new L(403,"app_not_granted",`${e.actor.app} holds no active grant for ${e.sub} on this machine`);const c=e.actor.node_scopes;return{class:"app",app:g(o,e.actor.app),place:e.actor.place,scopes:c===null?a.resolved:c.filter(u=>E(a.resolved,u))}}if(e.class==="cron"){const a=e.grant?h(e.grant)?.resolved??[]:_,c=e.scopes;return{class:"app",app:g(o,e.app),scopes:c===null?a:c.filter(u=>E(a,u)),place:{run:e.origin_run??"",isolated:e.isolated}}}const t=w(o),r=M(o,e.node),n=r.owner===null?null:t.object(r.owner);if(!n)throw new Error(`node ${e.node} has no owner`);const p=f(o,"SELECT cwd, profile_id FROM nodes WHERE node_id = ?").get(e.node);return{class:"node",node:r,app:n,scopes:H(e.app,e.sub,e.grant,e.scopes,t),cwd:p.cwd,...S(s(p.cwd)),profile:p.profile_id===null?null:t.profile(p.profile_id,n)}}l(K,"readerFor");function T(o,e,s,t=w(o),r=!1){const n=F(o,"SELECT o.* FROM objects o WHERE o.id = ? AND o.type = 'node'").get(e),p=F(o,`SELECT (SELECT external_id FROM identities WHERE object = ?) AS grantee,
2
+ sub, grant_id, scopes_json, cwd, profile_id FROM nodes WHERE node_id = ?`).get(n?.owner??null,e);if(!n||!p)throw new Error(`no node ${e}`);const{grantee:a,sub:c,grant_id:u,scopes_json:d,cwd:i,profile_id:R}=p,j=n.owner===null?null:t.object(n.owner);if(!j)throw new Error(`node ${e} has no owner`);const b=d===null?null:JSON.parse(d);return{class:"node",node:n,app:j,scopes:H(a,c??"",u,b,t,r),cwd:i,...S(s(i)),profile:R===null?null:t.profile(R,j)}}l(T,"readerForNode");function Q(o,e,s=()=>[],t=w(o)){const r=f(o,"SELECT envelope_json FROM peer_stubs WHERE object = ?").get(e.rowid);if(r!==void 0)return W(o,e,r.envelope_json===null?null:JSON.parse(r.envelope_json),t);if(e.type==="node")return T(o,e.id,s,t,!0);const n=e.owner===null?null:t.object(e.owner);if(!n)throw new Error(`watcher ${e.id} has no owner`);const a=(n.type==="app"?n:n.type==="profile"&&n.owner!==null?t.object(n.owner):null)??f(o,"SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object WHERE i.external_id = 'app:terminal' AND i.scheme = 'app'").get()??f(o,"SELECT * FROM objects WHERE type = 'person' AND name = 'user'").get();if(!a)throw new Error("no terminal app or person object");const c=f(o,"SELECT external_id FROM identities WHERE object = ? AND scheme = 'app'").get(a.rowid)?.external_id??"app:terminal",u=f(o,"SELECT resolved_scopes_json FROM grants WHERE grantee = ? AND status = 'active'").all(c),d=n.type==="person"?null:u.length===0?c==="app:terminal"&&!t.terminalHasIssuer()?_:null:u.length===1?JSON.parse(u[0].resolved_scopes_json).filter(i=>i.mode==="always").map(i=>C(i.scope)):null;return{class:"object",object:e,node:e,owner:n,app:a,scopes:d,repos:n.type==="repo"?[n]:[]}}l(Q,"readerForObject");function W(o,e,s,t){const r=m(o);if(s!==null&&s.app===null)return{class:"object",object:e,node:e,owner:r,app:r,scopes:null,repos:[]};const n=s?.app==null?null:g(o,s.app),p=s?.app==null?null:t.grantFor(s.app,s.sub),a=s===null||p===null?null:s.node_scopes===null?p.resolved:s.node_scopes.filter(u=>E(p.resolved,u)),c=n??(e.owner===null?r:t.object(e.owner)??r);return{class:"object",object:e,node:e,owner:c,app:c,scopes:a,repos:[],...s?.place==null?{}:{place:s.place}}}l(W,"readerForStub");function w(o){return{grantById:h,grantFor:v,terminalHasIssuer:l(()=>!!$("user").runtime?.issuer,"terminalHasIssuer"),object:l(e=>A(o,e),"object"),profile:l((e,s)=>B(o,e,s),"profile")}}l(w,"liveLookups");function X(o){const e=w(o),s=new Map,t=l((r,n)=>(s.has(r)||s.set(r,n()),s.get(r)),"memo");return{grantById:l(r=>t(`g\0${r}`,()=>e.grantById(r)),"grantById"),grantFor:l((r,n)=>t(`a\0${r}\0${n}`,()=>e.grantFor(r,n)),"grantFor"),terminalHasIssuer:l(()=>t("issuer",()=>e.terminalHasIssuer()),"terminalHasIssuer"),object:l(r=>t(`o\0${r}`,()=>e.object(r)),"object"),profile:l((r,n)=>t(`p\0${r}\0${n.rowid}`,()=>e.profile(r,n)),"profile")}}l(X,"publishLookups");function H(o,e,s,t,r,n=!1){const p=s?r.grantById(s):r.grantFor(o,e),a=p&&p.grantee===o?p.resolved:o==="app:terminal"&&!r.terminalHasIssuer()?_:null;if(a===null){if(n)return null;throw new Error(`the grant of ${o} is not active`)}return t===null?a:t.filter(c=>E(a,c))}l(H,"nodeScopes");function m(o){return O(o,"person","local-user","user:local","user")}l(m,"personObject");function g(o,e){return e==="user:local"?m(o):O(o,"app","app",e,e.replace(/^app:/,""))}l(g,"appObject");function B(o,e,s){return f(o,`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
3
+ WHERE i.scheme = 'profile' AND i.external_id = ?`).get(e)??O(o,"profile","profile",e,x(e).manifest.name,s)}l(B,"profileObject");export{w as liveLookups,B as profileObject,X as publishLookups,K as readerFor,T as readerForNode,Q as readerForObject};
@@ -11,12 +11,13 @@ export interface ReceivedMessage {
11
11
  level: Level;
12
12
  senderNodeId: string | null;
13
13
  elevatesTier: boolean;
14
+ idempotencyKey?: string;
14
15
  }
15
16
  export interface Receiver {
16
17
  bodyOwner(destinations: readonly ReceiverDestination[]): string | null;
17
18
  enqueue(db: DatabaseSync, message: ReceivedMessage, destinations: readonly ReceiverDestination[]): void;
18
19
  }
19
- export type ReceiverName = 'inbox' | 'argv' | 'url' | 'queue';
20
+ export type ReceiverName = 'inbox' | 'argv' | 'url' | 'queue' | 'peer';
20
21
  /** The receiver is per instance, not per kind. An action's historical argv JSON is unchanged. */
21
22
  export declare function receiverForObject(db: DatabaseSync, object: ObjectRow): ReceiverName | undefined;
22
23
  export declare const BACKLOG_CAP = 1000;