@north-light/cli 0.3.577 → 0.3.579

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 (69) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/api/dto/run-events.d.ts +10 -4
  4. package/dist/build-root.js +1 -1
  5. package/dist/builtin-memory/crouter-sdk/release-notes.md +8 -0
  6. package/dist/builtin-memory/crouter-sdk/streaming.md +2 -0
  7. package/dist/builtin-memory/northlight-apps/concepts/runs.md +2 -0
  8. package/dist/builtin-memory/northlight-apps/guides/stream-a-run.md +1 -1
  9. package/dist/builtin-memory/northlight-apps/guides/test-your-app.md +1 -1
  10. package/dist/clients/attach/viewer.js +338 -338
  11. package/dist/core/command.js +5 -5
  12. package/dist/core/human/feedback-companion.js +1 -1
  13. package/dist/core/human/page.d.ts +2 -4
  14. package/dist/core/human/page.js +6 -6
  15. package/dist/core/human/request-files.d.ts +14 -0
  16. package/dist/core/human/request-files.js +1 -0
  17. package/dist/core/human/requests.d.ts +3 -0
  18. package/dist/core/human/requests.js +18 -17
  19. package/dist/core/human/tickets.js +1 -1
  20. package/dist/core/human/visible-paths.d.ts +7 -0
  21. package/dist/core/human/visible-paths.js +1 -1
  22. package/dist/core/llm/refusal.d.ts +2 -1
  23. package/dist/core/llm/refusal.js +1 -1
  24. package/dist/core/review/document.d.ts +4 -4
  25. package/dist/core/review/document.js +2 -2
  26. package/dist/core/review/project.d.ts +7 -5
  27. package/dist/core/review/project.js +2 -1
  28. package/dist/core/review/remap.js +1 -1
  29. package/dist/core/review/stage.d.ts +2 -0
  30. package/dist/core/review/stage.js +1 -1
  31. package/dist/core/review/store.js +1 -1
  32. package/dist/core/runs/operations.d.ts +1 -1
  33. package/dist/core/runs/operations.js +5 -5
  34. package/dist/core/runtime/broker/held-deferred-inbox.d.ts +4 -0
  35. package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
  36. package/dist/core/runtime/broker/rebind.d.ts +3 -0
  37. package/dist/core/runtime/broker/rebind.js +1 -1
  38. package/dist/core/runtime/broker/run-steer.d.ts +15 -0
  39. package/dist/core/runtime/broker/run-steer.js +1 -0
  40. package/dist/core/runtime/broker/run-turn-queue.d.ts +2 -0
  41. package/dist/core/runtime/broker/run-turn-queue.js +1 -1
  42. package/dist/core/runtime/broker.js +1 -1
  43. package/dist/core/runtime/runner/blaxel/client.d.ts +2 -2
  44. package/dist/core/runtime/runner/blaxel/client.js +1 -1
  45. package/dist/core/runtime/runner/blaxel/runner.js +2 -2
  46. package/dist/daemon/api/bridge.d.ts +1 -1
  47. package/dist/daemon/api/bridge.js +5 -5
  48. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  49. package/dist/daemon/api/handlers/inbox-page.js +1 -1
  50. package/dist/daemon/api/handlers/inbox.js +1 -1
  51. package/dist/daemon/api/handlers/reviews.js +1 -1
  52. package/dist/daemon/api/handlers/runs.js +1 -1
  53. package/dist/daemon/profile-delete.js +1 -1
  54. package/dist/daemon/review/finish.js +1 -1
  55. package/dist/daemon/review/sweep.js +1 -1
  56. package/dist/daemon/runs/append-section.js +2 -2
  57. package/dist/daemon/runs/observer.js +3 -3
  58. package/dist/pi-extensions/canvas-bash-valve.js +6 -6
  59. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
  60. package/dist/shared/env.d.ts +5 -0
  61. package/dist/shared/env.js +2 -2
  62. package/docs/apps/concepts/runs.md +2 -0
  63. package/docs/apps/guides/stream-a-run.md +1 -1
  64. package/docs/apps/guides/test-your-app.md +1 -1
  65. package/docs/sdk/release-notes.md +8 -0
  66. package/docs/sdk/streaming.md +2 -0
  67. package/package.json +2 -2
  68. package/packages/crouter-identity/package.json +1 -1
  69. package/runtime.lock.json +162 -665
@@ -13,7 +13,7 @@ export declare class BlaxelApiError extends Error {
13
13
  readonly code: string | null;
14
14
  constructor(status: number, code: string | null, message: string);
15
15
  }
16
- /** Blaxel's sandbox API uses this 404 when the workload URL itself no longer exists. */
16
+ /** The sandbox gateway answers 404 `WORKLOAD_UNAVAILABLE` for a sandbox URL whose workload no longer exists (deleted, or never existed). */
17
17
  export declare const isWorkloadUnavailable: (error: unknown) => error is BlaxelApiError;
18
18
  export interface SandboxSpec {
19
19
  name: string;
@@ -63,7 +63,7 @@ export interface BlaxelClient {
63
63
  createSandbox(spec: SandboxSpec): Promise<SandboxInfo & {
64
64
  created: boolean;
65
65
  }>;
66
- /** Null when the sandbox does not exist. */
66
+ /** Null when the sandbox does not exist or is being deleted. */
67
67
  getSandbox(name: string): Promise<SandboxInfo | null>;
68
68
  /** Deleting an absent sandbox is not an error. */
69
69
  deleteSandbox(name: string): Promise<void>;
@@ -1 +1 @@
1
- var E=Object.defineProperty;var d=(s,o)=>E(s,"name",{value:o,configurable:!0});function h(s=process.env){const o=d(i=>{const f=s[i];if(!f)throw new Error(`${i} is required for the Blaxel runner`);return f},"need");return{apiKey:o("BLAXEL_API_KEY"),workspace:o("BLAXEL_WORKSPACE"),region:o("BLAXEL_REGION")}}d(h,"blaxelConfigFromEnv");class w extends Error{static{d(this,"BlaxelApiError")}status;code;constructor(o,i,f){super(f),this.status=o,this.code=i,this.name="BlaxelApiError"}}const b=d(s=>s instanceof w&&s.status===404&&s.code==="WORKLOAD_UNAVAILABLE","isWorkloadUnavailable");function $(s){const o=s.metadata.url;if(!o)throw new Error(`Blaxel sandbox ${s.metadata.name} has no url`);return{name:s.metadata.name,url:o,state:s.state??null,status:s.status??null}}d($,"toInfo");function A(s){const o=s.fetch??fetch,i=s.controlUrl??"https://api.blaxel.ai/v0",f=d(()=>({authorization:`Bearer ${s.apiKey}`,"x-blaxel-workspace":s.workspace,"content-type":"application/json"}),"headers");async function c(e,t,a,r=[]){const n=await o(e,{method:t,headers:f(),...a===void 0?{}:{body:JSON.stringify(a)}}),l=await n.text();let x=null;if(l!=="")try{x=JSON.parse(l)}catch{x={raw:l}}if(!n.ok&&!r.includes(n.status)){const p=x??{},m=typeof p.message=="string"?p.message:typeof p.error=="string"?p.error:l.slice(0,300);throw new w(n.status,typeof p.code=="string"?p.code:null,`Blaxel ${t} ${e} failed (${n.status}): ${m}`)}return{status:n.status,json:x}}d(c,"call");const u=encodeURIComponent;return{config:s,async createSandbox(e){const t=[...e.ports??[]].map(l=>({name:l.name,target:l.target,protocol:"HTTP"})),a={metadata:{name:e.name},spec:{runtime:{image:e.image,memory:e.memoryMb,...t.length>0?{ports:t}:{}},region:s.region}},{status:r,json:n}=await c(`${i}/sandboxes`,"POST",a,[409]);if(r===409){const l=await this.getSandbox(e.name);if(l===null)throw new Error(`Blaxel sandbox ${e.name} reported as existing but cannot be read`);return{...l,created:!1}}return{...$(n),created:!0}},async getSandbox(e){const{status:t,json:a}=await c(`${i}/sandboxes/${u(e)}`,"GET",void 0,[404]);return t===404?null:$(a)},async deleteSandbox(e){await c(`${i}/sandboxes/${u(e)}`,"DELETE",void 0,[404])},async startProcess(e,t){const{json:a}=await c(`${e.url}/process`,"POST",t);return a},async getProcess(e,t){const{status:a,json:r}=await c(`${e.url}/process/${u(t)}`,"GET",void 0,[404]);if(a===404){const n=r?.code;if(n==="WORKLOAD_UNAVAILABLE")throw new w(404,n,`Blaxel sandbox ${e.name} is unavailable`);return null}return r},async stopProcess(e,t){await c(`${e.url}/process/${u(t)}`,"DELETE",void 0,[404])},async killProcess(e,t){await c(`${e.url}/process/${u(t)}/kill`,"DELETE",void 0,[404])},async exec(e,t,a,r,n){return await this.startProcess(e,{name:t,command:a,waitForCompletion:!0,timeout:r,...n?{env:n}:{}})},async putFile(e,t,a,r="0644"){await c(`${e.url}/filesystem/${t.replace(/^\/+/,"").split("/").map(u).join("/")}`,"PUT",{content:a,permissions:r})},async ensurePreview(e,t,a){const{json:r}=await c(`${i}/sandboxes/${u(e)}/previews`,"POST",{metadata:{name:t},spec:{port:a,public:!1}},[409]),n=r?.spec?.url;if(n)return n;const x=(await c(`${i}/sandboxes/${u(e)}/previews`,"GET")).json.find(p=>p.metadata.name===t);if(!x)throw new Error(`Blaxel preview ${t} of ${e} not found`);return x.spec.url},async createPreviewToken(e,t,a){const{json:r}=await c(`${i}/sandboxes/${u(e)}/previews/${u(t)}/tokens`,"POST",{spec:{expiresAt:a.toISOString()}}),n=r.spec;return{token:n.token,expiresAt:n.expiresAt}}}}d(A,"createBlaxelClient");export{w as BlaxelApiError,h as blaxelConfigFromEnv,A as createBlaxelClient,b as isWorkloadUnavailable};
1
+ var $=Object.defineProperty;var p=(s,c)=>$(s,"name",{value:c,configurable:!0});function A(s=process.env){const c=p(n=>{const d=s[n];if(!d)throw new Error(`${n} is required for the Blaxel runner`);return d},"need");return{apiKey:c("BLAXEL_API_KEY"),workspace:c("BLAXEL_WORKSPACE"),region:c("BLAXEL_REGION")}}p(A,"blaxelConfigFromEnv");class w extends Error{static{p(this,"BlaxelApiError")}status;code;constructor(c,n,d){super(d),this.status=c,this.code=n,this.name="BlaxelApiError"}}const g=p(s=>s instanceof w&&s.status===404&&s.code==="WORKLOAD_UNAVAILABLE","isWorkloadUnavailable");function E(s){const c=s.metadata.url;if(!c)throw new Error(`Blaxel sandbox ${s.metadata.name} has no url`);return{name:s.metadata.name,url:c,state:s.state??null,status:s.status??null}}p(E,"toInfo");function y(s,c){const n=s??{},d=typeof n.error=="object"&&n.error!==null?n.error:{},i=typeof d.code=="string"?d.code:typeof n.code=="string"?n.code:null,l=[d.message,n.message,n.error].find(e=>typeof e=="string")??c.slice(0,300);return{code:i,message:l}}p(y,"errorFields");function T(s){const c=s.fetch??fetch,n=s.controlUrl??"https://api.blaxel.ai/v0",d=p(()=>({authorization:`Bearer ${s.apiKey}`,"x-blaxel-workspace":s.workspace,"content-type":"application/json"}),"headers");async function i(e,t,a,r=[]){const o=await c(e,{method:t,headers:d(),...a===void 0?{}:{body:JSON.stringify(a)}}),u=await o.text();let f=null;if(u!=="")try{f=JSON.parse(u)}catch{f={raw:u}}if(!o.ok){const{code:x,message:m}=y(f,u);if(o.status===404&&x==="WORKLOAD_UNAVAILABLE"||!r.includes(o.status))throw new w(o.status,x,`Blaxel ${t} ${e} failed (${o.status}): ${m}`)}return{status:o.status,json:f}}p(i,"call");const l=encodeURIComponent;return{config:s,async createSandbox(e){const t=[...e.ports??[]].map(u=>({name:u.name,target:u.target,protocol:"HTTP"})),a={metadata:{name:e.name},spec:{runtime:{image:e.image,memory:e.memoryMb,...t.length>0?{ports:t}:{}},region:s.region}},{status:r,json:o}=await i(`${n}/sandboxes`,"POST",a,[409]);if(r===409){const u=await this.getSandbox(e.name);if(u===null)throw new Error(`Blaxel sandbox ${e.name} reported as existing but cannot be read`);return{...u,created:!1}}return{...E(o),created:!0}},async getSandbox(e){const{status:t,json:a}=await i(`${n}/sandboxes/${l(e)}`,"GET",void 0,[404]);if(t===404)return null;const r=a;return r.status==="DELETING"||r.status==="TERMINATED"?null:E(r)},async deleteSandbox(e){await i(`${n}/sandboxes/${l(e)}`,"DELETE",void 0,[404])},async startProcess(e,t){const{json:a}=await i(`${e.url}/process`,"POST",t);return a},async getProcess(e,t){const{status:a,json:r}=await i(`${e.url}/process/${l(t)}`,"GET",void 0,[404]);return a===404?null:r},async stopProcess(e,t){await i(`${e.url}/process/${l(t)}`,"DELETE",void 0,[404])},async killProcess(e,t){await i(`${e.url}/process/${l(t)}/kill`,"DELETE",void 0,[404])},async exec(e,t,a,r,o){return await this.startProcess(e,{name:t,command:a,waitForCompletion:!0,timeout:r,...o?{env:o}:{}})},async putFile(e,t,a,r="0644"){await i(`${e.url}/filesystem/${t.replace(/^\/+/,"").split("/").map(l).join("/")}`,"PUT",{content:a,permissions:r})},async ensurePreview(e,t,a){const{json:r}=await i(`${n}/sandboxes/${l(e)}/previews`,"POST",{metadata:{name:t},spec:{port:a,public:!1}},[409]),o=r?.spec?.url;if(o)return o;const f=(await i(`${n}/sandboxes/${l(e)}/previews`,"GET")).json.find(x=>x.metadata.name===t);if(!f)throw new Error(`Blaxel preview ${t} of ${e} not found`);return f.spec.url},async createPreviewToken(e,t,a){const{json:r}=await i(`${n}/sandboxes/${l(e)}/previews/${l(t)}/tokens`,"POST",{spec:{expiresAt:a.toISOString()}}),o=r.spec;return{token:o.token,expiresAt:o.expiresAt}}}}p(T,"createBlaxelClient");export{w as BlaxelApiError,A as blaxelConfigFromEnv,T as createBlaxelClient,g as isWorkloadUnavailable};
@@ -1,3 +1,3 @@
1
- var g=Object.defineProperty;var o=(t,a)=>g(t,"name",{value:a,configurable:!0});import{withCanvasWrite as R,openDb as I}from"../../../canvas/db.js";import{mintNodeToken as k}from"../../../canvas/node-tokens.js";import{BlaxelApiError as T,isWorkloadUnavailable as b}from"./client.js";const x=4180,l="view",S="node /opt/crtr/current/scripts/sandbox-start.mjs --wake",C=3,N=/^crtr-sandbox-start: failed (\S+)/m,m=o(t=>`broker-${t}`,"brokerProcessName"),f=o(t=>t.sandboxName??`${t.nodeId}-${t.attempt}`,"sandboxNameFor"),y=o(async t=>await R(a=>k(a,t.executionId,t.nodeId,new Date().toISOString())),"defaultMint");async function P(t){return(await(await I()).prepare("SELECT DISTINCT sandbox_name FROM executions WHERE node_id = ? AND sandbox_name IS NOT NULL").all(t)).map(u=>u.sandbox_name)}o(P,"defaultSandboxNames");function $(t){const a=`${t.logs}
1
+ var g=Object.defineProperty;var o=(t,a)=>g(t,"name",{value:a,configurable:!0});import{withCanvasWrite as R,openDb as I}from"../../../canvas/db.js";import{mintNodeToken as k}from"../../../canvas/node-tokens.js";import{BlaxelApiError as T,isWorkloadUnavailable as x}from"./client.js";const f=4180,l="view",S="node /opt/crtr/current/scripts/sandbox-start.mjs --wake",C=3,N=/^crtr-sandbox-start: failed (\S+)/m,m=o(t=>`broker-${t}`,"brokerProcessName"),E=o(t=>t.sandboxName??`${t.nodeId}-${t.attempt}`,"sandboxNameFor"),y=o(async t=>await R(a=>k(a,t.executionId,t.nodeId,new Date().toISOString())),"defaultMint");async function P(t){return(await(await I()).prepare("SELECT DISTINCT sandbox_name FROM executions WHERE node_id = ? AND sandbox_name IS NOT NULL").all(t)).map(u=>u.sandbox_name)}o(P,"defaultSandboxNames");function $(t){const a=`${t.logs}
2
2
  ${t.stderr}
3
- ${t.stdout}`;return{state:"failed",reason:N.exec(a)?.[1]??"start_failed",code:t.exitCode}}o($,"failureOf");function v(t){const{client:a}=t,u=(t.stopGraceS??10)*1e3,E=new Map;async function d(n){return await a.getSandbox(f(n))}return o(d,"sandboxOf"),{kind:"blaxel",async start(n){const r=await t.launchContext(n),e=f(n),s=await a.createSandbox({name:e,image:r.image,memoryMb:r.memoryMb,ports:[{name:l,target:x}]}),i=m(n.executionId);if((await a.getProcess(s,i))?.status==="running")return{pid:null,exited:null};await a.ensurePreview(e,l,x);const w=await(t.mintToken??y)(n),p={...r.secrets,CRTR_API_URL:r.apiUrl,CRTR_EXECUTION_ID:n.executionId,CRTR_NODE_TOKEN:w,CRTR_RELEASE:n.release,CRTR_RELEASE_URL:r.releaseUrl,CRTR_RELEASE_SHA256:r.releaseSha256};return await a.startProcess(s,{name:i,command:t.bootstrap(),env:p,waitForCompletion:!1}),{pid:null,exited:null}},async wake(n){const r=await d(n);if(r===null)return"no_broker";try{const e=await a.exec(r,`wake-${n.executionId}-${Date.now()}`,`${S} ${n.executionId}`,30);if(e.exitCode===0)return"signalled";if(e.exitCode===C)return"no_broker";throw new Error(`wake of ${n.executionId} failed (exit ${e.exitCode}): ${(e.stderr||e.logs).slice(0,300)}`)}catch(e){if(b(e))return"no_broker";throw e}},async stop(n,r){try{const e=await d(n);if(e===null)return{confirmed:!0};const s=m(n.executionId),i=o(async()=>(await a.getProcess(e,s))?.status==="running","running");if(!await i())return{confirmed:!0};await a.stopProcess(e,s);const c=Date.now()+u;for(;Date.now()<c;){if(!await i())return{confirmed:!0};await new Promise(w=>setTimeout(w,250))}return await a.killProcess(e,s),{confirmed:!await i()}}catch(e){return{confirmed:!1,error:e instanceof Error?e:new Error(String(e))}}},async status(n){try{const r=await d(n);if(r===null)return{state:"gone"};const e=await a.getProcess(r,m(n.executionId));return e===null?{state:"gone"}:e.status==="running"?{state:"running",pid:null}:e.status==="failed"||e.status==="completed"&&e.exitCode!==0?$(e):{state:"gone"}}catch(r){return b(r)?{state:"gone"}:{state:"unknown",error:r instanceof Error?r.message:String(r)}}},async delete(n){for(const r of await(t.sandboxNamesForNode??P)(n))await a.deleteSandbox(r)},async viewerAddress(n){const r=f(n),e=E.get(r);if(e&&e.expiresAt-Date.now()>5*6e4)return _(e.url,e.token);const s=await a.ensurePreview(r,l,x),i=new Date(Date.now()+60*6e4),{token:c}=await a.createPreviewToken(r,l,i);return E.set(r,{url:s,token:c,expiresAt:i.getTime()}),_(s,c)},async sandboxState(n){return(await d(n))?.state??null}}}o(v,"createBlaxelRunner");function _(t,a){return{kind:"wss",url:`${t.replace(/^https:/,"wss:")}/view`,headers:{"x-blaxel-preview-token":a}}}o(_,"viewer");export{x as BROKER_VIEW_PORT,T as BlaxelApiError,C as EXIT_NO_BROKER,N as FAILURE_LINE,S as WAKE_COMMAND,m as brokerProcessName,v as createBlaxelRunner,f as sandboxNameFor};
3
+ ${t.stdout}`;return{state:"failed",reason:N.exec(a)?.[1]??"start_failed",code:t.exitCode}}o($,"failureOf");function v(t){const{client:a}=t,u=(t.stopGraceS??10)*1e3,b=new Map;async function d(n){return await a.getSandbox(E(n))}return o(d,"sandboxOf"),{kind:"blaxel",async start(n){const r=await t.launchContext(n),e=E(n),s=await a.createSandbox({name:e,image:r.image,memoryMb:r.memoryMb,ports:[{name:l,target:f}]}),i=m(n.executionId);if((await a.getProcess(s,i))?.status==="running")return{pid:null,exited:null};await a.ensurePreview(e,l,f);const w=await(t.mintToken??y)(n),p={...r.secrets,CRTR_API_URL:r.apiUrl,CRTR_EXECUTION_ID:n.executionId,CRTR_NODE_TOKEN:w,CRTR_RELEASE:n.release,CRTR_RELEASE_URL:r.releaseUrl,CRTR_RELEASE_SHA256:r.releaseSha256};return await a.startProcess(s,{name:i,command:t.bootstrap(),env:p,waitForCompletion:!1}),{pid:null,exited:null}},async wake(n){const r=await d(n);if(r===null)return"no_broker";try{const e=await a.exec(r,`wake-${n.executionId}-${Date.now()}`,`${S} ${n.executionId}`,30);if(e.exitCode===0)return"signalled";if(e.exitCode===C)return"no_broker";throw new Error(`wake of ${n.executionId} failed (exit ${e.exitCode}): ${(e.stderr||e.logs).slice(0,300)}`)}catch(e){if(x(e))return"no_broker";throw e}},async stop(n,r){try{const e=await d(n);if(e===null)return{confirmed:!0};const s=m(n.executionId),i=o(async()=>(await a.getProcess(e,s))?.status==="running","running");if(!await i())return{confirmed:!0};await a.stopProcess(e,s);const c=Date.now()+u;for(;Date.now()<c;){if(!await i())return{confirmed:!0};await new Promise(w=>setTimeout(w,250))}return await a.killProcess(e,s),{confirmed:!await i()}}catch(e){return x(e)?{confirmed:!0}:{confirmed:!1,error:e instanceof Error?e:new Error(String(e))}}},async status(n){try{const r=await d(n);if(r===null)return{state:"gone"};const e=await a.getProcess(r,m(n.executionId));return e===null?{state:"gone"}:e.status==="running"?{state:"running",pid:null}:e.status==="failed"||e.status==="completed"&&e.exitCode!==0?$(e):{state:"gone"}}catch(r){return x(r)?{state:"gone"}:{state:"unknown",error:r instanceof Error?r.message:String(r)}}},async delete(n){for(const r of await(t.sandboxNamesForNode??P)(n))await a.deleteSandbox(r)},async viewerAddress(n){const r=E(n),e=b.get(r);if(e&&e.expiresAt-Date.now()>5*6e4)return _(e.url,e.token);const s=await a.ensurePreview(r,l,f),i=new Date(Date.now()+60*6e4),{token:c}=await a.createPreviewToken(r,l,i);return b.set(r,{url:s,token:c,expiresAt:i.getTime()}),_(s,c)},async sandboxState(n){return(await d(n))?.state??null}}}o(v,"createBlaxelRunner");function _(t,a){return{kind:"wss",url:`${t.replace(/^https:/,"wss:")}/view`,headers:{"x-blaxel-preview-token":a}}}o(_,"viewer");export{f as BROKER_VIEW_PORT,T as BlaxelApiError,C as EXIT_NO_BROKER,N as FAILURE_LINE,S as WAKE_COMMAND,m as brokerProcessName,v as createBlaxelRunner,E as sandboxNameFor};
@@ -73,4 +73,4 @@ export declare function isCleanViewerClose(code: number): boolean;
73
73
  * 3. Revive opted out: a single connect probe. Dormant (socket refuses) →
74
74
  * refuse 409 (spec §5.3 asymmetry: a dead broker can't stream over WS).
75
75
  * 4. Only then upgrade and hand the live WS to {@link bridgeConnection}. */
76
- export declare function handleAttachUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer, nodeId: string, stillMayAttach: (role: 'observer' | 'controller') => Promise<boolean>): Promise<void>;
76
+ export declare function handleAttachUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer, nodeId: string, authorizedRole: (role: 'observer' | 'controller') => Promise<boolean>): Promise<void>;
@@ -1,8 +1,8 @@
1
- var q=Object.defineProperty;var n=(r,e)=>q(r,"name",{value:e,configurable:!0});import{WebSocketServer as z}from"ws";import{isSafeNodeId as N,viewSocketPath as K}from"../../core/canvas/paths.js";import{getNode as T}from"../../core/canvas/index.js";import{BROKER_READ_CAPS as Q,CLIENT_READ_CAPS as G,FrameDecoder as X,FrameOverflowError as Y}from"../../core/runtime/broker-protocol.js";import{emitEvent as O}from"../../core/events/emit.js";import{classify as A}from"../../core/fault-classifier.js";import{clearFault as v,markNodeRead as Z,recordFault as R}from"../../core/canvas/recovery.js";import{decideAttachRevive as I}from"../../core/runtime/revive.js";import{connectViewSocket as ee,connectWssView as re,probeViewSocket as te,waitForBrokerViewSocket as ae,waitForWssView as V}from"../../core/runtime/view-socket.js";import{liveExecutionForNode as ne}from"../../core/canvas/executions.js";import{runnerForExecution as oe}from"../../core/runtime/runner/index.js";import{isBeneathRefusal as ie}from"../../core/spaces/open-beneath.js";import{attachViewer as F,detachViewer as ce,holdViewerForRedial as se,presentViewer as le}from"./viewer-presence.js";const fe=new z({noServer:!0,maxPayload:Q.maxLineBytes}),ue=25e3,me=6e4;function de(r,e=ue,l=me){let a=!0,d=!1,o=Date.now();const i=n(()=>{a=!0},"acknowledge"),p=n(()=>{d||(d=!0,clearInterval(c),r.off("pong",i))},"stop");r.on("pong",i);const c=setInterval(()=>{if(Date.now()-o>l){a=!0;return}if(!a){p(),r.terminate();return}a=!1;try{r.ping()}catch{p(),r.terminate()}},e);return c.unref(),{stop:p,noteActivity:n(()=>{o=Date.now()},"noteActivity")}}n(de,"startRemoteAttachHeartbeat");function $(r){const e=Buffer.from(r,"utf8");return e.byteLength<=123?r:e.subarray(0,123).toString("utf8")}n($,"clampReason");function h(r,e,l,a,d){const o=JSON.stringify({error:{code:a,message:d,origin:"runtime"}}),i=`HTTP/1.1 ${e} ${l}\r
1
+ var j=Object.defineProperty;var a=(r,e)=>j(r,"name",{value:e,configurable:!0});import{WebSocketServer as K}from"ws";import{isSafeNodeId as P,viewSocketPath as Q}from"../../core/canvas/paths.js";import{getNode as T}from"../../core/canvas/index.js";import{requestPersonId as z,runAsPerson as G}from"../../core/canvas/person-state.js";import{BROKER_READ_CAPS as X,CLIENT_READ_CAPS as Y,FrameDecoder as Z,FrameOverflowError as I}from"../../core/runtime/broker-protocol.js";import{emitEvent as E}from"../../core/events/emit.js";import{classify as $}from"../../core/fault-classifier.js";import{clearFault as g,markNodeRead as ee,recordFault as x}from"../../core/canvas/recovery.js";import{decideAttachRevive as re}from"../../core/runtime/revive.js";import{connectViewSocket as te,connectWssView as ne,probeViewSocket as ae,waitForBrokerViewSocket as oe,waitForWssView as O}from"../../core/runtime/view-socket.js";import{liveExecutionForNode as ie}from"../../core/canvas/executions.js";import{runnerForExecution as ce}from"../../core/runtime/runner/index.js";import{isBeneathRefusal as se}from"../../core/spaces/open-beneath.js";import{attachViewer as V,detachViewer as le,holdViewerForRedial as fe,presentViewer as ue}from"./viewer-presence.js";const me=new K({noServer:!0,maxPayload:X.maxLineBytes}),de=25e3,he=6e4;function ye(r,e=de,s=he){let n=!0,h=!1,u=Date.now();const l=a(()=>{n=!0},"acknowledge"),i=a(()=>{h||(h=!0,clearInterval(o),r.off("pong",l))},"stop");r.on("pong",l);const o=setInterval(()=>{if(Date.now()-u>s){n=!0;return}if(!n){i(),r.terminate();return}n=!1;try{r.ping()}catch{i(),r.terminate()}},e);return o.unref(),{stop:i,noteActivity:a(()=>{u=Date.now()},"noteActivity")}}a(ye,"startRemoteAttachHeartbeat");function C(r){const e=Buffer.from(r,"utf8");return e.byteLength<=123?r:e.subarray(0,123).toString("utf8")}a(C,"clampReason");function w(r,e,s,n,h){const u=JSON.stringify({error:{code:n,message:h,origin:"runtime"}}),l=`HTTP/1.1 ${e} ${s}\r
2
2
  content-type: application/json; charset=utf-8\r
3
- content-length: ${Buffer.byteLength(o)}\r
3
+ content-length: ${Buffer.byteLength(u)}\r
4
4
  connection: close\r
5
5
  \r
6
- `+o;try{r.end(i)}catch{try{r.destroy()}catch{}}}n(h,"refuseUpgrade");function ye(r){if(new URL(r.url??"/","http://127.0.0.1").searchParams.get("revive")==="0")return!0;const l=r.headers["x-crtr-attach-revive"];return(Array.isArray(l)?l[0]:l)==="0"}n(ye,"reviveOptedOut");function he(r){return new URL(r.url??"/","http://127.0.0.1").searchParams.get("wakeParked")==="1"}n(he,"wakeParkedRequested");function P(r){return r===1e3||r===1001||r===1005}n(P,"isCleanViewerClose");function m(r,e,l){l.catch(a=>O({level:"error",event:e,node_id:r,error:a}))}n(m,"detach");async function D(r){const e=await ne(r);return e===null||e.runner==="process"?{kind:"unix",path:K(r)}:oe(e.runner).viewerAddress(e)}n(D,"brokerViewAddress");function pe(r,e){return e.kind==="wss"?re(e.url,e.headers):ee(r)}n(pe,"connectBrokerView");function we(r,e,l){return e.kind==="wss"?V(e.url,e.headers,void 0,l):te(r,l)}n(we,"probeBrokerView");async function ve(r,e,l,a){const d=[];let o=n(t=>{d.push(t)},"relayMessage");if(r.on("message",t=>o(t)),!N(e)){try{r.close(1008,$(`invalid node id: ${e}`))}catch{}return}if(await T(e)===null){try{r.close(1011,$(`no node ${e}`))}catch{}return}await v(e,{link:"viewer\u2194crtrd"});const i=pe(e,l),p=new X(G);let c=!1,w=!1,k,x=!1,C=!1,b="controller",B=Promise.resolve(),g=!1;const _=[];let E=!1;i.on("connect",()=>{x=!0,!c&&g&&m(e,"daemon.bridge.presence_failed",F(e,r)),m(e,"daemon.bridge.connect_failed",v(e,{link:"relay\u2194broker"}))});const y=n((t,f,s=!1)=>{if(!c){if(c=!0,m(e,"daemon.bridge.presence_failed",s?se(e,r):ce(e,r)),k?.stop(),!i.destroyed)try{i.destroy()}catch{}try{r.close(t,$(f))}catch{}}},"teardown");k=de(r);const U=n(async t=>{k?.noteActivity();let f;try{f=p.push(t)}catch(s){const u=s instanceof Y?`broker sent an oversized frame (${s.message})`:`failed to decode a broker frame for ${e}`;y(1009,u);return}_.push(...f),E||await L()},"onData"),L=n(async()=>{E=!0;try{for(;_.length>0&&!c;){const t=_.splice(0);let f;try{f=await a(b)}catch{y(1011,"revoked");return}if(c)return;if(!f){y(1008,"revoked");return}let s=!1;for(const u of t)r.readyState===r.OPEN&&(r.send(JSON.stringify(u)),u?.type==="welcome"&&(s=!0));if(g&&s)try{await le(e,r)&&await Z(e)}catch(u){O({level:"warn",event:"daemon.bridge.present_failed",node_id:e,error:u})}}}finally{E=!1}},"drainOutbound");i.on("data",t=>m(e,"daemon.bridge.data_failed",U(t)));const W=n(async t=>{const f=A("relay\u2194broker",t);await R(e,{link:"relay\u2194broker",op:"bridge socket",kind:f.kind,retry:{disposition:"manual"},message:t.message});const s=t.code==="ECONNREFUSED"||t.code==="ENOENT"?`no running broker for ${e}`:`view socket error for ${e}: ${t.message}`;y(1011,s,!0)},"onSocketError");i.on("error",t=>m(e,"daemon.bridge.socket_error_failed",W(t)));const H=n(async t=>{c||t||(await v(e,{link:"relay\u2194broker"}),y(1e3,"broker socket closed",!0))},"onSocketClose");i.on("close",t=>m(e,"daemon.bridge.socket_close_failed",H(t)));const J=n(async(t,f)=>{const s=f.toString("utf8");if(c){P(t)&&await v(e,{link:"viewer\u2194crtrd"});return}if(P(t))await v(e,{link:"viewer\u2194crtrd"});else if(!w){w=!0;const u=A("viewer\u2194crtrd",{code:t,reason:s});await R(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:u.kind,retry:{disposition:"auto"},message:s||`remote attach closed (${t})`})}y(t,s||`remote attach closed (${t})`)},"onWsClose");r.on("close",(t,f)=>m(e,"daemon.bridge.ws_close_failed",J(t,f)));const M=n(async()=>{if(!w){w=!0;const t=A("viewer\u2194crtrd",{code:1006,reason:"remote attach websocket error"});await R(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:t.kind,retry:{disposition:"auto"},message:"remote attach websocket error"})}y(1011,"remote attach websocket error")},"onWsError");r.on("error",()=>m(e,"daemon.bridge.ws_error_failed",M())),o=n(t=>{if(c||i.destroyed)return;k?.noteActivity();const s=(Array.isArray(t)?Buffer.concat(t):Buffer.isBuffer(t)?t:Buffer.from(new Uint8Array(t))).toString("utf8");if(!C)try{const S=JSON.parse(s);S?.type==="hello"&&(C=!0,b=S.role==="observer"?"observer":"controller",g=S.attends!==!1,g&&x&&m(e,"daemon.bridge.presence_failed",F(e,r)))}catch{}const u=s.endsWith(`
7
- `)?s:s+`
8
- `,j=n(()=>{try{i.write(u)}catch{}},"write");B=B.then(async()=>{c||(await a(b)?j():y(1008,"revoked"))}).catch(()=>y(1011,"revoked"))},"relayMessage");for(const t of d.splice(0))o(t)}n(ve,"bridgeConnection");async function Oe(r,e,l,a,d){e.on("error",()=>{});try{if(!N(a)){h(e,400,"Bad Request","invalid_node_id",`invalid node id: ${a}`);return}if(await T(a)===null){h(e,404,"Not Found","node_not_found",`no node ${a}`);return}let o;if(ye(r)){if(o=await D(a),!await we(a,o,250)){h(e,409,"Conflict","node_dormant",`node ${a} is dormant and revive was opted out (?revive=0)`);return}}else{let i;try{const c=await I(a,{resume:!0,capacity:"refuse"},he(r));if(c.parked){h(e,409,"Conflict","node_dormant",`node ${a} is parked; attach with ?wakeParked=1 to wake it`);return}i=c.result.launch?.exited}catch(c){const w=c instanceof Error?c.message:String(c);h(e,409,"Conflict","attach_revive_failed",`attach revive failed: ${w}`);return}if(o=await D(a),!(o.kind==="wss"?await V(o.url,o.headers,i):await ae(a,i))){h(e,409,"Conflict","broker_not_ready",`broker view socket for ${a} did not become listenable after revive`);return}}if(e.destroyed)return;fe.handleUpgrade(r,e,l,i=>m(a,"daemon.bridge.connection_failed",ve(i,a,o,d)))}catch(o){const i=o instanceof Error?o.message:String(o);if(ie(o)){h(e,400,"Bad Request","invalid_request",i);return}h(e,500,"Internal Server Error","internal",`attach upgrade failed: ${i}`)}}n(Oe,"handleAttachUpgrade");export{ue as REMOTE_ATTACH_HEARTBEAT_MS,me as REMOTE_ATTACH_QUIET_MS,Oe as handleAttachUpgrade,P as isCleanViewerClose,h as refuseUpgrade,de as startRemoteAttachHeartbeat};
6
+ `+u;try{r.end(l)}catch{try{r.destroy()}catch{}}}a(w,"refuseUpgrade");function pe(r){if(new URL(r.url??"/","http://127.0.0.1").searchParams.get("revive")==="0")return!0;const s=r.headers["x-crtr-attach-revive"];return(Array.isArray(s)?s[0]:s)==="0"}a(pe,"reviveOptedOut");function we(r){return new URL(r.url??"/","http://127.0.0.1").searchParams.get("wakeParked")==="1"}a(we,"wakeParkedRequested");function F(r){return r===1e3||r===1001||r===1005}a(F,"isCleanViewerClose");function y(r,e,s){s.catch(n=>E({level:"error",event:e,node_id:r,error:n}))}a(y,"detach");async function D(r){const e=await ie(r);return e===null||e.runner==="process"?{kind:"unix",path:Q(r)}:ce(e.runner).viewerAddress(e)}a(D,"brokerViewAddress");function ve(r,e){return e.kind==="wss"?ne(e.url,e.headers):te(r)}a(ve,"connectBrokerView");function ke(r,e,s){return e.kind==="wss"?O(e.url,e.headers,void 0,s):ae(r,s)}a(ke,"probeBrokerView");async function ge(r,e,s,n){const h=[];let u=a(t=>{h.push(t)},"relayMessage");if(r.on("message",t=>u(t)),!P(e)){try{r.close(1008,C(`invalid node id: ${e}`))}catch{}return}if(await T(e)===null){try{r.close(1011,C(`no node ${e}`))}catch{}return}await g(e,{link:"viewer\u2194crtrd"});const l=ve(e,s),i=new Z(Y);let o=!1,v=!1,d,b=!1,B=!1,A="controller",N=Promise.resolve(),_=!1;const S=[];let R=!1;l.on("connect",()=>{b=!0,!o&&_&&y(e,"daemon.bridge.presence_failed",V(e,r)),y(e,"daemon.bridge.connect_failed",g(e,{link:"relay\u2194broker"}))});const p=a((t,f,c=!1)=>{if(!o){if(o=!0,y(e,"daemon.bridge.presence_failed",c?fe(e,r):le(e,r)),d?.stop(),!l.destroyed)try{l.destroy()}catch{}try{r.close(t,C(f))}catch{}}},"teardown");d=ye(r);const U=a(async t=>{d?.noteActivity();let f;try{f=i.push(t)}catch(c){const m=c instanceof I?`broker sent an oversized frame (${c.message})`:`failed to decode a broker frame for ${e}`;p(1009,m);return}S.push(...f),R||await L()},"onData"),L=a(async()=>{R=!0;try{for(;S.length>0&&!o;){const t=S.splice(0);let f;try{f=await n(A)}catch(m){E({level:"error",event:"daemon.bridge.attach_recheck_failed",node_id:e,error:m}),p(1011,"revoked");return}if(o)return;if(!f){p(1008,"revoked");return}let c=!1;for(const m of t)r.readyState===r.OPEN&&(r.send(JSON.stringify(m)),m?.type==="welcome"&&(c=!0));if(_&&c)try{await ue(e,r)&&await ee(e)}catch(m){E({level:"warn",event:"daemon.bridge.present_failed",node_id:e,error:m})}}}finally{R=!1}},"drainOutbound");l.on("data",t=>y(e,"daemon.bridge.data_failed",U(t)));const W=a(async t=>{const f=$("relay\u2194broker",t);await x(e,{link:"relay\u2194broker",op:"bridge socket",kind:f.kind,retry:{disposition:"manual"},message:t.message});const c=t.code==="ECONNREFUSED"||t.code==="ENOENT"?`no running broker for ${e}`:`view socket error for ${e}: ${t.message}`;p(1011,c,!0)},"onSocketError");l.on("error",t=>y(e,"daemon.bridge.socket_error_failed",W(t)));const H=a(async t=>{o||t||(await g(e,{link:"relay\u2194broker"}),p(1e3,"broker socket closed",!0))},"onSocketClose");l.on("close",t=>y(e,"daemon.bridge.socket_close_failed",H(t)));const M=a(async(t,f)=>{const c=f.toString("utf8");if(o){F(t)&&await g(e,{link:"viewer\u2194crtrd"});return}if(F(t))await g(e,{link:"viewer\u2194crtrd"});else if(!v){v=!0;const m=$("viewer\u2194crtrd",{code:t,reason:c});await x(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:m.kind,retry:{disposition:"auto"},message:c||`remote attach closed (${t})`})}p(t,c||`remote attach closed (${t})`)},"onWsClose");r.on("close",(t,f)=>y(e,"daemon.bridge.ws_close_failed",M(t,f)));const J=a(async()=>{if(!v){v=!0;const t=$("viewer\u2194crtrd",{code:1006,reason:"remote attach websocket error"});await x(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:t.kind,retry:{disposition:"auto"},message:"remote attach websocket error"})}p(1011,"remote attach websocket error")},"onWsError");r.on("error",()=>y(e,"daemon.bridge.ws_error_failed",J())),u=a(t=>{if(o||l.destroyed)return;d?.noteActivity();const c=(Array.isArray(t)?Buffer.concat(t):Buffer.isBuffer(t)?t:Buffer.from(new Uint8Array(t))).toString("utf8");if(!B)try{const k=JSON.parse(c);k?.type==="hello"&&(B=!0,A=k.role==="observer"?"observer":"controller",_=k.attends!==!1,_&&b&&y(e,"daemon.bridge.presence_failed",V(e,r)))}catch{}const m=c.endsWith(`
7
+ `)?c:c+`
8
+ `,q=a(()=>{try{l.write(m)}catch{}},"write");N=N.then(async()=>{o||(await n(A)?q():p(1008,"revoked"))}).catch(k=>{E({level:"error",event:"daemon.bridge.attach_recheck_failed",node_id:e,error:k}),p(1011,"revoked")})},"relayMessage");for(const t of h.splice(0))u(t)}a(ge,"bridgeConnection");async function Fe(r,e,s,n,h){const u=z(),l=u===void 0?h:i=>G(u,()=>h(i));e.on("error",()=>{});try{if(!P(n)){w(e,400,"Bad Request","invalid_node_id",`invalid node id: ${n}`);return}if(await T(n)===null){w(e,404,"Not Found","node_not_found",`no node ${n}`);return}let i;if(pe(r)){if(i=await D(n),!await ke(n,i,250)){w(e,409,"Conflict","node_dormant",`node ${n} is dormant and revive was opted out (?revive=0)`);return}}else{let o;try{const d=await re(n,{resume:!0,capacity:"refuse"},we(r));if(d.parked){w(e,409,"Conflict","node_dormant",`node ${n} is parked; attach with ?wakeParked=1 to wake it`);return}o=d.result.launch?.exited}catch(d){const b=d instanceof Error?d.message:String(d);w(e,409,"Conflict","attach_revive_failed",`attach revive failed: ${b}`);return}if(i=await D(n),!(i.kind==="wss"?await O(i.url,i.headers,o):await oe(n,o))){w(e,409,"Conflict","broker_not_ready",`broker view socket for ${n} did not become listenable after revive`);return}}if(e.destroyed)return;me.handleUpgrade(r,e,s,o=>y(n,"daemon.bridge.connection_failed",ge(o,n,i,l)))}catch(i){const o=i instanceof Error?i.message:String(i);if(se(i)){w(e,400,"Bad Request","invalid_request",o);return}w(e,500,"Internal Server Error","internal",`attach upgrade failed: ${o}`)}}a(Fe,"handleAttachUpgrade");export{de as REMOTE_ATTACH_HEARTBEAT_MS,he as REMOTE_ATTACH_QUIET_MS,Fe as handleAttachUpgrade,F as isCleanViewerClose,w as refuseUpgrade,ye as startRemoteAttachHeartbeat};
@@ -1,3 +1,3 @@
1
- var g=Object.defineProperty;var c=(n,t)=>g(n,"name",{value:t,configurable:!0});import{basename as y}from"node:path";import{ApiError as a}from"../../../api/index.js";import{createPageFeedbackComment as I,readPageFeedback as u,resolvePageFeedbackComment as q,PageFeedbackCommentAlreadyResolvedError as C,PageTicketTerminalError as k,UnknownPageFeedbackCommentError as T}from"../../../core/human/feedback.js";import{discardFeedbackCompanion as w,pageDocumentSha256 as P,realizeFeedbackCompanion as N}from"../../../core/human/feedback-companion.js";import{parsePage as S}from"../../../core/human/page.js";import{pageTicketState as F,selectedPageDir as E}from"../../../core/human/tickets.js";import{deliverNodeMessage as x}from"../../messaging/node-message.js";import{requireTicketId as D,resolveTicket as O}from"./inbox.js";import{invalidRequest as b,objectBody as p,optionalString as R,requiredString as l}from"./validate.js";import{toPageFeedbackDTO as h,toPageFeedbackCommentDTO as v}from"../map.js";const M=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;async function _(n){const t=D(n.params),e=await O(t);if(e===null)throw new a(404,"ticket_not_found","no matching ticket");if(e.kind==="review")throw new a(409,"ticket_kind_unsupported","review tickets have no feedback comments");const o=await F(e.dir);if(o==="resolved"||o==="canceled")throw new a(409,"ticket_terminal","ticket already has a terminal outcome");return e}c(_,"requireMutablePageTicket");function j(n){const t=n.comment_id;if(t===void 0||!M.test(t))throw new a(400,"invalid_request","invalid feedback comment id");return t}c(j,"requireCommentId");function $(n){return[...n.quote.split(`
1
+ var g=Object.defineProperty;var c=(n,t)=>g(n,"name",{value:t,configurable:!0});import{basename as y}from"node:path";import{ApiError as a}from"../../../api/index.js";import{createPageFeedbackComment as I,readPageFeedback as u,resolvePageFeedbackComment as q,PageFeedbackCommentAlreadyResolvedError as C,PageTicketTerminalError as k,UnknownPageFeedbackCommentError as T}from"../../../core/human/feedback.js";import{discardFeedbackCompanion as w,pageDocumentSha256 as P,realizeFeedbackCompanion as N}from"../../../core/human/feedback-companion.js";import{readPageManifest as S}from"../../../core/human/page.js";import{pageTicketState as F,selectedPageDir as E}from"../../../core/human/tickets.js";import{deliverNodeMessage as x}from"../../messaging/node-message.js";import{requireTicketId as D,resolveTicket as M}from"./inbox.js";import{invalidRequest as b,objectBody as p,optionalString as O,requiredString as l}from"./validate.js";import{toPageFeedbackDTO as h,toPageFeedbackCommentDTO as v}from"../map.js";const R=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;async function _(n){const t=D(n.params),e=await M(t);if(e===null)throw new a(404,"ticket_not_found","no matching ticket");if(e.kind==="review")throw new a(409,"ticket_kind_unsupported","review tickets have no feedback comments");const o=await F(e.dir);if(o==="resolved"||o==="canceled")throw new a(409,"ticket_terminal","ticket already has a terminal outcome");return e}c(_,"requireMutablePageTicket");function j(n){const t=n.comment_id;if(t===void 0||!R.test(t))throw new a(400,"invalid_request","invalid feedback comment id");return t}c(j,"requireCommentId");function $(n){return[...n.quote.split(`
2
2
  `).map(e=>`> ${e}`),"",n.note].join(`
3
- `)}c($,"renderCommentTurn");async function z(n){const t=n.mutation.feedback.companionNodeId;if(t===void 0)throw new a(500,"internal","feedback has comments but no companion conversation");try{const e=await x({node_id:t,body:$(n.mutation.comment),label:n.title,from:"human",mode:"interactive",data:{page_id:y(n.dir),comment_id:n.mutation.comment.id,verb:"create"}});return{target_node_id:t,status:"delivered",via:e.via,woke:e.revived,...e.reason===void 0?{}:{reason:e.reason}}}catch(e){return{target_node_id:t,status:"failed",woke:!1,error:e instanceof Error?e.message:String(e)}}}c(z,"deliverCommentTurn");function A(n){const t=[],e=p(n.body,["quote","context","note"],t),o=l(e,"quote",t),i=R(e,"context",t),d=l(e,"note",t);return(t.length>0||o===void 0||d===void 0)&&b("page feedback comment request",t),{quote:o,...i===void 0?{}:{context:i},note:d}}c(A,"parseCreate");async function B(n){const t=A(n),e=await _(n),o=S(await E(e.dir));if(o.dialect!=="jsx")throw new a(409,"page_dialect_unsupported","feedback comments exist only on TSX pages");let i=(await u(e.dir)).companionNodeId,d,s=!1;if(i===void 0){const m=o.source?.nodeId;if(m===void 0)throw new a(409,"ticket_has_no_sender","this page carries no sending node to deliver feedback to");d=await P(e.dir);try{i=await N({ticketId:e.ticketId,dir:e.dir,senderNodeId:m,title:o.title})}catch(f){throw new a(409,"fork_failed",`the sending node could not be forked: ${f instanceof Error?f.message:String(f)}`)}s=!0}let r;try{r=await I(e.dir,{...t,companionNodeId:i,firstCommentedSha256:d})}catch(m){throw s&&await w(i),m instanceof k?new a(409,"ticket_terminal","ticket already has a terminal outcome"):m}return s&&r.feedback.companionNodeId!==i&&await w(i),{status:200,body:{comment:v(r.comment),feedback:h(r.feedback),delivery:await z({dir:e.dir,title:o.title,mutation:r})}}}c(B,"handleCreate");function U(n){const t=[],e=p(n.body,["node_id"],t),o=l(e,"node_id",t);return(t.length>0||o===void 0)&&b("feedback resolve request",t),o}c(U,"parseResolve");async function X(n){const t=await _(n),e=j(n.params),o=U(n),i=(await u(t.dir)).companionNodeId;if(i===void 0||o!==i)throw new a(403,"not_companion","only the ticket's bound feedback companion resolves its comments");let d;try{d=await q(t.dir,e)}catch(r){throw r instanceof T?new a(404,"unknown_comment",r.message):r instanceof C?new a(409,"comment_already_resolved",r.message):r instanceof k?new a(409,"ticket_terminal","ticket already has a terminal outcome"):r}return{status:200,body:{comment:v(d.comment),feedback:h(d.feedback)}}}c(X,"handleResolve");const ne=[{method:"POST",pattern:"/v1/human/inbox/:ticket_id/feedback-comments",handler:B},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/feedback-comments/:comment_id/resolve",handler:X}];export{ne as feedbackCommentRoutes};
3
+ `)}c($,"renderCommentTurn");async function z(n){const t=n.mutation.feedback.companionNodeId;if(t===void 0)throw new a(500,"internal","feedback has comments but no companion conversation");try{const e=await x({node_id:t,body:$(n.mutation.comment),label:n.title,from:"human",mode:"interactive",data:{page_id:y(n.dir),comment_id:n.mutation.comment.id,verb:"create"}});return{target_node_id:t,status:"delivered",via:e.via,woke:e.revived,...e.reason===void 0?{}:{reason:e.reason}}}catch(e){return{target_node_id:t,status:"failed",woke:!1,error:e instanceof Error?e.message:String(e)}}}c(z,"deliverCommentTurn");function A(n){const t=[],e=p(n.body,["quote","context","note"],t),o=l(e,"quote",t),i=O(e,"context",t),d=l(e,"note",t);return(t.length>0||o===void 0||d===void 0)&&b("page feedback comment request",t),{quote:o,...i===void 0?{}:{context:i},note:d}}c(A,"parseCreate");async function B(n){const t=A(n),e=await _(n),o=await S(await E(e.dir));if(o.dialect!=="jsx")throw new a(409,"page_dialect_unsupported","feedback comments exist only on TSX pages");let i=(await u(e.dir)).companionNodeId,d,s=!1;if(i===void 0){const m=o.source?.nodeId;if(m===void 0)throw new a(409,"ticket_has_no_sender","this page carries no sending node to deliver feedback to");d=await P(e.dir);try{i=await N({ticketId:e.ticketId,dir:e.dir,senderNodeId:m,title:o.title})}catch(f){throw new a(409,"fork_failed",`the sending node could not be forked: ${f instanceof Error?f.message:String(f)}`)}s=!0}let r;try{r=await I(e.dir,{...t,companionNodeId:i,firstCommentedSha256:d})}catch(m){throw s&&await w(i),m instanceof k?new a(409,"ticket_terminal","ticket already has a terminal outcome"):m}return s&&r.feedback.companionNodeId!==i&&await w(i),{status:200,body:{comment:v(r.comment),feedback:h(r.feedback),delivery:await z({dir:e.dir,title:o.title,mutation:r})}}}c(B,"handleCreate");function U(n){const t=[],e=p(n.body,["node_id"],t),o=l(e,"node_id",t);return(t.length>0||o===void 0)&&b("feedback resolve request",t),o}c(U,"parseResolve");async function X(n){const t=await _(n),e=j(n.params),o=U(n),i=(await u(t.dir)).companionNodeId;if(i===void 0||o!==i)throw new a(403,"not_companion","only the ticket's bound feedback companion resolves its comments");let d;try{d=await q(t.dir,e)}catch(r){throw r instanceof T?new a(404,"unknown_comment",r.message):r instanceof C?new a(409,"comment_already_resolved",r.message):r instanceof k?new a(409,"ticket_terminal","ticket already has a terminal outcome"):r}return{status:200,body:{comment:v(d.comment),feedback:h(d.feedback)}}}c(X,"handleResolve");const ne=[{method:"POST",pattern:"/v1/human/inbox/:ticket_id/feedback-comments",handler:B},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/feedback-comments/:comment_id/resolve",handler:X}];export{ne as feedbackCommentRoutes};
@@ -1 +1 @@
1
- var s=Object.defineProperty;var r=(e,t)=>s(e,"name",{value:t,configurable:!0});import{readFileSync as n}from"node:fs";import{readPageFeedback as u}from"../../../core/human/feedback.js";import{pageModulePath as a,pagePath as l}from"../../../core/human/convention.js";import{parsePage as p}from"../../../core/human/page.js";import{presentationDir as f}from"../../../core/human/requests.js";import{readPageProgress as g,readTicketResult as m}from"../../../core/human/tickets.js";import{toPageFeedbackDTO as k}from"../map.js";function y(e){const t={kind:e.kind,step:e.step,config:e.config};return e.id!==void 0&&(t.id=e.id),e.unvalidated===!0&&(t.unvalidated=!0),e.display===!0&&(t.display=!0),t}r(y,"pageSlotToDTO");function b(e){const t={schema:"crtr.page/v2",dialect:e.dialect,title:e.title,delivery:e.delivery,document:e.document,steps:e.steps,slots:e.slots.map(y)};if(e.subtitle!==void 0&&(t.subtitle=e.subtitle),e.source!==void 0){const i={};e.source.sessionName!==void 0&&(i.sessionName=e.source.sessionName),e.source.askedBy!==void 0&&(i.askedBy=e.source.askedBy),e.source.emittedAt!==void 0&&(i.emittedAt=e.source.emittedAt),e.source.profileName!==void 0&&(i.profileName=e.source.profileName),e.source.nodeId!==void 0&&(i.nodeId=e.source.nodeId),Object.keys(i).length>0&&(t.source=i)}return t}r(b,"pageManifestToDTO");function _(e){return e.settleable?e.state!=="settled"?"pending":e.outcome==="canceled"||e.outcome==="dismissed"?"canceled":"resolved":"passive"}r(_,"state");async function v(e){const t=await m(e.request_dir);if(t===null)return null;if(t.kind==="page"||t.kind==="canceled")return t;throw new Error(`page request has an invalid terminal result: ${e.request_id}`)}r(v,"result");async function O(e){if(e.kind!=="page")throw new Error(`request is not a page: ${e.request_id}`);const t=f(e),i=p(t),o=n(l(t,i.dialect),"utf8"),c=i.dialect==="jsx"?n(a(t),"utf8"):o,d=e.settleable?await g(e.request_dir):null;return{ticket_id:e.inbox_ticket_id,kind:"page",state:_(e),revision:t,page:b(i),document:c,source_document:o,document_media_type:i.dialect==="jsx"?"text/jsx":"text/html",progress:d===null?null:{responses:d},result:await v(e),feedback:k(await u(e.request_dir))}}r(O,"inboxPageProjection");export{O as inboxPageProjection};
1
+ var c=Object.defineProperty;var r=(e,t)=>c(e,"name",{value:t,configurable:!0});import{readPageFeedback as s}from"../../../core/human/feedback.js";import{pageModulePath as u,pagePath as l}from"../../../core/human/convention.js";import{readPageManifest as p}from"../../../core/human/page.js";import{readRequestText as n}from"../../../core/human/request-files.js";import{presentationDir as f}from"../../../core/human/requests.js";import{readPageProgress as g,readTicketResult as m}from"../../../core/human/tickets.js";import{toPageFeedbackDTO as k}from"../map.js";function b(e){const t={kind:e.kind,step:e.step,config:e.config};return e.id!==void 0&&(t.id=e.id),e.unvalidated===!0&&(t.unvalidated=!0),e.display===!0&&(t.display=!0),t}r(b,"pageSlotToDTO");function w(e){const t={schema:"crtr.page/v2",dialect:e.dialect,title:e.title,delivery:e.delivery,document:e.document,steps:e.steps,slots:e.slots.map(b)};if(e.subtitle!==void 0&&(t.subtitle=e.subtitle),e.source!==void 0){const i={};e.source.sessionName!==void 0&&(i.sessionName=e.source.sessionName),e.source.askedBy!==void 0&&(i.askedBy=e.source.askedBy),e.source.emittedAt!==void 0&&(i.emittedAt=e.source.emittedAt),e.source.profileName!==void 0&&(i.profileName=e.source.profileName),e.source.nodeId!==void 0&&(i.nodeId=e.source.nodeId),Object.keys(i).length>0&&(t.source=i)}return t}r(w,"pageManifestToDTO");function y(e){return e.settleable?e.state!=="settled"?"pending":e.outcome==="canceled"||e.outcome==="dismissed"?"canceled":"resolved":"passive"}r(y,"state");async function _(e){const t=await m(e.request_dir);if(t===null)return null;if(t.kind==="page"||t.kind==="canceled")return t;throw new Error(`page request has an invalid terminal result: ${e.request_id}`)}r(_,"result");async function O(e){if(e.kind!=="page")throw new Error(`request is not a page: ${e.request_id}`);const t=f(e),i=await p(t),o=await n(l(t,i.dialect)),a=i.dialect==="jsx"?await n(u(t)):o,d=e.settleable?await g(e.request_dir):null;return{ticket_id:e.inbox_ticket_id,kind:"page",state:y(e),revision:t,page:w(i),document:a,source_document:o,document_media_type:i.dialect==="jsx"?"text/jsx":"text/html",progress:d===null?null:{responses:d},result:await _(e),feedback:k(await s(e.request_dir))}}r(O,"inboxPageProjection");export{O as inboxPageProjection};
@@ -1 +1 @@
1
- var T=Object.defineProperty;var o=(e,i)=>T(e,"name",{value:i,configurable:!0});import{ApiError as t}from"../../../api/index.js";import{inboxPageProjection as A}from"./inbox-page.js";import{getRequestByInboxTicket as p}from"../../../core/human/requests.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{actsAsPerson as g,canReadNode as y}from"../router.js";import{parsePage as I}from"../../../core/human/page.js";import{scanInbox as P,scanPageHistory as E}from"../../../core/human/scan.js";import{readPageTicket as h,readTicketResult as k,selectedPageDir as x,validatePageTicketResponses as R,writePageProgress as S}from"../../../core/human/tickets.js";import{describePageAnswer as O}from"../../../core/human/answer.js";import{cancelHumanTicket as N,resolvePageTicket as C}from"../../human/finish.js";import{filterAsync as v}from"../../../core/async-array.js";const D="Canceled from the inbox.",j=1e3,G=/^[a-f0-9]{64}$/;async function c(e){const i=await p(e);return i===null?null:{ticketId:i.request_id,dir:i.request_dir,kind:i.kind}}o(c,"resolveTicket");function l(e){const i=e.ticket_id;if(i===void 0||!G.test(i))throw new t(400,"invalid_request","invalid ticket id");return i}o(l,"requireTicketId");function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(u,"isPlainObject");function f(e,i){for(const n of Object.keys(e))if(!i.includes(n))throw new t(400,"invalid_request",`unexpected field: ${n}`)}o(f,"requireNoExtraKeys");function L(e){if(!u(e))throw new t(400,"invalid_request","respond body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(L,"validateRespondRequest");function H(e){if(!u(e))throw new t(400,"invalid_request","progress body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(H,"validateProgressRequest");function K(e){if(e===void 0)return{};if(!u(e))throw new t(400,"invalid_request","cancel body must be an object");if(f(e,["reason"]),e.reason===void 0)return{};const i=e.reason;if(typeof i!="string")throw new t(400,"invalid_request","`reason` must be a string");if(i.trim()===""||i.length>j)throw new t(400,"invalid_request","`reason` must be nonempty after trim and at most 1000 characters");return{reason:i}}o(K,"validateCancelRequest");const $=[/^slot ".*?" /,/^response names unknown or non-response-bearing slot id/,/^final page response is missing response-bearing slot id/];function _(e){return e instanceof Error&&$.some(i=>i.test(e.message))}o(_,"isSlotValidationError");function q(e){const i={request_id:e.id,request_dir:e.dir,ticket_id:e.inboxTicketId,kind:"page",title:e.title,placement:e.placement,dialect:e.dialect,steps:e.steps,emitted_at:e.emittedAt,source:e.source,slot_kinds:e.slotKinds,inbox:e.inbox,awaits_response:e.awaitsResponse,state:e.state};return e.subtitle!==""&&(i.subtitle=e.subtitle),e.answerDigest!==void 0&&(i.answer_digest=e.answerDigest),e.recommendedOptions.length>0&&(i.recommended_options=e.recommendedOptions.map(n=>({slot_id:n.slotId,option_id:n.optionId,label:n.label}))),e.fastAction!==void 0&&(i.fast_action={kind:e.fastAction.kind,responses:e.fastAction.responses}),i}o(q,"pageSummaryToDTO");async function V(e){return{status:200,body:{tickets:(await v(await P(),async n=>{if(g(e.principal))return!0;const s=await p(n.inboxTicketId),r=s?.requester_node_id?await b(s.requester_node_id):null;return r!==null&&await y(e.principal,r.grantee??"app:terminal",r.node_id)})).map(n=>{if(n.kind==="page")return q(n);if(n.kind==="review")return{request_id:n.id,request_dir:n.dir,ticket_id:n.inboxTicketId,kind:"review",title:n.title,subtitle:n.subtitle,blocked_since:n.blockedSince,source:n.source};throw new Error(`unknown ticket kind: ${n.kind}`)}).sort((n,s)=>{const r=o(d=>d.kind==="page"?d.emitted_at:d.blocked_since,"pageTime"),a=Date.parse(r(s))-Date.parse(r(n));return a!==0?a:n.ticket_id.localeCompare(s.ticket_id)})}}}o(V,"handleList");const z=new Set(["pending","resolved","canceled","passive"]);async function B(e){const i=e.query.get("node_id");if(i==="")throw new t(400,"invalid_request","node_id must be nonempty when present");const n=e.query.get("state");if(n!==null&&!z.has(n))throw new t(400,"invalid_request","state must be one of pending, resolved, canceled, passive");const s=(await E(i??void 0)).filter(d=>n===null||d.state===n);return{status:200,body:{tickets:(await v(s,async d=>{if(g(e.principal))return!0;const m=await p(d.inboxTicketId),w=m?.requester_node_id?await b(m.requester_node_id):null;return w!==null&&await y(e.principal,w.grantee??"app:terminal",w.node_id)})).map(q)}}}o(B,"handleHistory");async function F(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have no page read operation");const s=await p(i);if(s===null||s.kind!=="page")throw new t(404,"ticket_not_found","no matching ticket");try{return{status:200,body:await A(s)}}catch{throw new t(500,"internal","the ticket could not be read")}}o(F,"handleGetPage");async function M(e){const i=l(e.params),n=L(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets submit through their own path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");if(r?.kind==="canceled")throw new t(409,"ticket_canceled","canceled tickets cannot be answered");try{await R(s.dir,n.responses)}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","validation failed")}try{const a=await C(s.ticketId,n.responses);if(a.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");return{status:200,body:a}}catch(a){throw a instanceof t?a:_(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","the ticket could not be finalized")}}o(M,"handleRespond");async function U(e){const i=l(e.params),n=H(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets do not support progress autosave");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");try{return await S(s.dir,n.responses),{status:200,body:{}}}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","progress could not be saved")}}o(U,"handleProgress");async function X(e){const i=l(e.params),n=K(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have their own cancel path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");try{const r=await N(s.ticketId,{reason:n.reason??D,actor:"human",disposition:"dismissed"});if(r.outcome==="already_settled"||r.result.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");return{status:200,body:r.result}}catch(r){throw r instanceof t?r:new t(500,"internal","the ticket could not be canceled")}}o(X,"handleCancel");async function J(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets fetch responses through their own path");const s=await k(n.dir);if(s===null)throw new t(404,"ticket_pending","ticket has no response yet");if(s.kind==="canceled")throw new t(404,"ticket_canceled","canceled tickets have no response");if(s.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");try{const r=I(await x(n.dir));return{status:200,body:{...s,answer:O(r,s.responses,{completedAt:s.completedAt})}}}catch{throw new t(500,"internal","the ticket answer could not be read")}}o(J,"handleGetResponse");const de=[{method:"GET",pattern:"/v1/human/inbox",handler:V},{method:"GET",pattern:"/v1/human/inbox/history",handler:B},{method:"GET",pattern:"/v1/human/inbox/:ticket_id",handler:F},{method:"GET",pattern:"/v1/human/inbox/:ticket_id/response",handler:J},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/respond",handler:M},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/progress",handler:U},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/cancel",handler:X}];export{de as inboxRoutes,l as requireTicketId,c as resolveTicket};
1
+ var T=Object.defineProperty;var o=(e,i)=>T(e,"name",{value:i,configurable:!0});import{ApiError as t}from"../../../api/index.js";import{inboxPageProjection as A}from"./inbox-page.js";import{getRequestByInboxTicket as p}from"../../../core/human/requests.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{actsAsPerson as g,canReadNode as y}from"../router.js";import{readPageManifest as I}from"../../../core/human/page.js";import{scanInbox as P,scanPageHistory as E}from"../../../core/human/scan.js";import{readPageTicket as h,readTicketResult as k,selectedPageDir as x,validatePageTicketResponses as R,writePageProgress as S}from"../../../core/human/tickets.js";import{describePageAnswer as O}from"../../../core/human/answer.js";import{cancelHumanTicket as N,resolvePageTicket as C}from"../../human/finish.js";import{filterAsync as v}from"../../../core/async-array.js";const D="Canceled from the inbox.",j=1e3,G=/^[a-f0-9]{64}$/;async function c(e){const i=await p(e);return i===null?null:{ticketId:i.request_id,dir:i.request_dir,kind:i.kind}}o(c,"resolveTicket");function l(e){const i=e.ticket_id;if(i===void 0||!G.test(i))throw new t(400,"invalid_request","invalid ticket id");return i}o(l,"requireTicketId");function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(u,"isPlainObject");function f(e,i){for(const n of Object.keys(e))if(!i.includes(n))throw new t(400,"invalid_request",`unexpected field: ${n}`)}o(f,"requireNoExtraKeys");function L(e){if(!u(e))throw new t(400,"invalid_request","respond body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(L,"validateRespondRequest");function H(e){if(!u(e))throw new t(400,"invalid_request","progress body is required");if(f(e,["responses"]),!u(e.responses))throw new t(400,"invalid_request","`responses` must be an object");return{responses:e.responses}}o(H,"validateProgressRequest");function K(e){if(e===void 0)return{};if(!u(e))throw new t(400,"invalid_request","cancel body must be an object");if(f(e,["reason"]),e.reason===void 0)return{};const i=e.reason;if(typeof i!="string")throw new t(400,"invalid_request","`reason` must be a string");if(i.trim()===""||i.length>j)throw new t(400,"invalid_request","`reason` must be nonempty after trim and at most 1000 characters");return{reason:i}}o(K,"validateCancelRequest");const $=[/^slot ".*?" /,/^response names unknown or non-response-bearing slot id/,/^final page response is missing response-bearing slot id/];function _(e){return e instanceof Error&&$.some(i=>i.test(e.message))}o(_,"isSlotValidationError");function q(e){const i={request_id:e.id,request_dir:e.dir,ticket_id:e.inboxTicketId,kind:"page",title:e.title,placement:e.placement,dialect:e.dialect,steps:e.steps,emitted_at:e.emittedAt,source:e.source,slot_kinds:e.slotKinds,inbox:e.inbox,awaits_response:e.awaitsResponse,state:e.state};return e.subtitle!==""&&(i.subtitle=e.subtitle),e.answerDigest!==void 0&&(i.answer_digest=e.answerDigest),e.recommendedOptions.length>0&&(i.recommended_options=e.recommendedOptions.map(n=>({slot_id:n.slotId,option_id:n.optionId,label:n.label}))),e.fastAction!==void 0&&(i.fast_action={kind:e.fastAction.kind,responses:e.fastAction.responses}),i}o(q,"pageSummaryToDTO");async function M(e){return{status:200,body:{tickets:(await v(await P(),async n=>{if(g(e.principal))return!0;const s=await p(n.inboxTicketId),r=s?.requester_node_id?await b(s.requester_node_id):null;return r!==null&&await y(e.principal,r.grantee??"app:terminal",r.node_id)})).map(n=>{if(n.kind==="page")return q(n);if(n.kind==="review")return{request_id:n.id,request_dir:n.dir,ticket_id:n.inboxTicketId,kind:"review",title:n.title,subtitle:n.subtitle,blocked_since:n.blockedSince,source:n.source};throw new Error(`unknown ticket kind: ${n.kind}`)}).sort((n,s)=>{const r=o(d=>d.kind==="page"?d.emitted_at:d.blocked_since,"pageTime"),a=Date.parse(r(s))-Date.parse(r(n));return a!==0?a:n.ticket_id.localeCompare(s.ticket_id)})}}}o(M,"handleList");const V=new Set(["pending","resolved","canceled","passive"]);async function z(e){const i=e.query.get("node_id");if(i==="")throw new t(400,"invalid_request","node_id must be nonempty when present");const n=e.query.get("state");if(n!==null&&!V.has(n))throw new t(400,"invalid_request","state must be one of pending, resolved, canceled, passive");const s=(await E(i??void 0)).filter(d=>n===null||d.state===n);return{status:200,body:{tickets:(await v(s,async d=>{if(g(e.principal))return!0;const m=await p(d.inboxTicketId),w=m?.requester_node_id?await b(m.requester_node_id):null;return w!==null&&await y(e.principal,w.grantee??"app:terminal",w.node_id)})).map(q)}}}o(z,"handleHistory");async function B(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have no page read operation");const s=await p(i);if(s===null||s.kind!=="page")throw new t(404,"ticket_not_found","no matching ticket");try{return{status:200,body:await A(s)}}catch{throw new t(500,"internal","the ticket could not be read")}}o(B,"handleGetPage");async function F(e){const i=l(e.params),n=L(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets submit through their own path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");if(r?.kind==="canceled")throw new t(409,"ticket_canceled","canceled tickets cannot be answered");try{await R(s.dir,n.responses)}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","validation failed")}try{const a=await C(s.ticketId,n.responses);if(a.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");return{status:200,body:a}}catch(a){throw a instanceof t?a:_(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","the ticket could not be finalized")}}o(F,"handleRespond");async function U(e){const i=l(e.params),n=H(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets do not support progress autosave");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");const r=await k(s.dir);if(r!==null&&r.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");try{return await S(s.dir,n.responses),{status:200,body:{}}}catch(a){throw _(a)?new t(400,"invalid_ticket_response",a.message):new t(500,"internal","progress could not be saved")}}o(U,"handleProgress");async function X(e){const i=l(e.params),n=K(e.body),s=await c(i);if(s===null)throw new t(404,"ticket_not_found","no matching ticket");if(s.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets have their own cancel path");if((await h(s.dir)).state==="passive")throw new t(409,"page_has_no_response_lifecycle","this page has no response lifecycle");try{const r=await N(s.ticketId,{reason:n.reason??D,actor:"human",disposition:"dismissed"});if(r.outcome==="already_settled"||r.result.kind!=="canceled")throw new t(409,"ticket_already_resolved","ticket was already resolved");return{status:200,body:r.result}}catch(r){throw r instanceof t?r:new t(500,"internal","the ticket could not be canceled")}}o(X,"handleCancel");async function J(e){const i=l(e.params),n=await c(i);if(n===null)throw new t(404,"ticket_not_found","no matching ticket");if(n.kind==="review")throw new t(409,"ticket_kind_unsupported","review tickets fetch responses through their own path");const s=await k(n.dir);if(s===null)throw new t(404,"ticket_pending","ticket has no response yet");if(s.kind==="canceled")throw new t(404,"ticket_canceled","canceled tickets have no response");if(s.kind!=="page")throw new t(409,"ticket_kind_mismatch","unexpected result kind");try{const r=await I(await x(n.dir));return{status:200,body:{...s,answer:O(r,s.responses,{completedAt:s.completedAt})}}}catch{throw new t(500,"internal","the ticket answer could not be read")}}o(J,"handleGetResponse");const de=[{method:"GET",pattern:"/v1/human/inbox",handler:M},{method:"GET",pattern:"/v1/human/inbox/history",handler:z},{method:"GET",pattern:"/v1/human/inbox/:ticket_id",handler:B},{method:"GET",pattern:"/v1/human/inbox/:ticket_id/response",handler:J},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/respond",handler:F},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/progress",handler:U},{method:"POST",pattern:"/v1/human/inbox/:ticket_id/cancel",handler:X}];export{de as inboxRoutes,l as requireTicketId,c as resolveTicket};
@@ -1 +1 @@
1
- var R=Object.defineProperty;var s=(t,e)=>R(t,"name",{value:e,configurable:!0});import{ApiError as m}from"../../../api/index.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{isSafeNodeId as p}from"../../../core/canvas/paths.js";import{readDocument as q}from"../../../core/graph/documents.js";import{getById as E}from"../../../core/graph/objects.js";import{checkoutsForCwd as S}from"../../../core/graph/repo-sync/repos.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{assertScope as T}from"../../../core/scopes.js";import{createReview as $}from"../../../core/review/birth.js";import{readAnchorBase as C,listReviews as I,requireVisibleReview as y}from"../../../core/review/store.js";import{cancelReview as A,submitReviewApproval as D}from"../../review/finish.js";import{toFeedbackResultDTO as O,toReviewDTO as F}from"../map.js";import{readerFor as L,resolveRef as P,warmReaderRepos as B}from"../reader.js";import{actsAsPerson as N,canReadNode as j}from"../router.js";import{invalidRequest as G,objectBody as v,optionalString as c,requireParam as V,requiredString as g}from"./validate.js";import{filterAsync as W}from"../../../core/async-array.js";const K=new Set(["open","approved","canceled"]),M=["origin_kind","origin_node_id","document","file","idempotency_key","subtitle","output_path"],z=["reason","actor","disposition"];function l(t){G("review request",t)}s(l,"invalidRequest");function w(t,e){return V(t,"review_id","must be a safe review id",p,e)}s(w,"requireReviewId");function f(t){return{...F(t,t.state==="approved"?{result_path:`${t.review_dir}/result.json`}:{}),review_dir:t.review_dir}}s(f,"toReviewResponseDTO");function H(t){const e=[],n=v(t.body,M,e),o=n.origin_kind,i=o==="ticket"||o==="inline"?o:void 0;i===void 0&&e.push({field:"origin_kind",problem:"must be ticket or inline",...o===void 0?{}:{received:o}});const r=g(n,"origin_node_id",e);r!==void 0&&!p(r)&&e.push({field:"origin_node_id",problem:"must be a safe node id",received:r});const d=c(n,"document",e),u=c(n,"file",e);i==="ticket"&&(d===void 0||u!==void 0)&&e.push({field:"document",problem:"is required for ticket reviews, and file is not allowed"}),i==="inline"&&u===void 0==(d===void 0)&&e.push({field:"file",problem:"inline reviews take exactly one of file or document"});const a=g(n,"idempotency_key",e);a!==void 0&&a.length>128&&e.push({field:"idempotency_key",problem:"must be at most 128 characters",received:a});const h=c(n,"subtitle",e),_=c(n,"output_path",e);return i==="ticket"&&h===void 0&&n.subtitle===void 0&&e.push({field:"subtitle",problem:"is required for ticket reviews"}),i==="inline"&&n.subtitle!==void 0&&e.push({field:"subtitle",problem:"is only allowed for ticket reviews",received:n.subtitle}),i==="inline"&&n.output_path!==void 0&&e.push({field:"output_path",problem:"is only allowed for ticket reviews",received:n.output_path}),(e.length>0||i===void 0||r===void 0||!p(r)||a===void 0||a.length>128)&&l(e),{origin_kind:i,origin_node_id:r,...d===void 0?{}:{document:d},...u===void 0?{}:{file:u},idempotency_key:a,...h===void 0?{}:{subtitle:h},..._===void 0?{}:{output_path:_}}}s(H,"parseCreate");function J(t){const e=[],n={},o=new Set(["state","origin_node_id","file"]);for(const[i,r]of t.query.entries())o.has(i)||e.push({field:i,problem:"is not allowed",received:r});for(const i of o){const r=t.query.getAll(i);if(r.length>1){e.push({field:i,problem:"must appear at most once",received:r});continue}const d=r[0];d!==void 0&&(i==="state"?K.has(d)?n.state=d:e.push({field:i,problem:"must be open, approved, or canceled",received:d}):i==="origin_node_id"?p(d)?n.origin_node_id=d:e.push({field:i,problem:"must be a safe node id",received:d}):d===""?e.push({field:i,problem:"must be a non-empty string",received:d}):n.file=d)}return e.length>0&&l(e),n}s(J,"parseList");function Q(t,e){const n=t.body===void 0?{}:v(t.body,z,e),o=c(n,"reason",e),i=c(n,"actor",e),r=c(n,"disposition",e);return r!==void 0&&r!=="canceled"&&r!=="dismissed"&&e.push({field:"disposition",problem:'must be "canceled" or "dismissed"',received:r}),{...o===void 0?{}:{reason:o},...i===void 0?{}:{actor:i},...r==="canceled"||r==="dismissed"?{disposition:r}:{}}}s(Q,"parseCancel");function U(t,e){v(t.body,[],e)}s(U,"requireEmptyBody");async function X(t){const e=H(t),n=await b(e.origin_node_id);n!==null&&T(n,"crtr:act");const{document:o,file:i,...r}=e,d=await $({...r,...o===void 0?{file:i??""}:{document:await Y(t,o)}});return{status:200,body:f(d)}}s(X,"handleCreate");async function Y(t,e){return await B(t.principal),await k(async n=>{const o=await L(n,t),i=await P(n,o,e);if("remote"in i)throw new m(404,"not_found",`no document named ${i.remote} on this runtime`,{next:"Check the name with `crtr canvas search`."});if("deleted"in i)throw new m(404,"not_found",`${i.deleted.name} is deleted`,{id:i.deleted.id,deleted:!0});if(i.type!=="document")throw new m(400,"invalid_request",`${e} is a ${i.type}, not a document`);const r=await q(n,o,i,async a=>await S(n,a));if(r.needsMerge)throw new m(409,"needs_merge",`${i.name} has ${r.heads.length} heads; merge them before a review`,{id:i.id,next:`Read it with \`crtr canvas read ${i.id}\` and write the merged body with \`crtr doc edit\`.`});const d=i.owner===null?null:await E(n,i.owner),u=d===null?"":d.type==="node"?d.id:d.name;return{id:i.id,name:u===""?i.name:`${u}/${i.name}`,base:r.record.heads_key,body:r.heads[0].body}})}s(Y,"reviewedDocument");async function Z(t){const e=J(t);return{status:200,body:{reviews:(await W(await I(e),async o=>{if(N(t.principal))return!0;const i=await b(o.origin_node_id);return i!==null&&await j(t.principal,i.grantee??"app:terminal",i.node_id)})).map(f)}}}s(Z,"handleList");async function x(t){const e=[],n=w(t,e);return(e.length>0||n===void 0)&&l(e),{status:200,body:f(await y(n))}}s(x,"handleRead");async function ee(t){const e=[],n=w(t,e);U(t,e),(e.length>0||n===void 0)&&l(e);const o=await D(n);return{status:200,body:o.outcome==="awaiting_companion"?{review:f(o.record),outcome:o.outcome}:{review:f(o.record),result:O(o.result),outcome:o.outcome}}}s(ee,"handleSubmit");async function ie(t){const e=[],n=w(t,e),o=Q(t,e);(e.length>0||n===void 0)&&l(e);const i=await A(n,o);return{status:200,body:{review:f(i.record),outcome:i.outcome}}}s(ie,"handleCancel");async function ne(t){const e=[],n=w(t,e);(e.length>0||n===void 0)&&l(e);const o=await y(n),i=await C(o.review_id);if(i===null)throw new Error(`review anchor base is missing: ${o.review_id}`);return{status:200,body:{review_id:o.review_id,anchor_base_sha256:i.sha256,anchor_base_text:new TextDecoder("utf-8",{fatal:!0}).decode(i.bytes),file:o.file}}}s(ne,"handleDocument");const ye=[{method:"POST",pattern:"/v1/human/reviews",scope:"crtr:act",handler:X},{method:"GET",pattern:"/v1/human/reviews",handler:Z},{method:"GET",pattern:"/v1/human/reviews/:review_id",handler:x},{method:"POST",pattern:"/v1/human/reviews/:review_id/submit",handler:ee},{method:"POST",pattern:"/v1/human/reviews/:review_id/cancel",handler:ie},{method:"GET",pattern:"/v1/human/reviews/:review_id/document",handler:ne}];export{H as parseCreate,ye as reviewRoutes};
1
+ var R=Object.defineProperty;var s=(n,e)=>R(n,"name",{value:e,configurable:!0});import{ApiError as p}from"../../../api/index.js";import{isCloudRole as k}from"../../../core/canvas/person.js";import{withCanvasWrite as q}from"../../../core/canvas/db.js";import{isSafeNodeId as w}from"../../../core/canvas/paths.js";import{readDocument as E}from"../../../core/graph/documents.js";import{getById as S}from"../../../core/graph/objects.js";import{checkoutsForCwd as T}from"../../../core/graph/repo-sync/repos.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{assertScope as C}from"../../../core/scopes.js";import{createReview as $}from"../../../core/review/birth.js";import{readAnchorBase as I,listReviews as A,requireVisibleReview as y}from"../../../core/review/store.js";import{cancelReview as D,submitReviewApproval as O}from"../../review/finish.js";import{toFeedbackResultDTO as F,toReviewDTO as L}from"../map.js";import{readerFor as P,resolveRef as j,warmReaderRepos as B}from"../reader.js";import{actsAsPerson as N,canReadNode as G}from"../router.js";import{invalidRequest as V,objectBody as _,optionalString as c,requireParam as W,requiredString as g}from"./validate.js";import{filterAsync as K}from"../../../core/async-array.js";const M=new Set(["open","approved","canceled"]),z=["origin_kind","origin_node_id","document","file","idempotency_key","subtitle","output_path"],H=["reason","actor","disposition"];function l(n){V("review request",n)}s(l,"invalidRequest");function h(n,e){return W(n,"review_id","must be a safe review id",w,e)}s(h,"requireReviewId");function f(n){return{...L(n,n.state==="approved"?{result_path:`${n.review_dir}/result.json`}:{}),review_dir:n.review_dir}}s(f,"toReviewResponseDTO");function J(n){const e=[],t=_(n.body,z,e),o=t.origin_kind,i=o==="ticket"||o==="inline"?o:void 0;i===void 0&&e.push({field:"origin_kind",problem:"must be ticket or inline",...o===void 0?{}:{received:o}});const r=g(t,"origin_node_id",e);r!==void 0&&!w(r)&&e.push({field:"origin_node_id",problem:"must be a safe node id",received:r});const d=c(t,"document",e),u=c(t,"file",e);i==="ticket"&&(d===void 0||u!==void 0)&&e.push({field:"document",problem:"is required for ticket reviews, and file is not allowed"}),i==="inline"&&u===void 0==(d===void 0)&&e.push({field:"file",problem:"inline reviews take exactly one of file or document"});const a=g(t,"idempotency_key",e);a!==void 0&&a.length>128&&e.push({field:"idempotency_key",problem:"must be at most 128 characters",received:a});const v=c(t,"subtitle",e),m=c(t,"output_path",e);return i==="ticket"&&v===void 0&&t.subtitle===void 0&&e.push({field:"subtitle",problem:"is required for ticket reviews"}),i==="inline"&&t.subtitle!==void 0&&e.push({field:"subtitle",problem:"is only allowed for ticket reviews",received:t.subtitle}),i==="inline"&&t.output_path!==void 0&&e.push({field:"output_path",problem:"is only allowed for ticket reviews",received:t.output_path}),m!==void 0&&k()&&e.push({field:"output_path",problem:"is not supported on the cloud: a review result is projected only to its own request files",received:m}),(e.length>0||i===void 0||r===void 0||!w(r)||a===void 0||a.length>128)&&l(e),{origin_kind:i,origin_node_id:r,...d===void 0?{}:{document:d},...u===void 0?{}:{file:u},idempotency_key:a,...v===void 0?{}:{subtitle:v},...m===void 0?{}:{output_path:m}}}s(J,"parseCreate");function Q(n){const e=[],t={},o=new Set(["state","origin_node_id","file"]);for(const[i,r]of n.query.entries())o.has(i)||e.push({field:i,problem:"is not allowed",received:r});for(const i of o){const r=n.query.getAll(i);if(r.length>1){e.push({field:i,problem:"must appear at most once",received:r});continue}const d=r[0];d!==void 0&&(i==="state"?M.has(d)?t.state=d:e.push({field:i,problem:"must be open, approved, or canceled",received:d}):i==="origin_node_id"?w(d)?t.origin_node_id=d:e.push({field:i,problem:"must be a safe node id",received:d}):d===""?e.push({field:i,problem:"must be a non-empty string",received:d}):t.file=d)}return e.length>0&&l(e),t}s(Q,"parseList");function U(n,e){const t=n.body===void 0?{}:_(n.body,H,e),o=c(t,"reason",e),i=c(t,"actor",e),r=c(t,"disposition",e);return r!==void 0&&r!=="canceled"&&r!=="dismissed"&&e.push({field:"disposition",problem:'must be "canceled" or "dismissed"',received:r}),{...o===void 0?{}:{reason:o},...i===void 0?{}:{actor:i},...r==="canceled"||r==="dismissed"?{disposition:r}:{}}}s(U,"parseCancel");function X(n,e){_(n.body,[],e)}s(X,"requireEmptyBody");async function Y(n){const e=J(n),t=await b(e.origin_node_id);t!==null&&C(t,"crtr:act");const{document:o,file:i,...r}=e,d=await $({...r,...o===void 0?{file:i??""}:{document:await Z(n,o)}});return{status:200,body:f(d)}}s(Y,"handleCreate");async function Z(n,e){return await B(n.principal),await q(async t=>{const o=await P(t,n),i=await j(t,o,e);if("remote"in i)throw new p(404,"not_found",`no document named ${i.remote} on this runtime`,{next:"Check the name with `crtr canvas search`."});if("deleted"in i)throw new p(404,"not_found",`${i.deleted.name} is deleted`,{id:i.deleted.id,deleted:!0});if(i.type!=="document")throw new p(400,"invalid_request",`${e} is a ${i.type}, not a document`);const r=await E(t,o,i,async a=>await T(t,a));if(r.needsMerge)throw new p(409,"needs_merge",`${i.name} has ${r.heads.length} heads; merge them before a review`,{id:i.id,next:`Read it with \`crtr canvas read ${i.id}\` and write the merged body with \`crtr doc edit\`.`});const d=i.owner===null?null:await S(t,i.owner),u=d===null?"":d.type==="node"?d.id:d.name;return{id:i.id,name:u===""?i.name:`${u}/${i.name}`,base:r.record.heads_key,body:r.heads[0].body}})}s(Z,"reviewedDocument");async function x(n){const e=Q(n);return{status:200,body:{reviews:(await K(await A(e),async o=>{if(N(n.principal))return!0;const i=await b(o.origin_node_id);return i!==null&&await G(n.principal,i.grantee??"app:terminal",i.node_id)})).map(f)}}}s(x,"handleList");async function ee(n){const e=[],t=h(n,e);return(e.length>0||t===void 0)&&l(e),{status:200,body:f(await y(t))}}s(ee,"handleRead");async function ie(n){const e=[],t=h(n,e);X(n,e),(e.length>0||t===void 0)&&l(e);const o=await O(t);return{status:200,body:o.outcome==="awaiting_companion"?{review:f(o.record),outcome:o.outcome}:{review:f(o.record),result:F(o.result),outcome:o.outcome}}}s(ie,"handleSubmit");async function te(n){const e=[],t=h(n,e),o=U(n,e);(e.length>0||t===void 0)&&l(e);const i=await D(t,o);return{status:200,body:{review:f(i.record),outcome:i.outcome}}}s(te,"handleCancel");async function ne(n){const e=[],t=h(n,e);(e.length>0||t===void 0)&&l(e);const o=await y(t),i=await I(o.review_id);if(i===null)throw new Error(`review anchor base is missing: ${o.review_id}`);return{status:200,body:{review_id:o.review_id,anchor_base_sha256:i.sha256,anchor_base_text:new TextDecoder("utf-8",{fatal:!0}).decode(i.bytes),file:o.file}}}s(ne,"handleDocument");const Re=[{method:"POST",pattern:"/v1/human/reviews",scope:"crtr:act",handler:Y},{method:"GET",pattern:"/v1/human/reviews",handler:x},{method:"GET",pattern:"/v1/human/reviews/:review_id",handler:ee},{method:"POST",pattern:"/v1/human/reviews/:review_id/submit",handler:ie},{method:"POST",pattern:"/v1/human/reviews/:review_id/cancel",handler:te},{method:"GET",pattern:"/v1/human/reviews/:review_id/document",handler:ne}];export{J as parseCreate,Re as reviewRoutes};
@@ -1 +1 @@
1
- var S=Object.defineProperty;var c=(a,t)=>S(a,"name",{value:t,configurable:!0});import{callerGrant as g}from"../principal.js";import{grantVersion as b,payerOfGrant as O}from"../../../core/grants/mirror.js";import{holdGrant as $}from"../../../core/grants/watch.js";import{RunError as n,idempotencyKeyOf as v,cancelRun as N,cloudPersonScope as A,deleteRun as j,interruptRun as x,listRuns as B,messageRun as J,renameRun as z,requestedScopes as D,startRun as E,traceRun as G,waitRun as K}from"../../../core/runs/operations.js";import{canReadRun as y,requireRun as w,runCallerApp as M}from"../router.js";import{waitSeconds as q}from"../long-poll-wait.js";import{outputSchemaProblem as P}from"../../../core/runtime/structured-output.js";function m(a){if(!a||typeof a!="object"||Array.isArray(a))throw new n(400,"invalid_request","a JSON object is required");return a}c(m,"object");function k(a,t,i,u,l){if(a===null)return l;if(!/^(0|[1-9]\d*)$/.test(a)||!Number.isSafeInteger(Number(a))||Number(a)<i||Number(a)>u)throw new n(400,"invalid_request",`invalid ${t}`,t);return Number(a)}c(k,"integer");const T=256*1024;async function Y(a,t,i,u,l,_,h){switch(a.name){case"grant.get":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=q("grant.get",u),s=u.get("since");if(s!==null&&!/^[A-Za-z0-9_-]{1,64}$/.test(s))throw new n(400,"invalid_request","invalid since","since");const d=e&&s!==null?$(t.grant):void 0;let o;try{if(o=await g(t),d){const f=Date.now()+e*1e3;for(;b(o)===s&&Date.now()<f;)await d.changed(Math.max(0,(f-Date.now())/1e3)),o=await g(t)}}finally{d?.release()}return{status:200,body:{sub:t.sub,grantee:t.grantee,client_id:t.client_id,grant:t.grant,rev:o.rev,resolved_scopes:o.resolved_scopes,spend_cap:o.spend_cap,exp:t.exp,payer:await O(t.grant),dependencies:o.dependencies??{},version:b(o)}}}case"run.start":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=m(l),s=["mode","parent","fork_from","node_id","env","tools","system_prompt"];for(const r of s)if(e[r]!==void 0)throw new n(403,"owner_only",`${r} is owner-only`,r);const d=["payer","model_policy","kind"];for(const r of d)if(e[r]!==void 0)throw new n(400,"invalid_request",`${r} is not available`,r);for(const r of Object.keys(e))if(!["prompt","scopes","metadata","profile","isolated","cwd","worktree","output_schema","message","model","deadline"].includes(r))throw new n(400,"invalid_request",`unknown field ${r}`,r);if(e.model!==void 0&&(typeof e.model!="string"||!e.model.trim()))throw new n(400,"invalid_request","model must be a non-empty string","model");if(e.deadline!==void 0&&(typeof e.deadline!="string"||!e.deadline.trim()))throw new n(400,"invalid_request","deadline must be a duration string such as 10m","deadline");if(e.profile!==void 0&&(typeof e.profile!="string"||!e.profile))throw new n(400,"invalid_request","profile must be a profile id","profile");if(e.isolated!==void 0&&typeof e.isolated!="boolean")throw new n(400,"invalid_request","isolated must be a boolean","isolated");if(e.cwd!==void 0&&(e.isolated!==!0||typeof e.cwd!="string"||!e.cwd))throw new n(400,"invalid_request","cwd is available only for an isolated run and must be an absolute path inside its root folder","cwd");if(e.worktree!==void 0&&e.isolated!==!0)throw new n(400,"invalid_request","worktree is not available","worktree");if(e.isolated===!0&&e.worktree!==void 0)throw new n(400,"invalid_request","worktree is unavailable for an isolated run","worktree");if(typeof e.prompt!="string"||!e.prompt.trim())throw new n(400,"invalid_request","prompt is required","prompt");if(e.message!==void 0&&(typeof e.message!="string"||!e.message.trim()))throw new n(400,"invalid_request","message must be a non-empty string","message");const o=e.metadata===void 0?{}:m(e.metadata);if(Buffer.byteLength(JSON.stringify(o))>4096)throw new n(400,"invalid_request","metadata exceeds 4 KB","metadata");let f;if(e.output_schema!==void 0){const r=e.output_schema;if(!r||typeof r!="object"||Array.isArray(r))throw new n(400,"invalid_request","output_schema must be a JSON Schema object","output_schema");if(Buffer.byteLength(JSON.stringify(r))>T)throw new n(400,"invalid_request","output_schema exceeds 256 KB","output_schema");const p=await P(r);if(p!==null)throw new n(400,"invalid_request",`output_schema is not usable: ${p}`,"output_schema");f=r}const R=D(t.resolved,e.scopes,t.grantee);return{status:201,body:await E(t,{prompt:e.prompt,scopes:R,metadata:o,profile:e.profile,isolated:e.isolated,cwd:e.cwd,...e.model===void 0?{}:{model:e.model},...e.deadline===void 0?{}:{deadline:e.deadline},...f===void 0?{}:{output_schema:f},...e.message===void 0?{}:{message:e.message},...h===void 0?{}:{causedBy:h}},v(_))}}case"run.list":{const e=u.get("status")??void 0;if(e&&!["running","waiting_on_user","idle","settled"].includes(e))throw new n(400,"invalid_request","invalid status","status");const s=u.get("app");if(s!==null&&!/^(?:app:)?[a-z0-9][a-z0-9-]*$/.test(s))throw new n(400,"invalid_request","invalid app","app");const d=s===null?M(t):s.startsWith("app:")?s:`app:${s}`;if(d!==null&&!await y(t,d))throw new n(404,"not_found","run not found");const o=A(t.sub);return{status:200,body:await B(d,{status:e,...o===void 0?{}:{sub:o},after:u.get("after")??void 0,limit:k(u.get("limit"),"limit",1,100,50),..."class"in t&&(t.class==="broker"||t.class==="cron")&&d!==null?{visible:c(async f=>await y(t,d,f),"visible")}:{}})}}case"run.get":{const e=q("run.get",u),s=await w(t,i),d=await K(i,s.grantee,e);return await w(t,i),{status:200,body:d}}case"run.message":{const e=m(l);if(typeof e.message!="string"||!e.message.trim())throw new n(400,"invalid_request","message is required","message");if(e.start_turn!==void 0&&typeof e.start_turn!="boolean")throw new n(400,"invalid_request","start_turn must be a boolean","start_turn");return{status:202,body:await J(await w(t,i,!0),e.message,void 0,e.start_turn,v(_))}}case"run.interrupt":{const e=m(l===void 0?{}:l);if(e.children!==void 0&&e.children!=="stop"&&e.children!=="continue")throw new n(400,"invalid_request","invalid children","children");return{status:200,body:await x(await w(t,i,!0),e.children)}}case"run.cancel":{const e=m(l===void 0?{}:l);if(e.keep_schedules!==void 0&&typeof e.keep_schedules!="boolean")throw new n(400,"invalid_request","invalid keep_schedules","keep_schedules");return{status:200,body:await N(await w(t,i,!0),{keepSchedules:e.keep_schedules})}}case"run.rename":{const e=m(l);for(const s of Object.keys(e))if(s!=="name")throw new n(400,"invalid_request",`unknown field ${s}`,s);return{status:200,body:await z(await w(t,i,!0),e.name)}}case"run.delete":{const{uploads_pending:e}=await j(await w(t,i,!0,!0));return{status:200,body:{run_id:i,uploads_pending:e}}}case"trace.get":{const e=u.get("format")??void 0;if(e&&e!=="entries")throw new n(400,"invalid_request","unknown trace format","format");const s=await w(t,i);return{status:200,body:await G(i,s.grantee,e)}}default:throw new n(404,"not_found","unknown operation")}}c(Y,"handleRunOperation");export{Y as handleRunOperation,k as integer};
1
+ var S=Object.defineProperty;var c=(a,t)=>S(a,"name",{value:t,configurable:!0});import{callerGrant as g}from"../principal.js";import{grantVersion as b,payerOfGrant as O}from"../../../core/grants/mirror.js";import{holdGrant as $}from"../../../core/grants/watch.js";import{RunError as n,idempotencyKeyOf as v,cancelRun as N,cloudPersonScope as A,deleteRun as j,interruptRun as x,listRuns as B,messageRun as J,renameRun as z,requestedScopes as D,startRun as E,traceRun as G,waitRun as K}from"../../../core/runs/operations.js";import{canReadRun as y,requireRun as w,runCallerApp as M}from"../router.js";import{waitSeconds as q}from"../long-poll-wait.js";import{outputSchemaProblem as P}from"../../../core/runtime/structured-output.js";function m(a){if(!a||typeof a!="object"||Array.isArray(a))throw new n(400,"invalid_request","a JSON object is required");return a}c(m,"object");function k(a,t,i,u,l){if(a===null)return l;if(!/^(0|[1-9]\d*)$/.test(a)||!Number.isSafeInteger(Number(a))||Number(a)<i||Number(a)>u)throw new n(400,"invalid_request",`invalid ${t}`,t);return Number(a)}c(k,"integer");const T=256*1024;async function Y(a,t,i,u,l,_,h){switch(a.name){case"grant.get":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=q("grant.get",u),s=u.get("since");if(s!==null&&!/^[A-Za-z0-9_-]{1,64}$/.test(s))throw new n(400,"invalid_request","invalid since","since");const d=e&&s!==null?$(t.grant):void 0;let o;try{if(o=await g(t),d){const f=Date.now()+e*1e3;for(;b(o)===s&&Date.now()<f;)await d.changed(Math.max(0,(f-Date.now())/1e3)),o=await g(t)}}finally{d?.release()}return{status:200,body:{sub:t.sub,grantee:t.grantee,client_id:t.client_id,grant:t.grant,rev:o.rev,resolved_scopes:o.resolved_scopes,spend_cap:o.spend_cap,exp:t.exp,payer:await O(t.grant),dependencies:o.dependencies??{},version:b(o)}}}case"run.start":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=m(l),s=["mode","parent","fork_from","node_id","env","tools","system_prompt"];for(const r of s)if(e[r]!==void 0)throw new n(403,"owner_only",`${r} is owner-only`,r);const d=["payer","model_policy","kind"];for(const r of d)if(e[r]!==void 0)throw new n(400,"invalid_request",`${r} is not available`,r);for(const r of Object.keys(e))if(!["prompt","scopes","metadata","profile","isolated","cwd","worktree","output_schema","message","model","deadline"].includes(r))throw new n(400,"invalid_request",`unknown field ${r}`,r);if(e.model!==void 0&&(typeof e.model!="string"||!e.model.trim()))throw new n(400,"invalid_request","model must be a non-empty string","model");if(e.deadline!==void 0&&(typeof e.deadline!="string"||!e.deadline.trim()))throw new n(400,"invalid_request","deadline must be a duration string such as 10m","deadline");if(e.profile!==void 0&&(typeof e.profile!="string"||!e.profile))throw new n(400,"invalid_request","profile must be a profile id","profile");if(e.isolated!==void 0&&typeof e.isolated!="boolean")throw new n(400,"invalid_request","isolated must be a boolean","isolated");if(e.cwd!==void 0&&(e.isolated!==!0||typeof e.cwd!="string"||!e.cwd))throw new n(400,"invalid_request","cwd is available only for an isolated run and must be an absolute path inside its root folder","cwd");if(e.worktree!==void 0&&e.isolated!==!0)throw new n(400,"invalid_request","worktree is not available","worktree");if(e.isolated===!0&&e.worktree!==void 0)throw new n(400,"invalid_request","worktree is unavailable for an isolated run","worktree");if(typeof e.prompt!="string"||!e.prompt.trim())throw new n(400,"invalid_request","prompt is required","prompt");if(e.message!==void 0&&(typeof e.message!="string"||!e.message.trim()))throw new n(400,"invalid_request","message must be a non-empty string","message");const o=e.metadata===void 0?{}:m(e.metadata);if(Buffer.byteLength(JSON.stringify(o))>4096)throw new n(400,"invalid_request","metadata exceeds 4 KB","metadata");let f;if(e.output_schema!==void 0){const r=e.output_schema;if(!r||typeof r!="object"||Array.isArray(r))throw new n(400,"invalid_request","output_schema must be a JSON Schema object","output_schema");if(Buffer.byteLength(JSON.stringify(r))>T)throw new n(400,"invalid_request","output_schema exceeds 256 KB","output_schema");const p=await P(r);if(p!==null)throw new n(400,"invalid_request",`output_schema is not usable: ${p}`,"output_schema");f=r}const R=D(t.resolved,e.scopes,t.grantee);return{status:201,body:await E(t,{prompt:e.prompt,scopes:R,metadata:o,profile:e.profile,isolated:e.isolated,cwd:e.cwd,...e.model===void 0?{}:{model:e.model},...e.deadline===void 0?{}:{deadline:e.deadline},...f===void 0?{}:{output_schema:f},...e.message===void 0?{}:{message:e.message},...h===void 0?{}:{causedBy:h}},v(_))}}case"run.list":{const e=u.get("status")??void 0;if(e&&!["running","waiting_on_user","idle","settled"].includes(e))throw new n(400,"invalid_request","invalid status","status");const s=u.get("app");if(s!==null&&!/^(?:app:)?[a-z0-9][a-z0-9-]*$/.test(s))throw new n(400,"invalid_request","invalid app","app");const d=s===null?M(t):s.startsWith("app:")?s:`app:${s}`;if(d!==null&&!await y(t,d))throw new n(404,"not_found","run not found");const o=A(t.sub);return{status:200,body:await B(d,{status:e,...o===void 0?{}:{sub:o},after:u.get("after")??void 0,limit:k(u.get("limit"),"limit",1,100,50),..."class"in t&&(t.class==="broker"||t.class==="cron")&&d!==null?{visible:c(async f=>await y(t,d,f),"visible")}:{}})}}case"run.get":{const e=q("run.get",u),s=await w(t,i),d=await K(i,s.grantee,e);return await w(t,i),{status:200,body:d}}case"run.message":{const e=m(l);if(typeof e.message!="string"||!e.message.trim())throw new n(400,"invalid_request","message is required","message");if(e.start_turn!==void 0&&typeof e.start_turn!="boolean")throw new n(400,"invalid_request","start_turn must be a boolean","start_turn");if(e.steer!==void 0&&typeof e.steer!="boolean")throw new n(400,"invalid_request","steer must be a boolean","steer");if(e.start_turn===!1&&e.steer===!0)throw new n(400,"invalid_request","steer requires start_turn","steer");return{status:202,body:await J(await w(t,i,!0),e.message,void 0,e.start_turn,v(_),e.steer)}}case"run.interrupt":{const e=m(l===void 0?{}:l);if(e.children!==void 0&&e.children!=="stop"&&e.children!=="continue")throw new n(400,"invalid_request","invalid children","children");return{status:200,body:await x(await w(t,i,!0),e.children)}}case"run.cancel":{const e=m(l===void 0?{}:l);if(e.keep_schedules!==void 0&&typeof e.keep_schedules!="boolean")throw new n(400,"invalid_request","invalid keep_schedules","keep_schedules");return{status:200,body:await N(await w(t,i,!0),{keepSchedules:e.keep_schedules})}}case"run.rename":{const e=m(l);for(const s of Object.keys(e))if(s!=="name")throw new n(400,"invalid_request",`unknown field ${s}`,s);return{status:200,body:await z(await w(t,i,!0),e.name)}}case"run.delete":{const{uploads_pending:e}=await j(await w(t,i,!0,!0));return{status:200,body:{run_id:i,uploads_pending:e}}}case"trace.get":{const e=u.get("format")??void 0;if(e&&e!=="entries")throw new n(400,"invalid_request","unknown trace format","format");const s=await w(t,i);return{status:200,body:await G(i,s.grantee,e)}}default:throw new n(404,"not_found","unknown operation")}}c(Y,"handleRunOperation");export{Y as handleRunOperation,k as integer};
@@ -1 +1 @@
1
- var q=Object.defineProperty;var d=(e,t)=>q(e,"name",{value:t,configurable:!0});import{existsSync as w,readFileSync as E,readdirSync as g,rmSync as p}from"node:fs";import{join as u}from"node:path";import{cronIdsForProfileDeletion as R,cronIdsForProfileDetach as C,deleteCronsForProfile as T,deleteNodes as O,getNode as m,getRow as W,globalizeCronsForProfile as A,listNodesByProfile as M,nodesRoot as B,reviewDir as $}from"../core/canvas/index.js";import{withCanvasWrite as v}from"../core/canvas/db.js";import{deleteProfileWithOwned as _}from"../core/graph/deletion.js";import{usage as f}from"../core/errors.js";import{emitEvent as z}from"../core/events/emit.js";import{clearDefaultProfileId as k,defaultProfileDirs as L}from"../core/profiles/default-binding.js";import{reserveProfileDeletion as J}from"../core/profiles/deletion-reservation.js";import{deleteProfile as D,loadProfileManifest as G}from"../core/profiles/manifest.js";import{deleteReviewsByOriginNodes as H,reviewIdsByOriginNodes as K}from"../core/review/store.js";import{boundFleet as U}from"../core/runtime/fleet.js";import{headlessBrokerHost as b}from"../core/runtime/host.js";import{transition as Q}from"../core/runtime/lifecycle.js";import{tearDownNode as V}from"../core/runtime/placement.js";import{reshapeNode as X}from"../core/runtime/promote.js";import{reviveNode as Y}from"../core/runtime/revive.js";import{filterAsync as Z,flatMapAsync as ee}from"../core/async-array.js";const P=1e3,N=new Set(["page.json","page.tsx","page.html","page.js","review.json","response.json","progress.json","claim.json","reply-route.json","feedback-comments.json","feedback.json",".ticket-lock"]),h=new Map;function te(e){if(typeof e!="object"||e===null||Array.isArray(e))throw f("profile deletion requires a JSON request body",{error:"force_required",message:"profile deletion requires --force",next:"Re-run `crtr profile delete <profile> --force` after reviewing its effects."});return e}d(te,"requestObject");function re(e){const t=te(e);if(t.force!==!0)throw f("profile deletion requires --force",{error:"force_required",message:"profile deletion requires --force",next:"Re-run `crtr profile delete <profile> --force` after reviewing its effects."});if(t.detach!==void 0&&typeof t.detach!="boolean")throw f("detach must be a boolean",{error:"invalid_detach",message:"detach must be a boolean",next:"Use --detach as a presence-only flag, or omit it."});const r=t.caller_node_id;if(r!=null&&(typeof r!="string"||r===""))throw f("caller_node_id must be a nonempty string or null");return{force:!0,...t.detach===!0?{detach:!0}:{},...r===void 0?{}:{caller_node_id:r}}}d(re,"validateRequest");function j(e){if(!w(e))return null;try{const t=JSON.parse(E(e,"utf8"));return typeof t.source?.nodeId=="string"&&t.source.nodeId!==""?t.source.nodeId:null}catch{return null}}d(j,"manifestSourceNodeId");async function oe(e){const t=new Set(e),r=B();if(t.size===0||!w(r))return[];const o=[];for(const n of g(r,{withFileTypes:!0})){if(!n.isDirectory())continue;const s=u(r,n.name);[j(u(s,"page.json")),j(u(s,"review.json"))].some(c=>c!==null&&t.has(c))&&o.push({dir:s,wholeDirectory:t.has(n.name)||await W(n.name)===null})}return o.sort((n,s)=>n.dir.localeCompare(s.dir))}d(oe,"ticketCleanupForNodes");function ne(e){if(N.has(e))return!0;for(const t of N)if(e.startsWith(`${t}.`)&&(e.endsWith(".tmp")||e.endsWith(".lock")))return!0;return!1}d(ne,"isTicketOwnedResidue");function ie(e){let t=0;for(const r of e)if(w(r.dir)){if(r.wholeDirectory){p(r.dir,{recursive:!0,force:!0}),t+=1;continue}for(const o of g(r.dir,{withFileTypes:!0}))ne(o.name)&&p(u(r.dir,o.name),{recursive:!0,force:!0});t+=1}return t}d(ie,"cleanTickets");function S(e){const t=e.filter(o=>o.managed_worktree?.state==="open").map(o=>({node_id:o.node_id,path:o.managed_worktree.path}));if(t.length===0)return null;const r=`profile deletion is blocked by ${t.length} open managed worktree${t.length===1?"":"s"}`;return f(r,{error:"open_managed_worktree",message:r,nodes:t,next:`Close ${t.map(({node_id:o})=>o).join(", ")} with \`crtr node worktree close <node>\`, then retry.`})}d(S,"openWorktreeError");async function se(e,t){const r=await M(e.profileId),o=S(r);if(o!==null)throw o;const n=r.map(({node_id:a})=>a),s=t.detach===!0?"detached":"deleted";return{entry:e,mode:s,nodes:r,nodeIds:n,tickets:s==="deleted"?await oe(n):[],cronIds:s==="deleted"?await R(e.profileId,n):await C(e.profileId),reviewIds:s==="deleted"?await K(n):[],defaultDirs:L(e.profileId)}}d(se,"prepareDeletion");async function x(e){const t=await ee(e.nodeIds,async o=>{const n=await m(o);return n===null?[]:[n]}),r=S(t);if(r!==null)throw r}d(x,"assertDeletionPlanPreconditions");function y(e){return{profile_id:e.entry.profileId,name:e.entry.manifest.name,mode:e.mode,nodes:e.nodeIds.length,crons:e.cronIds.length,tickets:e.tickets.length,reviews:e.reviewIds.length,default_pins:e.defaultDirs.length}}d(y,"resultForPlan");async function de(e){await x(e);const t=await Z(e.nodeIds,async i=>await m(i)!==null),r=U(),o=t.flatMap(i=>{const l=r.get(i);return l===void 0?[]:[{nodeId:i,exited:l.handle.exited}]}),n=[];for(const{nodeId:i}of o){const l=await m(i);(l?.status==="active"||l?.status==="idle")&&(await Q(i,"release"),n.push(i)),b.teardown(i)}await Promise.all(o.map(({exited:i})=>i));const s=[];for(const i of t)await m(i)!==null&&(await X(i,{profileId:null}),s.push(i));const a=await A(e.entry.profileId),c=await k(e.entry.profileId);await v(async i=>await _(i,e.entry.profileId)),await D(e.entry.profileId);const I=new Set(s);for(const i of n)I.has(i)&&await Y(i,{resume:!0,recovery:!0,capacity:"freeze"});return{...y(e),nodes:s.length,crons:a.length,default_pins:c.length}}d(de,"detachProfile");async function ae(e){await x(e);const t=await T(e.entry.profileId,e.nodeIds);for(const a of e.nodeIds)b.teardown(a),await V(a);const r=await H(e.nodeIds);for(const a of r)p($(a),{recursive:!0,force:!0});const o=ie(e.tickets),n=await O(e.nodeIds),s=await k(e.entry.profileId);return await v(async a=>await _(a,e.entry.profileId)),await D(e.entry.profileId),{...y(e),nodes:n.length,crons:t.length,tickets:o,reviews:r.length,default_pins:s.length}}d(ae,"hardDeleteProfile");async function F(e){return e.mode==="detached"?de(e):await ae(e)}d(F,"executePlan");function ce(e,t){const r={...y(e),accepted:!0,grace_ms:P};return h.set(e.entry.profileId,{result:r}),setTimeout(()=>{(async()=>{try{await F(e)}catch(o){z({level:"error",event:"profile.delete.deferred_failed",error:o instanceof Error?o:new Error(String(o)),fields:{profile_id:e.entry.profileId,mode:e.mode}})}finally{h.delete(e.entry.profileId),t()}})()},P),r}d(ce,"scheduleSelfDeletion");async function xe(e,t){const r=re(t),o=G(e),n=h.get(o.profileId);if(n!==void 0)return{status:202,body:n.result};const s=J(o.profileId);let a=!1;try{const c=await se(o,r);if(r.caller_node_id!=null&&c.nodeIds.includes(r.caller_node_id)){const i=ce(c,s);return a=!0,{status:202,body:i}}return{status:200,body:await F(c)}}finally{a||s()}}d(xe,"requestProfileDeletion");export{xe as requestProfileDeletion};
1
+ var q=Object.defineProperty;var d=(e,t)=>q(e,"name",{value:t,configurable:!0});import{existsSync as w,readFileSync as E,readdirSync as I,rmSync as g}from"node:fs";import{join as u}from"node:path";import{cronIdsForProfileDeletion as R,cronIdsForProfileDetach as C,deleteCronsForProfile as T,deleteNodes as O,getNode as m,getRow as W,globalizeCronsForProfile as A,listNodesByProfile as M,nodesRoot as B}from"../core/canvas/index.js";import{deleteReviewFiles as $}from"../core/review/stage.js";import{withCanvasWrite as v}from"../core/canvas/db.js";import{deleteProfileWithOwned as _}from"../core/graph/deletion.js";import{usage as f}from"../core/errors.js";import{emitEvent as z}from"../core/events/emit.js";import{clearDefaultProfileId as k,defaultProfileDirs as L}from"../core/profiles/default-binding.js";import{reserveProfileDeletion as J}from"../core/profiles/deletion-reservation.js";import{deleteProfile as D,loadProfileManifest as G}from"../core/profiles/manifest.js";import{deleteReviewsByOriginNodes as H,reviewIdsByOriginNodes as K}from"../core/review/store.js";import{boundFleet as U}from"../core/runtime/fleet.js";import{headlessBrokerHost as b}from"../core/runtime/host.js";import{transition as Q}from"../core/runtime/lifecycle.js";import{tearDownNode as V}from"../core/runtime/placement.js";import{reshapeNode as X}from"../core/runtime/promote.js";import{reviveNode as Y}from"../core/runtime/revive.js";import{filterAsync as Z,flatMapAsync as ee}from"../core/async-array.js";const P=1e3,N=new Set(["page.json","page.tsx","page.html","page.js","review.json","response.json","progress.json","claim.json","reply-route.json","feedback-comments.json","feedback.json",".ticket-lock"]),p=new Map;function te(e){if(typeof e!="object"||e===null||Array.isArray(e))throw f("profile deletion requires a JSON request body",{error:"force_required",message:"profile deletion requires --force",next:"Re-run `crtr profile delete <profile> --force` after reviewing its effects."});return e}d(te,"requestObject");function re(e){const t=te(e);if(t.force!==!0)throw f("profile deletion requires --force",{error:"force_required",message:"profile deletion requires --force",next:"Re-run `crtr profile delete <profile> --force` after reviewing its effects."});if(t.detach!==void 0&&typeof t.detach!="boolean")throw f("detach must be a boolean",{error:"invalid_detach",message:"detach must be a boolean",next:"Use --detach as a presence-only flag, or omit it."});const r=t.caller_node_id;if(r!=null&&(typeof r!="string"||r===""))throw f("caller_node_id must be a nonempty string or null");return{force:!0,...t.detach===!0?{detach:!0}:{},...r===void 0?{}:{caller_node_id:r}}}d(re,"validateRequest");function j(e){if(!w(e))return null;try{const t=JSON.parse(E(e,"utf8"));return typeof t.source?.nodeId=="string"&&t.source.nodeId!==""?t.source.nodeId:null}catch{return null}}d(j,"manifestSourceNodeId");async function oe(e){const t=new Set(e),r=B();if(t.size===0||!w(r))return[];const o=[];for(const n of I(r,{withFileTypes:!0})){if(!n.isDirectory())continue;const s=u(r,n.name);[j(u(s,"page.json")),j(u(s,"review.json"))].some(c=>c!==null&&t.has(c))&&o.push({dir:s,wholeDirectory:t.has(n.name)||await W(n.name)===null})}return o.sort((n,s)=>n.dir.localeCompare(s.dir))}d(oe,"ticketCleanupForNodes");function ne(e){if(N.has(e))return!0;for(const t of N)if(e.startsWith(`${t}.`)&&(e.endsWith(".tmp")||e.endsWith(".lock")))return!0;return!1}d(ne,"isTicketOwnedResidue");function ie(e){let t=0;for(const r of e)if(w(r.dir)){if(r.wholeDirectory){g(r.dir,{recursive:!0,force:!0}),t+=1;continue}for(const o of I(r.dir,{withFileTypes:!0}))ne(o.name)&&g(u(r.dir,o.name),{recursive:!0,force:!0});t+=1}return t}d(ie,"cleanTickets");function S(e){const t=e.filter(o=>o.managed_worktree?.state==="open").map(o=>({node_id:o.node_id,path:o.managed_worktree.path}));if(t.length===0)return null;const r=`profile deletion is blocked by ${t.length} open managed worktree${t.length===1?"":"s"}`;return f(r,{error:"open_managed_worktree",message:r,nodes:t,next:`Close ${t.map(({node_id:o})=>o).join(", ")} with \`crtr node worktree close <node>\`, then retry.`})}d(S,"openWorktreeError");async function se(e,t){const r=await M(e.profileId),o=S(r);if(o!==null)throw o;const n=r.map(({node_id:a})=>a),s=t.detach===!0?"detached":"deleted";return{entry:e,mode:s,nodes:r,nodeIds:n,tickets:s==="deleted"?await oe(n):[],cronIds:s==="deleted"?await R(e.profileId,n):await C(e.profileId),reviewIds:s==="deleted"?await K(n):[],defaultDirs:L(e.profileId)}}d(se,"prepareDeletion");async function F(e){const t=await ee(e.nodeIds,async o=>{const n=await m(o);return n===null?[]:[n]}),r=S(t);if(r!==null)throw r}d(F,"assertDeletionPlanPreconditions");function h(e){return{profile_id:e.entry.profileId,name:e.entry.manifest.name,mode:e.mode,nodes:e.nodeIds.length,crons:e.cronIds.length,tickets:e.tickets.length,reviews:e.reviewIds.length,default_pins:e.defaultDirs.length}}d(h,"resultForPlan");async function de(e){await F(e);const t=await Z(e.nodeIds,async i=>await m(i)!==null),r=U(),o=t.flatMap(i=>{const l=r.get(i);return l===void 0?[]:[{nodeId:i,exited:l.handle.exited}]}),n=[];for(const{nodeId:i}of o){const l=await m(i);(l?.status==="active"||l?.status==="idle")&&(await Q(i,"release"),n.push(i)),b.teardown(i)}await Promise.all(o.map(({exited:i})=>i));const s=[];for(const i of t)await m(i)!==null&&(await X(i,{profileId:null}),s.push(i));const a=await A(e.entry.profileId),c=await k(e.entry.profileId);await v(async i=>await _(i,e.entry.profileId)),await D(e.entry.profileId);const y=new Set(s);for(const i of n)y.has(i)&&await Y(i,{resume:!0,recovery:!0,capacity:"freeze"});return{...h(e),nodes:s.length,crons:a.length,default_pins:c.length}}d(de,"detachProfile");async function ae(e){await F(e);const t=await T(e.entry.profileId,e.nodeIds);for(const a of e.nodeIds)b.teardown(a),await V(a);const r=await H(e.nodeIds);for(const a of r)await $(a);const o=ie(e.tickets),n=await O(e.nodeIds),s=await k(e.entry.profileId);return await v(async a=>await _(a,e.entry.profileId)),await D(e.entry.profileId),{...h(e),nodes:n.length,crons:t.length,tickets:o,reviews:r.length,default_pins:s.length}}d(ae,"hardDeleteProfile");async function x(e){return e.mode==="detached"?de(e):await ae(e)}d(x,"executePlan");function ce(e,t){const r={...h(e),accepted:!0,grace_ms:P};return p.set(e.entry.profileId,{result:r}),setTimeout(()=>{(async()=>{try{await x(e)}catch(o){z({level:"error",event:"profile.delete.deferred_failed",error:o instanceof Error?o:new Error(String(o)),fields:{profile_id:e.entry.profileId,mode:e.mode}})}finally{p.delete(e.entry.profileId),t()}})()},P),r}d(ce,"scheduleSelfDeletion");async function xe(e,t){const r=re(t),o=G(e),n=p.get(o.profileId);if(n!==void 0)return{status:202,body:n.result};const s=J(o.profileId);let a=!1;try{const c=await se(o,r);if(r.caller_node_id!=null&&c.nodeIds.includes(r.caller_node_id)){const i=ce(c,s);return a=!0,{status:202,body:i}}return{status:200,body:await x(c)}}finally{a||s()}}d(xe,"requestProfileDeletion");export{xe as requestProfileDeletion};
@@ -1 +1 @@
1
- var g=Object.defineProperty;var n=(e,t)=>g(e,"name",{value:t,configurable:!0});import{withCanvasWrite as j}from"../../core/canvas/db.js";import{get as S}from"../../core/graph/bodies.js";import{documentById as q,editDocument as C,heads as $}from"../../core/graph/documents.js";import{readerFor as k}from"../../core/graph/reader.js";import{warmDocumentWatchers as J}from"../api/reader.js";import{checkoutsForCwd as x}from"../../core/graph/repo-sync/repos.js";import{snapshotCommentsLocked as N}from"../../core/review/comments.js";import{readReviewSource as O}from"../../core/review/document.js";import{projectReviewCancellation as T,projectReviewResult as D,buildReviewResult as A}from"../../core/review/project.js";import{signalReviewActivity as p}from"../../core/review/signal.js";import{clearReviewSubmit as E,getReview as W,getReviewByCompanion as B,requestReviewSubmit as F,requireVisibleReview as u}from"../../core/review/store.js";import{ReviewOperationError as w}from"../../core/review/types.js";import{settleHumanRequest as v}from"../human/settle.js";import{retireCompanion as h}from"../companion-retire.js";import{companionHasQueuedWork as H,companionMidTurn as M,companionStillWorking as L}from"./companion.js";import{renderApprovalMessage as P}from"./deliver.js";function f(){return new Date().toISOString()}n(f,"now");function l(e,t){throw new w("review_terminal","review already has a terminal outcome",{state:e.state,requested:t,review:e})}n(l,"terminalConflict");function _(e){if(e.result_json===null)throw new Error(`approved review lacks its immutable result: ${e.review_id}`);return JSON.parse(e.result_json)}n(_,"approvedResult");function y(e){return`${e.review_dir}/result.json`}n(y,"resultPath");function Q(e){return{project:n(async t=>{D(await u(t.request_id),e)},"project"),retire:n(async(t,o)=>{await h(t,o)},"retire")}}n(Q,"effects");function V(e){return{label:"review approved by the user",disposition:null,body_json:{review_id:e.review_id,result_path:y(e),body:P(e,y(e))}}}n(V,"approvalNotification");function z(e,t,o){const a=o==="dismissed";return{label:a?"review dismissed":"review canceled",disposition:"human-canceled",body_json:{review_id:e.review_id,disposition:o,body:a?`The review of \`${e.document_name??e.file}\` was dismissed without approval.`:`The review of \`${e.document_name??e.file}\` was canceled${t===""?"":` \u2014 ${t}`}.`}}}n(z,"cancellationNotification");async function G(e,t,o){if(t.document_id===null||t.document_base===null)return;const a=n(async d=>await x(e,d),"reposFn"),i=await q(e,t.document_id),s=await k(e,{class:"person"},a),c=await C(e,s,i,{body:o},`edited in review ${t.review_id}`,t.document_base,{},a);if(!("stale"in c))return;const r=await $(e,i);if(!(r.length===1&&S(r[0].body_hash)===o))throw new w("stale_revision",`${t.document_name??i.name} changed since the review began; the reviewed text was not saved`,{review_id:t.review_id,document:t.document_name??i.name,base:t.document_base,heads_key:c.stale.heads_key,revision:c.stale.revision,next:`Read the current revision with \`crtr canvas read ${i.id}\`, then start a new review of it.`})}n(G,"saveReviewedDocument");async function b(e){if(e.state==="canceled")return l(e,"submit");if(e.state==="approved")return{outcome:"already_settled",record:e,result:_(e)};const t=O(e.file,"submit");e.document_id!==null&&await J(e.document_id);const{result:o,resultJson:a}=await j(async c=>{const r=await u(e.review_id);if(r.state==="canceled")return l(r,"submit");if(r.state==="approved")return{result:_(r),resultJson:r.result_json};const d=await N(c,r.review_id),m=A(r,d,t.sha256,f());return m.changed&&await G(c,r,new TextDecoder("utf-8").decode(t.bytes)),{result:m,resultJson:JSON.stringify(m)}}),i=await v({request_id:e.review_id,outcome:"approved",result_json:a,notification:V({...e,changed:o.changed}),post_commit:Q(o)});i.outcome==="settled"&&p();const s=await u(e.review_id);return i.outcome==="already_settled"&&s.state!=="approved"?l(s,"submit"):{outcome:i.outcome,record:s,result:i.outcome==="settled"?o:_(s)}}n(b,"approveReview");async function le(e){const t=await u(e);return t.state==="canceled"?l(t,"submit"):t.state==="open"&&await L(t.companion_node_id)?(await F(e,f())&&p(),{outcome:"awaiting_companion",record:await u(e)}):b(t)}n(le,"submitReviewApproval");async function K(e,t){const o=await W(e);if(o===null||o.state!=="open"||o.submit_requested_at===null||await H(o.companion_node_id)||!t.atCompanionTurnEnd&&await M(o.companion_node_id))return null;try{return await b(o)}catch(a){throw a instanceof w&&a.code==="stale_revision"&&(await E(e),p()),a}}n(K,"completeRequestedSubmit");async function pe(e){const t=await B(e);return t===null?null:K(t.review_id,{atCompanionTurnEnd:!0})}n(pe,"completeCompanionRequestedSubmit");async function we(e,t={}){const o=await u(e);if(o.state==="approved")return l(o,"cancel");const a=f(),i=t.reason??"",s=t.disposition??"canceled",c=JSON.stringify({schema:"humanloop.cancel/v1",kind:"canceled",canceledAt:a,...i===""?{}:{reason:i},...t.actor===void 0?{}:{actor:t.actor},...s==="dismissed"?{disposition:s}:{}}),r=await v({request_id:e,outcome:s==="dismissed"?"dismissed":"canceled",result_json:c,notification:z(o,i,s),post_commit:{project:n(async m=>{T(await u(m.request_id),JSON.parse(c))},"project"),retire:n(async(m,R)=>{await h(m,R)},"retire")}});r.outcome==="settled"&&p();const d=await u(e);return r.outcome==="already_settled"&&d.state==="approved"?l(d,"cancel"):{outcome:r.outcome,record:d}}n(we,"cancelReview");export{we as cancelReview,pe as completeCompanionRequestedSubmit,K as completeRequestedSubmit,le as submitReviewApproval};
1
+ var g=Object.defineProperty;var n=(e,t)=>g(e,"name",{value:t,configurable:!0});import{withCanvasWrite as j}from"../../core/canvas/db.js";import{get as S}from"../../core/graph/bodies.js";import{documentById as q,editDocument as C,heads as $}from"../../core/graph/documents.js";import{readerFor as k}from"../../core/graph/reader.js";import{warmDocumentWatchers as J}from"../api/reader.js";import{checkoutsForCwd as x}from"../../core/graph/repo-sync/repos.js";import{snapshotCommentsLocked as N}from"../../core/review/comments.js";import{readReviewSource as O}from"../../core/review/document.js";import{projectReviewCancellation as T,projectReviewResult as D,buildReviewResult as A}from"../../core/review/project.js";import{signalReviewActivity as p}from"../../core/review/signal.js";import{clearReviewSubmit as E,getReview as W,getReviewByCompanion as B,requestReviewSubmit as F,requireVisibleReview as u}from"../../core/review/store.js";import{ReviewOperationError as w}from"../../core/review/types.js";import{settleHumanRequest as v}from"../human/settle.js";import{retireCompanion as h}from"../companion-retire.js";import{companionHasQueuedWork as H,companionMidTurn as M,companionStillWorking as L}from"./companion.js";import{renderApprovalMessage as P}from"./deliver.js";function f(){return new Date().toISOString()}n(f,"now");function l(e,t){throw new w("review_terminal","review already has a terminal outcome",{state:e.state,requested:t,review:e})}n(l,"terminalConflict");function _(e){if(e.result_json===null)throw new Error(`approved review lacks its immutable result: ${e.review_id}`);return JSON.parse(e.result_json)}n(_,"approvedResult");function y(e){return`${e.review_dir}/result.json`}n(y,"resultPath");function Q(e){return{project:n(async t=>{await D(await u(t.request_id),e)},"project"),retire:n(async(t,o)=>{await h(t,o)},"retire")}}n(Q,"effects");function V(e){return{label:"review approved by the user",disposition:null,body_json:{review_id:e.review_id,result_path:y(e),body:P(e,y(e))}}}n(V,"approvalNotification");function z(e,t,o){const i=o==="dismissed";return{label:i?"review dismissed":"review canceled",disposition:"human-canceled",body_json:{review_id:e.review_id,disposition:o,body:i?`The review of \`${e.document_name??e.file}\` was dismissed without approval.`:`The review of \`${e.document_name??e.file}\` was canceled${t===""?"":` \u2014 ${t}`}.`}}}n(z,"cancellationNotification");async function G(e,t,o){if(t.document_id===null||t.document_base===null)return;const i=n(async d=>await x(e,d),"reposFn"),a=await q(e,t.document_id),s=await k(e,{class:"person"},i),c=await C(e,s,a,{body:o},`edited in review ${t.review_id}`,t.document_base,{},i);if(!("stale"in c))return;const r=await $(e,a);if(!(r.length===1&&S(r[0].body_hash)===o))throw new w("stale_revision",`${t.document_name??a.name} changed since the review began; the reviewed text was not saved`,{review_id:t.review_id,document:t.document_name??a.name,base:t.document_base,heads_key:c.stale.heads_key,revision:c.stale.revision,next:`Read the current revision with \`crtr canvas read ${a.id}\`, then start a new review of it.`})}n(G,"saveReviewedDocument");async function b(e){if(e.state==="canceled")return l(e,"submit");if(e.state==="approved")return{outcome:"already_settled",record:e,result:_(e)};const t=await O(e.file,"submit");e.document_id!==null&&await J(e.document_id);const{result:o,resultJson:i}=await j(async c=>{const r=await u(e.review_id);if(r.state==="canceled")return l(r,"submit");if(r.state==="approved")return{result:_(r),resultJson:r.result_json};const d=await N(c,r.review_id),m=A(r,d,t.sha256,f());return m.changed&&await G(c,r,new TextDecoder("utf-8").decode(t.bytes)),{result:m,resultJson:JSON.stringify(m)}}),a=await v({request_id:e.review_id,outcome:"approved",result_json:i,notification:V({...e,changed:o.changed}),post_commit:Q(o)});a.outcome==="settled"&&p();const s=await u(e.review_id);return a.outcome==="already_settled"&&s.state!=="approved"?l(s,"submit"):{outcome:a.outcome,record:s,result:a.outcome==="settled"?o:_(s)}}n(b,"approveReview");async function le(e){const t=await u(e);return t.state==="canceled"?l(t,"submit"):t.state==="open"&&await L(t.companion_node_id)?(await F(e,f())&&p(),{outcome:"awaiting_companion",record:await u(e)}):b(t)}n(le,"submitReviewApproval");async function K(e,t){const o=await W(e);if(o===null||o.state!=="open"||o.submit_requested_at===null||await H(o.companion_node_id)||!t.atCompanionTurnEnd&&await M(o.companion_node_id))return null;try{return await b(o)}catch(i){throw i instanceof w&&i.code==="stale_revision"&&(await E(e),p()),i}}n(K,"completeRequestedSubmit");async function pe(e){const t=await B(e);return t===null?null:K(t.review_id,{atCompanionTurnEnd:!0})}n(pe,"completeCompanionRequestedSubmit");async function we(e,t={}){const o=await u(e);if(o.state==="approved")return l(o,"cancel");const i=f(),a=t.reason??"",s=t.disposition??"canceled",c=JSON.stringify({schema:"humanloop.cancel/v1",kind:"canceled",canceledAt:i,...a===""?{}:{reason:a},...t.actor===void 0?{}:{actor:t.actor},...s==="dismissed"?{disposition:s}:{}}),r=await v({request_id:e,outcome:s==="dismissed"?"dismissed":"canceled",result_json:c,notification:z(o,a,s),post_commit:{project:n(async m=>{await T(await u(m.request_id),JSON.parse(c))},"project"),retire:n(async(m,R)=>{await h(m,R)},"retire")}});r.outcome==="settled"&&p();const d=await u(e);return r.outcome==="already_settled"&&d.state==="approved"?l(d,"cancel"):{outcome:r.outcome,record:d}}n(we,"cancelReview");export{we as cancelReview,pe as completeCompanionRequestedSubmit,K as completeRequestedSubmit,le as submitReviewApproval};
@@ -1 +1 @@
1
- var o=Object.defineProperty;var i=(r,e)=>o(r,"name",{value:e,configurable:!0});import{requireVisibleReview as t}from"../../core/review/store.js";import{projectReviewResult as s}from"../../core/review/project.js";import{realizeReview as n}from"../../core/review/realize.js";import{retireCompanion as a}from"../companion-retire.js";import{sweepHumanRequests as m}from"../human/sweep.js";async function f(){return m({realizeReview:n,projectReview:i(async r=>{const e=await t(r.request_id);if(e.result_json===null)throw new Error(`approved review lacks its immutable result: ${e.review_id}`);s(e,JSON.parse(e.result_json))},"projectReview"),retireCompanion:i(async(r,e)=>{await a(r,e)},"retireCompanion")})}i(f,"sweepReviews");export{f as sweepReviews};
1
+ var t=Object.defineProperty;var i=(r,e)=>t(r,"name",{value:e,configurable:!0});import{requireVisibleReview as o}from"../../core/review/store.js";import{projectReviewResult as s}from"../../core/review/project.js";import{realizeReview as a}from"../../core/review/realize.js";import{retireCompanion as n}from"../companion-retire.js";import{sweepHumanRequests as m}from"../human/sweep.js";async function f(){return m({realizeReview:a,projectReview:i(async r=>{const e=await o(r.request_id);if(e.result_json===null)throw new Error(`approved review lacks its immutable result: ${e.review_id}`);await s(e,JSON.parse(e.result_json))},"projectReview"),retireCompanion:i(async(r,e)=>{await n(r,e)},"retireCompanion")})}i(f,"sweepReviews");export{f as sweepReviews};
@@ -1,2 +1,2 @@
1
- var T=Object.defineProperty;var _=(t,e)=>T(t,"name",{value:e,configurable:!0});import{appendRunEvent as c}from"../../core/runs/events.js";import{openQuestionIds as x}from"../../core/runs/questions.js";import{getNode as N}from"../../core/canvas/canvas.js";import{payerOfGrant as h}from"../../core/grants/mirror.js";import{runSchemaRepromptPending as S}from"../../core/runtime/stop-guard.js";const k="crtr-turn-input",D="crtr-system-prompt",g="crtr-turn-end",w=_(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"zero");function O(t){return typeof t=="string"?t:Array.isArray(t)?t.filter(e=>typeof e?.text=="string").map(e=>e.text).join(`
2
- `):""}_(O,"text");function j(t){if(t==null)return;const{code:e,message:n}=t;if(e!=="content_refused"&&e!=="model_error"||typeof n!="string")throw new Error("invalid crtr-turn-end error");return{code:e,message:n}}_(j,"turnEndError");function A(t){return{input_tokens:t.input??0,output_tokens:t.output??0,cache_read_tokens:t.cacheRead??0,cache_write_tokens:t.cacheWrite??0,cost_usd:t.cost?.total??0}}_(A,"usageParts");async function v(t,e,n){if(e!==t||!n.some(f=>f.type==="custom"&&f.customType===g))return!1;const m=await N(t);return m!==null&&await S(m)}_(v,"rootRepromptPending");async function Y(t,e,n,m,f,R){let d=f;for(const r of m){if(r.type==="custom"&&r.customType===k){const o=r.data?.message_ids;if(!Array.isArray(o)||!o.every(i=>typeof i=="string"))throw new Error("invalid crtr-turn-input message_ids");d=[...new Set([...d,...o])]}if(r.type==="custom"&&r.customType===D){const o=r.data?.text;if(typeof o!="string")throw new Error("invalid crtr-system-prompt text");await c(t,e,n,"node.system_prompt.set",{entry_id:r.id,text:o})}if(r.type==="custom"&&r.customType===g){const o=r.data,i=o?.stop_reason;if(typeof i!="string")throw new Error("invalid crtr-turn-end stop_reason");const a=j(o?.error),s=await t.prepare("SELECT body_json FROM run_events WHERE run_id = ? AND node_id = ? AND type = 'node.turn.started' ORDER BY sequence_number DESC LIMIT 1").get(e,n);if(!s)throw new Error("crtr-turn-end without node.turn.started");const{input:p}=JSON.parse(s.body_json),y=await t.prepare("SELECT status, pi_pid FROM node_rows WHERE run_id = ? AND node_id != ?").all(e,n),u=R??await v(e,n,[r]),l=y.some(E=>E.pi_pid!==null&&E.status==="active")||u?"running":(await x(e,t)).length?"waiting_on_user":"idle";await c(t,e,n,"node.turn.completed",{entry_id:r.id,message_ids:p.message_ids,run_status:l,stop_reason:i,...a?{error:a}:{}}),n===e&&await t.prepare("UPDATE nodes SET last_turn_error_json = ? WHERE node_id = ?").run(a?JSON.stringify(a):null,e)}if(r.type==="message"){const o=r.message;if(o.role==="user"){const i=await t.prepare("SELECT type, body_json FROM run_events WHERE run_id = ? AND node_id = ? AND type IN ('node.turn.started', 'node.turn.completed') ORDER BY sequence_number DESC LIMIT 1").get(e,n),a=i?.type==="node.turn.started"?JSON.parse(i.body_json).input.message_ids:[];await c(t,e,n,"node.turn.started",{entry_id:r.id,input:{text:O(o.content),message_ids:[...new Set([...a,...d])]}}),d=[]}if(o.role==="assistant"){let i=!1;const a=_(async()=>{if(i)return;i=!0;const s=o.content.filter(p=>p.type==="text").map(p=>p.text).join("");await c(t,e,n,"node.output_text.done",{entry_id:r.id,text:s,stop_reason:o.stopReason})},"doneText");for(const s of o.content)s.type==="thinking"&&await c(t,e,n,"node.thinking.done",{entry_id:r.id,text:s.thinking}),s.type==="text"&&await a(),s.type==="toolCall"&&(await a(),await c(t,e,n,"node.tool_call.started",{entry_id:r.id,tool_call_id:s.id,name:s.name,arguments:s.arguments}));if(await a(),o.usage){const s=A(o.usage),p=await t.prepare("SELECT usage_json, grant_id FROM nodes WHERE node_id = ?").get(e),y=p.usage_json?JSON.parse(p.usage_json):w(),u=w();for(const l of Object.keys(u))u[l]=y[l]+s[l];await t.prepare("UPDATE nodes SET usage_json = ? WHERE node_id = ?").run(JSON.stringify(u),e),await c(t,e,n,"usage",{entry_id:r.id,served_model:o.model,payer:await h(p.grant_id),...s,usage:u})}await t.prepare("DELETE FROM run_events WHERE run_id = ? AND node_id = ? AND type IN ('node.thinking.delta', 'node.output_text.delta')").run(e,n)}o.role==="toolResult"&&await c(t,e,n,"node.tool_call.completed",{entry_id:r.id,tool_call_id:o.toolCallId,name:o.toolName,result:o.content,status:o.isError?"error":"ok"})}}return d}_(Y,"appendConversationEvents");export{D as SYSTEM_PROMPT_TYPE,g as TURN_END_TYPE,k as TURN_INPUT_TYPE,Y as appendConversationEvents,v as rootRepromptPending};
1
+ var T=Object.defineProperty;var _=(t,e)=>T(t,"name",{value:e,configurable:!0});import{appendRunEvent as c}from"../../core/runs/events.js";import{openQuestionIds as x}from"../../core/runs/questions.js";import{getNode as h}from"../../core/canvas/canvas.js";import{payerOfGrant as N}from"../../core/grants/mirror.js";import{runSchemaRepromptPending as S}from"../../core/runtime/stop-guard.js";const k="crtr-turn-input",D="crtr-system-prompt",g="crtr-turn-end",w=_(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"zero");function O(t){return typeof t=="string"?t:Array.isArray(t)?t.filter(e=>typeof e?.text=="string").map(e=>e.text).join(`
2
+ `):""}_(O,"text");function j(t){if(t==null)return;const{code:e,message:o,retry_at:u}=t;if(e!=="content_refused"&&e!=="model_error"&&e!=="rate_limited"||typeof o!="string")throw new Error("invalid crtr-turn-end error");if(e==="rate_limited"){if(typeof u!="string")throw new Error("invalid crtr-turn-end error");return{code:e,message:o,retry_at:u}}return{code:e,message:o}}_(j,"turnEndError");function v(t){return{input_tokens:t.input??0,output_tokens:t.output??0,cache_read_tokens:t.cacheRead??0,cache_write_tokens:t.cacheWrite??0,cost_usd:t.cost?.total??0}}_(v,"usageParts");async function A(t,e,o){if(e!==t||!o.some(f=>f.type==="custom"&&f.customType===g))return!1;const u=await h(t);return u!==null&&await S(u)}_(A,"rootRepromptPending");async function Y(t,e,o,u,f,R){let l=f;for(const n of u){if(n.type==="custom"&&n.customType===k){const r=n.data?.message_ids;if(!Array.isArray(r)||!r.every(i=>typeof i=="string"))throw new Error("invalid crtr-turn-input message_ids");l=[...new Set([...l,...r])]}if(n.type==="custom"&&n.customType===D){const r=n.data?.text;if(typeof r!="string")throw new Error("invalid crtr-system-prompt text");await c(t,e,o,"node.system_prompt.set",{entry_id:n.id,text:r})}if(n.type==="custom"&&n.customType===g){const r=n.data,i=r?.stop_reason;if(typeof i!="string")throw new Error("invalid crtr-turn-end stop_reason");const a=j(r?.error),s=await t.prepare("SELECT body_json FROM run_events WHERE run_id = ? AND node_id = ? AND type = 'node.turn.started' ORDER BY sequence_number DESC LIMIT 1").get(e,o);if(!s)throw new Error("crtr-turn-end without node.turn.started");const{input:p}=JSON.parse(s.body_json),y=await t.prepare("SELECT status, pi_pid FROM node_rows WHERE run_id = ? AND node_id != ?").all(e,o),d=R??await A(e,o,[n]),m=y.some(E=>E.pi_pid!==null&&E.status==="active")||d?"running":(await x(e,t)).length?"waiting_on_user":"idle";await c(t,e,o,"node.turn.completed",{entry_id:n.id,message_ids:p.message_ids,run_status:m,stop_reason:i,...a?{error:a}:{}}),o===e&&await t.prepare("UPDATE nodes SET last_turn_error_json = ? WHERE node_id = ?").run(a?JSON.stringify(a):null,e)}if(n.type==="message"){const r=n.message;if(r.role==="user"){const i=await t.prepare("SELECT type, body_json FROM run_events WHERE run_id = ? AND node_id = ? AND type IN ('node.turn.started', 'node.turn.completed') ORDER BY sequence_number DESC LIMIT 1").get(e,o),a=i?.type==="node.turn.started"?JSON.parse(i.body_json).input.message_ids:[];await c(t,e,o,"node.turn.started",{entry_id:n.id,input:{text:O(r.content),message_ids:[...new Set([...a,...l])]}}),l=[]}if(r.role==="assistant"){let i=!1;const a=_(async()=>{if(i)return;i=!0;const s=r.content.filter(p=>p.type==="text").map(p=>p.text).join("");await c(t,e,o,"node.output_text.done",{entry_id:n.id,text:s,stop_reason:r.stopReason})},"doneText");for(const s of r.content)s.type==="thinking"&&await c(t,e,o,"node.thinking.done",{entry_id:n.id,text:s.thinking}),s.type==="text"&&await a(),s.type==="toolCall"&&(await a(),await c(t,e,o,"node.tool_call.started",{entry_id:n.id,tool_call_id:s.id,name:s.name,arguments:s.arguments}));if(await a(),r.usage){const s=v(r.usage),p=await t.prepare("SELECT usage_json, grant_id FROM nodes WHERE node_id = ?").get(e),y=p.usage_json?JSON.parse(p.usage_json):w(),d=w();for(const m of Object.keys(d))d[m]=y[m]+s[m];await t.prepare("UPDATE nodes SET usage_json = ? WHERE node_id = ?").run(JSON.stringify(d),e),await c(t,e,o,"usage",{entry_id:n.id,served_model:r.model,payer:await N(p.grant_id),...s,usage:d})}await t.prepare("DELETE FROM run_events WHERE run_id = ? AND node_id = ? AND type IN ('node.thinking.delta', 'node.output_text.delta')").run(e,o)}r.role==="toolResult"&&await c(t,e,o,"node.tool_call.completed",{entry_id:n.id,tool_call_id:r.toolCallId,name:r.toolName,result:r.content,status:r.isError?"error":"ok"})}}return l}_(Y,"appendConversationEvents");export{D as SYSTEM_PROMPT_TYPE,g as TURN_END_TYPE,k as TURN_INPUT_TYPE,Y as appendConversationEvents,A as rootRepromptPending};
@@ -1,6 +1,6 @@
1
- var k=Object.defineProperty;var l=(n,e)=>k(n,"name",{value:e,configurable:!0});import{randomUUID as v}from"node:crypto";import{openDb as d,withCanvasWrite as L}from"../../core/canvas/db.js";import{claimReconcilerLease as b,releaseReconcilerLease as O,renewReconcilerLease as C}from"../../core/canvas/reconciler-leases.js";import{getNode as R}from"../../core/canvas/canvas.js";import{BrokerClient as F,SocketTransport as y}from"../../core/broker-client/client.js";import{liveExecutionForNode as M}from"../../core/canvas/executions.js";import{runnerForExecution as x}from"../../core/runtime/runner/index.js";import{connectWssView as H}from"../../core/runtime/view-socket.js";import{ViewLinkTransport as W}from"./view-transport.js";import{emitRunEvent as w,emitRunSettlement as N,emitRunStatus as q}from"../../core/runs/events.js";import{cancelOpenQuestions as U}from"../../core/runs/questions.js";import{emitEvent as c}from"../../core/events/emit.js";import{toRunOutcomeDTO as j}from"../api/map.js";import{readOutputResult as J}from"../../core/runtime/output-result.js";const P=JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),p=new Map,h="run-observer",g=1e4,B=250;async function f(n,e,t){return await L(async i=>await i.prepare("SELECT 1 AS held FROM reconciler_leases WHERE lane = ? AND key = ? AND owner = ? AND expires_at > ? FOR SHARE").get(h,n,e,new Date().toISOString())===void 0?{held:!1}:{held:!0,value:await t()})}l(f,"whileHoldingLease");class S{static{l(this,"RunObserver")}nodeId;runId;owner;client;timer;status;piPid=null;outcomeRevision;openAssistant=!1;assistantStartSequence=0;block=null;thinking="";output="";pendingThinking="";pendingOutput="";flushTimer;closed=!1;lastRenewAt=Date.now();released=Promise.resolve();polling;frames=Promise.resolve();constructor(e,t,i,s,r){this.nodeId=e,this.runId=t,this.owner=i,this.status=s,this.outcomeRevision=r,this.timer=setInterval(()=>{this.pollOrClose()},250),this.timer.unref(),p.set(e,this)}static async start(e,t,i){const s=await d(),r=await s.prepare("SELECT body_json FROM run_events WHERE node_id = ? AND type = ? ORDER BY sequence_number DESC LIMIT 1").get(e,e===t?"node.settled":"child.settled"),o=await s.prepare("SELECT body_json FROM run_events WHERE node_id = ? AND type = 'node.status.changed' ORDER BY sequence_number DESC LIMIT 1").get(e),a=new S(e,t,i,o?JSON.parse(o.body_json).status:void 0,r?JSON.parse(r.body_json).outcome.revision:void 0);return await a.writeStream(null,null),await a.poll(),a}async pollOrClose(){try{await this.poll()}catch(e){this.close(),c({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:e})}}poll(){return this.polling===void 0&&(this.polling=this.pollOnce().finally(()=>{this.polling=void 0})),this.polling}async holdLease(){return Date.now()-this.lastRenewAt<g/3?!0:await C(h,this.nodeId,this.owner,g)?(this.lastRenewAt=Date.now(),!0):!1}async writeStream(e,t,i=!1){return(await(await d()).prepare(`UPDATE nodes SET ${i?"":`stream_message_seq = ${e===null?"NULL":"(SELECT event_cursor FROM nodes WHERE node_id = ?)"},`} stream_block = ?
2
- WHERE node_id = ? AND EXISTS (SELECT 1 FROM reconciler_leases WHERE lane = ? AND key = ? AND owner = ? AND expires_at > ?)`).run(...e===null||i?[]:[this.runId],t===null?null:JSON.stringify(t),this.nodeId,h,this.nodeId,this.owner,new Date().toISOString())).changes===1}async pollOnce(){if(this.closed)return;if(!await this.holdLease()){this.close();return}const e=await R(this.nodeId);if(!e||e.run_id!==this.runId||!await D(this.runId)){this.close();return}this.piPid=e.pi_pid??null;const t=e.pi_pid===null?"dormant":e.status;if(this.status!==t){if(!(await f(this.nodeId,this.owner,()=>q(this.runId,this.nodeId,t,e.intent??null))).held){this.close();return}this.status=t}const i=await j(e);if(i&&i.revision!==this.outcomeRevision&&Date.now()-Date.parse(i.settled_at)<720*60*6e4){if(this.nodeId===this.runId){const o=await(await d()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ? ORDER BY created DESC").all(this.runId,this.runId);for(const a of o)await T(a.node_id,this.runId),await p.get(a.node_id)?.poll()}this.outcomeRevision=i.revision;const s=this.nodeId===this.runId&&i.kind==="result"?await J(this.nodeId):void 0;if(!(await f(this.nodeId,this.owner,()=>this.nodeId===this.runId?N(this.runId,this.nodeId,"node.settled",{outcome:i,usage:JSON.parse(e.usage_json??P),...s===void 0?{}:{result:s}}):N(this.runId,this.nodeId,"child.settled",{child_node_id:this.nodeId,outcome:i}))).held){this.close();return}if(e.grantee&&e.grantee!=="app:terminal"){const o=this.nodeId===this.runId?{run_id:this.runId}:{node_id:this.nodeId};U(o,this.nodeId===this.runId?"the run settled":"the asking node was closed").catch(a=>c({level:"error",event:"daemon.runs.question_cancel_failed",node_id:this.nodeId,error:a}))}}e.pi_pid===null?this.close():await this.connect()}async settlePoll(){await this.polling?.catch(()=>{}),await this.frames,await this.released}async connect(){if(this.client||this.closed)return;const e=await V(this.nodeId);if(this.client||this.closed){e.destroy();return}const t=new F(this.nodeId,{transport:e});this.client=t,t.on("connect",()=>t.send({type:"hello",role:"observer",client_id:v(),attends:!1}));const i=l(async s=>{if(!(this.closed||this.client!==t)){if(s.type==="message_start"&&s.message.role==="assistant"){if(this.discardDeltas(),this.openAssistant=!0,this.block=null,this.thinking="",this.output="",!await this.writeStream("run-cursor",null)){this.close();return}this.assistantStartSequence=Number((await(await d()).prepare("SELECT stream_message_seq FROM nodes WHERE node_id = ?").get(this.nodeId)).stream_message_seq)}if(s.type==="message_update"&&s.message.role==="assistant"&&this.openAssistant){const r=s.message.content;if(!Array.isArray(r))return;const o=r.at(-1);if(o&&typeof o=="object"&&"type"in o&&(o.type==="toolCall"?this.block={kind:"tool_call",name:"name"in o&&typeof o.name=="string"?o.name:""}:(o.type==="text"||o.type==="thinking")&&(this.block={kind:o.type})),!await this.writeStream(null,this.block,!0)){this.close();return}const a=r.filter(u=>u?.type==="thinking").map(u=>u.thinking).join(""),_=r.filter(u=>u?.type==="text").map(u=>u.text).join(""),A=a.startsWith(this.thinking)?a.slice(this.thinking.length):a,I=_.startsWith(this.output)?_.slice(this.output.length):_;this.thinking=a,this.output=_,this.pendingThinking+=A,this.pendingOutput+=I,(this.pendingThinking||this.pendingOutput)&&!this.flushTimer&&(this.flushTimer=setTimeout(()=>{this.flushDeltas().catch(u=>c({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:u}))},B),this.flushTimer.unref())}s.type==="message_end"&&s.message.role==="assistant"&&(await this.flushDeltas(),this.openAssistant=!1,this.block=null,await this.writeStream(null,null))}},"handleFrame");t.on("frame",s=>{this.frames=this.frames.then(()=>i(s)).catch(r=>{this.close(),c({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:r})})}),t.on("error",()=>{}),t.on("close",()=>{this.client===t&&(this.client=void 0,this.discardDeltas(),this.openAssistant=!1,this.block=null,this.released=this.released.then(async()=>{await this.writeStream(null,null)}).catch(()=>{}))}),t.connect()}async flushDeltas(){this.flushTimer&&clearTimeout(this.flushTimer),this.flushTimer=void 0;const e=this.pendingThinking,t=this.pendingOutput;if(this.pendingThinking="",this.pendingOutput="",!this.openAssistant||!e&&!t)return;(await f(this.nodeId,this.owner,async()=>{if(await(await d()).prepare("SELECT 1 FROM run_events WHERE run_id = ? AND node_id = ? AND sequence_number >= ? AND type IN ('node.output_text.done', 'node.thinking.done', 'usage') LIMIT 1").get(this.runId,this.nodeId,this.assistantStartSequence)){this.openAssistant=!1;return}e&&await w(this.runId,this.nodeId,"node.thinking.delta",{delta:e}),t&&await w(this.runId,this.nodeId,"node.output_text.delta",{delta:t})})).held||this.close()}discardDeltas(){this.flushTimer&&clearTimeout(this.flushTimer),this.flushTimer=void 0,this.pendingThinking="",this.pendingOutput=""}close(){if(this.closed)return;this.closed=!0,this.discardDeltas(),clearInterval(this.timer),p.delete(this.nodeId),this.released=this.released.then(async()=>{await this.writeStream(null,null),await O(h,this.nodeId,this.owner)}).then(()=>{}).catch(t=>{c({level:"error",event:"daemon.runs.observer_release_failed",node_id:this.nodeId,error:t})});const e=this.client;this.client=void 0,e?.close()}}async function V(n){const e=await M(n);if(e===null)return new y(n,{recordsViewerFaults:!1});const t=await x(e.runner).viewerAddress(e);return t.kind==="unix"?new y(n,{recordsViewerFaults:!1}):new W(n,()=>H(t.url,t.headers))}l(V,"viewTransport");async function de(n){const e=await(await d()).prepare("SELECT stream_message_seq FROM node_rows WHERE node_id = ? AND pi_pid IS NOT NULL").get(n);return e?.stream_message_seq==null?null:Number(e.stream_message_seq)}l(de,"openAssistantMessageStartSequence");async function ue(n){const e=await(await d()).prepare("SELECT stream_message_seq, stream_block FROM node_rows WHERE node_id = ? AND pi_pid IS NOT NULL").get(n);return e?.stream_message_seq==null||e.stream_block===null?null:JSON.parse(e.stream_block)}l(ue,"openAssistantBlock");async function ce(){const n=[...p.values()];for(const e of n)e.close();await Promise.all(n.map(e=>e.settlePoll())),await Promise.all([...m.values()].map(e=>e.catch(()=>{})))}l(ce,"closeRunObserversForTests");async function D(n){return await(await d()).prepare("SELECT 1 FROM nodes WHERE node_id = ? AND run_id = ? AND event_cursor IS NOT NULL").get(n,n)!==void 0}l(D,"runRootExists");const m=new Map;async function T(n,e){const t=m.get(n);if(t)return t;if(p.has(n))return;const i=Y(n,e).finally(()=>m.delete(n));return m.set(n,i),i}l(T,"ensureRunObserver");async function Y(n,e){if(!await D(e))return;const t=v();if(await b(h,n,t,g)!==void 0)try{if(n!==e){const i=await R(n);if(!await(await d()).prepare("SELECT 1 FROM run_events WHERE run_id = ? AND node_id = ? AND type = 'child.started' LIMIT 1").get(e,n)&&i&&Date.now()-Date.parse(i.created)<720*60*6e4&&!(await f(n,t,()=>w(e,n,"child.started",{child_node_id:n,kind:i.kind}))).held)return}await S.start(n,e,t)}catch(i){throw await O(h,n,t).catch(()=>{}),i}}l(Y,"startRunObserver");async function he(){const n=new Date(Date.now()-2592e6).toISOString(),e=await(await d()).prepare(`SELECT n.node_id, n.run_id, n.pi_pid, COALESCE(n.outcome_revision, 1) AS revision, e.body_json
1
+ var b=Object.defineProperty;var l=(t,e)=>b(t,"name",{value:e,configurable:!0});import{randomUUID as y}from"node:crypto";import{openDb as d,withCanvasWrite as C}from"../../core/canvas/db.js";import{claimReconcilerLease as F,releaseReconcilerLease as O,renewReconcilerLease as M}from"../../core/canvas/reconciler-leases.js";import{getNode as R}from"../../core/canvas/canvas.js";import{BrokerClient as x,SocketTransport as D}from"../../core/broker-client/client.js";import{liveExecutionForNode as N}from"../../core/canvas/executions.js";import{runnerForExecution as H}from"../../core/runtime/runner/index.js";import{connectWssView as W}from"../../core/runtime/view-socket.js";import{ViewLinkTransport as q}from"./view-transport.js";import{emitRunEvent as g,emitRunSettlement as A,emitRunStatus as U}from"../../core/runs/events.js";import{cancelOpenQuestions as j}from"../../core/runs/questions.js";import{emitEvent as c}from"../../core/events/emit.js";import{toRunOutcomeDTO as J}from"../api/map.js";import{readOutputResult as P}from"../../core/runtime/output-result.js";const V=JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),p=new Map,h="run-observer",E=1e4,B=250;async function w(t,e,i){return await C(async s=>await s.prepare("SELECT 1 AS held FROM reconciler_leases WHERE lane = ? AND key = ? AND owner = ? AND expires_at > ? FOR SHARE").get(h,t,e,new Date().toISOString())===void 0?{held:!1}:{held:!0,value:await i()})}l(w,"whileHoldingLease");class v{static{l(this,"RunObserver")}nodeId;runId;owner;client;timer;status;piPid=null;outcomeRevision;openAssistant=!1;assistantStartSequence=0;block=null;thinking="";output="";pendingThinking="";pendingOutput="";flushTimer;closed=!1;lastRenewAt=Date.now();released=Promise.resolve();polling;frames=Promise.resolve();constructor(e,i,s,n,r){this.nodeId=e,this.runId=i,this.owner=s,this.status=n,this.outcomeRevision=r,this.timer=setInterval(()=>{this.pollOrClose()},250),this.timer.unref(),p.set(e,this)}static async start(e,i,s){const n=await d(),r=await n.prepare("SELECT body_json FROM run_events WHERE node_id = ? AND type = ? ORDER BY sequence_number DESC LIMIT 1").get(e,e===i?"node.settled":"child.settled"),a=await n.prepare("SELECT body_json FROM run_events WHERE node_id = ? AND type = 'node.status.changed' ORDER BY sequence_number DESC LIMIT 1").get(e),o=new v(e,i,s,a?JSON.parse(a.body_json).status:void 0,r?JSON.parse(r.body_json).outcome.revision:void 0);return await o.writeStream(null,null),await o.poll(),o}async pollOrClose(){try{await this.poll()}catch(e){this.close(),c({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:e})}}poll(){return this.polling===void 0&&(this.polling=this.pollOnce().finally(()=>{this.polling=void 0})),this.polling}async holdLease(){return Date.now()-this.lastRenewAt<E/3?!0:await M(h,this.nodeId,this.owner,E)?(this.lastRenewAt=Date.now(),!0):!1}async writeStream(e,i,s=!1){return(await(await d()).prepare(`UPDATE nodes SET ${s?"":`stream_message_seq = ${e===null?"NULL":"(SELECT event_cursor FROM nodes WHERE node_id = ?)"},`} stream_block = ?
2
+ WHERE node_id = ? AND EXISTS (SELECT 1 FROM reconciler_leases WHERE lane = ? AND key = ? AND owner = ? AND expires_at > ?)`).run(...e===null||s?[]:[this.runId],i===null?null:JSON.stringify(i),this.nodeId,h,this.nodeId,this.owner,new Date().toISOString())).changes===1}async pollOnce(){if(this.closed)return;if(!await this.holdLease()){this.close();return}const e=await R(this.nodeId);if(!e||e.run_id!==this.runId||!await T(this.runId)){this.close();return}this.piPid=e.pi_pid??null;const i=e.pi_pid===null?"dormant":e.status;if(this.status!==i){if(!(await w(this.nodeId,this.owner,()=>U(this.runId,this.nodeId,i,e.intent??null))).held){this.close();return}this.status=i}const s=await J(e);if(s&&s.revision!==this.outcomeRevision&&Date.now()-Date.parse(s.settled_at)<720*60*6e4){if(this.nodeId===this.runId){const a=await(await d()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ? ORDER BY created DESC").all(this.runId,this.runId);for(const o of a)await I(o.node_id,this.runId),await p.get(o.node_id)?.poll()}this.outcomeRevision=s.revision;const n=this.nodeId===this.runId&&s.kind==="result"?await P(this.nodeId):void 0;if(!(await w(this.nodeId,this.owner,()=>this.nodeId===this.runId?A(this.runId,this.nodeId,"node.settled",{outcome:s,usage:JSON.parse(e.usage_json??V),...n===void 0?{}:{result:n}}):A(this.runId,this.nodeId,"child.settled",{child_node_id:this.nodeId,outcome:s}))).held){this.close();return}if(e.grantee&&e.grantee!=="app:terminal"){const a=this.nodeId===this.runId?{run_id:this.runId}:{node_id:this.nodeId};j(a,this.nodeId===this.runId?"the run settled":"the asking node was closed").catch(o=>c({level:"error",event:"daemon.runs.question_cancel_failed",node_id:this.nodeId,error:o}))}}e.pi_pid===null?this.close():await this.connect()}async settlePoll(){await this.polling?.catch(()=>{}),await this.frames,await this.released}async viewAdmitted(){const e=await N(this.nodeId);return!(e?.runner==="blaxel"&&e.idleAt!==null)}async dropView(){const e=this.client;e&&(this.client=void 0,await this.frames,await this.flushDeltas(),this.discardDeltas(),this.openAssistant=!1,this.block=null,await this.writeStream(null,null),e.close())}async connect(){if(this.closed)return;if(!await this.viewAdmitted()){await this.dropView();return}if(this.client)return;const e=await Y(this.nodeId),i=await this.viewAdmitted();if(this.client||this.closed||!i){e.destroy();return}const s=new x(this.nodeId,{transport:e});this.client=s,s.on("connect",()=>s.send({type:"hello",role:"observer",client_id:y(),attends:!1}));const n=l(async r=>{if(!(this.closed||this.client!==s)){if(r.type==="message_start"&&r.message.role==="assistant"){if(this.discardDeltas(),this.openAssistant=!0,this.block=null,this.thinking="",this.output="",!await this.writeStream("run-cursor",null)){this.close();return}this.assistantStartSequence=Number((await(await d()).prepare("SELECT stream_message_seq FROM nodes WHERE node_id = ?").get(this.nodeId)).stream_message_seq)}if(r.type==="message_update"&&r.message.role==="assistant"&&this.openAssistant){const a=r.message.content;if(!Array.isArray(a))return;const o=a.at(-1);if(o&&typeof o=="object"&&"type"in o&&(o.type==="toolCall"?this.block={kind:"tool_call",name:"name"in o&&typeof o.name=="string"?o.name:""}:(o.type==="text"||o.type==="thinking")&&(this.block={kind:o.type})),!await this.writeStream(null,this.block,!0)){this.close();return}const f=a.filter(u=>u?.type==="thinking").map(u=>u.thinking).join(""),_=a.filter(u=>u?.type==="text").map(u=>u.text).join(""),k=f.startsWith(this.thinking)?f.slice(this.thinking.length):f,L=_.startsWith(this.output)?_.slice(this.output.length):_;this.thinking=f,this.output=_,this.pendingThinking+=k,this.pendingOutput+=L,(this.pendingThinking||this.pendingOutput)&&!this.flushTimer&&(this.flushTimer=setTimeout(()=>{this.flushDeltas().catch(u=>c({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:u}))},B),this.flushTimer.unref())}r.type==="message_end"&&r.message.role==="assistant"&&(await this.flushDeltas(),this.openAssistant=!1,this.block=null,await this.writeStream(null,null))}},"handleFrame");s.on("frame",r=>{this.frames=this.frames.then(()=>n(r)).catch(a=>{this.close(),c({level:"error",event:"daemon.runs.observer_failed",node_id:this.nodeId,error:a})})}),s.on("error",()=>{}),s.on("close",()=>{this.client===s&&(this.client=void 0,this.discardDeltas(),this.openAssistant=!1,this.block=null,this.released=this.released.then(async()=>{await this.writeStream(null,null)}).catch(()=>{}))}),s.connect()}async flushDeltas(){this.flushTimer&&clearTimeout(this.flushTimer),this.flushTimer=void 0;const e=this.pendingThinking,i=this.pendingOutput;if(this.pendingThinking="",this.pendingOutput="",!this.openAssistant||!e&&!i)return;(await w(this.nodeId,this.owner,async()=>{if(await(await d()).prepare("SELECT 1 FROM run_events WHERE run_id = ? AND node_id = ? AND sequence_number >= ? AND type IN ('node.output_text.done', 'node.thinking.done', 'usage') LIMIT 1").get(this.runId,this.nodeId,this.assistantStartSequence)){this.openAssistant=!1;return}e&&await g(this.runId,this.nodeId,"node.thinking.delta",{delta:e}),i&&await g(this.runId,this.nodeId,"node.output_text.delta",{delta:i})})).held||this.close()}discardDeltas(){this.flushTimer&&clearTimeout(this.flushTimer),this.flushTimer=void 0,this.pendingThinking="",this.pendingOutput=""}close(){if(this.closed)return;this.closed=!0,this.discardDeltas(),clearInterval(this.timer),p.delete(this.nodeId),this.released=this.released.then(async()=>{await this.writeStream(null,null),await O(h,this.nodeId,this.owner)}).then(()=>{}).catch(i=>{c({level:"error",event:"daemon.runs.observer_release_failed",node_id:this.nodeId,error:i})});const e=this.client;this.client=void 0,e?.close()}}async function Y(t){const e=await N(t);if(e===null)return new D(t,{recordsViewerFaults:!1});const i=await H(e.runner).viewerAddress(e);return i.kind==="unix"?new D(t,{recordsViewerFaults:!1}):new q(t,()=>W(i.url,i.headers))}l(Y,"viewTransport");async function ue(t){const e=await(await d()).prepare("SELECT stream_message_seq FROM node_rows WHERE node_id = ? AND pi_pid IS NOT NULL").get(t);return e?.stream_message_seq==null?null:Number(e.stream_message_seq)}l(ue,"openAssistantMessageStartSequence");async function ce(t){const e=await(await d()).prepare("SELECT stream_message_seq, stream_block FROM node_rows WHERE node_id = ? AND pi_pid IS NOT NULL").get(t);return e?.stream_message_seq==null||e.stream_block===null?null:JSON.parse(e.stream_block)}l(ce,"openAssistantBlock");async function he(){const t=[...p.values()];for(const e of t)e.close();await Promise.all(t.map(e=>e.settlePoll())),await Promise.all([...m.values()].map(e=>e.catch(()=>{})))}l(he,"closeRunObserversForTests");async function T(t){return await(await d()).prepare("SELECT 1 FROM nodes WHERE node_id = ? AND run_id = ? AND event_cursor IS NOT NULL").get(t,t)!==void 0}l(T,"runRootExists");const m=new Map;async function I(t,e){const i=m.get(t);if(i)return i;if(p.has(t))return;const s=$(t,e).finally(()=>m.delete(t));return m.set(t,s),s}l(I,"ensureRunObserver");async function $(t,e){if(!await T(e))return;const i=y();if(await F(h,t,i,E)!==void 0)try{if(t!==e){const s=await R(t);if(!await(await d()).prepare("SELECT 1 FROM run_events WHERE run_id = ? AND node_id = ? AND type = 'child.started' LIMIT 1").get(e,t)&&s&&Date.now()-Date.parse(s.created)<720*60*6e4&&!(await w(t,i,()=>g(e,t,"child.started",{child_node_id:t,kind:s.kind}))).held)return}await v.start(t,e,i)}catch(s){throw await O(h,t,i).catch(()=>{}),s}}l($,"startRunObserver");async function pe(){const t=new Date(Date.now()-2592e6).toISOString(),e=await(await d()).prepare(`SELECT n.node_id, n.run_id, n.pi_pid, COALESCE(n.outcome_revision, 1) AS revision, e.body_json
3
3
  FROM node_rows n
4
4
  LEFT JOIN run_events e ON n.pi_pid IS NULL AND e.run_id = n.run_id AND e.node_id = n.node_id
5
5
  AND e.type = CASE WHEN n.node_id = n.run_id THEN 'node.settled' ELSE 'child.settled' END
6
- WHERE n.run_id IS NOT NULL AND (n.pi_pid IS NOT NULL OR (n.outcome_at IS NOT NULL AND n.outcome_at > ? AND n.outcome_reason != 'parked'))`).all(n),t=new Map;for(const s of e){const r=t.get(s.node_id)??{node_id:s.node_id,run_id:s.run_id,reported:!1};s.body_json!==null&&JSON.parse(s.body_json).outcome?.revision===s.revision&&(r.reported=!0),t.set(s.node_id,r)}const i=[...t.values()].filter(s=>!s.reported);for(const s of i)try{await T(s.node_id,s.run_id),E.delete(s.node_id)}catch(r){$(s.node_id,r)}}l(he,"pollRunObservers");const E=new Map;function $(n,e){const t=e instanceof Error?e.message:String(e);E.get(n)!==t&&(E.set(n,t),c({level:"error",event:"daemon.runs.observer_failed",node_id:n,error:e}))}l($,"reportObserverFailure");export{ce as closeRunObserversForTests,T as ensureRunObserver,ue as openAssistantBlock,de as openAssistantMessageStartSequence,he as pollRunObservers};
6
+ WHERE n.run_id IS NOT NULL AND (n.pi_pid IS NOT NULL OR (n.outcome_at IS NOT NULL AND n.outcome_at > ? AND n.outcome_reason != 'parked'))`).all(t),i=new Map;for(const n of e){const r=i.get(n.node_id)??{node_id:n.node_id,run_id:n.run_id,reported:!1};n.body_json!==null&&JSON.parse(n.body_json).outcome?.revision===n.revision&&(r.reported=!0),i.set(n.node_id,r)}const s=[...i.values()].filter(n=>!n.reported);for(const n of s)try{await I(n.node_id,n.run_id),S.delete(n.node_id)}catch(r){G(n.node_id,r)}}l(pe,"pollRunObservers");const S=new Map;function G(t,e){const i=e instanceof Error?e.message:String(e);S.get(t)!==i&&(S.set(t,i),c({level:"error",event:"daemon.runs.observer_failed",node_id:t,error:e}))}l(G,"reportObserverFailure");export{he as closeRunObserversForTests,I as ensureRunObserver,ce as openAssistantBlock,ue as openAssistantMessageStartSequence,pe as pollRunObservers};