@north-light/crouter 0.3.355 → 0.3.357

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 (65) hide show
  1. package/dist/api/dto/delivery.d.ts +13 -3
  2. package/dist/api/dto/objects.d.ts +24 -22
  3. package/dist/api/dto/objects.js +1 -1
  4. package/dist/clients/attach/session/document-links.js +1 -1
  5. package/dist/clients/attach/viewer.js +829 -755
  6. package/dist/commands/canvas/list.js +3 -3
  7. package/dist/commands/canvas/read.d.ts +2 -4
  8. package/dist/commands/canvas/read.js +3 -5
  9. package/dist/commands/canvas/search.js +1 -1
  10. package/dist/commands/sys/context/admin/actions.js +1 -1
  11. package/dist/commands/sys/sync-deps.js +1 -1
  12. package/dist/commands/sys/sync-shared.js +1 -1
  13. package/dist/core/canvas/daemon-actions.d.ts +43 -0
  14. package/dist/core/canvas/daemon-actions.js +26 -0
  15. package/dist/core/canvas/deliveries.d.ts +1 -0
  16. package/dist/core/canvas/deliveries.js +1 -1
  17. package/dist/core/canvas/migrations.js +106 -46
  18. package/dist/core/feed/messages.d.ts +2 -20
  19. package/dist/core/feed/messages.js +6 -10
  20. package/dist/core/graph/events.d.ts +10 -5
  21. package/dist/core/graph/events.js +5 -5
  22. package/dist/core/graph/jobs.js +2 -2
  23. package/dist/core/graph/kinds/document.d.ts +19 -0
  24. package/dist/core/graph/kinds/document.js +5 -0
  25. package/dist/core/graph/kinds/dto.d.ts +5 -0
  26. package/dist/core/graph/kinds/dto.js +1 -0
  27. package/dist/core/graph/kinds/job.d.ts +2 -0
  28. package/dist/core/graph/kinds/job.js +2 -0
  29. package/dist/core/graph/kinds/kind.d.ts +39 -0
  30. package/dist/core/graph/kinds/kind.js +1 -0
  31. package/dist/core/graph/kinds/node.d.ts +2 -0
  32. package/dist/core/graph/kinds/node.js +4 -0
  33. package/dist/core/graph/kinds/owner.d.ts +2 -0
  34. package/dist/core/graph/kinds/owner.js +2 -0
  35. package/dist/core/graph/kinds/registry.d.ts +8 -0
  36. package/dist/core/graph/kinds/registry.js +1 -0
  37. package/dist/core/graph/kinds/render.d.ts +9 -0
  38. package/dist/core/graph/kinds/render.js +1 -0
  39. package/dist/core/graph/objects.d.ts +5 -4
  40. package/dist/core/graph/objects.js +5 -5
  41. package/dist/core/graph/types.d.ts +6 -5
  42. package/dist/core/providers/errors.d.ts +97 -0
  43. package/dist/core/providers/errors.js +1 -0
  44. package/dist/core/runtime/broker-protocol.d.ts +2 -7
  45. package/dist/daemon/api/app-listener.js +12 -12
  46. package/dist/daemon/api/handlers/human-requests.js +1 -1
  47. package/dist/daemon/api/handlers/objects.js +2 -5
  48. package/dist/daemon/api/handlers/providers.d.ts +69 -0
  49. package/dist/daemon/api/handlers/providers.js +1 -0
  50. package/dist/daemon/api/operations.js +1 -1
  51. package/dist/daemon/api/server.js +1 -1
  52. package/dist/daemon/cron/settle.js +1 -1
  53. package/dist/daemon/cron/sinks.d.ts +3 -3
  54. package/dist/daemon/cron/sinks.js +2 -2
  55. package/dist/daemon/crtrd.js +6 -6
  56. package/dist/daemon/human/finish.js +6 -6
  57. package/dist/daemon/human/settle.d.ts +2 -3
  58. package/dist/daemon/human/settle.js +1 -1
  59. package/dist/daemon/reconcilers/action-lane.d.ts +8 -0
  60. package/dist/daemon/reconcilers/action-lane.js +1 -0
  61. package/dist/daemon/reconcilers/delivery-lane.d.ts +1 -1
  62. package/dist/daemon/reconcilers/delivery-lane.js +1 -1
  63. package/package.json +2 -2
  64. package/packages/crouter-identity/package.json +1 -1
  65. package/runtime.lock.json +11 -11
@@ -3,9 +3,14 @@
3
3
  import type { DatabaseSync } from 'node:sqlite';
4
4
  import { type ReposFn } from './reader.js';
5
5
  import { type Subscription } from './watches.js';
6
- import { type CanvasEvent, type EventName, type Level, type ListenConfig, type ListenDelivery, type ObjectRow, type ObjectType } from './types.js';
7
- /** The event's default level, or the caller's. Notices have no default: they carry the caller's tier. */
8
- export declare function levelFor(event: EventName, override?: Level, objectType?: ObjectType): Level;
6
+ import { type CanvasEvent, type Level, type ListenConfig, type ListenDelivery, type ObjectRow } from './types.js';
7
+ /** The event's default level, or the caller's. Notices have no default: they carry the caller's tier.
8
+ * Jobs no longer use it: their events publish at the level their kind declares. */
9
+ export declare function levelFor(event: string, override?: Level, objectType?: string): Level;
10
+ /** The level `ev` publishes at: the declared level when none is supplied; a supplied level
11
+ * must equal it or be one of the event's declared `levels`. Throws `unknown_event` for an
12
+ * event the object's kind does not declare, `invalid_level` for any other level. */
13
+ export declare function resolveLevel(ev: Pick<CanvasEvent, 'event' | 'object' | 'level'>): Level;
9
14
  /** NATS-style match over dot-separated event names: `*` is one part, `>` the rest (at least one part). */
10
15
  export declare function matchSubject(pattern: string, event: string): boolean;
11
16
  /** Per config, the delivery of the highest threshold the level reaches; across configs, the
@@ -14,13 +19,13 @@ export declare function resolveDelivery(configs: readonly ListenConfig[], level:
14
19
  /** A subscription's own config, else the watcher node's default, else the global default. */
15
20
  export declare function listenConfigFor(db: DatabaseSync, subscription: Pick<Subscription, 'config'>, watcher: ObjectRow): ListenConfig;
16
21
  /**
17
- * Push `ev` to every node watching the object itself whose subscriptions receive it — a watch on
22
+ * Validate the event against its object's kind (`resolveLevel`), then push it to every node watching the object itself whose subscriptions receive it — a watch on
18
23
  * an owner hears that owner's own events, not those of the objects it owns — inside the caller's write transaction. Document and job events reach
19
24
  * only live (active|idle) watchers; node events also reach done ones without waking them. The
20
25
  * actor is never pushed its own act, access is re-checked per watcher, and apps, persons and
21
26
  * other non-node watchers get nothing. Writes a `messages` row only when a delivery results.
22
27
  * `reposFn` resolves a watcher's repos when the object lives in a repo space.
23
28
  */
24
- export declare function publish(db: DatabaseSync, ev: CanvasEvent, reposFn?: ReposFn): {
29
+ export declare function publish(db: DatabaseSync, input: CanvasEvent, reposFn?: ReposFn): {
25
30
  delivered: number;
26
31
  };
@@ -1,7 +1,7 @@
1
- var C=Object.defineProperty;var i=(t,e)=>C(t,"name",{value:e,configurable:!0});import{evalCondition as M}from"../predicate.js";import{insertEventMail as j,listenDeliveryTier as W}from"../feed/messages.js";import{ownerChain as k,readCheck as H}from"./access.js";import{publishLookups as J,readerForNode as $}from"./reader.js";import{configFromJson as g}from"./watches.js";import{DEFAULT_LISTEN as N}from"./types.js";const x={created:4,read:1,watched:1,edited:8,messaged:2,sent:1,configured:1,"report.update":5,"report.final":7,"report.result":5,exited:7,killed:7};function ne(t,e,o){if(e!==void 0)return e;if(t==="deleted")return o==="document"?8:5;if(t.startsWith("notice."))throw new Error(`${t} has no default level; pass the notice's tier as a level`);return x[t]}i(ne,"levelFor");function B(t,e){const o=t.split("."),r=e.split(".");for(let s=0;s<o.length;s++){if(o[s]===">")return s===o.length-1&&r.length>s;if(s>=r.length||o[s]!=="*"&&o[s]!==r[s])return!1}return o.length===r.length}i(B,"matchSubject");const b={"next-turn":1,"after-turn":2,steer:3,interrupt:4};function U(t,e){let o=null;for(const r of t){let s=null;for(const d of r)d.threshold<=e&&(!s||d.threshold>s.threshold)&&(s=d);s&&(!o||b[s.delivery]>b[o])&&(o=s.delivery)}return o}i(U,"resolveDelivery");function oe(t,e,o){if(e.config)return e.config;const r=t.prepare("SELECT listen_default_json FROM nodes WHERE node_id = ?").get(o.id);return r?.listen_default_json?g(r.listen_default_json):N}i(oe,"listenConfigFor");const V=new Set(["messaged","sent","configured","report.update","report.final","report.result","notice.crash","notice.deadline","notice.close"]);function G(t){return t.event==="report.final"?"final":t.event==="report.result"?"completed":t.level>=7?"urgent":"update"}i(G,"inboxKind");function K(t,e){return e.type==="node"?e.id:t.prepare(`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}i(K,"ownerHandleOf");function X(t,e,o){const r=e.object.owner===null?null:t.prepare("SELECT * FROM objects WHERE rowid = ?").get(e.object.owner),s=r?.id??null,d=r?K(t,r):null;return{event:e.event,level:e.level,object:{id:e.object.id,type:e.object.type,kind:e.object.kind,owner:s,owner_handle:d,name:e.object.name},actor:e.actor?.id??null,attrs:o,...e.pointer===void 0?{}:{pointer:e.pointer}}}i(X,"pushFields");function O(t,e,o){const r=X(t,e,o),s=e.messageBody!==void 0&&(e.event.startsWith("report.")||e.event.startsWith("notice.")),d=s?typeof e.messageBody=="object"&&e.messageBody!==null&&!Array.isArray(e.messageBody)?{...e.messageBody,push:r}:{body:e.messageBody,push:r}:{push:r},l=s&&e.event.startsWith("report.")?e.messageBody:null;return{kind:G(e),label:l?.label??`${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:d,event:e.event,objectId:e.object.id,level:e.level,actorId:e.actor?.id??null}}i(O,"mailInput");function q(t){const e=t.attrs.revision,o=t.attrs.from_revision??(typeof e=="number"?e-1:null),r=t.attrs.rationale;return{...t.attrs,from_revision:o,revision:e,authors:t.actor?[t.actor.id]:[],rationales:typeof r=="string"?[r]:[]}}i(q,"editAttrs");function z(t,e){const o=[...t.authors??[],...e.authors.filter(r=>!(t.authors??[]).includes(r))];return{...e,from_revision:t.from_revision??null,authors:o,rationales:[...t.rationales??[],...e.rationales]}}i(z,"mergeEdits");function re(t,e,o=()=>[]){const r=k(t,e.object),s=V.has(e.event),d="w.object = ? AND w.watcher != ?",l="w.watcher, o.id AS watcher_id, n.status, n.listen_default_json, s.pattern, s.filter_json, s.config_json",m=t.prepare(s?`SELECT ${l} FROM watches w JOIN watch_subscriptions s ON s.watch = w.rowid
3
- JOIN objects o ON o.rowid = w.watcher AND o.type = 'node' JOIN nodes n ON n.node_id = o.id WHERE ${d}`:`SELECT ${l} FROM nodes n CROSS JOIN objects o CROSS JOIN watches w CROSS JOIN watch_subscriptions s
4
- WHERE n.status IN ('active','idle') AND o.id = n.node_id AND w.watcher = o.rowid AND s.watch = w.rowid AND ${d}`).all(e.object.rowid,e.actor?.rowid??-1);if(m.length===0)return{delivered:0};const R={event:e.event,level:e.level,id:e.object.id,type:e.object.type,kind:e.object.kind,actor:e.actor?.id??null,...e.attrs},p=new Map;for(const n of m){if(!B(n.pattern,e.event)||n.filter_json!==null&&!M(JSON.parse(n.filter_json),R))continue;const c=n.config_json?g(n.config_json):n.listen_default_json?g(n.listen_default_json):N,a=p.get(n.watcher)??{id:n.watcher_id,status:n.status,configs:[]};a.configs.push(c),p.set(n.watcher,a)}const S=(r.length?r[r.length-1]:e.object).type==="repo",D=i(()=>[],"noRepos"),A=H(t,e.object),I=J(t),u=[];for(const n of p.values()){const c=U(n.configs,e.level);if(c===null||!A($(t,n.id,S?o:D,I)))continue;const a=W(c),f=n.status==="active"||n.status==="idle";u.push({nodeId:n.id,urgency:a.urgency,wake:f?a.wake:0})}if(u.length===0)return{delivered:0};if(e.event!=="edited")return j(t,O(t,e,e.attrs),u),{delivered:u.length};const E=q(e),T=t.prepare(`SELECT d.delivery_id, d.message_id, m.body_json FROM deliveries d
1
+ var F=Object.defineProperty;var i=(t,e)=>F(t,"name",{value:e,configurable:!0});import{evalCondition as $}from"../predicate.js";import{insertEventMail as b,listenDeliveryTier as M}from"../feed/messages.js";import{ownerChain as W,readCheck as k}from"./access.js";import{publishLookups as x,readerForNode as H}from"./reader.js";import{configFromJson as m}from"./watches.js";import{CrtrError as J}from"../errors.js";import{ExitCode as B}from"../../types.js";import{eventSpec as U}from"./kinds/registry.js";import{DEFAULT_LISTEN as N}from"./types.js";const G={created:4,read:1,watched:1,edited:8,messaged:2,sent:1,configured:1,"report.update":5,"report.final":7,"report.result":5};function ce(t,e,o){if(e!==void 0)return e;if(t==="deleted")return o==="document"?8:5;if(t.startsWith("notice."))throw new Error(`${t} has no default level; pass the notice's tier as a level`);const n=G[t];if(n===void 0)throw new Error(`${t} has no default level`);return n}i(ce,"levelFor");function V(t){const e=U(t.object.type,t.event);if(t.level===void 0||t.level===e.level||e.levels?.includes(t.level))return t.level??e.level;const o=[e.level,...e.levels??[]].filter((n,r,l)=>l.indexOf(n)===r).sort((n,r)=>n-r);throw new J("invalid_level",`${t.object.type} ${t.event} publishes at level ${o.join(", ")}, not ${t.level}`,B.USAGE,{kind:t.object.type,event:t.event,level:t.level,allowed:o})}i(V,"resolveLevel");function K(t,e){const o=t.split("."),n=e.split(".");for(let r=0;r<o.length;r++){if(o[r]===">")return r===o.length-1&&n.length>r;if(r>=n.length||o[r]!=="*"&&o[r]!==n[r])return!1}return o.length===n.length}i(K,"matchSubject");const v={"next-turn":1,"after-turn":2,steer:3,interrupt:4};function X(t,e){let o=null;for(const n of t){let r=null;for(const l of n)l.threshold<=e&&(!r||l.threshold>r.threshold)&&(r=l);r&&(!o||v[r.delivery]>v[o])&&(o=r.delivery)}return o}i(X,"resolveDelivery");function ue(t,e,o){if(e.config)return e.config;const n=t.prepare("SELECT listen_default_json FROM nodes WHERE node_id = ?").get(o.id);return n?.listen_default_json?m(n.listen_default_json):N}i(ue,"listenConfigFor");const q=new Set(["messaged","sent","configured","report.update","report.final","report.result","notice.crash","notice.deadline","notice.close"]);function z(t){return t.event==="report.final"?"final":t.event==="report.result"?"completed":t.level>=7?"urgent":"update"}i(z,"inboxKind");function P(t,e){return e.type==="node"?e.id:t.prepare(`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}i(P,"ownerHandleOf");function Q(t,e,o){const n=e.object.owner===null?null:t.prepare("SELECT * FROM objects WHERE rowid = ?").get(e.object.owner),r=n?.id??null,l=n?P(t,n):null;return{event:e.event,level:e.level,object:{id:e.object.id,type:e.object.type,kind:e.object.kind,owner:r,owner_handle:l,name:e.object.name},actor:e.actor?.id??null,attrs:o,...e.pointer===void 0?{}:{pointer:e.pointer}}}i(Q,"pushFields");function O(t,e,o){const n=Q(t,e,o),r=e.messageBody!==void 0&&(e.event.startsWith("report.")||e.event.startsWith("notice.")),l=r?typeof e.messageBody=="object"&&e.messageBody!==null&&!Array.isArray(e.messageBody)?{...e.messageBody,push:n}:{body:e.messageBody,push:n}:{push:n},a=r&&e.event.startsWith("report.")?e.messageBody:null;return{kind:z(e),label:a?.label??`${e.event} ${e.object.type} ${e.object.name}`,...a?.bodyRef===void 0?{}:{bodyRef:a.bodyRef},...a?.disposition===void 0?{}:{disposition:a.disposition},senderNodeId:e.actor?.type==="node"?e.actor.id:null,senderName:e.actor?.name??null,bodyJson:l,event:e.event,objectId:e.object.id,level:e.level,actorId:e.actor?.id??null}}i(O,"mailInput");function Y(t){const e=t.attrs.revision,o=t.attrs.from_revision??(typeof e=="number"?e-1:null),n=t.attrs.rationale;return{...t.attrs,from_revision:o,revision:e,authors:t.actor?[t.actor.id]:[],rationales:typeof n=="string"?[n]:[]}}i(Y,"editAttrs");function Z(t,e){const o=[...t.authors??[],...e.authors.filter(n=>!(t.authors??[]).includes(n))];return{...e,from_revision:t.from_revision??null,authors:o,rationales:[...t.rationales??[],...e.rationales]}}i(Z,"mergeEdits");function fe(t,e,o=()=>[]){const n={...e,level:V(e)},r=W(t,n.object),l=q.has(n.event),a="w.object = ? AND w.watcher != ?",g="w.watcher, o.id AS watcher_id, n.status, n.listen_default_json, s.pattern, s.filter_json, s.config_json",E=t.prepare(l?`SELECT ${g} FROM watches w JOIN watch_subscriptions s ON s.watch = w.rowid
3
+ JOIN objects o ON o.rowid = w.watcher AND o.type = 'node' JOIN nodes n ON n.node_id = o.id WHERE ${a}`:`SELECT ${g} FROM nodes n CROSS JOIN objects o CROSS JOIN watches w CROSS JOIN watch_subscriptions s
4
+ WHERE n.status IN ('active','idle') AND o.id = n.node_id AND w.watcher = o.rowid AND s.watch = w.rowid AND ${a}`).all(n.object.rowid,n.actor?.rowid??-1);if(E.length===0)return{delivered:0};const S={event:n.event,level:n.level,id:n.object.id,type:n.object.type,kind:n.object.kind,actor:n.actor?.id??null,...n.attrs},p=new Map;for(const s of E){if(!K(s.pattern,n.event)||s.filter_json!==null&&!$(JSON.parse(s.filter_json),S))continue;const c=s.config_json?m(s.config_json):s.listen_default_json?m(s.listen_default_json):N,d=p.get(s.watcher)??{id:s.watcher_id,status:s.status,configs:[]};d.configs.push(c),p.set(s.watcher,d)}const R=(r.length?r[r.length-1]:n.object).type==="repo",D=i(()=>[],"noRepos"),A=k(t,n.object),I=x(t),u=[];for(const s of p.values()){const c=X(s.configs,n.level);if(c===null||!A(H(t,s.id,R?o:D,I)))continue;const d=M(c),f=s.status==="active"||s.status==="idle";u.push({nodeId:s.id,urgency:d.urgency,wake:f?d.wake:0})}if(u.length===0)return{delivered:0};if(n.event!=="edited")return b(t,O(t,n,n.attrs),u),{delivered:u.length};const _=Y(n),L=t.prepare(`SELECT d.delivery_id, d.message_id, m.body_json FROM deliveries d
5
5
  JOIN messages m ON m.message_id = d.message_id
6
6
  WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? AND d.state = 'pending' AND d.canceled_at IS NULL
7
- AND m.event = 'edited' AND m.object_id = ?`),h=new Map,_=new Set,L=t.prepare("DELETE FROM deliveries WHERE delivery_id = ?");for(const n of u){const c=T.all(n.nodeId,e.object.id),a=c.at(-1);for(const w of c)L.run(w.delivery_id),_.add(w.message_id);const f=a?.message_id??null,y=h.get(f)??{prior:a?.body_json?JSON.parse(a.body_json).push?.attrs??null:null,destinations:[]};y.destinations.push(n),h.set(f,y)}for(const n of h.values())j(t,O(t,e,n.prior?z(n.prior,E):E),n.destinations);const F=t.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)");for(const n of _)F.run(n,n);return{delivered:u.length}}i(re,"publish");export{ne as levelFor,oe as listenConfigFor,B as matchSubject,re as publish,U as resolveDelivery};
7
+ AND m.event = 'edited' AND m.object_id = ?`),h=new Map,w=new Set,T=t.prepare("DELETE FROM deliveries WHERE delivery_id = ?");for(const s of u){const c=L.all(s.nodeId,n.object.id),d=c.at(-1);for(const j of c)T.run(j.delivery_id),w.add(j.message_id);const f=d?.message_id??null,y=h.get(f)??{prior:d?.body_json?JSON.parse(d.body_json).push?.attrs??null:null,destinations:[]};y.destinations.push(s),h.set(f,y)}for(const s of h.values())b(t,O(t,n,s.prior?Z(s.prior,_):_),s.destinations);const C=t.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)");for(const s of w)C.run(s,s);return{delivered:u.length}}i(fe,"publish");export{ce as levelFor,ue as listenConfigFor,K as matchSubject,fe as publish,X as resolveDelivery,V as resolveLevel};
@@ -1,2 +1,2 @@
1
- var w=Object.defineProperty;var n=(t,r)=>w(t,"name",{value:r,configurable:!0});import{recordActedOn as s}from"./edges.js";import{levelFor as u,publish as d}from"./events.js";import{createObject as E,getByRowid as h}from"./objects.js";import{recordWatch as b}from"./watches.js";function p(t){return t.split(`
2
- `,1)[0].trim()}n(p,"firstLine");function m(t,r){return`${p(t)} (${r})`}n(m,"previewOf");function j(t,r){return t.prepare("SELECT * FROM jobs WHERE object = ?").get(r.rowid)??null}n(j,"jobRow");function R(t,r,o){if(r.type!=="node")throw new Error(`startJob: ${r.id} is a ${r.type}, not a node`);const e=E(t,{...o.id===void 0?{}:{id:o.id},type:"job",kind:"bash",owner:r.rowid,privacy:"public",name:p(o.command)||"bash",preview:m(o.command,"running")});return t.prepare("INSERT INTO jobs (object, command, status, exit_code, job_dir, log_path) VALUES (?, ?, 'running', NULL, ?, ?)").run(e.rowid,o.command,o.jobDir,o.logPath),s(t,r,e),b(t,r,e,"job-start"),d(t,{event:"created",object:e,actor:r,level:u("created"),attrs:{}}),e}n(R,"startJob");function T(t,r,o){const e=j(t,r);if(e===null)throw new Error(`finishJob: ${r.id} has no jobs row`);if(e.status!=="running")return r;const i="killedBy"in o,a=i?"killed":"exited",l=i?null:o.exitCode;t.prepare("UPDATE jobs SET status = ?, exit_code = ? WHERE object = ?").run(a,l,r.rowid);const f=i?"killed":`exited ${l}`;t.prepare("UPDATE objects SET preview = ? WHERE rowid = ?").run(m(e.command,f),r.rowid);const c=h(t,r.rowid);return i&&o.killedBy!==null&&s(t,o.killedBy,c),d(t,{event:a,object:c,actor:i?o.killedBy:null,level:u(a),attrs:i?{"log-path":e.log_path,...o.reason===void 0?{}:{reason:o.reason}}:{"exit-code":l,"log-path":e.log_path}}),c}n(T,"finishJob");export{T as finishJob,j as jobRow,R as startJob};
1
+ var f=Object.defineProperty;var n=(t,r)=>f(t,"name",{value:r,configurable:!0});import{recordActedOn as s}from"./edges.js";import{publish as u}from"./events.js";import{createObject as w,getByRowid as E}from"./objects.js";import{recordWatch as h}from"./watches.js";function d(t){return t.split(`
2
+ `,1)[0].trim()}n(d,"firstLine");function p(t,r){return`${d(t)} (${r})`}n(p,"previewOf");function b(t,r){return t.prepare("SELECT * FROM jobs WHERE object = ?").get(r.rowid)??null}n(b,"jobRow");function R(t,r,o){if(r.type!=="node")throw new Error(`startJob: ${r.id} is a ${r.type}, not a node`);const e=w(t,{...o.id===void 0?{}:{id:o.id},type:"job",kind:"bash",owner:r.rowid,privacy:"public",name:d(o.command)||"bash",preview:p(o.command,"running")});return t.prepare("INSERT INTO jobs (object, command, status, exit_code, job_dir, log_path) VALUES (?, ?, 'running', NULL, ?, ?)").run(e.rowid,o.command,o.jobDir,o.logPath),s(t,r,e),h(t,r,e,"job-start"),u(t,{event:"created",object:e,actor:r,attrs:{}}),e}n(R,"startJob");function T(t,r,o){const e=b(t,r);if(e===null)throw new Error(`finishJob: ${r.id} has no jobs row`);if(e.status!=="running")return r;const i="killedBy"in o,c=i?"killed":"exited",a=i?null:o.exitCode;t.prepare("UPDATE jobs SET status = ?, exit_code = ? WHERE object = ?").run(c,a,r.rowid);const m=i?"killed":`exited ${a}`;t.prepare("UPDATE objects SET preview = ? WHERE rowid = ?").run(p(e.command,m),r.rowid);const l=E(t,r.rowid);return i&&o.killedBy!==null&&s(t,o.killedBy,l),u(t,{event:c,object:l,actor:i?o.killedBy:null,attrs:i?{"log-path":e.log_path,...o.reason===void 0?{}:{reason:o.reason}}:{"exit-code":a,"log-path":e.log_path}}),l}n(T,"finishJob");export{T as finishJob,b as jobRow,R as startJob};
@@ -0,0 +1,19 @@
1
+ import type { DocFieldsDTO, ObjectDTO } from '../../../api/dto/objects.js';
2
+ import { type ObjectKind } from './kind.js';
3
+ export interface DocumentReadData {
4
+ summary: string | null;
5
+ revision: number;
6
+ heads: Array<{
7
+ rev_id: string;
8
+ number: number;
9
+ author_id: string | null;
10
+ created_at: string;
11
+ }>;
12
+ /** One body per head, in `heads` order. */
13
+ bodies: string[];
14
+ needs_merge: boolean;
15
+ listing?: ObjectDTO[];
16
+ attachments?: string;
17
+ fields?: DocFieldsDTO;
18
+ }
19
+ export declare const documentKind: ObjectKind;
@@ -0,0 +1,5 @@
1
+ var y=Object.defineProperty;var m=(n,e)=>y(n,"name",{value:e,configurable:!0});import{recordMemoryReadsIn as v}from"../../canvas/memory-reads.js";import{deliveryDocOf as _,nodeSubject as x,ownerHandle as $}from"../../substrate/delivery/corpus.js";import{recordContentDelivery as R}from"../../substrate/delivery/deliver.js";import{renderExplicitRead as D}from"../../substrate/delivery/render-event.js";import{canRead as S}from"../access.js";import{documentRecord as j,readDocument as k}from"../documents.js";import{loadLedger as E,saveLedger as N}from"../exposures.js";import{getByRowid as O}from"../objects.js";import{rulesOf as f}from"../rules.js";import{objectDTO as d}from"./dto.js";import{withBase as M}from"./kind.js";import{attr as l,header as I,section as L}from"./render.js";function A(n,e,t){const i=`${t.name}/`;return n.prepare(`SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL AND owner IS ?
2
+ AND substr(name, 1, ?) = ? ORDER BY name`).all(t.owner,i.length,i).filter(s=>!s.name.slice(i.length).includes("/")&&S(n,e,s)).map(s=>d(n,s))}m(A,"listingOf");function b(n,e,t){const i=e.class==="node",o=i?E(n,e.node.rowid):{node:0,preferenceSnapshot:null,objects:new Map,listed:new Map},s={db:n,reader:e,subject:i?x(n,e):null,ledger:o,channel:"transcript"},a=D(s,_(n,e,t));return i&&(R(n,e,a.delivered),N(n,o)),{attachments:a.attachments,linked:a.linked.map(u=>u.object)}}m(b,"explicitReadAttachments");function B(n,e,t){if(e.class!=="node"||t.kind!=="knowledge"&&t.kind!=="preference"||t.owner===null)return;const i=O(n,t.owner);i!==null&&v(n,e.node.id,[{name:t.name,scope:$(i),at:new Date().toISOString(),type:"read"}])}m(B,"recordExplicitMemoryRead");function T(n,e){const t=[];if(e.attachments&&t.push(e.attachments,""),t.push(I("object",n,l("privacy",n.privacy)+l("revision",e.revision))),t.push(`Preview: ${n.preview}`),e.summary&&t.push(`Summary: ${e.summary}`),e.fields!==void 0&&t.push(`<fields>
3
+ ${JSON.stringify(e.fields,null,2)}
4
+ </fields>`),e.needs_merge?(e.heads.forEach((i,o)=>{t.push(`<head${l("rev",i.rev_id)}${l("number",i.number)}${l("author",i.author_id)}>`,e.bodies[o]??"","</head>")}),t.push(`This document has ${e.heads.length} heads. Your next \`crtr doc edit\` writes the merged body and joins them into one revision.`)):t.push("",e.bodies[0]??""),e.listing&&e.listing.length>0){const i=e.listing.some(a=>a.owner?.id===n.owner?.id&&a.name.startsWith(`${n.name}/`)),o=e.listing.some(a=>a.owner?.id!==n.owner?.id||!a.name.startsWith(`${n.name}/`)),s=i&&o?`Names under ${n.name}/ and documents it links to`:i?`Names under ${n.name}/`:"Documents it links to";t.push("",...L(s,e.listing))}return t.join(`
5
+ `)}m(T,"render");const V={name:"document",addressable:"name",events:M({edited:{level:8},deleted:{level:8}}),listItem(n,e){return{summary:j(n,e).summary??null,rules:f(n,e).length}},read(n,e,t,i){const o=k(n,e,t,i.reposFn);B(n,e,t);const s=o.record,a=A(n,e,t),{attachments:u,linked:h}=b(n,e,t),g=new Set(a.map(r=>r.id)),p=[...a,...h.filter(r=>!g.has(r.id)).map(r=>d(n,r))],c={summary:s.summary??null,revision:s.revision,heads:o.heads.map(r=>({rev_id:r.rev_id,number:r.number,author_id:r.author,created_at:r.created_at})),bodies:o.heads.map(r=>r.body),needs_merge:o.needsMerge,...p.length>0?{listing:p}:{},...u===""?{}:{attachments:u}};return i.fields&&(c.fields={why_it_exists:s.why_it_exists??null,unlisted:s.unlisted,lint_ignore:s.lint_ignore??[],extensions:s.extensions??{},rules:f(n,t).map(({matchFrontmatter:r,...w})=>({...w,...r===void 0?{}:{match_frontmatter:r}}))}),{data:c,text:T(d(n,t),c)}}};export{V as documentKind};
@@ -0,0 +1,5 @@
1
+ /** An object row as the `/v1/objects` wire shape. */
2
+ import type { DatabaseSync } from 'node:sqlite';
3
+ import type { ObjectDTO } from '../../../api/dto/objects.js';
4
+ import type { ObjectRow } from '../types.js';
5
+ export declare function objectDTO(db: DatabaseSync, o: ObjectRow): ObjectDTO;
@@ -0,0 +1 @@
1
+ var i=Object.defineProperty;var d=(n,e)=>i(n,"name",{value:e,configurable:!0});import{getByRowid as a}from"../objects.js";function c(n,e){const t=e.owner===null?null:a(n,e.owner),r=t?{id:t.id,type:t.type,handle:t.type==="node"?t.id:t.name}:null;return{id:e.id,type:e.type,kind:e.kind,owner:r,privacy:e.privacy,name:e.name,preview:e.preview??"",created_at:e.created_at,deleted_at:e.deleted_at}}d(c,"objectDTO");export{c as objectDTO};
@@ -0,0 +1,2 @@
1
+ import { type ObjectKind } from './kind.js';
2
+ export declare const jobKind: ObjectKind;
@@ -0,0 +1,2 @@
1
+ import{jobRow as m}from"../jobs.js";import{objectDTO as c}from"./dto.js";import{recordLiveRead as l,withBase as p}from"./kind.js";import{attr as a,header as h}from"./render.js";const f={name:"job",addressable:"id",events:p({exited:{level:7},killed:{level:7}}),read(r,s,e,d){const o=m(r,e);if(!o)throw new Error(`job object ${e.id} has no jobs row`);l(r,s,e,d.reposFn);const n=c(r,e),t={command:o.command,status:o.status,exit_code:o.exit_code,log_path:o.log_path},i=[h("object",n,a("status",t.status)+a("exit_code",t.exit_code)),`Command: ${t.command}`,`Log: ${t.log_path}`].join(`
2
+ `);return{data:t,text:i}}};export{f as jobKind};
@@ -0,0 +1,39 @@
1
+ /** The one interface every canvas object kind implements: its declared events, how it is
2
+ * addressed, and how it reads. The kind name is what `objects.type` stores. */
3
+ import type { DatabaseSync } from 'node:sqlite';
4
+ import type { ReposFn } from '../reader.js';
5
+ import type { Level, ObjectRow, Reader } from '../types.js';
6
+ /** One declared event: its default level and, only where a sender may choose, the levels it may pick. */
7
+ export interface EventSpec {
8
+ level: Level;
9
+ levels?: readonly Level[];
10
+ }
11
+ export type EventCatalog = Readonly<Record<string, EventSpec>>;
12
+ /** What a read answers: the kind's structured fields and exactly the text `crtr canvas read` prints. */
13
+ export interface KindRead {
14
+ data: Record<string, unknown>;
15
+ text: string;
16
+ }
17
+ export interface ReadOpts {
18
+ /** A document read also returns its optional fields. */
19
+ fields: boolean;
20
+ reposFn: ReposFn;
21
+ }
22
+ export interface ObjectKind {
23
+ /** Stored in `objects.type`. */
24
+ name: string;
25
+ /** The base events merged with the kind's own. */
26
+ events: EventCatalog;
27
+ /** `name`: found by `<owner>/<name>`; `id`: by id only; `handle`: an owner's handle. */
28
+ addressable: 'name' | 'id' | 'handle';
29
+ /** Records the read (a watch and a `read` event) and renders the object. */
30
+ read(db: DatabaseSync, reader: Reader, object: ObjectRow, opts: ReadOpts): KindRead;
31
+ /** Extra fields a list or search row carries. */
32
+ listItem?(db: DatabaseSync, object: ObjectRow): Record<string, unknown>;
33
+ }
34
+ /** Events every kind inherits. */
35
+ export declare const BASE_EVENTS: EventCatalog;
36
+ /** The base events with a kind's own merged over them. */
37
+ export declare function withBase(own?: EventCatalog): EventCatalog;
38
+ /** A live read of a non-document object: the reader watches it and its other watchers hear `read`. */
39
+ export declare function recordLiveRead(db: DatabaseSync, reader: Reader, object: ObjectRow, reposFn: ReposFn): void;
@@ -0,0 +1 @@
1
+ var l=Object.defineProperty;var d=(e,r)=>l(e,"name",{value:r,configurable:!0});import{actorOf as a}from"../documents.js";import{publish as c}from"../events.js";import{recordWatch as n}from"../watches.js";const f={created:{level:4},read:{level:1},watched:{level:1},deleted:{level:5},configured:{level:1}};function s(e={}){return{...f,...e}}d(s,"withBase");function h(e,r,t,i){const o=a(r);o.rowid!==t.rowid&&(n(e,o,t,"read"),c(e,{event:"read",object:t,actor:o,attrs:{}},i))}d(h,"recordLiveRead");export{f as BASE_EVENTS,h as recordLiveRead,s as withBase};
@@ -0,0 +1,2 @@
1
+ import { type ObjectKind } from './kind.js';
2
+ export declare const nodeKind: ObjectKind;
@@ -0,0 +1,4 @@
1
+ var R=Object.defineProperty;var a=(e,t)=>R(e,"name",{value:t,configurable:!0});import{readGoal as u}from"../../runtime/kickoff.js";import{canRead as w}from"../access.js";import{objectDTO as p}from"./dto.js";import{recordLiveRead as j,withBase as L}from"./kind.js";import{attr as O,header as _,section as d}from"./render.js";const n=[3,5,7];function h(e){return e?.split(`
2
+ `).map(t=>t.trim()).find(t=>t!=="")??""}a(h,"firstLine");const F={name:"node",addressable:"id",events:L({messaged:{level:2},sent:{level:1},"report.update":{level:5,levels:n},"report.final":{level:7},"report.result":{level:5},"notice.crash":{level:5,levels:n},"notice.deadline":{level:5,levels:n},"notice.close":{level:5,levels:n}}),read(e,t,o,m){const E=e.prepare("SELECT status FROM nodes WHERE node_id = ?").get(o.id),s=a(l=>l.filter(i=>w(e,t,i)).map(i=>p(e,i)),"visible"),v=e.prepare(`SELECT o.* FROM nodes n JOIN objects o ON o.id = n.node_id
3
+ WHERE n.parent = ? AND o.deleted_at IS NULL ORDER BY o.rowid`).all(o.id),c=e.prepare("SELECT * FROM objects WHERE owner = ? AND deleted_at IS NULL ORDER BY rowid").all(o.rowid);j(e,t,o,m.reposFn);const r={status:E?.status??"deleted",mandate_first_line:h(u(o.id)),children:s(v),owned:s(c.filter(l=>l.type!=="job")),jobs:s(c.filter(l=>l.type==="job"))},f=[_("object",p(e,o),O("status",r.status)),`Mandate: ${r.mandate_first_line}`,...d("Children",r.children),...d("Owns",r.owned),...d("Jobs",r.jobs)].join(`
4
+ `);return{data:r,text:f}}};export{F as nodeKind};
@@ -0,0 +1,2 @@
1
+ import { type ObjectKind } from './kind.js';
2
+ export declare const ownerKinds: ObjectKind[];
@@ -0,0 +1,2 @@
1
+ var m=Object.defineProperty;var d=(a,t)=>m(a,"name",{value:t,configurable:!0});import{objectDTO as c}from"./dto.js";import{recordLiveRead as l,withBase as h}from"./kind.js";import{header as y}from"./render.js";function n(a){return{name:a,addressable:"handle",events:h(),read(t,o,e,s){const r=t.prepare("SELECT scheme, external_id FROM identities WHERE object = ?").get(e.rowid);l(t,o,e,s.reposFn);const i={handle:e.name,display_name:e.preview||e.name,identity:{scheme:r?.scheme??"",external_id:r?.external_id??""}},p=[y("object",c(t,e)),`Handle: ${i.handle}`,`Display name: ${i.display_name}`,`Identity: ${i.identity.scheme}:${i.identity.external_id}`].join(`
2
+ `);return{data:i,text:p}}}}d(n,"ownerKind");const E=[n("person"),n("app"),n("repo"),n("profile")];export{E as ownerKinds};
@@ -0,0 +1,8 @@
1
+ import type { EventSpec, ObjectKind } from './kind.js';
2
+ /** The names of every declared kind. */
3
+ export declare function kindNames(): string[];
4
+ export declare function isKind(name: string): boolean;
5
+ /** The kind named `name`, or throws `unknown_kind`. */
6
+ export declare function kindOf(name: string): ObjectKind;
7
+ /** The declared spec of `event` on `kind`, or throws `unknown_event` naming the declared events. */
8
+ export declare function eventSpec(kind: string, event: string): EventSpec;
@@ -0,0 +1 @@
1
+ var s=Object.defineProperty;var t=(n,e)=>s(n,"name",{value:e,configurable:!0});import{CrtrError as d}from"../../errors.js";import{ExitCode as c}from"../../../types.js";import{documentKind as u}from"./document.js";import{jobKind as m}from"./job.js";import{nodeKind as p}from"./node.js";import{ownerKinds as a}from"./owner.js";let f=null;function r(){return f??=new Map([u,p,m,...a].map(n=>[n.name,n])),f}t(r,"kinds");function k(){return[...r().keys()]}t(k,"kindNames");function K(n){return r().has(n)}t(K,"isKind");function w(n){const e=r().get(n);if(e===void 0)throw new d("unknown_kind",`no object kind named ${JSON.stringify(n)}; the kinds are ${k().join(", ")}`,c.USAGE,{kind:n});return e}t(w,"kindOf");function S(n,e){const o=w(n).events,i=Object.hasOwn(o,e)?o[e]:void 0;if(i===void 0)throw new d("unknown_event",`${n} declares no event ${JSON.stringify(e)}; it declares ${Object.keys(o).join(", ")}`,c.USAGE,{kind:n,event:e,declared:Object.keys(o)});return i}t(S,"eventSpec");export{S as eventSpec,K as isKind,k as kindNames,w as kindOf};
@@ -0,0 +1,9 @@
1
+ /** Text helpers every kind's `read` renders with, and the CLI renders deleted answers with.
2
+ * Pure: imports only DTO types, so the CLI can load it without the canvas. */
3
+ import type { ObjectDTO } from '../../../api/dto/objects.js';
4
+ export declare function attr(name: string, value: string | number | null | undefined): string;
5
+ /** One line per object: id, name, type/kind, owner handle and preview. */
6
+ export declare function objectRow(o: ObjectDTO): string;
7
+ /** `<tag type kind id name owner …extra>` */
8
+ export declare function header(tag: string, o: ObjectDTO, extra?: string): string;
9
+ export declare function section(title: string, rows: ObjectDTO[]): string[];
@@ -0,0 +1 @@
1
+ var i=Object.defineProperty;var t=(e,n)=>i(e,"name",{value:n,configurable:!0});function r(e,n){return n==null||n===""?"":` ${e}="${String(n).replace(/"/g,"&quot;")}"`}t(r,"attr");function p(e){const n=e.kind?`${e.type}/${e.kind}`:e.type,d=e.owner?` owner=${e.owner.handle}`:"",$=e.deleted_at?" (deleted)":"";return`- ${e.id} [[${e.name}]] ${n}${d}${$} \u2014 ${e.preview}`}t(p,"objectRow");function u(e,n,d=""){return`<${e}${r("type",n.type)}${r("kind",n.kind)}${r("id",n.id)}${r("name",n.name)}${r("owner",n.owner?.handle)}${d}>`}t(u,"header");function a(e,n){return n.length===0?[]:[`${e}:`,...n.map(p)]}t(a,"section");export{r as attr,u as header,p as objectRow,a as section};
@@ -1,10 +1,11 @@
1
1
  import type { DatabaseSync } from 'node:sqlite';
2
- import type { ObjectRow, ObjectType, Privacy, Reader } from './types.js';
2
+ import type { ObjectRow, Privacy, Reader } from './types.js';
3
+ export type OwnerType = 'person' | 'app' | 'repo' | 'profile';
3
4
  export declare function getById(db: DatabaseSync, id: string): ObjectRow | null;
4
5
  export declare function getByRowid(db: DatabaseSync, rowid: number): ObjectRow | null;
5
6
  export declare function createObject(db: DatabaseSync, spec: {
6
7
  id?: string;
7
- type: ObjectType;
8
+ type: string;
8
9
  kind: string | null;
9
10
  owner: number | null;
10
11
  privacy: Privacy;
@@ -15,7 +16,7 @@ export declare function createObject(db: DatabaseSync, spec: {
15
16
  * (scheme, externalId). Handles are unique per owner type: a handle already held
16
17
  * by a different identity of the same type throws `conflict` naming both; it is
17
18
  * never renamed silently. */
18
- export declare function ensureOwner(db: DatabaseSync, type: ObjectType, scheme: string, externalId: string, handle: string, owner?: ObjectRow): ObjectRow;
19
+ export declare function ensureOwner(db: DatabaseSync, type: OwnerType, scheme: string, externalId: string, handle: string, owner?: ObjectRow): ObjectRow;
19
20
  /** The owner object of a node grantee: `user:local` → person `user`; `app:<x>` → app `<x>`. */
20
21
  export declare function ownerForGrantee(db: DatabaseSync, grantee: string): ObjectRow;
21
22
  /** The owner's live document named `name`, or null. */
@@ -24,7 +25,7 @@ export declare function nodeObject(db: DatabaseSync, nodeId: string): ObjectRow;
24
25
  export declare function setObjectName(db: DatabaseSync, object: ObjectRow, name: string): void;
25
26
  export declare function setObjectKind(db: DatabaseSync, object: ObjectRow, kind: string | null): void;
26
27
  export interface ObjectFilter {
27
- type?: ObjectType;
28
+ type?: string;
28
29
  kind?: string;
29
30
  owner?: ObjectRow;
30
31
  privacy?: Privacy;
@@ -1,9 +1,9 @@
1
- var S=Object.defineProperty;var s=(o,e)=>S(o,"name",{value:e,configurable:!0});import{CrtrError as L,notFound as j}from"../errors.js";import{ExitCode as b}from"../../types.js";import{newNodeId as T}from"../runtime/nodes.js";import{canRead as l}from"./access.js";import{bodyOf as x}from"./bodies.js";const y=new Set(["person","app","repo","profile"]);function h(o){return o||null}s(h,"rowFrom");function I(o,e){return h(o.prepare("SELECT * FROM objects WHERE id = ?").get(e))}s(I,"getById");function W(o,e){return h(o.prepare("SELECT * FROM objects WHERE rowid = ?").get(e))}s(W,"getByRowid");function _(o,e){return o.prepare(`INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at)
2
- VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(e.id??T(),e.type,e.kind,e.owner,e.privacy,e.name,e.preview,new Date().toISOString())}s(_,"createObject");function R(o,e,t,p,r,n){if(!y.has(e))throw new Error(`ensureOwner: ${e} is not an owner type`);const a=o.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
1
+ var S=Object.defineProperty;var s=(o,e)=>S(o,"name",{value:e,configurable:!0});import{CrtrError as L,notFound as j}from"../errors.js";import{ExitCode as b}from"../../types.js";import{newNodeId as T}from"../runtime/nodes.js";import{kindOf as x}from"./kinds/registry.js";import{canRead as l}from"./access.js";import{bodyOf as y}from"./bodies.js";const I=new Set(["person","app","repo","profile"]);function h(o){return o||null}s(h,"rowFrom");function _(o,e){return h(o.prepare("SELECT * FROM objects WHERE id = ?").get(e))}s(_,"getById");function M(o,e){return h(o.prepare("SELECT * FROM objects WHERE rowid = ?").get(e))}s(M,"getByRowid");function g(o,e){return x(e.type),o.prepare(`INSERT INTO objects (id, type, kind, owner, privacy, name, preview, created_at)
2
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(e.id??T(),e.type,e.kind,e.owner,e.privacy,e.name,e.preview,new Date().toISOString())}s(g,"createObject");function R(o,e,t,p,r,n){if(!I.has(e))throw new Error(`ensureOwner: ${e} is not an owner type`);const a=o.prepare(`SELECT o.* FROM identities i JOIN objects o ON o.rowid = i.object
3
3
  WHERE i.scheme = ? AND i.external_id = ?`).get(t,p);if(a)return a;const c=o.prepare(`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(r,e);if(c)throw new L("conflict",`handle "${r}" is held by ${c.type} ${c.id} (${c.scheme}:${c.external_id}); cannot give it to ${e} ${t}:${p}`,b.GENERAL,{handle:r,holder:c.id});const d=_(o,{type:e,kind:null,owner:e==="profile"?n?.rowid??null:null,privacy:"public",name:r,preview:""});return o.prepare("INSERT INTO identities (object, scheme, external_id) VALUES (?, ?, ?)").run(d.rowid,t,p),d}s(R,"ensureOwner");function H(o,e){if(e==="user:local")return R(o,"person","local-user",e,"user");if(/^app:[a-z0-9][a-z0-9._-]*$/.test(e))return R(o,"app","app",e,e.slice(4));throw new Error(`node grantee ${JSON.stringify(e)} is neither app:<name> nor user:local`)}s(H,"ownerForGrantee");function M(o,e,t){return h(o.prepare("SELECT * FROM objects WHERE type = 'document' AND owner = ? AND name = ? AND deleted_at IS NULL").get(e,t))}s(M,"liveDocumentNamed");function U(o,e){const t=I(o,e);if(!t||t.type!=="node")throw j(`no node object ${e}`);return t}s(U,"nodeObject");function k(o,e,t){o.prepare("UPDATE objects SET name = ? WHERE rowid = ?").run(t,e.rowid)}s(k,"setObjectName");function P(o,e,t){o.prepare("UPDATE objects SET kind = ? WHERE rowid = ?").run(t,e.rowid)}s(P,"setObjectKind");function B(o,e,t,p={}){const r=["rowid > ?"],n=[p.after_rowid??0];return t.type!==void 0&&(r.push("type = ?"),n.push(t.type)),t.kind!==void 0&&(r.push("kind = ?"),n.push(t.kind)),t.owner!==void 0&&(r.push("owner = ?"),n.push(t.owner.rowid)),t.privacy!==void 0&&(r.push("privacy = ?"),n.push(t.privacy)),t.name!==void 0&&(r.push("name = ?"),n.push(t.name)),r.push(t.deleted?"deleted_at IS NOT NULL":"deleted_at IS NULL"),g(o,e,`SELECT * FROM objects WHERE ${r.join(" AND ")}`,n,Math.min(p.limit??100,100))}s(B,"listObjects");function J(o,e,t){const r=[],n=`%${t.text.replace(/[\\%_]/g,i=>`\\${i}`)}%`;if(!t.grep){const i=o.prepare(`SELECT o.*, d.summary AS _summary FROM objects o LEFT JOIN documents d ON d.object = o.rowid
5
+ WHERE o.name = ? AND o.type = ?`).get(r,e);if(c)throw new L("conflict",`handle "${r}" is held by ${c.type} ${c.id} (${c.scheme}:${c.external_id}); cannot give it to ${e} ${t}:${p}`,b.GENERAL,{handle:r,holder:c.id});const d=g(o,{type:e,kind:null,owner:e==="profile"?n?.rowid??null:null,privacy:"public",name:r,preview:""});return o.prepare("INSERT INTO identities (object, scheme, external_id) VALUES (?, ?, ?)").run(d.rowid,t,p),d}s(R,"ensureOwner");function k(o,e){if(e==="user:local")return R(o,"person","local-user",e,"user");if(/^app:[a-z0-9][a-z0-9._-]*$/.test(e))return R(o,"app","app",e,e.slice(4));throw new Error(`node grantee ${JSON.stringify(e)} is neither app:<name> nor user:local`)}s(k,"ownerForGrantee");function U(o,e,t){return h(o.prepare("SELECT * FROM objects WHERE type = 'document' AND owner = ? AND name = ? AND deleted_at IS NULL").get(e,t))}s(U,"liveDocumentNamed");function P(o,e){const t=_(o,e);if(!t||t.type!=="node")throw j(`no node object ${e}`);return t}s(P,"nodeObject");function B(o,e,t){o.prepare("UPDATE objects SET name = ? WHERE rowid = ?").run(t,e.rowid)}s(B,"setObjectName");function J(o,e,t){o.prepare("UPDATE objects SET kind = ? WHERE rowid = ?").run(t,e.rowid)}s(J,"setObjectKind");function K(o,e,t,p={}){const r=["rowid > ?"],n=[p.after_rowid??0];return t.type!==void 0&&(r.push("type = ?"),n.push(t.type)),t.kind!==void 0&&(r.push("kind = ?"),n.push(t.kind)),t.owner!==void 0&&(r.push("owner = ?"),n.push(t.owner.rowid)),t.privacy!==void 0&&(r.push("privacy = ?"),n.push(t.privacy)),t.name!==void 0&&(r.push("name = ?"),n.push(t.name)),r.push(t.deleted?"deleted_at IS NOT NULL":"deleted_at IS NULL"),A(o,e,`SELECT * FROM objects WHERE ${r.join(" AND ")}`,n,Math.min(p.limit??100,100))}s(K,"listObjects");function Y(o,e,t){const r=[],n=`%${t.text.replace(/[\\%_]/g,i=>`\\${i}`)}%`;if(!t.grep){const i=o.prepare(`SELECT o.*, d.summary AS _summary FROM objects o LEFT JOIN documents d ON d.object = o.rowid
6
6
  WHERE o.deleted_at IS NULL AND (o.name LIKE ?1 ESCAPE '\\' OR o.preview LIKE ?1 ESCAPE '\\' OR d.summary LIKE ?1 ESCAPE '\\')
7
7
  ORDER BY o.rowid`).all(n),u=t.text.toLowerCase();for(const m of i){const{_summary:w,...E}=m,f=E;if(!l(o,e,f))continue;const O=f.name.toLowerCase().includes(u)?"name":f.preview.toLowerCase().includes(u)?"preview":"summary";if(r.push({object:f,field:O,...O==="summary"?{text:String(w)}:{}}),r.length>=100)return r}}if(!t.body&&!t.grep)return r;const a=new Set(r.map(i=>i.object.rowid)),c=t.grep?(i=>u=>i.test(u))(new RegExp(t.text)):i=>i.includes(t.text),d=o.prepare("SELECT * FROM objects WHERE type = 'document' AND deleted_at IS NULL ORDER BY rowid").all(),N=o.prepare(`SELECT r.body_hash, d.source_path FROM revisions r JOIN documents d ON d.object = r.document WHERE r.document = ?
8
- AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rowid)`);for(const i of d)if(!(a.has(i.rowid)||!l(o,e,i)))for(const{body_hash:u,source_path:m}of N.all(i.rowid)){const w=x(u,m).split(`
9
- `),E=w.findIndex(c);if(!(E<0)){if(r.push({object:i,field:"body",line:E+1,text:w[E]}),r.length>=100)return r;break}}return r}s(J,"searchObjects");function g(o,e,t,p,r){const n=[],a=o.prepare(`${t} ORDER BY rowid LIMIT ${r*4}`);for(;;){const c=a.all(...p);for(const d of c)if(l(o,e,d)&&n.push(d),n.length===r)return n;if(c.length<r*4)return n;p[0]=c[c.length-1].rowid}}s(g,"readablePages");export{_ as createObject,R as ensureOwner,I as getById,W as getByRowid,B as listObjects,M as liveDocumentNamed,U as nodeObject,H as ownerForGrantee,J as searchObjects,P as setObjectKind,k as setObjectName};
8
+ AND NOT EXISTS (SELECT 1 FROM revision_parents p WHERE p.parent = r.rowid)`);for(const i of d)if(!(a.has(i.rowid)||!l(o,e,i)))for(const{body_hash:u,source_path:m}of N.all(i.rowid)){const w=y(u,m).split(`
9
+ `),E=w.findIndex(c);if(!(E<0)){if(r.push({object:i,field:"body",line:E+1,text:w[E]}),r.length>=100)return r;break}}return r}s(Y,"searchObjects");function A(o,e,t,p,r){const n=[],a=o.prepare(`${t} ORDER BY rowid LIMIT ${r*4}`);for(;;){const c=a.all(...p);for(const d of c)if(l(o,e,d)&&n.push(d),n.length===r)return n;if(c.length<r*4)return n;p[0]=c[c.length-1].rowid}}s(A,"readablePages");export{g as createObject,R as ensureOwner,_ as getById,M as getByRowid,K as listObjects,U as liveDocumentNamed,P as nodeObject,k as ownerForGrantee,Y as searchObjects,J as setObjectKind,B as setObjectName};
@@ -1,10 +1,10 @@
1
1
  /** Shared types of the canvas graph: documents, nodes, jobs and their owners as rows of `objects`. */
2
- export type ObjectType = 'document' | 'node' | 'job' | 'person' | 'app' | 'repo' | 'profile';
3
2
  export type Privacy = 'public' | 'friends' | 'private';
4
3
  export interface ObjectRow {
5
4
  rowid: number;
6
5
  id: string;
7
- type: ObjectType;
6
+ /** The kind name (`node`, `document`, `job`, an owner kind); the kind registry validates it on create. */
7
+ type: string;
8
8
  kind: string | null;
9
9
  owner: number | null;
10
10
  privacy: Privacy;
@@ -13,7 +13,6 @@ export interface ObjectRow {
13
13
  created_at: string;
14
14
  deleted_at: string | null;
15
15
  }
16
- export type EventName = 'created' | 'read' | 'watched' | 'deleted' | 'edited' | 'messaged' | 'sent' | 'configured' | 'report.update' | 'report.final' | 'report.result' | 'notice.crash' | 'notice.deadline' | 'notice.close' | 'exited' | 'killed';
17
16
  export type Level = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
18
17
  export type ListenDelivery = 'interrupt' | 'steer' | 'after-turn' | 'next-turn';
19
18
  /** Stored as `{"10":"interrupt",...}`. */
@@ -24,10 +23,12 @@ export type ListenConfig = Array<{
24
23
  export declare const DEFAULT_LISTEN: ListenConfig;
25
24
  export type WatchStart = 'spawn' | 'job-start' | 'ask' | 'read' | 'write' | 'act' | 'message' | 'deliver' | 'fork';
26
25
  export interface CanvasEvent {
27
- event: EventName;
26
+ /** An event the object's kind declares; `publish` validates it against the registry. */
27
+ event: string;
28
28
  object: ObjectRow;
29
29
  actor: ObjectRow | null;
30
- level: Level;
30
+ /** Omitted: the declared level. Supplied: must equal it or be one of the event's declared `levels`. */
31
+ level?: Level;
31
32
  /** revision, rationale, exit-code, log-path, request id …; never content */
32
33
  attrs: Record<string, string | number | boolean | null>;
33
34
  /** the act's pointer (inbox entry id, report doc id, revision rev_id) */
@@ -0,0 +1,97 @@
1
+ import { RunError } from '../runs/operations.js';
2
+ export type ErrorType = 'invalid_request_error' | 'authentication_error' | 'permission_error' | 'rate_limit_error' | 'server_error';
3
+ export type ErrorOrigin = 'directory' | 'router' | 'runtime' | 'provider';
4
+ export interface CodeRow {
5
+ type: ErrorType;
6
+ status: number;
7
+ retryable: boolean;
8
+ user_action?: string;
9
+ }
10
+ /** The provider rows of the daemon-operations code table. `provider_error`'s
11
+ * status is the provider's own 4xx; 400 is its default (a provider `scope_missing`). */
12
+ export declare const PROVIDER_CODES: {
13
+ readonly provider_error: {
14
+ readonly type: "invalid_request_error";
15
+ readonly status: 400;
16
+ readonly retryable: false;
17
+ };
18
+ readonly provider_rate_limited: {
19
+ readonly type: "rate_limit_error";
20
+ readonly status: 429;
21
+ readonly retryable: true;
22
+ };
23
+ readonly provider_unavailable: {
24
+ readonly type: "server_error";
25
+ readonly status: 503;
26
+ readonly retryable: true;
27
+ };
28
+ readonly provider_refused: {
29
+ readonly type: "permission_error";
30
+ readonly status: 403;
31
+ readonly retryable: false;
32
+ };
33
+ readonly provider_not_connected: {
34
+ readonly type: "permission_error";
35
+ readonly status: 403;
36
+ readonly retryable: false;
37
+ };
38
+ readonly provider_version_retired: {
39
+ readonly type: "permission_error";
40
+ readonly status: 403;
41
+ readonly retryable: false;
42
+ };
43
+ readonly storage_unavailable: {
44
+ readonly type: "server_error";
45
+ readonly status: 503;
46
+ readonly retryable: true;
47
+ };
48
+ readonly approval_pending: {
49
+ readonly type: "permission_error";
50
+ readonly status: 403;
51
+ readonly retryable: false;
52
+ readonly user_action: "answer_question";
53
+ };
54
+ readonly user_denied: {
55
+ readonly type: "permission_error";
56
+ readonly status: 403;
57
+ readonly retryable: false;
58
+ };
59
+ };
60
+ export type ProviderCode = keyof typeof PROVIDER_CODES;
61
+ /** The non-provider codes a provider call also returns (the rest of the table
62
+ * rows it reaches). Anything absent falls back by status. */
63
+ declare const OTHER_CODES: Record<string, CodeRow>;
64
+ export declare function codeRow(code: string, status: number): CodeRow;
65
+ export interface ProviderErrorOptions {
66
+ /** Overrides the table status (`provider_error` carries the provider's 4xx). */
67
+ status?: number;
68
+ param?: string;
69
+ details?: unknown;
70
+ scope?: string;
71
+ origin?: ErrorOrigin;
72
+ retry_after_s?: number;
73
+ question_id?: string;
74
+ run_id?: string;
75
+ }
76
+ export declare class ProviderError extends RunError {
77
+ readonly origin: ErrorOrigin;
78
+ readonly type: ErrorType;
79
+ readonly retry_after_s?: number;
80
+ readonly question_id?: string;
81
+ readonly user_action?: string;
82
+ readonly run_id?: string;
83
+ constructor(code: ProviderCode | keyof typeof OTHER_CODES, message: string, options?: ProviderErrorOptions);
84
+ }
85
+ /** `scope_missing` naming the `<provider>:<group>` string. */
86
+ export declare function scopeMissing(scope: string): ProviderError;
87
+ /** The contract-2 error envelope for any value a provider operation throws.
88
+ * A value that is not a `RunError` is a 500 `server_error` (its cause is logged by the caller).
89
+ * `directoryCaller`: the caller holds a directory token (app listener). */
90
+ export declare function providerErrorBody(thrown: unknown, requestId: string, directoryCaller: boolean, runId?: string): {
91
+ status: number;
92
+ body: {
93
+ error: Record<string, unknown>;
94
+ };
95
+ headers: Record<string, string>;
96
+ };
97
+ export {};
@@ -0,0 +1 @@
1
+ var c=Object.defineProperty;var a=(e,t)=>c(e,"name",{value:t,configurable:!0});import{RunError as o}from"../runs/operations.js";const m={provider_error:{type:"invalid_request_error",status:400,retryable:!1},provider_rate_limited:{type:"rate_limit_error",status:429,retryable:!0},provider_unavailable:{type:"server_error",status:503,retryable:!0},provider_refused:{type:"permission_error",status:403,retryable:!1},provider_not_connected:{type:"permission_error",status:403,retryable:!1},provider_version_retired:{type:"permission_error",status:403,retryable:!1},storage_unavailable:{type:"server_error",status:503,retryable:!0},approval_pending:{type:"permission_error",status:403,retryable:!1,user_action:"answer_question"},user_denied:{type:"permission_error",status:403,retryable:!1}},v={unauthorized:{type:"authentication_error",status:401,retryable:!1},grant_revoked:{type:"authentication_error",status:401,retryable:!1},scope_missing:{type:"permission_error",status:403,retryable:!1},permission_error:{type:"permission_error",status:403,retryable:!1},invalid_request:{type:"invalid_request_error",status:400,retryable:!1},not_found:{type:"invalid_request_error",status:404,retryable:!1},not_implemented:{type:"server_error",status:501,retryable:!1},runtime_starting:{type:"server_error",status:503,retryable:!0},server_error:{type:"server_error",status:500,retryable:!0}};function u(e,t){return m[e]??v[e]??{type:t===401?"authentication_error":t===403?"permission_error":t===429?"rate_limit_error":t>=500?"server_error":"invalid_request_error",status:t,retryable:t>=500}}a(u,"codeRow");class _ extends o{static{a(this,"ProviderError")}origin;type;retry_after_s;question_id;user_action;run_id;constructor(t,n,s={}){const r=u(t,s.status??500);super(s.status??r.status,t,n,s.param,s.details,s.scope,r.retryable),this.type=r.type,this.origin=s.origin??"runtime",s.retry_after_s!==void 0&&(this.retry_after_s=s.retry_after_s),s.question_id!==void 0&&(this.question_id=s.question_id),s.run_id!==void 0&&(this.run_id=s.run_id),r.user_action!==void 0&&(this.user_action=r.user_action)}}function E(e){return new _("scope_missing",`missing scope ${e}`,{scope:e})}a(E,"scopeMissing");function b(e){if(!(e instanceof Error))return null;const{status:t,code:n,details:s}=e;return typeof t!="number"||typeof n!="string"||t<400||t>599?null:new o(t,n,e.message,void 0,s,void 0,u(n,t).retryable)}a(b,"shaped");function g(e){if(e.code==="unauthorized"||e.code==="grant_revoked")return"reconnect";if(e.code==="scope_missing"&&e.scope)return`grant_scope:${e.scope}`}a(g,"directoryAction");function R(e,t,n,s){const r=e instanceof o?e:b(e)??new _("server_error","provider request failed"),i=r instanceof _?r:null,p=u(r.code,r.status),d=i?.run_id??s,l=i?.user_action??(n?g(r):void 0),f={code:r.code,message:r.message,...r.details===void 0?{}:{details:r.details},type:i?.type??p.type,...r.param?{param:r.param}:{},origin:i?.origin??"runtime",retryable:r.retryable,...i?.retry_after_s===void 0?{}:{retry_after_s:i.retry_after_s},request_id:t,...d?{run_id:d}:{},...i?.question_id?{question_id:i.question_id}:{},...l?{user_action:l}:{},...r.scope?{scope:r.scope}:{}},y={"Request-Id":t};return i?.retry_after_s!==void 0&&(y["Retry-After"]=String(i.retry_after_s)),{status:r.status,body:{error:f},headers:y}}a(R,"providerErrorBody");export{m as PROVIDER_CODES,_ as ProviderError,u as codeRow,R as providerErrorBody,E as scopeMissing};
@@ -1,6 +1,7 @@
1
1
  import type { AgentSession, AgentSessionEvent, RpcExtensionUIRequest, RpcExtensionUIResponse, RpcSessionState, SessionEntry, SessionInfo, SettingsConfig, SessionStats } from '@earendil-works/pi-coding-agent';
2
2
  import type { Api, ImageContent, Model } from '@earendil-works/pi-ai';
3
3
  import type { BrokerErrorFrame, BrokerNodeNamedFrame } from '../../api/dto/broker.js';
4
+ import type { DeliveryRefDTO } from '../../api/dto/delivery.js';
4
5
  import type { ToolGroupSummary } from '../../shared/tool-groups.js';
5
6
  import type { ModelIntent } from '../../types.js';
6
7
  import type { MessageOrigin, MessageVisibility } from './stamp/protocol.js';
@@ -524,13 +525,7 @@ export interface WireSettings extends SettingsConfig {
524
525
  * resolution is by `name` alone). No body/source path: this is a metadata-only
525
526
  * inventory, never the document content. `gatewayVisible` is broker-computed
526
527
  * chat-surface eligibility; terminal viewers retain every row. */
527
- export interface RefMeta {
528
- name: string;
529
- kind: 'knowledge' | 'preference';
530
- scope: 'node' | 'project' | 'profile' | 'user' | 'builtin';
531
- shortForm: string;
532
- gatewayVisible: boolean;
533
- }
528
+ export type RefMeta = DeliveryRefDTO;
534
529
  export interface ListModelsData {
535
530
  type: 'data';
536
531
  id: string;
@@ -1,21 +1,21 @@
1
- var k=Object.defineProperty;var i=(e,r)=>k(e,"name",{value:r,configurable:!0});import{randomUUID as C}from"node:crypto";import{createServer as x}from"node:http";import{Router as H}from"./router.js";import{OPERATIONS as I}from"./operations.js";import{AppPrincipalResolver as J}from"./principal.js";import{handleRunOperation as $,integer as z}from"./handlers/runs.js";import{parseLlmRequest as D,runLlm as U}from"../../core/llm/run.js";import{RunError as u}from"../../core/runs/operations.js";import{requireRun as T}from"./router.js";import{readRunEvents as A,registerGrantStream as X,subscribeRunEvents as j}from"../../core/runs/events.js";import{activeGrant as B}from"../../core/grants/mirror.js";import{emitEvent as G}from"../../core/events/emit.js";import{handleVendorSignIn as L,isVendorSignInPath as F}from"./vendor-signin.js";import{listAppProfiles as M}from"./handlers/app-profiles.js";import{handleTransferDownload as Q,handleTransferOperation as V,TRANSFER_OPERATIONS as W}from"./handlers/transfer.js";import{handleQuestionOperation as Y,QUESTION_OPERATIONS as K}from"./handlers/questions.js";import{vendorStatus as Z}from"./handlers/vendor-status.js";const ee={other_app_run:"permission_error",unauthorized:"authentication_error",grant_revoked:"authentication_error",scope_missing:"permission_error",owner_only:"permission_error",invalid_request:"invalid_request_error",not_found:"invalid_request_error",model_unavailable:"invalid_request_error",substitution_refused:"invalid_request_error",payer_not_app:"invalid_request_error",all_routes_exhausted:"rate_limit_error",idempotency_conflict:"invalid_request_error",run_settled:"invalid_request_error",stream_gap:"invalid_request_error",stream_dropped:"server_error",runtime_starting:"server_error",storage_unavailable:"server_error",server_error:"server_error"};function R(e,r,a){return{error:{code:e.code,message:e.message,...e.details===void 0?{}:{details:e.details},type:ee[e.code]??"api_error",...e.param?{param:e.param}:{},origin:"runtime",retryable:e.retryable||e.code==="stream_dropped",request_id:r,...a?{run_id:a}:{},...e.scope?{scope:e.scope}:{},...e.code==="unauthorized"?{user_action:"reconnect"}:{},...e.code==="all_routes_exhausted"?{retry_after_s:e.details.retry_after_s}:{}}}}i(R,"envelope");function te(e,r,a,s){const d=r instanceof u?r:new u(500,"server_error","runtime request failed");if(r instanceof u||console.error("[app-listener]",r),e.headersSent&&!String(e.getHeader("Content-Type")??"").startsWith("text/event-stream")){e.destroy();return}if(e.headersSent){e.end(`event: error
2
- data: ${JSON.stringify(R(d,a,s))}
1
+ var C=Object.defineProperty;var s=(e,r)=>C(e,"name",{value:r,configurable:!0});import{randomUUID as k}from"node:crypto";import{createServer as I}from"node:http";import{Router as x}from"./router.js";import{OPERATIONS as H}from"./operations.js";import{AppPrincipalResolver as J}from"./principal.js";import{handleRunOperation as $,integer as D}from"./handlers/runs.js";import{parseLlmRequest as X,runLlm as j}from"../../core/llm/run.js";import{RunError as u}from"../../core/runs/operations.js";import{requireRun as A}from"./router.js";import{readRunEvents as P,registerGrantStream as z,subscribeRunEvents as U}from"../../core/runs/events.js";import{activeGrant as B}from"../../core/grants/mirror.js";import{emitEvent as F}from"../../core/events/emit.js";import{handleVendorSignIn as G,isVendorSignInPath as L}from"./vendor-signin.js";import{listAppProfiles as V}from"./handlers/app-profiles.js";import{handleTransferDownload as M,handleTransferOperation as Q,TRANSFER_OPERATIONS as W}from"./handlers/transfer.js";import{handleQuestionOperation as Y,QUESTION_OPERATIONS as K}from"./handlers/questions.js";import{vendorStatus as Z}from"./handlers/vendor-status.js";import{abortOnClose as ee,callerFromApp as te,handleProviderOperation as re,PROVIDER_OPERATIONS as ne,providerFailure as ae}from"./handlers/providers.js";const oe={other_app_run:"permission_error",unauthorized:"authentication_error",grant_revoked:"authentication_error",scope_missing:"permission_error",owner_only:"permission_error",invalid_request:"invalid_request_error",not_found:"invalid_request_error",model_unavailable:"invalid_request_error",substitution_refused:"invalid_request_error",payer_not_app:"invalid_request_error",all_routes_exhausted:"rate_limit_error",idempotency_conflict:"invalid_request_error",run_settled:"invalid_request_error",stream_gap:"invalid_request_error",stream_dropped:"server_error",runtime_starting:"server_error",storage_unavailable:"server_error",server_error:"server_error"};function N(e,r,a){return{error:{code:e.code,message:e.message,...e.details===void 0?{}:{details:e.details},type:oe[e.code]??"api_error",...e.param?{param:e.param}:{},origin:"runtime",retryable:e.retryable||e.code==="stream_dropped",request_id:r,...a?{run_id:a}:{},...e.scope?{scope:e.scope}:{},...e.code==="unauthorized"?{user_action:"reconnect"}:{},...e.code==="all_routes_exhausted"?{retry_after_s:e.details.retry_after_s}:{}}}}s(N,"envelope");function ie(e,r,a,i){const l=r instanceof u?r:new u(500,"server_error","runtime request failed");if(r instanceof u||console.error("[app-listener]",r),e.headersSent&&!String(e.getHeader("Content-Type")??"").startsWith("text/event-stream")){e.destroy();return}if(e.headersSent){e.end(`event: error
2
+ data: ${JSON.stringify(N(l,a,i))}
3
3
 
4
- `);return}e.writeHead(d.status,{"Content-Type":"application/json; charset=utf-8","Request-Id":a,"X-Content-Type-Options":"nosniff",...d.code==="all_routes_exhausted"?{"Retry-After":String(d.details.retry_after_s)}:{}}),e.end(JSON.stringify(R(d,a,s)))}i(te,"fail");function y(e,r,a){e.writeHead(r,{"Content-Type":"application/json; charset=utf-8"}),e.end(JSON.stringify(a))}i(y,"json");async function N(e){const r=[];let a=0;for await(const s of e){if(a+=s.length,a>4*1024*1024)throw new u(400,"invalid_request","request body too large");r.push(s)}if(a)try{return JSON.parse(Buffer.concat(r).toString("utf8"))}catch{throw new u(400,"invalid_request","invalid JSON body")}}i(N,"bodyOf");function re(e,r){const a=e.headers["last-event-id"]??r.get("after");if(Array.isArray(a))throw new u(400,"invalid_request","invalid cursor","after");return z(a??null,"after",0,Number.MAX_SAFE_INTEGER,0)}i(re,"cursor");function ne(e){return`id: ${e.sequence_number}
4
+ `);return}e.writeHead(l.status,{"Content-Type":"application/json; charset=utf-8","Request-Id":a,"X-Content-Type-Options":"nosniff",...l.code==="all_routes_exhausted"?{"Retry-After":String(l.details.retry_after_s)}:{}}),e.end(JSON.stringify(N(l,a,i)))}s(ie,"fail");function g(e,r,a){e.writeHead(r,{"Content-Type":"application/json; charset=utf-8"}),e.end(JSON.stringify(a))}s(g,"json");async function T(e){const r=[];let a=0;for await(const i of e){if(a+=i.length,a>4*1024*1024)throw new u(400,"invalid_request","request body too large");r.push(i)}if(a)try{return JSON.parse(Buffer.concat(r).toString("utf8"))}catch{throw new u(400,"invalid_request","invalid JSON body")}}s(T,"bodyOf");function se(e,r){const a=e.headers["last-event-id"]??r.get("after");if(Array.isArray(a))throw new u(400,"invalid_request","invalid cursor","after");return D(a??null,"after",0,Number.MAX_SAFE_INTEGER,0)}s(se,"cursor");function ue(e){return`id: ${e.sequence_number}
5
5
  event: ${e.type}
6
6
  data: ${JSON.stringify(e)}
7
7
 
8
- `}i(ne,"frame");function ae(e,r,a,s,d,b,m){T(s,a);let p=A(a,d);if(d>p.latest_sequence)throw new u(400,"invalid_request","cursor is beyond the run","after");if(d<p.latest_sequence&&(p.earliest_sequence===null||d<p.earliest_sequence-1))throw new u(409,"stream_gap","events have expired",void 0,{earliest_sequence:p.earliest_sequence,latest_sequence:p.latest_sequence});if(r.destroyed||e.destroyed)return r.destroy(),Promise.resolve();r.writeHead(200,{"Content-Type":m==="sse"?"text/event-stream":"application/x-ndjson","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),m==="sse"?r.write(`: connected
8
+ `}s(ue,"frame");function de(e,r,a,i,l,R,m){A(i,a);let p=P(a,l);if(l>p.latest_sequence)throw new u(400,"invalid_request","cursor is beyond the run","after");if(l<p.latest_sequence&&(p.earliest_sequence===null||l<p.earliest_sequence-1))throw new u(409,"stream_gap","events have expired",void 0,{earliest_sequence:p.earliest_sequence,latest_sequence:p.latest_sequence});if(r.destroyed||e.destroyed)return r.destroy(),Promise.resolve();r.writeHead(200,{"Content-Type":m==="sse"?"text/event-stream":"application/x-ndjson","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),m==="sse"?r.write(`: connected
9
9
 
10
- `):r.flushHeaders();let n=d,t=!1,f=!1,_=!1,l;const v=new Promise(h=>{l=h}),o=i(()=>{t||(t=!0,clearInterval(P),q(),E(),r.off("close",o),r.off("drain",w),r.end(),l())},"end"),w=i(()=>{_=!1,c()},"onDrain"),g=i(h=>{t||r.write(m==="sse"?`event: error
11
- data: ${JSON.stringify(R(h,b,a))}
10
+ `):r.flushHeaders();let n=l,t=!1,c=!1,_=!1,f;const v=new Promise(w=>{f=w}),o=s(()=>{t||(t=!0,clearInterval(q),S(),E(),r.off("close",o),r.off("drain",h),r.end(),f())},"end"),h=s(()=>{_=!1,d()},"onDrain"),O=s(w=>{t||r.write(m==="sse"?`event: error
11
+ data: ${JSON.stringify(N(w,R,a))}
12
12
 
13
- `:`${JSON.stringify({type:"error",...R(h,b,a)})}
14
- `),o()},"error"),c=i(()=>{if(!(t||f)){f=!0;try{if(!O()){S();return}if(T(s,a),p=A(a,n),p.backlog_count>1e3||p.backlog_bytes>32*1024*1024){g(new u(503,"stream_dropped","stream reader fell behind",void 0,{last_sequence:n}));return}for(const h of p.events){if(_)break;_=!r.write(m==="sse"?ne(h):`${JSON.stringify(h)}
15
- `),n=h.sequence_number}}catch(h){g(h instanceof u?h:new u(500,"server_error","stream failed"))}finally{f=!1}}},"flush"),S=i(()=>g(new u(401,"grant_revoked","grant is not active")),"revoked"),O=i(()=>"class"in s||B(s.grant)!==null,"grantActive"),q=j(a,c),E="class"in s?()=>{}:X(s.grant,S),P=setInterval(()=>{!t&&!_&&m==="sse"&&r.write(`: keepalive
13
+ `:`${JSON.stringify({type:"error",...N(w,R,a)})}
14
+ `),o()},"error"),d=s(()=>{if(!(t||c)){c=!0;try{if(!y()){b();return}if(A(i,a),p=P(a,n),p.backlog_count>1e3||p.backlog_bytes>32*1024*1024){O(new u(503,"stream_dropped","stream reader fell behind",void 0,{last_sequence:n}));return}for(const w of p.events){if(_)break;_=!r.write(m==="sse"?ue(w):`${JSON.stringify(w)}
15
+ `),n=w.sequence_number}}catch(w){O(w instanceof u?w:new u(500,"server_error","stream failed"))}finally{c=!1}}},"flush"),b=s(()=>O(new u(401,"grant_revoked","grant is not active")),"revoked"),y=s(()=>"class"in i||B(i.grant)!==null,"grantActive"),S=U(a,d),E="class"in i?()=>{}:z(i.grant,b),q=setInterval(()=>{!t&&!_&&m==="sse"&&r.write(`: keepalive
16
16
 
17
- `)},15e3);return r.on("drain",w),r.on("close",o),c(),v}i(ae,"streamRunEvents");function Oe(e,r,a){if(!e.issuer||!e.audience||!e.listener?.address||!Number.isInteger(e.listener.port)||e.listener.port<1||e.listener.port>65535)throw new Error("runtime issuer, audience and listener address/port are required");if(["0.0.0.0","::","[::]"].includes(e.listener.address))throw new Error("runtime listener must bind a specific address");const s=new J(e.issuer,e.audience,a,e.jwksUrl),d=new H,b=new Map;for(const n of I.filter(t=>t.stage!==void 0)){const t=i(()=>({status:500}),"handler");d.register({method:n.method,pattern:n.path,handler:t}),b.set(t,n)}const m=x((n,t)=>{(async()=>{const f=C();t.setHeader("Request-Id",f);let _;try{const l=new URL(n.url??"/","http://runtime.local");if(F(l.pathname)){await L(n,t,l,s);return}if((n.method!=="GET"||l.pathname!=="/v1/health")&&(!s.isReady()||!r()))throw new u(503,"runtime_starting","runtime_starting");const v=d.match(n.method??"GET",l.pathname);if(!v)throw new u(404,"not_found","unknown operation");const o=b.get(v.handler);if(!o)throw new u(404,"not_found","unknown operation");if(_=v.params.run_id,o.name==="health"){y(t,200,{status:"ok",api_version:"2026-09-23"});return}if(o.name==="runtime.sync-notice"){if(!await s.verifySyncNotice(n.headers.authorization)){t.writeHead(204),t.end();return}G({level:"info",event:"daemon.grants.sync_notice",fields:{request_id:f}});try{await a({fresh:!0})}catch{throw new u(503,"server_error","grant sync failed")}t.writeHead(204),t.end();return}if(o.name==="runtime.vendor-status"){await s.verifyDirectoryRead(n.headers.authorization,"vendor_status"),y(t,200,Z());return}const w=await s.resolve(n.headers.authorization,o.scope);if(t.setHeader("Request-Id",f),_&&T(w,_,["run.message","run.interrupt","run.cancel","run.rename","run.delete"].includes(o.name),o.name==="run.delete"),o.stream==="bytes"){await Q(n,t,w,l.searchParams);return}if(o.stream){ae(n,t,_,w,re(n,l.searchParams),f,"sse");return}if(o.name==="llm"){const c=D(await N(n)),S=new AbortController,O=i(()=>S.abort(),"abort");t.on("close",O);try{const q=await U(c,S.signal,c.stream?E=>{t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.write(`data: ${JSON.stringify(E)}
17
+ `)},15e3);return r.on("drain",h),r.on("close",o),d(),v}s(de,"streamRunEvents");function Pe(e,r,a){if(!e.issuer||!e.audience||!e.listener?.address||!Number.isInteger(e.listener.port)||e.listener.port<1||e.listener.port>65535)throw new Error("runtime issuer, audience and listener address/port are required");if(["0.0.0.0","::","[::]"].includes(e.listener.address))throw new Error("runtime listener must bind a specific address");const i=new J(e.issuer,e.audience,a,e.jwksUrl),l=new x,R=new Map;for(const n of H.filter(t=>t.stage!==void 0)){const t=s(()=>({status:500}),"handler");l.register({method:n.method,pattern:n.path,handler:t}),R.set(t,n)}const m=I((n,t)=>{(async()=>{const c=k();t.setHeader("Request-Id",c);let _;try{const f=new URL(n.url??"/","http://runtime.local");if(L(f.pathname)){await G(n,t,f,i);return}if((n.method!=="GET"||f.pathname!=="/v1/health")&&(!i.isReady()||!r()))throw new u(503,"runtime_starting","runtime_starting");const v=l.match(n.method??"GET",f.pathname);if(!v)throw new u(404,"not_found","unknown operation");const o=R.get(v.handler);if(!o)throw new u(404,"not_found","unknown operation");if(_=v.params.run_id,o.name==="health"){g(t,200,{status:"ok",api_version:"2026-09-23"});return}if(o.name==="runtime.sync-notice"){if(!await i.verifySyncNotice(n.headers.authorization)){t.writeHead(204),t.end();return}F({level:"info",event:"daemon.grants.sync_notice",fields:{request_id:c}});try{await a({fresh:!0})}catch{throw new u(503,"server_error","grant sync failed")}t.writeHead(204),t.end();return}if(o.name==="runtime.vendor-status"){await i.verifyDirectoryRead(n.headers.authorization,"vendor_status"),g(t,200,Z());return}const h=await i.resolve(n.headers.authorization,o.scope);if(t.setHeader("Request-Id",c),_&&A(h,_,["run.message","run.interrupt","run.cancel","run.rename","run.delete"].includes(o.name),o.name==="run.delete"),o.stream==="bytes"){await M(n,t,h,f.searchParams);return}if(o.stream){de(n,t,_,h,se(n,f.searchParams),c,"sse");return}if(o.name==="llm"){const d=X(await T(n)),b=new AbortController,y=s(()=>b.abort(),"abort");t.on("close",y);try{const S=await j(d,b.signal,d.stream?E=>{t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.write(`data: ${JSON.stringify(E)}
18
18
 
19
- `)}:void 0);c.stream?(t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.end(`data: [DONE]
19
+ `)}:void 0);d.stream?(t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.end(`data: [DONE]
20
20
 
21
- `)):y(t,200,q.completion)}finally{t.off("close",O)}return}if(o.name==="profile.list"){y(t,200,M(w));return}if(K.has(o.name)){const c=await Y(o.name,w,v.params.question_id,o.method==="POST"?await N(n):void 0);y(t,c.status,c.body);return}if(W.has(o.name)){const c=await V(o,w,v.params,l.searchParams,await N(n));y(t,c.status,c.body);return}const g=await $(o,w,_,l.searchParams,await N(n),n.headers["idempotency-key"]);y(t,g.status,g.body)}catch(l){te(t,l,f,_)}})()});return{ready:new Promise((n,t)=>{m.once("error",t),m.listen(e.listener.port,e.listener.address,()=>{m.off("error",t),n()})}),close:i(async()=>{s.close(),m.closeAllConnections(),await new Promise((n,t)=>m.close(f=>f?t(f):n()))},"close")}}i(Oe,"createAppListener");export{Oe as createAppListener,re as cursor,ae as streamRunEvents};
21
+ `)):g(t,200,S.completion)}finally{t.off("close",y)}return}if(o.name==="profile.list"){g(t,200,V(h));return}if(K.has(o.name)){const d=await Y(o.name,h,v.params.question_id,o.method==="POST"?await T(n):void 0);g(t,d.status,d.body);return}if(ne.has(o.name)){const{signal:d,release:b}=ee(t);try{const y=await re(o.name,te(h),{params:v.params,body:o.method==="POST"?await T(n):void 0},{requestId:c,signal:d});g(t,y.status,y.body)}catch(y){const S=ae(y,c,!0);t.writeHead(S.status,{"Content-Type":"application/json; charset=utf-8","X-Content-Type-Options":"nosniff",...S.headers}),t.end(JSON.stringify(S.body))}finally{b()}return}if(W.has(o.name)){const d=await Q(o,h,v.params,f.searchParams,await T(n));g(t,d.status,d.body);return}const O=await $(o,h,_,f.searchParams,await T(n),n.headers["idempotency-key"]);g(t,O.status,O.body)}catch(f){ie(t,f,c,_)}})()});return{ready:new Promise((n,t)=>{m.once("error",t),m.listen(e.listener.port,e.listener.address,()=>{m.off("error",t),n()})}),close:s(async()=>{i.close(),m.closeAllConnections(),await new Promise((n,t)=>m.close(c=>c?t(c):n()))},"close")}}s(Pe,"createAppListener");export{Pe as createAppListener,se as cursor,de as streamRunEvents};